From dfef859ede0257b2002be1a1b51556aa9a18d0f5 Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Sat, 25 Apr 2026 18:20:07 +0200 Subject: [PATCH] basic party analysis --- api/biome.json | 35 ++ api/package.json | 48 +-- api/src/db/schema.ts | 2 +- api/src/test/factories.ts | 372 ++++++++++++++++++ api/src/test/setup.ts | 67 ++++ .../__tests__/party-analysis.test.ts | 323 +++++++++++++++ api/tsconfig.json | 9 +- api/vitest.config.ts | 14 + bun.lock | 137 +++++-- 9 files changed, 960 insertions(+), 47 deletions(-) create mode 100644 api/biome.json create mode 100644 api/src/test/factories.ts create mode 100644 api/src/test/setup.ts create mode 100644 api/src/workflows/__tests__/party-analysis.test.ts create mode 100644 api/vitest.config.ts diff --git a/api/biome.json b/api/biome.json new file mode 100644 index 0000000..6e37aea --- /dev/null +++ b/api/biome.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.11/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "includes": [ + "**/src/**/*", + "**/.vscode/**/*", + "**/index.html", + "**/vite.config.ts", + "!**/src/routeTree.gen.ts", + "!**/src/styles.css" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/api/package.json b/api/package.json index 497c01a..f262fa6 100644 --- a/api/package.json +++ b/api/package.json @@ -1,24 +1,28 @@ { - "name": "api", - "module": "index.ts", - "type": "module", - "private": true, - "main": "src/index.ts", - "scripts": { - "dev": "bun run --watch src/index.ts", - "typecheck": "tsc --noEmit" - }, - "devDependencies": { - "@types/bun": "latest" - }, - "peerDependencies": { - "typescript": "^5" - }, - "dependencies": { - "@dbos-inc/dbos-sdk": "^4.14.6", - "@spotify/web-api-ts-sdk": "^1.2.0", - "@statsfm/statsfm.js": "github.com:statsfm/statsfm.js", - "better-auth": "^1.6.5", - "elysia": "catalog:" - } + "name": "api", + "module": "index.ts", + "type": "module", + "private": true, + "main": "src/index.ts", + "scripts": { + "dev": "bun run --watch src/index.ts", + "typecheck": "tsc --noEmit", + "test": "DATABASE_URL=postgres://postgres:postgres@localhost/postgres vitest run" + }, + "devDependencies": { + "@types/bun": "latest", + "tsx": "^4.19.2", + "vitest": "^3.0.5" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "@dbos-inc/dbos-sdk": "^4.14.6", + "@spotify/web-api-ts-sdk": "^1.2.0", + "@statsfm/statsfm.js": "github.com:statsfm/statsfm.js", + "better-auth": "^1.6.5", + "elysia": "catalog:", + "vite": "^8.0.10" + } } diff --git a/api/src/db/schema.ts b/api/src/db/schema.ts index 9c603a7..1446d59 100644 --- a/api/src/db/schema.ts +++ b/api/src/db/schema.ts @@ -46,7 +46,7 @@ export const partyMember = pgTable( partyId: uuid() .references(() => party.id) .notNull(), - userId: uuid() + userId: text() .references(() => user.id) .notNull(), joinedAt: timestamp().defaultNow().notNull(), diff --git a/api/src/test/factories.ts b/api/src/test/factories.ts new file mode 100644 index 0000000..77ec59a --- /dev/null +++ b/api/src/test/factories.ts @@ -0,0 +1,372 @@ +import { randomUUID } from "node:crypto"; +import { db } from "../db"; +import * as schema from "../db/schema"; + +export interface TestUser { + id: string; + name: string; + email: string; +} + +export interface TestParty { + partyId: string; + hostId: string; +} + +export interface TestGenre { + id: string; + name: string; +} + +export interface TestArtist { + id: string; + name: string; + genreIds: string[]; +} + +export interface TestAlbum { + id: string; + name: string; + artistIds: string[]; + genreIds: string[]; +} + +export interface TestTrack { + id: string; + name: string; + albumId: string; + artistIds: string[]; +} + +export async function createGenre(name: string): Promise { + const id = randomUUID(); + await db.insert(schema.genre).values({ id, name }); + return { id, name }; +} + +export async function createArtist( + name: string, + genreIds: string[] = [], +): Promise { + const id = randomUUID(); + await db.insert(schema.artist).values({ + id, + platform_id: `test-artist-${id}`, + platform: "spotify", + name, + popularity: 50, + }); + + if (genreIds.length > 0) { + const relations = genreIds.map((genreId) => ({ + artistId: id, + genreId, + })); + await db.insert(schema.artistGenre).values(relations); + } + + return { id, name, genreIds }; +} + +export async function createAlbum( + name: string, + artistIds: string[] = [], + genreIds: string[] = [], +): Promise { + const id = randomUUID(); + await db.insert(schema.album).values({ + id, + type: "album", + name, + platform: "spotify", + platform_id: `test-album-${id}`, + popularity: 70, + }); + + if (artistIds.length > 0) { + const relations = artistIds.map((artistId) => ({ + albumId: id, + artistId, + })); + await db.insert(schema.albumArtist).values(relations); + } + + if (genreIds.length > 0) { + const relations = genreIds.map((genreId) => ({ + albumId: id, + genreId, + })); + await db.insert(schema.albumGenre).values(relations); + } + + return { id, name, artistIds, genreIds }; +} + +export async function createTrack( + name: string, + albumId: string, + artistIds: string[] = [], +): Promise { + const id = randomUUID(); + await db.insert(schema.track).values({ + id, + albumId, + name, + platform: "spotify", + platform_id: `test-track-${id}`, + popularity: 80, + duration: 180000, + explicit: false, + disc_number: 1, + track_number: 1, + }); + + if (artistIds.length > 0) { + const relations = artistIds.map((artistId) => ({ + trackId: id, + artistId, + })); + await db.insert(schema.trackArtist).values(relations); + } + + return { id, name, albumId, artistIds }; +} + +export async function createUser(name: string): Promise { + const id = randomUUID(); + const emailValue = `test-${id}@example.com`; + await db.insert(schema.user).values({ + id, + name, + email: emailValue, + emailVerified: true, + }); + return { id, name, email: emailValue }; +} + +export async function createParty(hostId: string): Promise { + const partyId = randomUUID(); + await db.insert(schema.party).values({ + id: partyId, + hostId, + status: "created", + }); + return { partyId, hostId }; +} + +export async function joinParty( + partyId: string, + userId: string, +): Promise { + await db.insert(schema.partyMember).values({ + partyId, + userId, + }); +} + +export async function addTopTrack( + userId: string, + trackId: string, + position: number, + timeline: "short_term" | "medium_term" | "long_term" = "medium_term", +): Promise { + await db.insert(schema.topTrack).values({ + userId, + trackId, + position, + timeline, + }); +} + +export async function addTopArtist( + userId: string, + artistId: string, + position: number, + timeline: "short_term" | "medium_term" | "long_term" = "medium_term", +): Promise { + await db.insert(schema.topArtist).values({ + userId, + artistId, + position, + timeline, + }); +} + +export async function addSavedTrack( + userId: string, + trackId: string, +): Promise { + await db.insert(schema.savedTrack).values({ + userId, + trackId, + }); +} + +export async function addPlaybackHistory( + userId: string, + trackId: string, + playedAt: Date = new Date(), +): Promise { + await db.insert(schema.playbackHistory).values({ + userId, + trackId, + played_at: playedAt, + }); +} + +export async function addFollowedArtist( + userId: string, + artistId: string, +): Promise { + await db.insert(schema.followedArtist).values({ + userId, + artistId, + }); +} + +export async function addSavedAlbum( + userId: string, + albumId: string, +): Promise { + await db.insert(schema.savedAlbum).values({ + userId, + albumId, + }); +} + +export async function seedPartyWithTwoSimilarUsers(): Promise<{ + partyId: string; + hostId: string; + userIdA: string; + userIdB: string; + sharedTrackId: string; + sharedArtistId: string; + sharedGenreId: string; + uniqueTrackIdA?: string; + uniqueTrackIdB?: string; +}> { + // Create shared genre with unique name + const sharedGenreName = `shared-rock-${randomUUID()}`; + const genre = await createGenre(sharedGenreName); + const genreId = genre.id; + + // Create shared artist with genre + const sharedArtist = await createArtist(`shared-artist-${randomUUID()}`, [ + genreId, + ]); + const sharedArtistId = sharedArtist.id; + + // Create shared album + const sharedAlbum = await createAlbum( + `shared-album-${randomUUID()}`, + [sharedArtistId], + [genreId], + ); + + // Create shared track + const sharedTrack = await createTrack( + `shared-track-${randomUUID()}`, + sharedAlbum.id, + [sharedArtistId], + ); + const sharedTrackId = sharedTrack.id; + + // Create users + const userA = await createUser("User A"); + const userB = await createUser("User B"); + + // Create party with user A as host + const party = await createParty(userA.id); + + // Both users join the party + await joinParty(party.partyId, userA.id); + await joinParty(party.partyId, userB.id); + + // Give both users the shared track at position 1 + await addTopTrack(userA.id, sharedTrackId, 1); + await addTopTrack(userB.id, sharedTrackId, 1); + + // Give both users the shared artist at position 1 + await addTopArtist(userA.id, sharedArtistId, 1); + await addTopArtist(userB.id, sharedArtistId, 1); + + // Add some saved tracks for user A + await addSavedTrack(userA.id, sharedTrackId); + + // Add playback history (recent) + await addPlaybackHistory(userA.id, sharedTrackId, new Date()); + await addPlaybackHistory(userB.id, sharedTrackId, new Date()); + + return { + partyId: party.partyId, + hostId: party.hostId, + userIdA: userA.id, + userIdB: userB.id, + sharedTrackId, + sharedArtistId, + sharedGenreId: genreId, + }; +} + +export async function seedPartyWithThreeDiverseUsers(): Promise<{ + partyId: string; + userIds: string[]; + genres: TestGenre[]; + artists: TestArtist[]; + tracks: TestTrack[]; +}> { + // Create 3 distinct genres with unique names + const genres = await Promise.all([ + createGenre(`jazz-${randomUUID()}`), + createGenre(`electronic-${randomUUID()}`), + createGenre(`classical-${randomUUID()}`), + ]); + + const userIds: string[] = []; + const artists: TestArtist[] = []; + const tracks: TestTrack[] = []; + + // Create one user per genre with no overlap + for (let i = 0; i < 3; i++) { + const user = await createUser(`Diverse User ${i}`); + userIds.push(user.id); + + const genre = genres[i]!; + const artist = await createArtist(`artist-${i}-${randomUUID()}`, [ + genre.id, + ]); + artists.push(artist); + + const album = await createAlbum( + `album-${i}-${randomUUID()}`, + [artist.id], + [genre.id], + ); + const track = await createTrack(`track-${i}-${randomUUID()}`, album.id, [ + artist.id, + ]); + tracks.push(track); + } + + // Create party with first user as host + const hostId = userIds[0]!; + const party = await createParty(hostId); + + for (const userId of userIds) { + await joinParty(party.partyId, userId); + } + + // Give each user their unique track + for (let i = 0; i < 3; i++) { + await addTopTrack(userIds[i]!, tracks[i]?.id, 1); + await addTopArtist(userIds[i]!, artists[i]?.id, 1); + } + + return { + partyId: party.partyId, + userIds, + genres, + artists, + tracks, + }; +} diff --git a/api/src/test/setup.ts b/api/src/test/setup.ts new file mode 100644 index 0000000..007809d --- /dev/null +++ b/api/src/test/setup.ts @@ -0,0 +1,67 @@ +import { execSync } from "node:child_process"; +import { Pool } from "pg"; +import * as schema from "../db/schema"; + +const url = process.env.DATABASE_URL; + +if (!url) { + throw new Error("DATABASE_URL is required for tests"); +} + +let pool: Pool | null = null; + +const getPool = (): Pool => { + if (!pool) { + pool = new Pool({ connectionString: url! }); + } + return pool; +}; + +const getTableNames = (): string[] => { + const tables: string[] = []; + for (const [, value] of Object.entries(schema)) { + if ( + typeof value === "object" && + value !== null && + "tableName" in value && + typeof (value as { tableName: unknown }).tableName === "string" + ) { + tables.push((value as { tableName: string }).tableName); + } + } + return tables; +}; + +const _runMigrations = async () => { + try { + execSync("bun x drizzle-kit push", { + stdio: "pipe", + env: { ...process.env }, + cwd: `${process.cwd()}/../..`, + }); + } catch { + // migrations may have already been applied + } +}; + +const truncateTables = async () => { + const pool = getPool(); + const tables = getTableNames(); + if (tables.length === 0) return; + const deleteSQL = `DELETE FROM ${tables.map((t) => `"${t}"`).join(", ")} CASCADE;`; + try { + await pool.query(deleteSQL); + } catch { + // some tables may not exist yet, ignore + } +}; + +beforeEach(async () => { + await truncateTables(); +}); + +afterAll(async () => { + if (pool) { + await pool.end(); + } +}); diff --git a/api/src/workflows/__tests__/party-analysis.test.ts b/api/src/workflows/__tests__/party-analysis.test.ts new file mode 100644 index 0000000..129ea20 --- /dev/null +++ b/api/src/workflows/__tests__/party-analysis.test.ts @@ -0,0 +1,323 @@ +import { DBOS } from "@dbos-inc/dbos-sdk"; +import { describe, expect, it } from "vitest"; +import { + addFollowedArtist, + addPlaybackHistory, + addSavedTrack, + addTopArtist, + addTopTrack, + createAlbum, + createArtist, + createParty, + createTrack, + createUser, + joinParty, + seedPartyWithThreeDiverseUsers, + seedPartyWithTwoSimilarUsers, +} from "../../test/factories"; +import { partyAnalysisWorkflow } from "../party-analysis"; +import "../../dbos"; + +await DBOS.launch(); + +describe("PartyAnalysisWorkflow", () => { + describe("analyzeParty - basic behavior", () => { + it("returns empty results for party with fewer than 2 members", async () => { + const user = await createUser("Solo User"); + const party = await createParty(user.id); + await joinParty(party.partyId, user.id); + + const result = await partyAnalysisWorkflow.analyzeParty(party.partyId); + + expect(result.storyClusters).toHaveLength(0); + expect(result.pairwise).toHaveLength(0); + expect(result.groupSummary.totalMembers).toBe(1); + expect(result.groupSummary.mostDiverseMember).toBeNull(); + expect(result.groupSummary.mostAlignedPair).toBeNull(); + expect(result.memberProfiles).toHaveLength(0); + }); + + it("returns empty results for party with 0 members", async () => { + const user = await createUser("Host Only"); + const party = await createParty(user.id); + // Don't add any members + + const result = await partyAnalysisWorkflow.analyzeParty(party.partyId); + + expect(result.storyClusters).toHaveLength(0); + expect(result.groupSummary.totalMembers).toBe(0); + }); + + it("returns empty results for party with 1 member and no data", async () => { + const user = await createUser("Empty Member"); + const party = await createParty(user.id); + await joinParty(party.partyId, user.id); + + const result = await partyAnalysisWorkflow.analyzeParty(party.partyId); + + expect(result.storyClusters).toHaveLength(0); + expect(result.groupSummary.totalMembers).toBe(1); + expect(result.memberProfiles).toHaveLength(0); + }); + }); + + describe("analyzeParty - two similar users", () => { + it("correctly identifies shared tracks, artists, and genres", async () => { + const { partyId, userIdA, userIdB, sharedTrackId, sharedArtistId } = + await seedPartyWithTwoSimilarUsers(); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + expect(result.storyClusters).toHaveLength(1); + const cluster = result.storyClusters[0]!; + expect(cluster.memberCount).toBe(2); + + // The shared track should appear in the cluster + const sharedClusterTrack = cluster.tracks.find( + (t) => t.id === sharedTrackId, + ); + expect(sharedClusterTrack).toBeDefined(); + if (sharedClusterTrack) { + expect(sharedClusterTrack.memberCount).toBe(2); + } + + // The shared artist should appear in the cluster + const sharedClusterArtist = cluster.artists.find( + (a) => a.id === sharedArtistId, + ); + expect(sharedClusterArtist).toBeDefined(); + if (sharedClusterArtist) { + expect(sharedClusterArtist.memberCount).toBe(2); + } + + // Should have exactly 1 pairwise comparison + expect(result.pairwise).toHaveLength(1); + const comparison = result.pairwise[0]!; + expect(comparison.userIdA).toBe(userIdA); + expect(comparison.userIdB).toBe(userIdB); + expect(comparison.sharedTracks).toBeGreaterThan(0); + expect(comparison.sharedArtists).toBeGreaterThan(0); + expect(comparison.similarity).toBeGreaterThan(0); + expect(comparison.similarity).toBeLessThan(1); + + // Should have member profiles + expect(result.memberProfiles).toHaveLength(2); + const profileA = result.memberProfiles.find((p) => p.userId === userIdA); + const profileB = result.memberProfiles.find((p) => p.userId === userIdB); + expect(profileA).toBeDefined(); + expect(profileB).toBeDefined(); + if (profileA) { + expect(profileA.trackCount).toBeGreaterThan(0); + } + if (profileB) { + expect(profileB.trackCount).toBeGreaterThan(0); + } + }); + + it("correctly identifies group summary", async () => { + const { partyId, userIdA } = await seedPartyWithTwoSimilarUsers(); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + expect(result.groupSummary.totalMembers).toBe(2); + expect(result.groupSummary.mostAlignedPair).toBeDefined(); + if (result.groupSummary.mostAlignedPair) { + expect(result.groupSummary.mostAlignedPair.userIdA).toBe(userIdA); + } + expect(result.groupSummary.mostSharedGenres).toHaveLength(1); + }); + }); + + describe("analyzeParty - three diverse users", () => { + it("does not find shared tracks across all members", async () => { + const { partyId } = await seedPartyWithThreeDiverseUsers(); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + expect(result.storyClusters).toHaveLength(3); + expect(result.pairwise).toHaveLength(3); // C(3,2) = 3 pairs + + // No track should be shared by all 3 members + const allMembersCluster = result.storyClusters.find( + (c) => c.memberCount === 3, + ); + expect(allMembersCluster).toBeUndefined(); + }); + + it("identifies pairwise comparisons for all member pairs", async () => { + const { partyId } = await seedPartyWithThreeDiverseUsers(); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + expect(result.pairwise).toHaveLength(3); + result.pairwise.forEach((comparison) => { + expect(comparison.sharedTracks).toBe(0); + expect(comparison.similarity).toBe(0); + }); + }); + + it("correctly identifies genre diversity for each member", async () => { + const { partyId } = await seedPartyWithThreeDiverseUsers(); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + expect(result.memberProfiles).toHaveLength(3); + expect(result.groupSummary.mostDiverseMember).toBeDefined(); + + if (result.groupSummary.mostDiverseMember) { + expect( + result.groupSummary.mostDiverseMember.genreEntropy, + ).toBeGreaterThan(0); + } + }); + }); + + describe("analyzeParty - scoring sources", () => { + it("includes playback history in scoring", async () => { + const { partyId, userIdA, userIdB } = + await seedPartyWithTwoSimilarUsers(); + + // Add old playback history (more than a week ago) + const oldDate = new Date(); + oldDate.setDate(oldDate.getDate() - 8); + const { id: trackC } = await createTrack( + "Old Track C", + (await createAlbum("Old Album C")).id, + ); + await addTopTrack(userIdA, trackC, 3); + await addTopTrack(userIdB, trackC, 4); + await addPlaybackHistory(userIdA, trackC, oldDate); + await addPlaybackHistory(userIdB, trackC, oldDate); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + const comparison = result.pairwise[0]!; + expect(comparison.sharedTracks).toBeGreaterThan(1); // sharedTrack + trackC + + // Both members should have track count reflecting playback data + const profileA = result.memberProfiles.find((p) => p.userId === userIdA); + if (profileA) { + expect(profileA.trackCount).toBeGreaterThan(1); + } + }); + + it("includes saved tracks in scoring", async () => { + const { partyId, userIdA } = await seedPartyWithTwoSimilarUsers(); + + const extraAlbum = await createAlbum("Extra Saved Album"); + const extraArtist = await createArtist("Extra Saved Artist"); + const extraTrack = await createTrack("Extra Saved Track", extraAlbum.id, [ + extraArtist.id, + ]); + await addSavedTrack(userIdA, extraTrack.id); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + const profileA = result.memberProfiles.find((p) => p.userId === userIdA); + expect(profileA).toBeDefined(); + if (profileA) { + expect(profileA.trackCount).toBeGreaterThan(1); + } + }); + + it("includes followed artists in scoring", async () => { + const { partyId, userIdA } = await seedPartyWithTwoSimilarUsers(); + + const followedArtist = await createArtist("Followed Artist"); + await addTopArtist(userIdA, followedArtist.id, 5); + await addFollowedArtist(userIdA, followedArtist.id); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + const profileA = result.memberProfiles.find((p) => p.userId === userIdA); + expect(profileA).toBeDefined(); + if (profileA) { + expect(profileA.artistCount).toBeGreaterThan(1); + } + }); + }); + + describe("analyzeParty - story clusters", () => { + it("sorts clusters with all-member cluster first", async () => { + const { partyId, sharedTrackId } = await seedPartyWithTwoSimilarUsers(); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + // The cluster with both members should be first + expect(result.storyClusters[0]?.memberCount).toBe(2); + + // The shared track should be in the first (all-members) cluster + const firstCluster = result.storyClusters[0]!; + const trackInFirstCluster = firstCluster.tracks.find( + (t) => t.id === sharedTrackId, + ); + expect(trackInFirstCluster).toBeDefined(); + }); + + it("sorts tracks within cluster by total score descending", async () => { + const { partyId, userIdA, userIdB, sharedTrackId } = + await seedPartyWithTwoSimilarUsers(); + + // Add another shared track at a lower position (lower score) + const extraAlbum = await createAlbum("Extra Album A"); + const extraArtist = await createArtist("Extra Artist A"); + const extraTrack = await createTrack("Extra Track A", extraAlbum.id, [ + extraArtist.id, + ]); + await addTopTrack(userIdA, extraTrack.id, 50); + await addTopTrack(userIdB, extraTrack.id, 50); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + const cluster = result.storyClusters[0]!; + expect(cluster.tracks.length).toBeGreaterThan(1); + + // Shared track (position 1) should have higher score than extra track (position 50) + const sharedClusterTrack = cluster.tracks.find( + (t) => t.id === sharedTrackId, + ); + if (sharedClusterTrack) { + expect(sharedClusterTrack.memberScores.length).toBe(2); + } + }); + }); + + describe("analyzeParty - similarity calculation", () => { + it("calculates Jaccard-like similarity using min/max scoring", async () => { + const { partyId, userIdA, userIdB } = + await seedPartyWithTwoSimilarUsers(); + + const result = await partyAnalysisWorkflow.analyzeParty(partyId); + + const comparison = result.pairwise[0]!; + expect(comparison.sharedTracks).toBeGreaterThanOrEqual(1); + expect(comparison.sharedArtists).toBeGreaterThanOrEqual(1); + expect(comparison.sharedGenres).toBeGreaterThanOrEqual(1); + expect(comparison.similarity).toBeGreaterThanOrEqual(0); + expect(comparison.similarity).toBeLessThanOrEqual(1); + }); + }); + + describe("analyzeParty - save analysis", () => { + it("saves the analysis result to the party record", async () => { + const { partyId } = await seedPartyWithTwoSimilarUsers(); + + await partyAnalysisWorkflow.analyzeParty(partyId); + + const { db } = await import("../../db"); + const { party } = await import("../../db/schema"); + + const savedParty = await db.query.party.findFirst({ + where: { id: partyId }, + }); + + expect(savedParty).toBeDefined(); + expect(savedParty?.analysisData).toBeDefined(); + const analysisData = savedParty?.analysisData as + | Record + | undefined; + expect(analysisData?.storyClusters).toHaveLength(1); + expect(analysisData?.pairwise).toHaveLength(1); + }); + }); +}); diff --git a/api/tsconfig.json b/api/tsconfig.json index ada99f2..1cdc82d 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -25,6 +25,11 @@ // Some stricter flags (disabled by default) "noUnusedLocals": false, "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false - } + "noPropertyAccessFromIndexSignature": false, + + // Test support + "types": ["vitest/globals"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] } diff --git a/api/vitest.config.ts b/api/vitest.config.ts new file mode 100644 index 0000000..b4e860c --- /dev/null +++ b/api/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vitest/config"; + +process.env.SPOTIFY_CLIENT_ID = process.env.SPOTIFY_CLIENT_ID || "test-client-id"; +process.env.SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET || "test-client-secret"; + +export default defineConfig(({ mode }) => ({ + test: { + globals: true, + environment: "node", + include: ["src/**/*.test.ts"], + testTimeout: 30000, + setupFiles: ["./src/test/setup.ts"], + }, +})); diff --git a/bun.lock b/bun.lock index ae693b0..f86ce6e 100644 --- a/bun.lock +++ b/bun.lock @@ -20,9 +20,12 @@ "@statsfm/statsfm.js": "github.com:statsfm/statsfm.js", "better-auth": "^1.6.5", "elysia": "catalog:", + "vite": "^8.0.10", }, "devDependencies": { "@types/bun": "latest", + "tsx": "^4.19.2", + "vitest": "^3.0.5", }, "peerDependencies": { "typescript": "^5", @@ -219,9 +222,9 @@ "@elysiajs/eden": ["@elysiajs/eden@1.4.9", "", { "peerDependencies": { "elysia": ">=1.4.19" } }, "sha512-3CKVD4ycVjB8nCNssfmhnUuq3SzSHkUES3v5PNCFr9LxIrx39/HVRAZ8z2sLxrFqzUs48dCBZaxoZzJ5UUVHDA=="], - "@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], - "@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], @@ -351,7 +354,7 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], - "@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="], + "@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="], "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], @@ -387,35 +390,35 @@ "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.17", "", { "os": "android", "cpu": "arm64" }, "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm" }, "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "s390x" }, "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "x64" }, "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.17", "", { "os": "linux", "cpu": "x64" }, "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.17", "", { "os": "none", "cpu": "arm64" }, "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="], + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.17", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.17", "", { "os": "win32", "cpu": "x64" }, "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.7", "", {}, "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA=="], @@ -609,7 +612,7 @@ "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], - "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + "@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], @@ -1355,7 +1358,7 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rolldown": ["rolldown@1.0.0-rc.15", "", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="], + "rolldown": ["rolldown@1.0.0-rc.17", "", { "dependencies": { "@oxc-project/types": "=0.127.0", "@rolldown/pluginutils": "1.0.0-rc.17" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-x64": "1.0.0-rc.17", "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA=="], "rollup": ["rollup@4.60.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.2", "@rollup/rollup-android-arm64": "4.60.2", "@rollup/rollup-darwin-arm64": "4.60.2", "@rollup/rollup-darwin-x64": "4.60.2", "@rollup/rollup-freebsd-arm64": "4.60.2", "@rollup/rollup-freebsd-x64": "4.60.2", "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", "@rollup/rollup-linux-arm-musleabihf": "4.60.2", "@rollup/rollup-linux-arm64-gnu": "4.60.2", "@rollup/rollup-linux-arm64-musl": "4.60.2", "@rollup/rollup-linux-loong64-gnu": "4.60.2", "@rollup/rollup-linux-loong64-musl": "4.60.2", "@rollup/rollup-linux-ppc64-gnu": "4.60.2", "@rollup/rollup-linux-ppc64-musl": "4.60.2", "@rollup/rollup-linux-riscv64-gnu": "4.60.2", "@rollup/rollup-linux-riscv64-musl": "4.60.2", "@rollup/rollup-linux-s390x-gnu": "4.60.2", "@rollup/rollup-linux-x64-gnu": "4.60.2", "@rollup/rollup-linux-x64-musl": "4.60.2", "@rollup/rollup-openbsd-x64": "4.60.2", "@rollup/rollup-openharmony-arm64": "4.60.2", "@rollup/rollup-win32-arm64-msvc": "4.60.2", "@rollup/rollup-win32-ia32-msvc": "4.60.2", "@rollup/rollup-win32-x64-gnu": "4.60.2", "@rollup/rollup-win32-x64-msvc": "4.60.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ=="], @@ -1543,7 +1546,7 @@ "vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="], - "vite": ["vite@8.0.8", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="], + "vite": ["vite@8.0.10", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.10", "rolldown": "1.0.0-rc.17", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw=="], "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], @@ -1643,10 +1646,14 @@ "@tanstack/start-plugin-core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "@types/pg/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], "@types/set-cookie-parser/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + "@vitest/mocker/vite": ["vite@8.0.8", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="], + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "bun-types/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], @@ -1699,7 +1706,7 @@ "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], - "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="], + "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.17", "", {}, "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg=="], "router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], @@ -1721,6 +1728,8 @@ "vitest/vite": ["vite@7.3.2", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="], + "web/vite": ["vite@8.0.8", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="], + "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -1739,10 +1748,14 @@ "@dotenvx/dotenvx/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "@types/bun/bun-types/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + "@types/pg/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], "@types/set-cookie-parser/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + "@vitest/mocker/vite/rolldown": ["rolldown@1.0.0-rc.15", "", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="], + "bun-types/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -1813,6 +1826,44 @@ "vitest/vite/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + "web/vite/rolldown": ["rolldown@1.0.0-rc.15", "", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="], + + "@types/bun/bun-types/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + + "@vitest/mocker/vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="], + + "@vitest/mocker/vite/rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="], + "isomorphic-unfetch/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "isomorphic-unfetch/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -1925,6 +1976,48 @@ "vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "web/vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="], + + "web/vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="], + + "web/vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="], + + "web/vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="], + + "web/vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="], + + "web/vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="], + + "web/vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="], + + "web/vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="], + + "web/vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="], + + "web/vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="], + + "web/vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="], + + "web/vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="], + + "web/vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="], + + "web/vite/rolldown/@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="], + + "web/vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="], + + "web/vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="], + + "web/vite/rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], + + "@vitest/mocker/vite/rolldown/@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + "msw/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "web/vite/rolldown/@rolldown/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], + + "web/vite/rolldown/@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], } }