From 6e9d857ba651d3b91e82b88dc70171fb9090060d Mon Sep 17 00:00:00 2001 From: Devin Zuczek Date: Thu, 9 Jul 2026 00:56:38 -0400 Subject: [PATCH] migrating more stuff into domain pkg --- apps/accounts/package.json | 1 + apps/accounts/src/accounts.app.ts | 8 +- .../accounts/src/test/integration/api.test.ts | 2 +- apps/api/static/gameconfigs-v1-all.json | 4 +- apps/auth/migrations/0001_accounts.sql | 2 +- apps/auth/migrations/0002_avatar.sql | 2 +- apps/auth/package.json | 1 + apps/auth/src/accounts-db.ts | 173 ------- apps/auth/src/auth.app.ts | 28 +- apps/auth/src/password.ts | 6 +- apps/auth/src/test/integration/api.test.ts | 62 ++- apps/econ/src/avatar-db.ts | 2 +- apps/match/package.json | 1 + apps/match/src/match.app.ts | 5 +- apps/match/src/rooms-db.ts | 6 +- packages/domain/README.md | 5 + packages/domain/package.json | 17 + .../domain}/src/accounts-db.ts | 41 +- packages/domain/src/enums.ts | 32 ++ packages/domain/src/index.ts | 2 + packages/domain/tsconfig.json | 8 + packages/tools/bin/run-wrangler-deploy | 22 +- pnpm-lock.yaml | 426 +++++++++++++++--- 23 files changed, 588 insertions(+), 268 deletions(-) delete mode 100644 apps/auth/src/accounts-db.ts create mode 100644 packages/domain/README.md create mode 100644 packages/domain/package.json rename {apps/accounts => packages/domain}/src/accounts-db.ts (81%) create mode 100644 packages/domain/src/enums.ts create mode 100644 packages/domain/src/index.ts create mode 100644 packages/domain/tsconfig.json diff --git a/apps/accounts/package.json b/apps/accounts/package.json index 9be7414..29e2b2b 100644 --- a/apps/accounts/package.json +++ b/apps/accounts/package.json @@ -15,6 +15,7 @@ "test": "run-vitest" }, "dependencies": { + "@repo/domain": "workspace:*", "@repo/hono-helpers": "workspace:*", "hono": "4.12.27", "workers-tagged-logger": "1.0.1" diff --git a/apps/accounts/src/accounts.app.ts b/apps/accounts/src/accounts.app.ts index 006b535..3efa360 100644 --- a/apps/accounts/src/accounts.app.ts +++ b/apps/accounts/src/accounts.app.ts @@ -1,8 +1,6 @@ import { Hono } from 'hono' import { useWorkersLogger } from 'workers-tagged-logger' -import { logger, withNotFound, withOnError } from '@repo/hono-helpers' - import { createAccount, defaultAccount, @@ -11,11 +9,13 @@ import { getAccountsByIds, searchAccounts, updateAccount, -} from './accounts-db' +} from '@repo/domain' +import { logger, withNotFound, withOnError } from '@repo/hono-helpers' + import { validateAndGetAccountId } from './jwt' import type { Context } from 'hono' -import type { Account } from './accounts-db' +import type { Account } from '@repo/domain' import type { App } from './context' /** diff --git a/apps/accounts/src/test/integration/api.test.ts b/apps/accounts/src/test/integration/api.test.ts index f78e38e..b34c4c3 100644 --- a/apps/accounts/src/test/integration/api.test.ts +++ b/apps/accounts/src/test/integration/api.test.ts @@ -4,7 +4,7 @@ import { beforeAll, describe, expect, test } from 'vitest' import '../../accounts.app' -import { SCHEMA_DDL } from '../../accounts-db' +import { SCHEMA_DDL } from '@repo/domain' import type { Env } from '../../context' diff --git a/apps/api/static/gameconfigs-v1-all.json b/apps/api/static/gameconfigs-v1-all.json index c58654c..27b07aa 100644 --- a/apps/api/static/gameconfigs-v1-all.json +++ b/apps/api/static/gameconfigs-v1-all.json @@ -243,7 +243,7 @@ "EndTime": null, "Key": "DataCollection.OneTimeData.Enabled", "StartTime": null, - "Value": "true" + "Value": "false" }, { "EndTime": null, @@ -279,7 +279,7 @@ "EndTime": null, "Key": "Debug.TraceProbability", "StartTime": null, - "Value": "0" + "Value": "1" }, { "EndTime": null, diff --git a/apps/auth/migrations/0001_accounts.sql b/apps/auth/migrations/0001_accounts.sql index 609be57..84d6567 100644 --- a/apps/auth/migrations/0001_accounts.sql +++ b/apps/auth/migrations/0001_accounts.sql @@ -1,5 +1,5 @@ -- Accounts stored as a JSON blob with generated (virtual) columns for querying. --- Generated from src/accounts-db.ts (SCHEMA_DDL) — keep in sync. +-- Generated from @repo/domain's accounts-db.ts (SCHEMA_DDL) — keep in sync. CREATE TABLE IF NOT EXISTS accounts ( data TEXT NOT NULL, diff --git a/apps/auth/migrations/0002_avatar.sql b/apps/auth/migrations/0002_avatar.sql index 458c288..f079000 100644 --- a/apps/auth/migrations/0002_avatar.sql +++ b/apps/auth/migrations/0002_avatar.sql @@ -1,5 +1,5 @@ -- Store the player's avatar (set via the econ worker's /api/avatar/v2/set). It's -- an opaque JSON payload that isn't queried, so a single nullable TEXT column on --- the account row suffices. Kept in sync with SCHEMA_DDL in src/accounts-db.ts. +-- the account row suffices. Kept in sync with SCHEMA_DDL in @repo/domain's accounts-db.ts. ALTER TABLE accounts ADD COLUMN avatar TEXT; diff --git a/apps/auth/package.json b/apps/auth/package.json index 3fc996f..0f39996 100644 --- a/apps/auth/package.json +++ b/apps/auth/package.json @@ -16,6 +16,7 @@ "test": "run-vitest" }, "dependencies": { + "@repo/domain": "workspace:*", "@repo/hono-helpers": "workspace:*", "hono": "4.12.27", "workers-tagged-logger": "1.0.1" diff --git a/apps/auth/src/accounts-db.ts b/apps/auth/src/accounts-db.ts deleted file mode 100644 index 7ba5ed3..0000000 --- a/apps/auth/src/accounts-db.ts +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Account storage on the shared `recflare` D1 database. Each account is a single - * JSON blob in the `data` column; queryable fields (AccountId, Username) are - * SQLite generated (virtual) columns extracted from that JSON and indexed — - * the same JSON-blob pattern the `rooms` worker uses. - * - * The `auth` worker owns this schema/migration (see migrations/0001_accounts.sql, - * applied with its own `migrations_table` so it doesn't clash with the rooms - * migrations that share the database). Other workers bind the table read/write - * and keep these helpers in sync. - */ - -/** Schema DDL (mirror of migrations 0001_accounts + 0002_avatar, sans seed INSERTs). */ -export const SCHEMA_DDL: string[] = [ - `CREATE TABLE IF NOT EXISTS accounts ( - data TEXT NOT NULL, - avatar TEXT, - account_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.accountId')) VIRTUAL, - username_lower TEXT GENERATED ALWAYS AS (lower(json_extract(data, '$.username'))) VIRTUAL - )`, - `CREATE UNIQUE INDEX IF NOT EXISTS idx_accounts_account_id ON accounts (account_id)`, - `CREATE INDEX IF NOT EXISTS idx_accounts_username_lower ON accounts (username_lower)`, -] - -/** Client-facing account shape (camelCase, exactly as the client's AccountDTO). */ -export interface Account { - accountId: number - username: string - displayName: string - profileImage: string - isJunior: boolean - platforms: number - personalPronouns: number - identityFlags: number - createdAt: string -} - -interface AccountRow { - data: string -} - -const parseOne = (row: AccountRow | null): Account | null => - row ? (JSON.parse(row.data) as Account) : null -const parseAll = (rows: AccountRow[]): Account[] => rows.map((r) => JSON.parse(r.data) as Account) - -/** Word lists for auto-assigned usernames (players don't pick one on signup). */ -const ADJECTIVES = [ - 'Swift', - 'Brave', - 'Clever', - 'Happy', - 'Mighty', - 'Lucky', - 'Sunny', - 'Cosmic', - 'Witty', - 'Nimble', - 'Jolly', - 'Bold', - 'Gentle', - 'Fuzzy', - 'Speedy', - 'Shiny', -] -const NOUNS = [ - 'Fox', - 'Otter', - 'Falcon', - 'Panda', - 'Tiger', - 'Comet', - 'Maple', - 'Pixel', - 'Robin', - 'Wolf', - 'Koala', - 'Dragon', - 'Penguin', - 'Badger', - 'Heron', - 'Lynx', -] - -/** A random, readable username (e.g. "SwiftFox4821"). */ -export function randomUsername(): string { - const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)] - const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)] - const n = Math.floor(Math.random() * 10000) - return `${adj}${noun}${n}` -} - -/** - * Build a full account object from an id, applying default fallbacks for any - * column the caller doesn't override. Used both to synthesize accounts that - * aren't in the DB and as the base for a freshly created account. - */ -export function defaultAccount(id: number, overrides: Partial = {}): Account { - return { - accountId: id, - username: `Player${id}`, - displayName: `Player${id}`, - profileImage: 'DefaultProfileImage.jpg', - isJunior: false, - platforms: 0, - personalPronouns: 0, - identityFlags: 0, - createdAt: new Date().toISOString(), - ...overrides, - } -} - -/** Look up a single account by AccountId. */ -export async function getAccount(db: D1Database, id: number): Promise { - return parseOne( - await db.prepare('SELECT data FROM accounts WHERE account_id = ?1').bind(id).first() - ) -} - -/** Look up multiple accounts by AccountId (order not guaranteed). */ -export async function getAccountsByIds(db: D1Database, ids: number[]): Promise { - if (ids.length === 0) return [] - const placeholders = ids.map((_, i) => `?${i + 1}`).join(',') - const { results } = await db - .prepare(`SELECT data FROM accounts WHERE account_id IN (${placeholders})`) - .bind(...ids) - .all() - return parseAll(results) -} - -/** - * Create and persist a new account. The id is the next free integer (above the - * seeded system accounts); the username is auto-assigned (players don't choose - * one initially) and the display name defaults to it. - */ -export async function createAccount( - db: D1Database, - overrides: Partial = {} -): Promise { - const row = await db - .prepare('SELECT COALESCE(MAX(account_id), 1) + 1 AS next FROM accounts') - .first<{ next: number }>() - const id = row?.next ?? 2 - const username = overrides.username ?? randomUsername() - const account = defaultAccount(id, { username, displayName: username, ...overrides }) - await db.prepare('INSERT INTO accounts (data) VALUES (?1)').bind(JSON.stringify(account)).run() - return account -} - -/** - * Read the account's stored password hash (`salt:hash`), or null when the account - * has none / doesn't exist. Kept in the account JSON blob but out of the public - * account DTO (which projects only known fields), so it never leaks. - */ -export async function getPasswordHash(db: D1Database, id: number): Promise { - const row = await db - .prepare( - "SELECT json_extract(data, '$.passwordHash') AS hash FROM accounts WHERE account_id = ?1" - ) - .bind(id) - .first<{ hash: string | null }>() - return row?.hash ?? null -} - -/** Persist the account's password hash. Returns false when no such account exists. */ -export async function setPasswordHash(db: D1Database, id: number, hash: string): Promise { - const { meta } = await db - .prepare( - "UPDATE accounts SET data = json_set(data, '$.passwordHash', ?2) WHERE account_id = ?1" - ) - .bind(id, hash) - .run() - return meta.changes > 0 -} diff --git a/apps/auth/src/auth.app.ts b/apps/auth/src/auth.app.ts index 20b04c6..58cdb7b 100644 --- a/apps/auth/src/auth.app.ts +++ b/apps/auth/src/auth.app.ts @@ -1,9 +1,9 @@ import { Hono } from 'hono' import { useWorkersLogger } from 'workers-tagged-logger' +import { createAccount, getPasswordHash, RoomInstanceType, setPasswordHash } from '@repo/domain' import { logger, withNotFound, withOnError } from '@repo/hono-helpers' -import { createAccount, getPasswordHash, setPasswordHash } from './accounts-db' import { generateToken, TOKEN_TTL_SECONDS, validateAndGetAccountId } from './jwt' import { hashPassword, verifyPassword } from './password' import { consumeRefreshToken, issueRefreshToken } from './refresh-db' @@ -65,7 +65,7 @@ async function placeNewPlayerInOrientation(env: App['Bindings'], accountId: numb roomInstanceId: ORIENTATION_INSTANCE_ID, roomId: ORIENTATION_ROOM_ID, subRoomId: num(sub?.SubRoomId, 1), - roomInstanceType: 0, + roomInstanceType: RoomInstanceType.Public, location: str(sub?.UnitySceneId), dataBlob: str(sub?.DataBlob), eventId: 0, @@ -153,14 +153,23 @@ const app = new Hono() // Resolve the account this token is for: // - create_account: mint + persist a brand-new account (auto-assigned random // username — players don't pick one initially); the token's `sub` is its id. + // A `password` may be posted to establish the account's login credential. // - refresh_token: redeem a stored (single-use) refresh token for its account + // platform, so an expiring session renews without re-login. - // - otherwise: the request MUST post a valid account_id — never fall back to a - // stub account (issuing account 1 to anyone would be bad). + // - otherwise: a credential login. The request MUST post a valid account_id AND + // the account's correct `password`. An account with no password set can't be + // logged into by id (no credential to verify) — closing the account_id-only + // takeover. New accounts establish a password via create_account or + // /account/me/changepassword. let accountId: string if (grantType === 'create_account') { const account = await createAccount(c.env.DB, { platforms: platformInt || 0 }) accountId = String(account.accountId) + // Establish the login password when one is posted (raw password never stored). + const password = typeof body.password === 'string' ? body.password : '' + if (password !== '') { + await setPasswordHash(c.env.DB, account.accountId, await hashPassword(password)) + } // Place the new player in Orientation (they don't explicitly matchmake into it). await placeNewPlayerInOrientation(c.env, account.accountId) } else if (grantType === 'refresh_token') { @@ -183,6 +192,17 @@ const app = new Hono() 400 ) } + // The account's password MUST be presented and match. An account with no + // stored hash has no credential to authenticate against, so login by id is + // refused — this closes the account_id-only takeover. + const storedHash = await getPasswordHash(c.env.DB, Number(posted)) + const password = typeof body.password === 'string' ? body.password : '' + if (!storedHash || !(await verifyPassword(password, storedHash))) { + return c.json( + { error: 'invalid_grant', error_description: 'invalid account_id or password' }, + 400 + ) + } accountId = posted } diff --git a/apps/auth/src/password.ts b/apps/auth/src/password.ts index f45144b..4d86707 100644 --- a/apps/auth/src/password.ts +++ b/apps/auth/src/password.ts @@ -1,7 +1,7 @@ /** - * Password hashing for /account/me/changepassword. PBKDF2-SHA256 with a random - * per-password salt, stored as `salt:hash` (both base64). Not login-critical yet - * (login is account-id based), but we never store the raw password. + * Password hashing for /connect/token credential login and + * /account/me/changepassword. PBKDF2-SHA256 with a random per-password salt, + * stored as `salt:hash` (both base64). The raw password is never persisted. */ const ITERATIONS = 100_000 diff --git a/apps/auth/src/test/integration/api.test.ts b/apps/auth/src/test/integration/api.test.ts index 2ea8394..3afef6a 100644 --- a/apps/auth/src/test/integration/api.test.ts +++ b/apps/auth/src/test/integration/api.test.ts @@ -4,7 +4,9 @@ import { beforeAll, describe, expect, test } from 'vitest' import '../../auth.app' -import { SCHEMA_DDL } from '../../accounts-db' +import { SCHEMA_DDL } from '@repo/domain' + +import { hashPassword } from '../../password' import { REFRESH_SCHEMA_DDL } from '../../refresh-db' import type { Env } from '../../context' @@ -18,6 +20,10 @@ const ORIGIN = 'https://example.com' // The Orientation room (RoomId 13) new accounts are placed into on signup. const ORIENTATION_SCENE = 'c79709d8-a31b-48aa-9eb8-cc31ba9505e8' +// Credential login requires the account's password; seed a known one for the +// accounts the login tests authenticate as (42, 77). +const LOGIN_PASSWORD = 'correct-horse' + // Apply the accounts schema so create_account can persist (mirrors the migration), // and seed the Orientation room (owned by the rooms worker) so signup can place // the new player there. @@ -26,6 +32,14 @@ beforeAll(async () => { await adminSecretsStore(env.JWT_SECRET).create('test-signing-key') for (const stmt of SCHEMA_DDL) await env.DB.prepare(stmt).run() for (const stmt of REFRESH_SCHEMA_DDL) await env.DB.prepare(stmt).run() + + // Seed the accounts the credential-login tests use, each with LOGIN_PASSWORD set. + const hash = await hashPassword(LOGIN_PASSWORD) + for (const id of [42, 77]) { + await env.DB.prepare('INSERT OR IGNORE INTO accounts (data) VALUES (?1)') + .bind(JSON.stringify({ accountId: id, username: `Player${id}`, passwordHash: hash })) + .run() + } await env.DB.prepare( `CREATE TABLE IF NOT EXISTS room ( data TEXT NOT NULL, @@ -106,7 +120,7 @@ describe('auth worker routes', () => { const res = await exports.default.fetch(`${ORIGIN}/connect/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: 'account_id=42&platform_id=steam-123', + body: `account_id=42&platform_id=steam-123&password=${LOGIN_PASSWORD}`, }) expect(res.status).toBe(200) const json = (await res.json()) as { @@ -150,6 +164,42 @@ describe('auth worker routes', () => { expect(res.status).toBe(400) }) + test('POST /connect/token rejects a credential login with the wrong password', async () => { + const res = await postToken('account_id=42&password=wrong-password') + expect(res.status).toBe(400) + expect(res.json.error).toBe('invalid_grant') + }) + + test('POST /connect/token rejects a credential login with no password', async () => { + const res = await postToken('account_id=42') + expect(res.status).toBe(400) + expect(res.json.error).toBe('invalid_grant') + }) + + test('POST /connect/token refuses login to an account with no password set', async () => { + // Account 999 exists but never set a password — it has no credential to verify, + // so login by id alone is refused (this is the closed takeover hole). + await env.DB.prepare('INSERT OR IGNORE INTO accounts (data) VALUES (?1)') + .bind(JSON.stringify({ accountId: 999, username: 'NoPass' })) + .run() + const res = await postToken('account_id=999&password=anything') + expect(res.status).toBe(400) + expect(res.json.error).toBe('invalid_grant') + }) + + test('POST /connect/token create_account can set a password used for later login', async () => { + const created = await postToken('grant_type=create_account&platform_id=steam-pw2&password=hunter2') + expect(created.status).toBe(200) + const sub = decodePayload(created.json.access_token as string).sub as string + + // The password set at creation authenticates a subsequent credential login. + const ok = await postToken(`account_id=${sub}&password=hunter2`) + expect(ok.status).toBe(200) + // A wrong password for that same account is rejected. + const bad = await postToken(`account_id=${sub}&password=nope`) + expect(bad.status).toBe(400) + }) + test('POST /connect/token grant_type=create_account persists a new account', async () => { const payload = await tokenFor('grant_type=create_account&platform_id=steam-123') // The token's sub is the new account id, allocated above the system accounts. @@ -180,12 +230,14 @@ describe('auth worker routes', () => { }) test('POST /connect/token maps the platform int to its enum name', async () => { - const payload = await tokenFor('account_id=42&platform=0') + const payload = await tokenFor(`account_id=42&platform=0&password=${LOGIN_PASSWORD}`) expect(payload.platform).toBe('Steam') }) test('POST /connect/token returns a refresh_token that redeems for a new token', async () => { - const login = await postToken('account_id=42&platform=0&platform_id=steam-123') + const login = await postToken( + `account_id=42&platform=0&platform_id=steam-123&password=${LOGIN_PASSWORD}` + ) expect(login.status).toBe(200) const refreshToken = login.json.refresh_token as string expect(typeof refreshToken).toBe('string') @@ -205,7 +257,7 @@ describe('auth worker routes', () => { }) test('POST /connect/token refresh_token is single-use (rejected on reuse)', async () => { - const login = await postToken('account_id=77&platform=0') + const login = await postToken(`account_id=77&platform=0&password=${LOGIN_PASSWORD}`) const refreshToken = login.json.refresh_token as string const first = await postToken( diff --git a/apps/econ/src/avatar-db.ts b/apps/econ/src/avatar-db.ts index 63db290..eb3d5b0 100644 --- a/apps/econ/src/avatar-db.ts +++ b/apps/econ/src/avatar-db.ts @@ -6,7 +6,7 @@ * * The `auth` worker owns the accounts schema/migrations; econ only reads/writes * the avatar column. SCHEMA_DDL mirrors the table so tests can build it without - * depending on the auth worker — keep it in sync with auth's accounts-db.ts. + * depending on the auth worker — keep it in sync with @repo/domain's accounts-db.ts. */ /** Schema DDL for tests — the accounts table including the avatar column. */ diff --git a/apps/match/package.json b/apps/match/package.json index cca9e3e..526e26c 100644 --- a/apps/match/package.json +++ b/apps/match/package.json @@ -15,6 +15,7 @@ "test": "run-vitest" }, "dependencies": { + "@repo/domain": "workspace:*", "@repo/hono-helpers": "workspace:*", "hono": "4.12.27", "workers-tagged-logger": "1.0.1" diff --git a/apps/match/src/match.app.ts b/apps/match/src/match.app.ts index 6dd3295..f125693 100644 --- a/apps/match/src/match.app.ts +++ b/apps/match/src/match.app.ts @@ -1,6 +1,7 @@ import { Hono } from 'hono' import { useWorkersLogger } from 'workers-tagged-logger' +import { RoomInstanceType } from '@repo/domain' import { withNotFound, withOnError } from '@repo/hono-helpers' import { validateAndGetAccountId } from './jwt' @@ -149,7 +150,7 @@ function dormRoomInstance() { roomInstanceId: 1, roomId: 1, subRoomId: 1, - roomInstanceType: 2, + roomInstanceType: RoomInstanceType.Dormroom, location: '76d98498-60a1-430c-ab76-b54a29b7a163', dataBlob: '', eventId: 0, @@ -189,7 +190,7 @@ function instanceFieldsFromRoom(room: Room) { dataBlob: str(sub?.DataBlob), name, maxCapacity: num(sub?.MaxPlayers, 4), - roomInstanceType: room.IsDorm === true ? 2 : 0, + roomInstanceType: room.IsDorm === true ? RoomInstanceType.Dormroom : RoomInstanceType.Public, isDorm: room.IsDorm === true, } } diff --git a/apps/match/src/rooms-db.ts b/apps/match/src/rooms-db.ts index bb8628b..3bf1bbb 100644 --- a/apps/match/src/rooms-db.ts +++ b/apps/match/src/rooms-db.ts @@ -6,6 +6,8 @@ * sync with the rooms worker's. */ +import { Accessibility, Role } from '@repo/domain' + /** A stored room — the parsed JSON blob (full client-facing room response). */ export type Room = Record @@ -84,12 +86,12 @@ export async function getOrCreateDormRoom(db: D1Database, accountId: number): Pr const username = (await getUsername(db, accountId)) ?? `Player${accountId}` const room: Room = { - ...(template ?? { Accessibility: 2 }), + ...(template ?? { Accessibility: Accessibility.Unlisted }), RoomId: roomId, Name: `@${username}'s Dorm`, CreatorAccountId: accountId, IsDorm: true, - Roles: [{ AccountId: accountId, Role: 255, LastChangedByAccountId: null, InvitedRole: 0 }], + Roles: [{ AccountId: accountId, Role: Role.Owner, LastChangedByAccountId: null, InvitedRole: 0 }], SubRooms: [{ ...templateSub, CreatorAccountId: accountId }], CreatedAt: new Date().toISOString(), } diff --git a/packages/domain/README.md b/packages/domain/README.md new file mode 100644 index 0000000..bfbee4f --- /dev/null +++ b/packages/domain/README.md @@ -0,0 +1,5 @@ +# domain + +Shared domain enums and constants for the recflare workers. Single source of +truth for the numeric values the client (and our D1 JSON blobs) encode, so the +same magic numbers aren't re-hardcoded per worker. diff --git a/packages/domain/package.json b/packages/domain/package.json new file mode 100644 index 0000000..86c26b0 --- /dev/null +++ b/packages/domain/package.json @@ -0,0 +1,17 @@ +{ + "name": "@repo/domain", + "version": "0.1.0", + "private": true, + "sideEffects": false, + "type": "module", + "main": "src/index.ts", + "scripts": { + "check:lint": "run-oxlint", + "check:types": "run-tsc" + }, + "devDependencies": { + "@cloudflare/workers-types": "4.20260630.1", + "@repo/tools": "workspace:*", + "@repo/typescript-config": "workspace:*" + } +} diff --git a/apps/accounts/src/accounts-db.ts b/packages/domain/src/accounts-db.ts similarity index 81% rename from apps/accounts/src/accounts-db.ts rename to packages/domain/src/accounts-db.ts index 4dc4161..2424de4 100644 --- a/apps/accounts/src/accounts-db.ts +++ b/packages/domain/src/accounts-db.ts @@ -4,10 +4,11 @@ * SQLite generated (virtual) columns extracted from that JSON and indexed — * the same JSON-blob pattern the `rooms` worker uses. * - * The `auth` worker owns this schema/migration (see migrations/0001_accounts.sql, - * applied with its own `migrations_table` so it doesn't clash with the rooms - * migrations that share the database). Other workers bind the table read/write - * and keep these helpers in sync. + * The `auth` worker owns this schema/migration (see apps/auth/migrations/ + * 0001_accounts.sql, applied with its own `migrations_table` so it doesn't clash + * with the rooms migrations that share the database). This module is the single + * source of truth for the helpers; the `auth` and `accounts` workers both import + * it from `@repo/domain` (each uses the subset it needs). */ /** Schema DDL (mirror of migrations 0001_accounts + 0002_avatar, sans seed INSERTs). */ @@ -41,6 +42,12 @@ export interface Account { bio?: string /** Remaining username changes; decremented by PUT /account/me/username. */ availableUsernameChanges?: number + /** + * PBKDF2 `salt:hash` for credential login; set via /account/me/changepassword + * or create_account. Kept in the JSON blob but never projected into a public + * DTO (the DTO builders pick only known fields), so it doesn't leak. + */ + passwordHash?: string } interface AccountRow { @@ -217,3 +224,29 @@ export async function createAccount( await db.prepare('INSERT INTO accounts (data) VALUES (?1)').bind(JSON.stringify(account)).run() return account } + +/** + * Read the account's stored password hash (`salt:hash`), or null when the account + * has none / doesn't exist. Kept in the account JSON blob but out of the public + * account DTO (which projects only known fields), so it never leaks. + */ +export async function getPasswordHash(db: D1Database, id: number): Promise { + const row = await db + .prepare( + "SELECT json_extract(data, '$.passwordHash') AS hash FROM accounts WHERE account_id = ?1" + ) + .bind(id) + .first<{ hash: string | null }>() + return row?.hash ?? null +} + +/** Persist the account's password hash. Returns false when no such account exists. */ +export async function setPasswordHash(db: D1Database, id: number, hash: string): Promise { + const { meta } = await db + .prepare( + "UPDATE accounts SET data = json_set(data, '$.passwordHash', ?2) WHERE account_id = ?1" + ) + .bind(id, hash) + .run() + return meta.changes > 0 +} diff --git a/packages/domain/src/enums.ts b/packages/domain/src/enums.ts new file mode 100644 index 0000000..679fc4b --- /dev/null +++ b/packages/domain/src/enums.ts @@ -0,0 +1,32 @@ +/** + * Domain enums — the numeric codes the Rec Room client encodes into the room / + * room-instance JSON we store in D1. Single source of truth so workers reference + * a name instead of re-hardcoding the integer. Regular (not `const`) enums, since + * the tsconfig sets `isolatedModules` (which disallows `const enum` across files). + */ + +/** The kind of a room instance (live session), matching the client's `RoomInstanceType`. */ +export enum RoomInstanceType { + Public = 0, + Private = 1, + Dormroom = 2, + Event = 3, + Meetup = 4, + Clubhouse = 5, +} + +/** A room's (or image's) visibility, matching the client's `RoomAccessibility`. */ +export enum Accessibility { + Private = 0, + Public = 1, + Unlisted = 2, +} + +/** + * A room-role tier (the `Role` byte on a room's `Roles` entries). Named tiers we + * reference by value today — the owner (max byte) and co-owner. + */ +export enum Role { + CoOwner = 30, + Owner = 255, +} diff --git a/packages/domain/src/index.ts b/packages/domain/src/index.ts new file mode 100644 index 0000000..e1cf840 --- /dev/null +++ b/packages/domain/src/index.ts @@ -0,0 +1,2 @@ +export { RoomInstanceType, Accessibility, Role } from './enums' +export * from './accounts-db' diff --git a/packages/domain/tsconfig.json b/packages/domain/tsconfig.json new file mode 100644 index 0000000..d438c16 --- /dev/null +++ b/packages/domain/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@repo/typescript-config/workers-lib.json", + // domain has no vitest of its own, so pull in only the D1/Workers ambient types + // (drop @cloudflare/vitest-pool-workers/types that workers-lib.json adds). + "compilerOptions": { + "types": ["@cloudflare/workers-types"] + } +} diff --git a/packages/tools/bin/run-wrangler-deploy b/packages/tools/bin/run-wrangler-deploy index 5a4b87d..a61c7f0 100755 --- a/packages/tools/bin/run-wrangler-deploy +++ b/packages/tools/bin/run-wrangler-deploy @@ -48,11 +48,24 @@ HOST="$SUBDOMAIN.$DOMAIN" # Secrets Store — RECFLARE_SECRETS_STORE: a single store id (all workers bind the # one shared store for the JWT signing key). CONFIG="wrangler.jsonc" + +# Workers built with @cloudflare/vite-plugin (e.g. the React SPA in www) emit a +# deploy-ready config into dist// that carries the built worker +# (main: index.js), the resolved assets.directory, and no_bundle. The committed +# wrangler.jsonc leaves assets.directory out on purpose — the plugin fills it in — +# so deploying the source config fails with "assets ... missing the required +# directory property". Prefer the generated config when it exists. These workers +# have no D1/KV/Secrets bindings, so the id-splicing below is skipped. +VITE_CONFIG="dist/$DIR/wrangler.json" +if [ -f "$VITE_CONFIG" ]; then + CONFIG="$VITE_CONFIG" +fi + NEEDS_D1=$(grep -q '"database_id": *"local"' wrangler.jsonc 2>/dev/null && echo 1 || true) NEEDS_KV=$(grep -q '"id": *"local"' wrangler.jsonc 2>/dev/null && echo 1 || true) NEEDS_STORE=$(grep -q '"store_id": *"local"' wrangler.jsonc 2>/dev/null && echo 1 || true) -if [ -n "$NEEDS_D1" ] || [ -n "$NEEDS_KV" ] || [ -n "$NEEDS_STORE" ]; then +if [ "$CONFIG" = "wrangler.jsonc" ] && { [ -n "$NEEDS_D1" ] || [ -n "$NEEDS_KV" ] || [ -n "$NEEDS_STORE" ]; }; then CONFIG="wrangler.generated.jsonc" # Generated alongside the original so its relative paths (main, migrations_dir) # still resolve. Gitignored; removed on exit so `wrangler dev` is unaffected. @@ -116,6 +129,11 @@ if [ -n "$NEEDS_D1" ] || [ -n "$NEEDS_KV" ] || [ -n "$NEEDS_STORE" ]; then fi fi +# Vite-built configs set no_bundle (vite already bundled and minified), which is +# incompatible with --minify. Only pass --minify when wrangler does the bundling. +MINIFY="--minify" +[ "$CONFIG" = "$VITE_CONFIG" ] && MINIFY="" + # Deploy with wrangler using the extracted values as binding variables echo "Deploying worker $NAME version $VERSION to $HOST" wrangler deploy \ @@ -124,5 +142,5 @@ wrangler deploy \ --var SENTRY_RELEASE:"$VERSION" \ --var DOMAIN:"$DOMAIN" \ --domain "$HOST" \ - --minify \ + $MINIFY \ "$@" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 041222b..8686c61 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: devDependencies: '@babel/plugin-transform-explicit-resource-management': specifier: 8.0.1 - version: 8.0.1(@babel/core@7.28.5) + version: 8.0.1(@babel/core@7.29.7) '@ianvs/prettier-plugin-sort-imports': specifier: 4.7.1 version: 4.7.1(prettier@3.9.4) @@ -49,10 +49,13 @@ importers: version: 6.0.3 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) apps/accounts: dependencies: + '@repo/domain': + specifier: workspace:* + version: link:../../packages/domain '@repo/hono-helpers': specifier: workspace:* version: link:../../packages/hono-helpers @@ -65,7 +68,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -77,7 +80,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -96,7 +99,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -108,13 +111,16 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) apps/auth: dependencies: + '@repo/domain': + specifier: workspace:* + version: link:../../packages/domain '@repo/hono-helpers': specifier: workspace:* version: link:../../packages/hono-helpers @@ -127,7 +133,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -139,7 +145,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -158,7 +164,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -170,7 +176,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -189,7 +195,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -201,7 +207,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -220,7 +226,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -232,7 +238,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -251,7 +257,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -263,7 +269,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -282,7 +288,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -294,7 +300,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -316,7 +322,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -328,13 +334,16 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) apps/match: dependencies: + '@repo/domain': + specifier: workspace:* + version: link:../../packages/domain '@repo/hono-helpers': specifier: workspace:* version: link:../../packages/hono-helpers @@ -347,7 +356,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -359,7 +368,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -378,7 +387,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -390,7 +399,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -409,7 +418,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -421,7 +430,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -440,7 +449,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -452,7 +461,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -471,7 +480,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -483,7 +492,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) @@ -502,7 +511,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@repo/tools': specifier: workspace:* version: link:../../packages/tools @@ -514,11 +523,75 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.105.0 version: 4.105.0(@cloudflare/workers-types@4.20260630.1) + apps/www: + dependencies: + '@repo/hono-helpers': + specifier: workspace:* + version: link:../../packages/hono-helpers + hono: + specifier: 4.12.27 + version: 4.12.27 + react: + specifier: 19.2.7 + version: 19.2.7 + react-dom: + specifier: 19.2.7 + version: 19.2.7(react@19.2.7) + workers-tagged-logger: + specifier: 1.0.1 + version: 1.0.1 + devDependencies: + '@cloudflare/vite-plugin': + specifier: 1.42.0 + version: 1.42.0(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))(workerd@1.20260625.1)(wrangler@4.105.0(@cloudflare/workers-types@4.20260630.1)) + '@cloudflare/vitest-pool-workers': + specifier: 0.16.20 + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + '@repo/tools': + specifier: workspace:* + version: link:../../packages/tools + '@repo/typescript-config': + specifier: workspace:* + version: link:../../packages/typescript-config + '@types/node': + specifier: 26.0.1 + version: 26.0.1 + '@types/react': + specifier: 19.2.17 + version: 19.2.17 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.17) + '@vitejs/plugin-react': + specifier: 5.2.0 + version: 5.2.0(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + vite: + specifier: 6.4.3 + version: 6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0) + vitest: + specifier: 4.1.9 + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + wrangler: + specifier: 4.105.0 + version: 4.105.0(@cloudflare/workers-types@4.20260630.1) + + packages/domain: + devDependencies: + '@cloudflare/workers-types': + specifier: 4.20260630.1 + version: 4.20260630.1 + '@repo/tools': + specifier: workspace:* + version: link:../tools + '@repo/typescript-config': + specifier: workspace:* + version: link:../typescript-config + packages/hono-helpers: dependencies: '@hono/standard-validator': @@ -539,7 +612,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.16.20 - version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) + version: 0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))) '@cloudflare/workers-types': specifier: 4.20260630.1 version: 4.20260630.1 @@ -551,7 +624,7 @@ importers: version: link:../typescript-config vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) packages/oxlint-config: dependencies: @@ -567,7 +640,7 @@ importers: version: 26.0.1 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) packages/tools: dependencies: @@ -625,7 +698,7 @@ importers: version: 1.3.14 vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) packages/typescript-config: {} @@ -655,7 +728,7 @@ importers: version: link:../tools vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) turbo/generators: dependencies: @@ -680,7 +753,7 @@ importers: version: link:../../packages/typescript-config vitest: specifier: 4.1.9 - version: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) packages: @@ -692,8 +765,8 @@ packages: resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.5': - resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} '@babel/generator@7.29.7': @@ -718,6 +791,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@8.0.1': resolution: {integrity: sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==} engines: {node: ^22.18.0 || >=24.11.0} @@ -757,6 +834,18 @@ packages: peerDependencies: '@babel/core': ^8.0.0 + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} @@ -788,6 +877,13 @@ packages: workerd: optional: true + '@cloudflare/vite-plugin@1.42.0': + resolution: {integrity: sha512-U8Bpcn9l10NNCyYo6kMI2RPZhKRWU0i3udrS/+LHHBDSa61Ra6r7OaDY5LnZw86tOC5vZIKRUm5E51MKjOvfwg==} + hasBin: true + peerDependencies: + vite: ^6.1.0 || ^7.0.0 || ^8.0.0 + wrangler: ^4.102.0 + '@cloudflare/vitest-pool-workers@0.16.20': resolution: {integrity: sha512-buw0YgsAMT7s60wcmyxbtciEJjMJzKcWzayDMPhWaqMqfQzW+0WPLV67Lobn4C80nkNQhYocEJPnrEhLWnOf+A==} peerDependencies: @@ -795,30 +891,60 @@ packages: '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 + '@cloudflare/workerd-darwin-64@1.20260617.1': + resolution: {integrity: sha512-jWwmgEVVWbsHNrLSNXzwjJaH90VzRxq1cWkQFUidxyeUPnMxemeNE8I9qFAfrpzGgE11e9sKDcE3ettJW08swQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + '@cloudflare/workerd-darwin-64@1.20260625.1': resolution: {integrity: sha512-naCfBv0WnnTQIQPTniqMoUlklOIFjrAcSn1X+IAOhY8aFLF/xGYtFjs1eEE8sFib3ZuChGGpU23FFORVczqr0A==} engines: {node: '>=16'} cpu: [x64] os: [darwin] + '@cloudflare/workerd-darwin-arm64@1.20260617.1': + resolution: {integrity: sha512-LHH7b565g9znfCUOkwbec6FG2rmRbsgCy6aJiU9KN662mNheWl5sw/iKleiFSiljPKQQP3HkjnC/NSkdgi/aSA==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + '@cloudflare/workerd-darwin-arm64@1.20260625.1': resolution: {integrity: sha512-jmH6zjp6Wrux46+qtFwDwrj+vd7s5bdwEqeGvdnwE0a4IEeAhKs0L42HQOyID+g5lkrHq9m55+AbhtmRAm63Pw==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] + '@cloudflare/workerd-linux-64@1.20260617.1': + resolution: {integrity: sha512-FMnaAKXe4Cfd8TQurCVd9fs2XQVBFRCsP+Id/SRdUv89MlwYu9zXfoyx6BxM+brPTIUK38SHbo8iaxiwzLi9JQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + '@cloudflare/workerd-linux-64@1.20260625.1': resolution: {integrity: sha512-MiQkpA/dX8d83Zp64pzHUKfd6ca4cvwxnNobSP6CnXvfESvnNI9pfa+nfwnParla36sPmnYntNkjR7NjRuDeKQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] + '@cloudflare/workerd-linux-arm64@1.20260617.1': + resolution: {integrity: sha512-MRoifFYcqbxxIIQy7PqO5tFY/qPFSnjXzakWl0sO93l+HLyG35jRAgOi6jfqa4kBxc7gKKtH861DcewjxUfkjA==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + '@cloudflare/workerd-linux-arm64@1.20260625.1': resolution: {integrity: sha512-LxxW7Qv60Xvv37+w6gUSDpYZziyqMy+cZWd9IvSA5ehVgKAxmzEaYPMiSZlxk32nbIWL9u/tfjXYCOKJ4Lo+XQ==} engines: {node: '>=16'} cpu: [arm64] os: [linux] + '@cloudflare/workerd-windows-64@1.20260617.1': + resolution: {integrity: sha512-rgBV9wQrv0OSKgCTTbhFUFY3sLGNANZ88aqaLvtmEn2gmbFVb1J4PDGochVUdB7NSEp4D/ghHva6/8SZmbONpw==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + '@cloudflare/workerd-windows-64@1.20260625.1': resolution: {integrity: sha512-LH6iIX1HHaTwVKV5VokDxxUErXJzQoNZFRwVm7Vx/3fB/ApcTcRCUaMqcxI4as94jEUqg+pmX5czOndiveohow==} engines: {node: '>=16'} @@ -1632,6 +1758,9 @@ packages: '@poppinss/exception@1.2.2': resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==} + '@rolldown/pluginutils@1.0.0-rc.3': + resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + '@rollup/rollup-android-arm-eabi@4.62.2': resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} cpu: [arm] @@ -1807,6 +1936,18 @@ packages: cpu: [arm64] os: [win32] + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/bun@1.3.14': resolution: {integrity: sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw==} @@ -1828,6 +1969,14 @@ packages: '@types/node@26.0.1': resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260629.1': resolution: {integrity: sha512-wXRExZJweYoTzE4atRR7T5HwKJYkl6/KHxON0eF0iy2fvgLXDlyq4AQqhmV8mMx10PQKc/4sNbfhD4kjWWvm8A==} engines: {node: '>=16.20.0'} @@ -1875,6 +2024,12 @@ packages: engines: {node: '>=16.20.0'} hasBin: true + '@vitejs/plugin-react@5.2.0': + resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + '@vitest/expect@4.1.9': resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} @@ -1971,6 +2126,9 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -2163,6 +2321,11 @@ packages: memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + miniflare@4.20260617.0: + resolution: {integrity: sha512-A+H5gcOCQZsKFg7/daZUtx8WHn4gGxwUfH1jnNDAisyAWSvvSZHe+GCeQWs16uthnUDcm72UQIQ1NXDJtnuo9Q==} + engines: {node: '>=22.0.0'} + hasBin: true + miniflare@4.20260625.0: resolution: {integrity: sha512-3kKXwRUObJsnBYPBgR0NiNZYKF/yv8GFyha1cx2EeAEraxNODgRVcyeRo+F1ok1tg5Mg7iUpOWSkknQTHuFhwA==} engines: {node: '>=22.0.0'} @@ -2245,6 +2408,19 @@ packages: engines: {node: '>=14'} hasBin: true + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} + peerDependencies: + react: ^19.2.7 + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} + engines: {node: '>=0.10.0'} + rollup@4.62.2: resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2253,6 +2429,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2407,8 +2586,8 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - vite@6.3.4: - resolution: {integrity: sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==} + vite@6.4.3: + resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -2493,6 +2672,11 @@ packages: engines: {node: '>=8'} hasBin: true + workerd@1.20260617.1: + resolution: {integrity: sha512-Re5pl6pdowt3ZmWUzGlOuB7jbRIIPetgKalmo4cYmucQnVhpo7/3e4MfpekbhLi2EhZZz5EY9NWRu8zFzuEZew==} + engines: {node: '>=16'} + hasBin: true + workerd@1.20260625.1: resolution: {integrity: sha512-GApQvFX52SDM6L4u0+RRnUDB1wJOnEwoXjinkmOPtIyofWBxrlZckdegJSYc1leg++lLZ3+DQ4zMVmBqYVtzfA==} engines: {node: '>=16'} @@ -2566,12 +2750,12 @@ snapshots: '@babel/compat-data@7.29.7': {} - '@babel/core@7.28.5': + '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.28.5) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 @@ -2611,18 +2795,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.28.5)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@8.0.1(@babel/core@7.28.5)': + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-plugin-utils@8.0.1(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.7 '@babel/helper-string-parser@7.29.7': {} @@ -2639,16 +2825,26 @@ snapshots: dependencies: '@babel/types': 7.29.7 - '@babel/plugin-transform-destructuring@8.0.1(@babel/core@7.28.5)': + '@babel/plugin-transform-destructuring@8.0.1(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 8.0.1(@babel/core@7.28.5) + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@7.29.7) - '@babel/plugin-transform-explicit-resource-management@8.0.1(@babel/core@7.28.5)': + '@babel/plugin-transform-explicit-resource-management@8.0.1(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 8.0.1(@babel/core@7.28.5) - '@babel/plugin-transform-destructuring': 8.0.1(@babel/core@7.28.5) + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@7.29.7) + '@babel/plugin-transform-destructuring': 8.0.1(@babel/core@7.29.7) + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 '@babel/template@7.29.7': dependencies: @@ -2687,14 +2883,27 @@ snapshots: optionalDependencies: workerd: 1.20260625.1 - '@cloudflare/vitest-pool-workers@0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)))': + '@cloudflare/vite-plugin@1.42.0(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))(workerd@1.20260625.1)(wrangler@4.105.0(@cloudflare/workers-types@4.20260630.1))': + dependencies: + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260625.1) + miniflare: 4.20260617.0 + unenv: 2.0.0-rc.24 + vite: 6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0) + wrangler: 4.105.0(@cloudflare/workers-types@4.20260630.1) + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - workerd + + '@cloudflare/vitest-pool-workers@0.16.20(@cloudflare/workers-types@4.20260630.1)(@vitest/runner@4.1.9)(@vitest/snapshot@4.1.9)(vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)))': dependencies: '@vitest/runner': 4.1.9 '@vitest/snapshot': 4.1.9 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 4.20260625.0 - vitest: 4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + vitest: 4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) wrangler: 4.105.0(@cloudflare/workers-types@4.20260630.1) zod: 3.25.76 transitivePeerDependencies: @@ -2702,18 +2911,33 @@ snapshots: - bufferutil - utf-8-validate + '@cloudflare/workerd-darwin-64@1.20260617.1': + optional: true + '@cloudflare/workerd-darwin-64@1.20260625.1': optional: true + '@cloudflare/workerd-darwin-arm64@1.20260617.1': + optional: true + '@cloudflare/workerd-darwin-arm64@1.20260625.1': optional: true + '@cloudflare/workerd-linux-64@1.20260617.1': + optional: true + '@cloudflare/workerd-linux-64@1.20260625.1': optional: true + '@cloudflare/workerd-linux-arm64@1.20260617.1': + optional: true + '@cloudflare/workerd-linux-arm64@1.20260625.1': optional: true + '@cloudflare/workerd-windows-64@1.20260617.1': + optional: true + '@cloudflare/workerd-windows-64@1.20260625.1': optional: true @@ -3247,6 +3471,8 @@ snapshots: '@poppinss/exception@1.2.2': {} + '@rolldown/pluginutils@1.0.0-rc.3': {} + '@rollup/rollup-android-arm-eabi@4.62.2': optional: true @@ -3359,6 +3585,27 @@ snapshots: '@turbo/windows-arm64@2.10.1': optional: true + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.7 + '@types/bun@1.3.14': dependencies: bun-types: 1.3.14 @@ -3385,6 +3632,14 @@ snapshots: dependencies: undici-types: 8.3.0 + '@types/react-dom@19.2.3(@types/react@19.2.17)': + dependencies: + '@types/react': 19.2.17 + + '@types/react@19.2.17': + dependencies: + csstype: 3.2.3 + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260629.1': optional: true @@ -3416,6 +3671,18 @@ snapshots: '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260629.1 '@typescript/native-preview-win32-x64': 7.0.0-dev.20260629.1 + '@vitejs/plugin-react@5.2.0(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-rc.3 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + '@vitest/expect@4.1.9': dependencies: '@standard-schema/spec': 1.1.0 @@ -3425,13 +3692,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.9(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))': + '@vitest/mocker@4.1.9(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0) + vite: 6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0) '@vitest/pretty-format@4.1.9': dependencies: @@ -3509,6 +3776,8 @@ snapshots: cookie@1.0.2: {} + csstype@3.2.3: {} + debug@4.4.3: dependencies: ms: 2.1.3 @@ -3685,6 +3954,18 @@ snapshots: memoize-one@6.0.0: {} + miniflare@4.20260617.0: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + sharp: 0.34.5 + undici: 7.28.0 + workerd: 1.20260617.1 + ws: 8.21.0 + youch: 4.1.0-beta.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + miniflare@4.20260625.0: dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -3768,6 +4049,15 @@ snapshots: prettier@3.9.4: {} + react-dom@19.2.7(react@19.2.7): + dependencies: + react: 19.2.7 + scheduler: 0.27.0 + + react-refresh@0.18.0: {} + + react@19.2.7: {} + rollup@4.62.2: dependencies: '@types/estree': 1.0.9 @@ -3801,6 +4091,8 @@ snapshots: safer-buffer@2.1.2: {} + scheduler@0.27.0: {} + semver@6.3.1: {} semver@7.8.5: {} @@ -3956,7 +4248,7 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0): + vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -3972,10 +4264,10 @@ snapshots: tsx: 4.22.4 yaml: 2.9.0 - vitest@4.1.9(@types/node@26.0.1)(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)): + vitest@4.1.9(@types/node@26.0.1)(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/mocker': 4.1.9(vite@6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.9 '@vitest/runner': 4.1.9 '@vitest/snapshot': 4.1.9 @@ -3992,7 +4284,7 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 6.3.4(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0) + vite: 6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.29.2)(tsx@4.22.4)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.0.1 @@ -4004,6 +4296,14 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + workerd@1.20260617.1: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20260617.1 + '@cloudflare/workerd-darwin-arm64': 1.20260617.1 + '@cloudflare/workerd-linux-64': 1.20260617.1 + '@cloudflare/workerd-linux-arm64': 1.20260617.1 + '@cloudflare/workerd-windows-64': 1.20260617.1 + workerd@1.20260625.1: optionalDependencies: '@cloudflare/workerd-darwin-64': 1.20260625.1