From 88aef19c3f572d26059d70fbe08635aba8d7805b Mon Sep 17 00:00:00 2001 From: JOY <5027251+JOY@users.noreply.github.com> Date: Sat, 19 Sep 2026 02:21:10 +0700 Subject: [PATCH 1/3] chore(sso): remove deprecated crove-sso Cloudflare Worker The standalone OAuth bridge worker (sso.crove.com / beta-sso.crove.com) was deprecated in favor of the central PKCE bridge embedded in api.dos.me. Removes the worker app, its deploy workflow, lockfile entries, and active-doc rows. Live traffic sampled at zero (60s tail) before removal; login flow no longer references the worker. --- .github/workflows/deploy-sso.yml | 81 -- apps/crove-sso/package.json | 21 - apps/crove-sso/src/env.ts | 13 - apps/crove-sso/src/index.ts | 219 ----- apps/crove-sso/src/oauth.ts | 121 --- apps/crove-sso/src/state-store.ts | 86 -- apps/crove-sso/test/env.d.ts | 5 - apps/crove-sso/test/oauth.test.ts | 112 --- apps/crove-sso/test/state-store.test.ts | 49 -- apps/crove-sso/test/worker.test.ts | 257 ------ apps/crove-sso/tsconfig.json | 13 - apps/crove-sso/vitest.config.ts | 21 - apps/crove-sso/worker-configuration.d.ts | 25 - apps/crove-sso/wrangler.jsonc | 76 -- docs/beta-environment.md | 1 - docs/cicd.md | 1 - pnpm-lock.yaml | 1024 +--------------------- 17 files changed, 4 insertions(+), 2121 deletions(-) delete mode 100644 .github/workflows/deploy-sso.yml delete mode 100644 apps/crove-sso/package.json delete mode 100644 apps/crove-sso/src/env.ts delete mode 100644 apps/crove-sso/src/index.ts delete mode 100644 apps/crove-sso/src/oauth.ts delete mode 100644 apps/crove-sso/src/state-store.ts delete mode 100644 apps/crove-sso/test/env.d.ts delete mode 100644 apps/crove-sso/test/oauth.test.ts delete mode 100644 apps/crove-sso/test/state-store.test.ts delete mode 100644 apps/crove-sso/test/worker.test.ts delete mode 100644 apps/crove-sso/tsconfig.json delete mode 100644 apps/crove-sso/vitest.config.ts delete mode 100644 apps/crove-sso/worker-configuration.d.ts delete mode 100644 apps/crove-sso/wrangler.jsonc diff --git a/.github/workflows/deploy-sso.yml b/.github/workflows/deploy-sso.yml deleted file mode 100644 index 14c35f35cd..0000000000 --- a/.github/workflows/deploy-sso.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Deploy Crove SSO Bridge - -on: - push: - branches: - - main - - dev - paths: - - 'apps/crove-sso/**' - - '.github/workflows/deploy-sso.yml' - workflow_dispatch: - inputs: - environment: - description: 'Target deployment environment' - required: true - default: 'beta' - type: choice - options: - - beta - - prod - -jobs: - test-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - environment: ${{ github.event.inputs.environment == 'prod' && 'production' || github.ref_name == 'main' && 'production' || 'beta' }} - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Setup Node.js & PNPM - uses: actions/setup-node@v4 - with: - node-version: 22 - - - name: Setup PNPM - uses: pnpm/action-setup@v4 - with: - version: 10.6.1 - - - name: Install SSO Dependencies - working-directory: apps/crove-sso - run: pnpm install - - - name: Run Vitest Unit Tests - working-directory: apps/crove-sso - run: pnpm test - - - name: Determine Target Environment - id: env - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "target=${{ github.event.inputs.environment }}" >> "$GITHUB_OUTPUT" - elif [ "${{ github.ref_name }}" = "main" ]; then - echo "target=prod" >> "$GITHUB_OUTPUT" - else - echo "target=beta" >> "$GITHUB_OUTPUT" - fi - - - name: Deploy to Cloudflare Workers (Beta) - if: steps.env.outputs.target == 'beta' - uses: cloudflare/wrangler-action@v3 - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - workingDirectory: 'apps/crove-sso' - command: 'deploy -e beta' - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - - - name: Deploy to Cloudflare Workers (Production) - if: steps.env.outputs.target == 'prod' - uses: cloudflare/wrangler-action@v3 - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - workingDirectory: 'apps/crove-sso' - command: 'deploy' - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/apps/crove-sso/package.json b/apps/crove-sso/package.json deleted file mode 100644 index 955b3dedcf..0000000000 --- a/apps/crove-sso/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@crove/sso", - "private": true, - "type": "module", - "scripts": { - "dev": "wrangler dev", - "test": "vitest run", - "typecheck": "tsc --noEmit", - "cf-typegen": "wrangler types --include-runtime false", - "deploy": "wrangler deploy", - "deploy:beta": "wrangler deploy -e beta", - "deploy:prod": "wrangler deploy" - }, - "devDependencies": { - "@cloudflare/vitest-pool-workers": "0.21.3", - "@cloudflare/workers-types": "5.20260813.1", - "typescript": "5.5.4", - "vitest": "4.1.10", - "wrangler": "4.123.0" - } -} diff --git a/apps/crove-sso/src/env.ts b/apps/crove-sso/src/env.ts deleted file mode 100644 index eb7e6214ae..0000000000 --- a/apps/crove-sso/src/env.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface Env { - OAUTH_STATE: DurableObjectNamespace; - UPSTREAM_AUTHORIZE_URL: string; - UPSTREAM_TOKEN_URL: string; - UPSTREAM_USERINFO_URL: string; - UPSTREAM_REDIRECT_URI: string; - UPSTREAM_CLIENT_ID: string; - UPSTREAM_CLIENT_SECRET: string; - DOWNSTREAM_REDIRECT_URI: string; - DOWNSTREAM_CLIENT_ID: string; - DOWNSTREAM_CLIENT_SECRET: string; - ALLOWED_SCOPE: string; -} diff --git a/apps/crove-sso/src/index.ts b/apps/crove-sso/src/index.ts deleted file mode 100644 index 0099148e27..0000000000 --- a/apps/crove-sso/src/index.ts +++ /dev/null @@ -1,219 +0,0 @@ -import type { Env } from './env'; -import { - createPkce, - OAuthRequestError, - oauthError, - parseAuthorizeRequest, - parseTokenRequest, - randomToken, -} from './oauth'; -import { putState, takeState } from './state-store'; - -const AUTHORIZATION_TTL_SECONDS = 600; -const BRIDGE_CODE_TTL_SECONDS = 60; - -export interface AuthorizationTransaction { - redirectUri: string; - scope: string; - downstreamState?: string; - verifier: string; -} - -async function authorize(request: Request, env: Env): Promise { - const downstream = parseAuthorizeRequest(request, env); - const upstreamState = randomToken(); - const pkce = await createPkce(); - await putState( - env, - `authorize:${upstreamState}`, - { - redirectUri: downstream.redirectUri, - scope: downstream.scope, - downstreamState: downstream.state, - verifier: pkce.verifier, - }, - AUTHORIZATION_TTL_SECONDS, - ); - - const target = new URL(env.UPSTREAM_AUTHORIZE_URL); - target.search = new URLSearchParams({ - client_id: env.UPSTREAM_CLIENT_ID, - redirect_uri: env.UPSTREAM_REDIRECT_URI, - response_type: 'code', - scope: downstream.scope, - state: upstreamState, - code_challenge: pkce.challenge, - code_challenge_method: 'S256', - }).toString(); - - return new Response(null, { - status: 302, - headers: { - Location: target.toString(), - 'Referrer-Policy': 'no-referrer', - 'Cache-Control': 'no-store', - }, - }); -} - -async function callback(request: Request, env: Env): Promise { - const params = new URL(request.url).searchParams; - const code = params.get('code'); - const state = params.get('state'); - if (!code || !state) { - throw new OAuthRequestError('invalid_request'); - } - - const transaction = await takeState(env, `authorize:${state}`); - if (!transaction) { - throw new OAuthRequestError('invalid_request'); - } - - const upstreamResponse = await globalThis.fetch(env.UPSTREAM_TOKEN_URL, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: new URLSearchParams({ - grant_type: 'authorization_code', - client_id: env.UPSTREAM_CLIENT_ID, - client_secret: env.UPSTREAM_CLIENT_SECRET, - code, - redirect_uri: env.UPSTREAM_REDIRECT_URI, - code_verifier: transaction.verifier, - }), - }); - if (!upstreamResponse.ok) { - return oauthError('temporarily_unavailable', 'Identity provider unavailable', 502); - } - - let tokenResponse: Record; - try { - tokenResponse = await upstreamResponse.json>(); - } catch { - return oauthError('temporarily_unavailable', 'Identity provider unavailable', 502); - } - if (typeof tokenResponse.access_token !== 'string' || tokenResponse.access_token.length === 0) { - return oauthError('temporarily_unavailable', 'Identity provider unavailable', 502); - } - - const bridgeCode = randomToken(); - await putState(env, `code:${bridgeCode}`, tokenResponse, BRIDGE_CODE_TTL_SECONDS); - - const target = new URL(transaction.redirectUri); - target.searchParams.set('code', bridgeCode); - if (transaction.downstreamState) { - target.searchParams.set('state', transaction.downstreamState); - } - return new Response(null, { - status: 302, - headers: { - Location: target.toString(), - 'Referrer-Policy': 'no-referrer', - 'Cache-Control': 'no-store', - }, - }); -} - -async function token(request: Request, env: Env): Promise { - const tokenRequest = await parseTokenRequest(request, env); - const tokenResponse = await takeState>(env, `code:${tokenRequest.code}`); - if (!tokenResponse) { - throw new OAuthRequestError('invalid_grant'); - } - - return Response.json(tokenResponse, { - headers: { - 'Cache-Control': 'no-store', - Pragma: 'no-cache', - }, - }); -} - -async function userinfo(request: Request, env: Env): Promise { - const authorization = request.headers.get('authorization') || ''; - const bearer = /^Bearer ([^\s]+)$/.exec(authorization)?.[1]; - if (!bearer) { - return oauthError('invalid_token', 'Bearer token required', 401); - } - - const upstreamResponse = await globalThis.fetch(env.UPSTREAM_USERINFO_URL, { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${bearer}`, - }, - }); - if (!upstreamResponse.ok) { - if (upstreamResponse.status === 401 || upstreamResponse.status === 403) { - return oauthError('invalid_token', 'Identity token rejected', 401); - } - return oauthError('temporarily_unavailable', 'Identity provider unavailable', 502); - } - - let claims: Record; - try { - claims = await upstreamResponse.json>(); - } catch { - return oauthError('temporarily_unavailable', 'Identity provider unavailable', 502); - } - if (typeof claims.sub !== 'string' || claims.sub.length === 0) { - return oauthError('invalid_token', 'Identity token rejected', 401); - } - - return Response.json(claims, { - headers: { - 'Cache-Control': 'no-store', - Pragma: 'no-cache', - }, - }); -} - -async function handleRequest(request: Request, env: Env): Promise { - const { pathname } = new URL(request.url); - try { - if (request.method === 'GET' && pathname === '/health') { - return Response.json({ status: 'ok', service: 'crove-sso' }); - } - if (request.method === 'GET' && pathname === '/authorize') { - return await authorize(request, env); - } - if (request.method === 'GET' && pathname === '/callback') { - return await callback(request, env); - } - if (request.method === 'POST' && pathname === '/token') { - return await token(request, env); - } - if (request.method === 'GET' && pathname === '/userinfo') { - return await userinfo(request, env); - } - return new Response('Not found', { status: 404 }); - } catch (error) { - if (error instanceof OAuthRequestError) { - return oauthError(error.code, 'OAuth request rejected', error.status); - } - return oauthError('server_error', 'OAuth service unavailable', 500); - } -} - -async function fetch(request: Request, env: Env): Promise { - const requestId = crypto.randomUUID(); - const endpoint = new URL(request.url).pathname; - const startedAt = performance.now(); - const response = await handleRequest(request, env); - const logEntry: Record = { - requestId, - endpoint, - status: response.status, - durationMs: Math.round(performance.now() - startedAt), - }; - if (response.status >= 400) { - logEntry.errorCode = `http_${response.status}`; - } - console.log(JSON.stringify(logEntry)); - return response; -} - -export default { fetch }; - -export { OAuthStateStore } from './state-store'; diff --git a/apps/crove-sso/src/oauth.ts b/apps/crove-sso/src/oauth.ts deleted file mode 100644 index 6f6c1aaac0..0000000000 --- a/apps/crove-sso/src/oauth.ts +++ /dev/null @@ -1,121 +0,0 @@ -import type { Env } from './env'; - -export interface AuthorizeRequest { - redirectUri: string; - scope: string; - state?: string; -} - -export interface TokenRequest { - code: string; -} - -export class OAuthRequestError extends Error { - constructor( - readonly code: string, - readonly status = 400, - ) { - super(code); - this.name = 'OAuthRequestError'; - } -} - -function base64Url(bytes: Uint8Array): string { - return btoa(String.fromCharCode(...bytes)) - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=+$/g, ''); -} - -export function randomToken(): string { - return base64Url(crypto.getRandomValues(new Uint8Array(32))); -} - -async function constantTimeEqual(actual: string, expected: string): Promise { - const encoder = new TextEncoder(); - const [actualDigest, expectedDigest] = await Promise.all([ - crypto.subtle.digest('SHA-256', encoder.encode(actual)), - crypto.subtle.digest('SHA-256', encoder.encode(expected)), - ]); - const actualBytes = new Uint8Array(actualDigest); - const expectedBytes = new Uint8Array(expectedDigest); - let difference = 0; - for (let index = 0; index < actualBytes.length; index += 1) { - difference |= actualBytes[index] ^ expectedBytes[index]; - } - return difference === 0; -} - -export function parseAuthorizeRequest(request: Request, env: Env): AuthorizeRequest { - const params = new URL(request.url).searchParams; - if (params.get('client_id') !== env.DOWNSTREAM_CLIENT_ID) { - throw new OAuthRequestError('invalid_client', 401); - } - if (params.get('redirect_uri') !== env.DOWNSTREAM_REDIRECT_URI) { - throw new OAuthRequestError('invalid_request'); - } - if (params.get('response_type') !== 'code') { - throw new OAuthRequestError('unsupported_response_type'); - } - if (params.get('scope') !== env.ALLOWED_SCOPE) { - throw new OAuthRequestError('invalid_scope'); - } - - return { - redirectUri: env.DOWNSTREAM_REDIRECT_URI, - scope: env.ALLOWED_SCOPE, - state: params.get('state') || undefined, - }; -} - -export async function createPkce(): Promise<{ verifier: string; challenge: string }> { - const verifier = randomToken(); - const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)); - return { verifier, challenge: base64Url(new Uint8Array(digest)) }; -} - -export async function parseTokenRequest(request: Request, env: Env): Promise { - const contentType = request.headers.get('content-type')?.toLowerCase() || ''; - if (!contentType.startsWith('application/x-www-form-urlencoded')) { - throw new OAuthRequestError('invalid_request'); - } - - const form = await request.formData(); - const params = new URLSearchParams(); - for (const [key, value] of form.entries()) { - if (typeof value === 'string') params.append(key, value); - } - if (params.get('grant_type') !== 'authorization_code') { - throw new OAuthRequestError('unsupported_grant_type'); - } - const clientIdMatches = params.get('client_id') === env.DOWNSTREAM_CLIENT_ID; - const secretMatches = await constantTimeEqual( - params.get('client_secret') || '', - env.DOWNSTREAM_CLIENT_SECRET, - ); - if (!clientIdMatches || !secretMatches) { - throw new OAuthRequestError('invalid_client', 401); - } - if (params.get('redirect_uri') !== env.DOWNSTREAM_REDIRECT_URI) { - throw new OAuthRequestError('invalid_grant'); - } - const code = params.get('code'); - if (!code) { - throw new OAuthRequestError('invalid_grant'); - } - - return { code }; -} - -export function oauthError(error: string, description: string, status = 400): Response { - return Response.json( - { error, error_description: description }, - { - status, - headers: { - 'Cache-Control': 'no-store', - Pragma: 'no-cache', - }, - }, - ); -} diff --git a/apps/crove-sso/src/state-store.ts b/apps/crove-sso/src/state-store.ts deleted file mode 100644 index b3ac18aaca..0000000000 --- a/apps/crove-sso/src/state-store.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { DurableObject } from 'cloudflare:workers'; - -import type { Env } from './env'; - -interface StoredEntry { - value: T; -} - -export class OAuthStateStore extends DurableObject { - constructor(ctx: DurableObjectState, env: Env) { - super(ctx, env); - ctx.blockConcurrencyWhile(async () => { - ctx.storage.sql.exec(` - CREATE TABLE IF NOT EXISTS oauth_entries ( - id TEXT PRIMARY KEY, - value TEXT NOT NULL, - expires_at INTEGER NOT NULL - ) - `); - }); - } - - async fetch(request: Request): Promise { - const path = new URL(request.url).pathname; - if (request.method === 'POST' && path === '/put') { - const entry = await request.json<{ - key: string; - value: unknown; - expiresAt: number; - }>(); - this.ctx.storage.sql.exec( - 'INSERT OR REPLACE INTO oauth_entries (id, value, expires_at) VALUES (?, ?, ?)', - entry.key, - JSON.stringify({ value: entry.value }), - entry.expiresAt, - ); - return new Response(null, { status: 204 }); - } - - if (request.method === 'POST' && path === '/take') { - const { key } = await request.json<{ key: string }>(); - const rows = [ - ...this.ctx.storage.sql.exec<{ value: string }>( - 'DELETE FROM oauth_entries WHERE id = ? AND expires_at > ? RETURNING value', - key, - Date.now(), - ), - ]; - this.ctx.storage.sql.exec('DELETE FROM oauth_entries WHERE id = ? AND expires_at <= ?', key, Date.now()); - if (rows.length !== 1) return new Response(null, { status: 404 }); - return Response.json(JSON.parse(rows[0].value) as StoredEntry); - } - - return new Response(null, { status: 404 }); - } -} - -function store(env: Env): DurableObjectStub { - return env.OAUTH_STATE.get(env.OAUTH_STATE.idFromName('oauth-state')); -} - -export async function putState( - env: Env, - key: string, - value: T, - ttlSeconds: number, -): Promise { - const response = await store(env).fetch('https://state.internal/put', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ key, value, expiresAt: Date.now() + ttlSeconds * 1000 }), - }); - if (!response.ok) throw new Error('state_store_unavailable'); -} - -export async function takeState(env: Env, key: string): Promise { - const response = await store(env).fetch('https://state.internal/take', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ key }), - }); - if (response.status === 404) return null; - if (!response.ok) throw new Error('state_store_unavailable'); - const entry = await response.json>(); - return entry.value; -} diff --git a/apps/crove-sso/test/env.d.ts b/apps/crove-sso/test/env.d.ts deleted file mode 100644 index 88936844c7..0000000000 --- a/apps/crove-sso/test/env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Env } from '../src/env'; - -declare module 'cloudflare:workers' { - interface ProvidedEnv extends Env {} -} diff --git a/apps/crove-sso/test/oauth.test.ts b/apps/crove-sso/test/oauth.test.ts deleted file mode 100644 index e5dc2bf799..0000000000 --- a/apps/crove-sso/test/oauth.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import type { Env } from '../src/env'; -import { - createPkce, - oauthError, - parseAuthorizeRequest, - parseTokenRequest, -} from '../src/oauth'; - -const env = { - DOWNSTREAM_CLIENT_ID: 'crove-postiz', - DOWNSTREAM_CLIENT_SECRET: 'bridge-secret-value', - DOWNSTREAM_REDIRECT_URI: 'https://post.crove.com/settings', - ALLOWED_SCOPE: 'openid profile email', -} as Env; - -function authorizeUrl(overrides: Record = {}) { - const params = new URLSearchParams({ - client_id: env.DOWNSTREAM_CLIENT_ID, - redirect_uri: env.DOWNSTREAM_REDIRECT_URI, - response_type: 'code', - scope: env.ALLOWED_SCOPE, - state: 'downstream-state', - ...overrides, - }); - return new Request(`https://sso.crove.com/authorize?${params}`); -} - -describe('parseAuthorizeRequest', () => { - it('accepts the exact Postiz authorization request', () => { - expect(parseAuthorizeRequest(authorizeUrl(), env)).toEqual({ - redirectUri: env.DOWNSTREAM_REDIRECT_URI, - scope: env.ALLOWED_SCOPE, - state: 'downstream-state', - }); - }); - - it.each([ - ['unknown client', { client_id: 'unknown' }, 'invalid_client'], - ['wrong redirect', { redirect_uri: 'https://evil.example/callback' }, 'invalid_request'], - ['wrong response type', { response_type: 'token' }, 'unsupported_response_type'], - ['extra scope', { scope: 'openid profile email admin' }, 'invalid_scope'], - ])('rejects %s', (_name, overrides, code) => { - expect(() => parseAuthorizeRequest(authorizeUrl(overrides), env)).toThrow(code); - }); -}); - -describe('createPkce', () => { - it('returns a valid S256 verifier and challenge', async () => { - const { verifier, challenge } = await createPkce(); - const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)); - const expected = btoa(String.fromCharCode(...new Uint8Array(digest))) - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=+$/g, ''); - - expect(verifier).toMatch(/^[A-Za-z0-9_-]{43}$/); - expect(challenge).toBe(expected); - }); -}); - -function tokenRequest(overrides: Record = {}, contentType = 'application/x-www-form-urlencoded') { - const body = new URLSearchParams({ - grant_type: 'authorization_code', - client_id: env.DOWNSTREAM_CLIENT_ID, - client_secret: env.DOWNSTREAM_CLIENT_SECRET, - redirect_uri: env.DOWNSTREAM_REDIRECT_URI, - code: 'single-use-code', - ...overrides, - }); - return new Request('https://sso.crove.com/token', { - method: 'POST', - headers: { 'Content-Type': contentType }, - body, - }); -} - -describe('parseTokenRequest', () => { - it('accepts the exact Postiz token request', async () => { - await expect(parseTokenRequest(tokenRequest(), env)).resolves.toEqual({ code: 'single-use-code' }); - }); - - it.each([ - ['wrong grant type', { grant_type: 'refresh_token' }, 'unsupported_grant_type'], - ['unknown client', { client_id: 'unknown' }, 'invalid_client'], - ['invalid client secret', { client_secret: 'wrong-secret' }, 'invalid_client'], - ['wrong redirect', { redirect_uri: 'https://evil.example/callback' }, 'invalid_grant'], - ['missing code', { code: '' }, 'invalid_grant'], - ])('rejects %s', async (_name, overrides, code) => { - await expect(parseTokenRequest(tokenRequest(overrides), env)).rejects.toThrow(code); - }); - - it('rejects a non-form request', async () => { - await expect(parseTokenRequest(tokenRequest({}, 'application/json'), env)).rejects.toThrow( - 'invalid_request', - ); - }); -}); - -describe('oauthError', () => { - it('returns a safe no-store OAuth response', async () => { - const response = oauthError('invalid_grant', 'Authorization code is invalid', 400); - expect(response.status).toBe(400); - expect(response.headers.get('cache-control')).toBe('no-store'); - expect(response.headers.get('pragma')).toBe('no-cache'); - await expect(response.json()).resolves.toEqual({ - error: 'invalid_grant', - error_description: 'Authorization code is invalid', - }); - }); -}); diff --git a/apps/crove-sso/test/state-store.test.ts b/apps/crove-sso/test/state-store.test.ts deleted file mode 100644 index 3ee490e97f..0000000000 --- a/apps/crove-sso/test/state-store.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { env } from 'cloudflare:workers'; -import { evictDurableObject } from 'cloudflare:test'; -import { describe, expect, it } from 'vitest'; - -import type { Env } from '../src/env'; -import { putState, takeState } from '../src/state-store'; - -const workerEnv = env as unknown as Env; - -describe('OAuthStateStore', () => { - it('returns a stored value exactly once', async () => { - const key = crypto.randomUUID(); - await putState(workerEnv, key, { state: 'stored' }, 60); - - await expect(takeState<{ state: string }>(workerEnv, key)).resolves.toEqual({ state: 'stored' }); - await expect(takeState(workerEnv, key)).resolves.toBeNull(); - }); - - it('does not return an expired value', async () => { - const key = crypto.randomUUID(); - await putState(workerEnv, key, { state: 'expired' }, 0); - - await expect(takeState(workerEnv, key)).resolves.toBeNull(); - }); - - it('allows exactly one concurrent consumer', async () => { - const key = crypto.randomUUID(); - await putState(workerEnv, key, { state: 'race' }, 60); - - const values = await Promise.all([ - takeState<{ state: string }>(workerEnv, key), - takeState<{ state: string }>(workerEnv, key), - ]); - - expect(values.filter((value) => value !== null)).toHaveLength(1); - expect(values.find((value) => value !== null)).toEqual({ state: 'race' }); - }); - - it('preserves state across Durable Object eviction', async () => { - const key = crypto.randomUUID(); - await putState(workerEnv, key, { state: 'persisted' }, 60); - const id = workerEnv.OAUTH_STATE.idFromName('oauth-state'); - await evictDurableObject(workerEnv.OAUTH_STATE.get(id)); - - await expect(takeState<{ state: string }>(workerEnv, key)).resolves.toEqual({ - state: 'persisted', - }); - }); -}); diff --git a/apps/crove-sso/test/worker.test.ts b/apps/crove-sso/test/worker.test.ts deleted file mode 100644 index b928d2b216..0000000000 --- a/apps/crove-sso/test/worker.test.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { env, exports } from 'cloudflare:workers'; -import { afterEach, describe, expect, it, vi } from 'vitest'; - -import type { Env } from '../src/env'; - -const workerEnv = env as unknown as Env; -const worker = exports as unknown as { - default: { fetch(request: Request): Promise }; -}; - -afterEach(() => { - vi.restoreAllMocks(); -}); - -function authorizeUrl(overrides: Record = {}) { - const params = new URLSearchParams({ - client_id: workerEnv.DOWNSTREAM_CLIENT_ID, - redirect_uri: workerEnv.DOWNSTREAM_REDIRECT_URI, - response_type: 'code', - scope: workerEnv.ALLOWED_SCOPE, - state: 'postiz-state', - ...overrides, - }); - return new Request(`https://sso.crove.com/authorize?${params}`, { redirect: 'manual' }); -} - -async function issueBridgeCode(): Promise { - const authorization = await worker.default.fetch(authorizeUrl()); - const upstreamState = new URL(authorization.headers.get('location')!).searchParams.get('state')!; - vi.spyOn(globalThis, 'fetch').mockImplementation(async () => - Response.json({ - access_token: 'upstream-access-token', - token_type: 'bearer', - expires_in: 3600, - refresh_token: 'upstream-refresh-token', - }), - ); - const callback = await worker.default.fetch( - new Request(`https://sso.crove.com/callback?code=supabase-code&state=${upstreamState}`, { - redirect: 'manual', - }), - ); - vi.restoreAllMocks(); - return new URL(callback.headers.get('location')!).searchParams.get('code')!; -} - -function tokenRequest(code: string, overrides: Record = {}): Request { - return new Request('https://sso.crove.com/token', { - method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ - grant_type: 'authorization_code', - client_id: workerEnv.DOWNSTREAM_CLIENT_ID, - client_secret: workerEnv.DOWNSTREAM_CLIENT_SECRET, - redirect_uri: workerEnv.DOWNSTREAM_REDIRECT_URI, - code, - ...overrides, - }), - }); -} - -describe('Crove SSO Worker', () => { - it('serves a static health response', async () => { - const response = await worker.default.fetch(new Request('https://sso.crove.com/health')); - expect(response.status).toBe(200); - await expect(response.json()).resolves.toEqual({ status: 'ok', service: 'crove-sso' }); - }); - - it('redirects an exact authorization request to DOS ID with S256 PKCE', async () => { - const response = await worker.default.fetch(authorizeUrl()); - expect(response.status).toBe(302); - const location = new URL(response.headers.get('location')!); - expect(location.origin + location.pathname).toBe(workerEnv.UPSTREAM_AUTHORIZE_URL); - expect(location.searchParams.get('client_id')).toBe(workerEnv.UPSTREAM_CLIENT_ID); - expect(location.searchParams.get('redirect_uri')).toBe(workerEnv.UPSTREAM_REDIRECT_URI); - expect(location.searchParams.get('response_type')).toBe('code'); - expect(location.searchParams.get('scope')).toBe(workerEnv.ALLOWED_SCOPE); - expect(location.searchParams.get('code_challenge_method')).toBe('S256'); - expect(location.searchParams.get('code_challenge')).toMatch(/^[A-Za-z0-9_-]{43}$/); - expect(location.searchParams.get('state')).toMatch(/^[A-Za-z0-9_-]{43}$/); - expect(location.searchParams.get('state')).not.toContain('postiz-state'); - expect(response.headers.get('referrer-policy')).toBe('no-referrer'); - }); - - it('rejects an invalid authorization request without redirecting', async () => { - const response = await worker.default.fetch( - authorizeUrl({ redirect_uri: 'https://evil.example/callback' }), - ); - expect(response.status).toBe(400); - expect(response.headers.get('location')).toBeNull(); - await expect(response.json()).resolves.toMatchObject({ error: 'invalid_request' }); - }); - - it('exchanges an upstream callback and preserves downstream state', async () => { - const authorization = await worker.default.fetch(authorizeUrl()); - const upstreamState = new URL(authorization.headers.get('location')!).searchParams.get('state')!; - let submittedVerifier = ''; - vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => { - const request = new Request(input, init); - expect(request.url).toBe(workerEnv.UPSTREAM_TOKEN_URL); - const form = await request.formData(); - submittedVerifier = String(form.get('code_verifier')); - expect(form.get('code')).toBe('supabase-code'); - expect(form.get('client_id')).toBe(workerEnv.UPSTREAM_CLIENT_ID); - expect(form.get('client_secret')).toBe(workerEnv.UPSTREAM_CLIENT_SECRET); - expect(form.get('redirect_uri')).toBe(workerEnv.UPSTREAM_REDIRECT_URI); - return Response.json({ - access_token: 'upstream-access-token', - token_type: 'bearer', - expires_in: 3600, - refresh_token: 'upstream-refresh-token', - }); - }); - - const callback = await worker.default.fetch( - new Request( - `https://sso.crove.com/callback?code=supabase-code&state=${encodeURIComponent(upstreamState)}`, - { redirect: 'manual' }, - ), - ); - - expect(callback.status).toBe(302); - expect(submittedVerifier).toMatch(/^[A-Za-z0-9_-]{43}$/); - expect(callback.headers.get('referrer-policy')).toBe('no-referrer'); - const downstream = new URL(callback.headers.get('location')!); - expect(downstream.origin + downstream.pathname).toBe(workerEnv.DOWNSTREAM_REDIRECT_URI); - expect(downstream.searchParams.get('state')).toBe('postiz-state'); - expect(downstream.searchParams.get('code')).toMatch(/^[A-Za-z0-9_-]{43}$/); - }); - - it('rejects a replayed upstream callback', async () => { - const authorization = await worker.default.fetch(authorizeUrl()); - const upstreamState = new URL(authorization.headers.get('location')!).searchParams.get('state')!; - vi.spyOn(globalThis, 'fetch').mockImplementation(async () => - Response.json({ access_token: 'upstream-access-token', token_type: 'bearer' }), - ); - const callbackUrl = `https://sso.crove.com/callback?code=supabase-code&state=${upstreamState}`; - - expect((await worker.default.fetch(new Request(callbackUrl, { redirect: 'manual' }))).status).toBe(302); - const replay = await worker.default.fetch(new Request(callbackUrl, { redirect: 'manual' })); - expect(replay.status).toBe(400); - await expect(replay.json()).resolves.toMatchObject({ error: 'invalid_request' }); - }); - - it('does not expose an upstream token error body', async () => { - const authorization = await worker.default.fetch(authorizeUrl()); - const upstreamState = new URL(authorization.headers.get('location')!).searchParams.get('state')!; - vi.spyOn(globalThis, 'fetch').mockImplementation(async () => - Response.json({ error: 'invalid_client', leaked: 'upstream-secret-body' }, { status: 401 }), - ); - - const response = await worker.default.fetch( - new Request(`https://sso.crove.com/callback?code=bad&state=${upstreamState}`), - ); - expect(response.status).toBe(502); - expect(await response.text()).not.toContain('upstream-secret-body'); - }); - - it('exchanges a bridge code for the upstream token response', async () => { - const bridgeCode = await issueBridgeCode(); - const response = await worker.default.fetch(tokenRequest(bridgeCode)); - - expect(response.status).toBe(200); - expect(response.headers.get('cache-control')).toBe('no-store'); - expect(response.headers.get('pragma')).toBe('no-cache'); - await expect(response.json()).resolves.toEqual({ - access_token: 'upstream-access-token', - token_type: 'bearer', - expires_in: 3600, - refresh_token: 'upstream-refresh-token', - }); - }); - - it('rejects a replayed bridge code', async () => { - const bridgeCode = await issueBridgeCode(); - expect((await worker.default.fetch(tokenRequest(bridgeCode))).status).toBe(200); - - const replay = await worker.default.fetch(tokenRequest(bridgeCode)); - expect(replay.status).toBe(400); - await expect(replay.json()).resolves.toMatchObject({ error: 'invalid_grant' }); - }); - - it('rejects an invalid downstream secret without consuming the bridge code', async () => { - const bridgeCode = await issueBridgeCode(); - const invalid = await worker.default.fetch( - tokenRequest(bridgeCode, { client_secret: 'wrong-secret' }), - ); - expect(invalid.status).toBe(401); - await expect(invalid.json()).resolves.toMatchObject({ error: 'invalid_client' }); - - expect((await worker.default.fetch(tokenRequest(bridgeCode))).status).toBe(200); - }); - - it('rejects a wrong downstream redirect URI without consuming the bridge code', async () => { - const bridgeCode = await issueBridgeCode(); - const invalid = await worker.default.fetch( - tokenRequest(bridgeCode, { redirect_uri: 'https://evil.example/callback' }), - ); - expect(invalid.status).toBe(400); - await expect(invalid.json()).resolves.toMatchObject({ error: 'invalid_grant' }); - - expect((await worker.default.fetch(tokenRequest(bridgeCode))).status).toBe(200); - }); - - it('proxies userinfo with the bearer token and strips upstream headers', async () => { - const log = vi.spyOn(console, 'log').mockImplementation(() => undefined); - vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => { - const upstream = new Request(input, init); - expect(upstream.url).toBe(workerEnv.UPSTREAM_USERINFO_URL); - expect(upstream.headers.get('authorization')).toBe('Bearer upstream-access-token'); - return Response.json( - { sub: 'dos-id-subject', email: 'joy@dos.ai', email_verified: true, name: 'JOY' }, - { headers: { 'Set-Cookie': 'should-not-pass=1', 'X-Upstream': 'private' } }, - ); - }); - - const response = await worker.default.fetch( - new Request('https://sso.crove.com/userinfo', { - headers: { Authorization: 'Bearer upstream-access-token' }, - }), - ); - expect(response.status).toBe(200); - expect(response.headers.get('set-cookie')).toBeNull(); - expect(response.headers.get('x-upstream')).toBeNull(); - expect(response.headers.get('cache-control')).toBe('no-store'); - await expect(response.json()).resolves.toEqual({ - sub: 'dos-id-subject', - email: 'joy@dos.ai', - email_verified: true, - name: 'JOY', - }); - const logOutput = log.mock.calls.flat().join(' '); - expect(logOutput).toContain('"endpoint":"/userinfo"'); - expect(logOutput).not.toContain('upstream-access-token'); - expect(logOutput).not.toContain('joy@dos.ai'); - expect(logOutput).not.toContain('dos-id-subject'); - }); - - it('rejects userinfo without an exact bearer token', async () => { - const response = await worker.default.fetch(new Request('https://sso.crove.com/userinfo')); - expect(response.status).toBe(401); - await expect(response.json()).resolves.toMatchObject({ error: 'invalid_token' }); - }); - - it('does not expose an upstream userinfo error body', async () => { - vi.spyOn(globalThis, 'fetch').mockImplementation(async () => - Response.json({ leaked: 'upstream-private-error' }, { status: 401 }), - ); - const response = await worker.default.fetch( - new Request('https://sso.crove.com/userinfo', { - headers: { Authorization: 'Bearer bad-token' }, - }), - ); - expect(response.status).toBe(401); - expect(await response.text()).not.toContain('upstream-private-error'); - }); -}); diff --git a/apps/crove-sso/tsconfig.json b/apps/crove-sso/tsconfig.json deleted file mode 100644 index ad9b11aed0..0000000000 --- a/apps/crove-sso/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "Bundler", - "lib": ["ES2022", "WebWorker"], - "types": ["@cloudflare/workers-types", "@cloudflare/vitest-pool-workers/types", "vitest/globals"], - "strict": true, - "noEmit": true, - "skipLibCheck": true - }, - "include": ["worker-configuration.d.ts", "src/**/*.ts", "test/**/*.ts", "vitest.config.ts"] -} diff --git a/apps/crove-sso/vitest.config.ts b/apps/crove-sso/vitest.config.ts deleted file mode 100644 index 4ecdecb3b8..0000000000 --- a/apps/crove-sso/vitest.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { cloudflareTest } from '@cloudflare/vitest-pool-workers'; -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - plugins: [ - cloudflareTest({ - wrangler: { configPath: './wrangler.jsonc' }, - miniflare: { - bindings: { - UPSTREAM_CLIENT_ID: 'supabase-crove-client', - UPSTREAM_CLIENT_SECRET: 'supabase-crove-secret', - DOWNSTREAM_CLIENT_ID: 'crove-postiz', - DOWNSTREAM_CLIENT_SECRET: 'bridge-secret-value', - }, - }, - }), - ], - test: { - globals: true, - }, -}); diff --git a/apps/crove-sso/worker-configuration.d.ts b/apps/crove-sso/worker-configuration.d.ts deleted file mode 100644 index db30f51567..0000000000 --- a/apps/crove-sso/worker-configuration.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable */ -// Generated by Wrangler by running `wrangler types --include-runtime=false` (hash: 5c92285b797f3da694d2250982e79c06) -interface __BaseEnv_Env { - UPSTREAM_AUTHORIZE_URL: "https://gulptwduchsjcsbndmua.supabase.co/auth/v1/oauth/authorize"; - UPSTREAM_TOKEN_URL: "https://gulptwduchsjcsbndmua.supabase.co/auth/v1/oauth/token"; - UPSTREAM_USERINFO_URL: "https://gulptwduchsjcsbndmua.supabase.co/auth/v1/oauth/userinfo"; - UPSTREAM_REDIRECT_URI: "https://sso.crove.com/callback"; - DOWNSTREAM_REDIRECT_URI: "https://post.crove.com/settings"; - ALLOWED_SCOPE: "openid profile email"; - OAUTH_STATE: DurableObjectNamespace; -} -declare namespace Cloudflare { - interface GlobalProps { - mainModule: typeof import("./src/index"); - durableNamespaces: "OAuthStateStore"; - } - interface Env extends __BaseEnv_Env {} -} -interface Env extends __BaseEnv_Env {} -type StringifyValues> = { - [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string; -}; -declare namespace NodeJS { - interface ProcessEnv extends StringifyValues> {} -} diff --git a/apps/crove-sso/wrangler.jsonc b/apps/crove-sso/wrangler.jsonc deleted file mode 100644 index b3a9f1c24c..0000000000 --- a/apps/crove-sso/wrangler.jsonc +++ /dev/null @@ -1,76 +0,0 @@ -{ - "$schema": "node_modules/wrangler/config-schema.json", - "name": "crove-sso", - "account_id": "5f2a58925e790423dfafa0e6bee46b28", - "main": "src/index.ts", - "compatibility_date": "2026-08-13", - "compatibility_flags": ["nodejs_compat"], - "routes": [ - { - "pattern": "sso.crove.com", - "custom_domain": true - } - ], - "observability": { - "enabled": true - }, - "durable_objects": { - "bindings": [ - { - "name": "OAUTH_STATE", - "class_name": "OAuthStateStore" - } - ] - }, - "migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["OAuthStateStore"] - } - ], - "vars": { - "UPSTREAM_AUTHORIZE_URL": "https://gulptwduchsjcsbndmua.supabase.co/auth/v1/oauth/authorize", - "UPSTREAM_TOKEN_URL": "https://gulptwduchsjcsbndmua.supabase.co/auth/v1/oauth/token", - "UPSTREAM_USERINFO_URL": "https://gulptwduchsjcsbndmua.supabase.co/auth/v1/oauth/userinfo", - "UPSTREAM_REDIRECT_URI": "https://sso.crove.com/callback", - "UPSTREAM_CLIENT_ID": "18790ccb-4d71-48cd-ad24-aee5f3ced3da", - "DOWNSTREAM_REDIRECT_URI": "https://post.crove.com/settings", - "DOWNSTREAM_CLIENT_ID": "crove-postiz", - "ALLOWED_SCOPE": "openid profile email" - }, - "env": { - "beta": { - "name": "crove-sso-beta", - "routes": [ - { - "pattern": "beta-sso.crove.com", - "custom_domain": true - } - ], - "durable_objects": { - "bindings": [ - { - "name": "OAUTH_STATE", - "class_name": "OAuthStateStore" - } - ] - }, - "migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["OAuthStateStore"] - } - ], - "vars": { - "UPSTREAM_AUTHORIZE_URL": "https://oyajkbadsykigtfrpdpg.supabase.co/auth/v1/oauth/authorize", - "UPSTREAM_TOKEN_URL": "https://oyajkbadsykigtfrpdpg.supabase.co/auth/v1/oauth/token", - "UPSTREAM_USERINFO_URL": "https://oyajkbadsykigtfrpdpg.supabase.co/auth/v1/oauth/userinfo", - "UPSTREAM_REDIRECT_URI": "https://beta-sso.crove.com/callback", - "UPSTREAM_CLIENT_ID": "7ef5e5f1-68e6-42a7-901e-1f39e9471d24", - "DOWNSTREAM_REDIRECT_URI": "https://beta-post.crove.com/settings", - "DOWNSTREAM_CLIENT_ID": "crove-postiz", - "ALLOWED_SCOPE": "openid profile email" - } - } - } -} diff --git a/docs/beta-environment.md b/docs/beta-environment.md index 1693cf70aa..9fd7473c04 100644 --- a/docs/beta-environment.md +++ b/docs/beta-environment.md @@ -10,7 +10,6 @@ Tài liệu này mô tả chi tiết kiến trúc phân chia 2 môi trường ** | :--- | :--- | :--- | :--- | | **Landing Page** (`apps/web`) | `https://crove.com`
`https://www.crove.com` | `https://beta.crove.com` | Next.js 16 App Router, đa ngôn ngữ VI/EN, Dark/Light theme | | **App Dashboard** (`apps/frontend` + `backend`) | `https://post.crove.com` | `https://beta-post.crove.com` | Core App Postiz, quản lý 28+ mạng xã hội | -| **SSO Worker** (`apps/crove-sso`) | `https://sso.crove.com` | `https://beta-sso.crove.com` | Cloudflare Worker OAuth 2.1 PKCE Bridge kết nối Supabase | | **Supabase Client ID** | `18790ccb-4d71-48cd-ad24-aee5f3ced3da` | `7ef5e5f1-68e6-42a7-901e-1f39e9471d24` | Ứng dụng OAuth riêng biệt trên Supabase / DOS ID | | **Docker Compose Stack** | `scripts/docker-compose.prod.yaml` | `scripts/docker-compose.beta.yaml` | Độc lập về Database & Redis, chia sẻ Temporal cluster | | **Environment File** | `scripts/crove-server.env` | `scripts/crove-server.beta.env` | Chứa các biến cấu hình riêng biệt | diff --git a/docs/cicd.md b/docs/cicd.md index 5acdcd86f2..299acec488 100644 --- a/docs/cicd.md +++ b/docs/cicd.md @@ -8,7 +8,6 @@ The Crove Post CI/CD system provides end-to-end automation: source code verifica | Workflow | File | Trigger | Purpose | | :--- | :--- | :--- | :--- | -| **Deploy SSO Bridge** | `.github/workflows/deploy-sso.yml` | Push to `apps/crove-sso/**` or dispatch | Runs Vitest (31 tests) + deploys Cloudflare Worker to `sso.crove.com` (main) or `beta-sso.crove.com` (dev). | | **Build & Deploy Containers** | `.github/workflows/build-deploy-crove.yml` | Push to `dev` or published release | Runs Branding Guard -> Builds & pushes GHCR image `ghcr.io/dos/crove-post` (`beta` / `latest`). | | **Build & Publish Containers** | `.github/workflows/build-containers.yml` | Push to `main` / `dev` or tags | Multi-arch container build (amd64 + arm64) for `ghcr.io/dos/crove-post`. | | **Branding Guard CI** | `.github/workflows/branding-guard.yml` | Push / PR to `main` or `dev` | Validates runtime branding engine contracts and AGPL-3.0 compliance. | diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1bf278495f..534dc91744 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -833,24 +833,6 @@ importers: apps/commands: {} - apps/crove-sso: - devDependencies: - '@cloudflare/vitest-pool-workers': - specifier: 0.21.3 - version: 0.21.3(@cloudflare/workers-types@5.20260813.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vitest@4.1.10(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(@vitest/coverage-v8@1.6.0(vitest@3.1.4))(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jsdom@29.1.0(@noble/hashes@2.0.1)(canvas@2.11.2))(vite@8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3))) - '@cloudflare/workers-types': - specifier: 5.20260813.1 - version: 5.20260813.1 - typescript: - specifier: 5.5.4 - version: 5.5.4 - vitest: - specifier: 4.1.10 - version: 4.1.10(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(@vitest/coverage-v8@1.6.0(vitest@3.1.4))(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jsdom@29.1.0(@noble/hashes@2.0.1)(canvas@2.11.2))(vite@8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3)) - wrangler: - specifier: 4.123.0 - version: 4.123.0(@cloudflare/workers-types@5.20260813.1)(bufferutil@4.1.0)(utf-8-validate@6.0.6) - apps/extension: {} apps/frontend: {} @@ -2001,59 +1983,6 @@ packages: '@clack/prompts@1.2.0': resolution: {integrity: sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w==} - '@cloudflare/kv-asset-handler@0.5.0': - resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} - engines: {node: '>=22.0.0'} - - '@cloudflare/unenv-preset@2.16.1': - resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} - peerDependencies: - unenv: 2.0.0-rc.24 - workerd: '>1.20260305.0 <2.0.0-0' - peerDependenciesMeta: - workerd: - optional: true - - '@cloudflare/vitest-pool-workers@0.21.3': - resolution: {integrity: sha512-jCoGRQ6FlsP5adp1GJISvITOGdTHTpsWOq3KkSGIfeDY/5RKjTUagDwaXjpqBXY5gAk6id/QbgnGuCTAg6lSTQ==} - peerDependencies: - '@vitest/runner': ^4.1.0 - '@vitest/snapshot': ^4.1.0 - vitest: ^4.1.0 - - '@cloudflare/workerd-darwin-64@1.20260811.1': - resolution: {integrity: sha512-i5jqz+ywtOefr0AJbiAc8qxBLfSim/B0WJG7aW3B+pWnoVfMJdUQvi+BWcFKZJ0MoCci3KadTx6g31VfuEEqpQ==} - engines: {node: '>=16'} - cpu: [x64] - os: [darwin] - - '@cloudflare/workerd-darwin-arm64@1.20260811.1': - resolution: {integrity: sha512-NoOUM/nvaDdm2Onlnz33FikWjtatzulNtvwvy4xs0IrHaTCHwC0c8NwIt6s+AI13FkDs02/vm2I3GTPLCT9+hQ==} - engines: {node: '>=16'} - cpu: [arm64] - os: [darwin] - - '@cloudflare/workerd-linux-64@1.20260811.1': - resolution: {integrity: sha512-sdYq2jL1AD1supa3fsi5O4zTB28wSjvTHj7Migh6/ts8EROPdvrSwv+rdGHhv8HJNAz/wbIAY3wZsi1Rw4uUIg==} - engines: {node: '>=16'} - cpu: [x64] - os: [linux] - - '@cloudflare/workerd-linux-arm64@1.20260811.1': - resolution: {integrity: sha512-RIRv4shbu1kg05sD+DHTpSFCNnb5Dl2SkPDMUykqZa508tkPqe7VVw7gO0Q5msTBGyL0FfFrLuRxwwfA8u5Sow==} - engines: {node: '>=16'} - cpu: [arm64] - os: [linux] - - '@cloudflare/workerd-windows-64@1.20260811.1': - resolution: {integrity: sha512-g6VquwjASlYAibcNW/0E6Zszht4qLkmnXOGwIjjRHl2A0Qz48kVeMcGvyH6eA0G9U3OzZojjYFpP+YeyQmmdjw==} - engines: {node: '>=16'} - cpu: [x64] - os: [win32] - - '@cloudflare/workers-types@5.20260813.1': - resolution: {integrity: sha512-RQNfm7xD10hNHEQZFxQPmyGMJ9+aDGPcdFZ0x1LtmjRoLFcgZkGvfaqJAbOMQBAUFSESO3bYJS4p9mOLv28Ihg==} - '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -2255,12 +2184,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} @@ -2279,12 +2202,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} @@ -2303,12 +2220,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} @@ -2327,12 +2238,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} @@ -2351,12 +2256,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} @@ -2375,12 +2274,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} @@ -2399,12 +2292,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} @@ -2423,12 +2310,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} @@ -2447,12 +2328,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} @@ -2471,12 +2346,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} @@ -2495,12 +2364,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} @@ -2519,12 +2382,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} @@ -2543,12 +2400,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} @@ -2567,12 +2418,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} @@ -2591,12 +2436,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} @@ -2615,12 +2454,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} @@ -2639,12 +2472,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} @@ -2663,12 +2490,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} @@ -2687,12 +2508,6 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} @@ -2711,12 +2526,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} @@ -2735,12 +2544,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} @@ -2759,12 +2562,6 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} @@ -2783,12 +2580,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} @@ -2807,12 +2598,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} @@ -2831,12 +2616,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} @@ -2855,12 +2634,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3078,12 +2851,6 @@ packages: cpu: [arm64] os: [darwin] - '@img/sharp-darwin-arm64@0.35.2': - resolution: {integrity: sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [darwin] - '@img/sharp-darwin-arm64@0.35.3': resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} engines: {node: '>=20.9.0'} @@ -3096,23 +2863,12 @@ packages: cpu: [x64] os: [darwin] - '@img/sharp-darwin-x64@0.35.2': - resolution: {integrity: sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [darwin] - '@img/sharp-darwin-x64@0.35.3': resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-freebsd-wasm32@0.35.2': - resolution: {integrity: sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==} - engines: {node: '>=20.9.0'} - os: [freebsd] - '@img/sharp-freebsd-wasm32@0.35.3': resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} engines: {node: '>=20.9.0'} @@ -3123,11 +2879,6 @@ packages: cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.3.1': - resolution: {integrity: sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==} - cpu: [arm64] - os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.3.2': resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} cpu: [arm64] @@ -3138,11 +2889,6 @@ packages: cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.3.1': - resolution: {integrity: sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==} - cpu: [x64] - os: [darwin] - '@img/sharp-libvips-darwin-x64@1.3.2': resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} cpu: [x64] @@ -3153,11 +2899,6 @@ packages: cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm64@1.3.1': - resolution: {integrity: sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==} - cpu: [arm64] - os: [linux] - '@img/sharp-libvips-linux-arm64@1.3.2': resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} cpu: [arm64] @@ -3168,31 +2909,16 @@ packages: cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-arm@1.3.1': - resolution: {integrity: sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==} - cpu: [arm] - os: [linux] - '@img/sharp-libvips-linux-arm@1.3.2': resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.3.1': - resolution: {integrity: sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==} - cpu: [ppc64] - os: [linux] - '@img/sharp-libvips-linux-ppc64@1.3.2': resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-riscv64@1.3.1': - resolution: {integrity: sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==} - cpu: [riscv64] - os: [linux] - '@img/sharp-libvips-linux-riscv64@1.3.2': resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} cpu: [riscv64] @@ -3203,11 +2929,6 @@ packages: cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-s390x@1.3.1': - resolution: {integrity: sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==} - cpu: [s390x] - os: [linux] - '@img/sharp-libvips-linux-s390x@1.3.2': resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} cpu: [s390x] @@ -3218,11 +2939,6 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-libvips-linux-x64@1.3.1': - resolution: {integrity: sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==} - cpu: [x64] - os: [linux] - '@img/sharp-libvips-linux-x64@1.3.2': resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} cpu: [x64] @@ -3233,11 +2949,6 @@ packages: cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.3.1': - resolution: {integrity: sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==} - cpu: [arm64] - os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} cpu: [arm64] @@ -3248,11 +2959,6 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.3.1': - resolution: {integrity: sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==} - cpu: [x64] - os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.3.2': resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} cpu: [x64] @@ -3264,12 +2970,6 @@ packages: cpu: [arm64] os: [linux] - '@img/sharp-linux-arm64@0.35.2': - resolution: {integrity: sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [linux] - '@img/sharp-linux-arm64@0.35.3': resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} engines: {node: '>=20.9.0'} @@ -3282,36 +2982,18 @@ packages: cpu: [arm] os: [linux] - '@img/sharp-linux-arm@0.35.2': - resolution: {integrity: sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==} - engines: {node: '>=20.9.0'} - cpu: [arm] - os: [linux] - '@img/sharp-linux-arm@0.35.3': resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.35.2': - resolution: {integrity: sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==} - engines: {node: '>=20.9.0'} - cpu: [ppc64] - os: [linux] - '@img/sharp-linux-ppc64@0.35.3': resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] - '@img/sharp-linux-riscv64@0.35.2': - resolution: {integrity: sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==} - engines: {node: '>=20.9.0'} - cpu: [riscv64] - os: [linux] - '@img/sharp-linux-riscv64@0.35.3': resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} engines: {node: '>=20.9.0'} @@ -3324,12 +3006,6 @@ packages: cpu: [s390x] os: [linux] - '@img/sharp-linux-s390x@0.35.2': - resolution: {integrity: sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==} - engines: {node: '>=20.9.0'} - cpu: [s390x] - os: [linux] - '@img/sharp-linux-s390x@0.35.3': resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} engines: {node: '>=20.9.0'} @@ -3342,12 +3018,6 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-linux-x64@0.35.2': - resolution: {integrity: sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [linux] - '@img/sharp-linux-x64@0.35.3': resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} engines: {node: '>=20.9.0'} @@ -3360,12 +3030,6 @@ packages: cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.35.2': - resolution: {integrity: sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [linux] - '@img/sharp-linuxmusl-arm64@0.35.3': resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} engines: {node: '>=20.9.0'} @@ -3378,12 +3042,6 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-x64@0.35.2': - resolution: {integrity: sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [linux] - '@img/sharp-linuxmusl-x64@0.35.3': resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} engines: {node: '>=20.9.0'} @@ -3395,30 +3053,15 @@ packages: engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-wasm32@0.35.2': - resolution: {integrity: sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==} - engines: {node: '>=20.9.0'} - '@img/sharp-wasm32@0.35.3': resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} engines: {node: '>=20.9.0'} - '@img/sharp-webcontainers-wasm32@0.35.2': - resolution: {integrity: sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==} - engines: {node: '>=20.9.0'} - cpu: [wasm32] - '@img/sharp-webcontainers-wasm32@0.35.3': resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.35.2': - resolution: {integrity: sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [win32] - '@img/sharp-win32-arm64@0.35.3': resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} engines: {node: '>=20.9.0'} @@ -3431,12 +3074,6 @@ packages: cpu: [ia32] os: [win32] - '@img/sharp-win32-ia32@0.35.2': - resolution: {integrity: sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==} - engines: {node: ^20.9.0} - cpu: [ia32] - os: [win32] - '@img/sharp-win32-ia32@0.35.3': resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} engines: {node: ^20.9.0} @@ -3449,12 +3086,6 @@ packages: cpu: [x64] os: [win32] - '@img/sharp-win32-x64@0.35.2': - resolution: {integrity: sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [win32] - '@img/sharp-win32-x64@0.35.3': resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} engines: {node: '>=20.9.0'} @@ -6419,15 +6050,6 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@poppinss/colors@4.1.6': - resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} - - '@poppinss/dumper@0.6.5': - resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} - - '@poppinss/exception@1.2.3': - resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} - '@posthog/core@1.23.2': resolution: {integrity: sha512-zTDdda9NuSHrnwSOfFMxX/pyXiycF4jtU1kTr8DL61dHhV+7LF6XF1ndRZZTuaGGbfbb/GJYkEsjEX9SXfNZeQ==} @@ -7530,10 +7152,6 @@ packages: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - '@sindresorhus/is@7.2.0': - resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} - engines: {node: '>=18'} - '@sindresorhus/merge-streams@4.0.0': resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} @@ -8127,9 +7745,6 @@ packages: peerDependencies: '@solana/web3.js': '*' - '@speed-highlight/core@1.2.24': - resolution: {integrity: sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==} - '@standard-community/standard-json@0.3.5': resolution: {integrity: sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA==} peerDependencies: @@ -8852,9 +8467,6 @@ packages: '@types/caseless@0.12.5': resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/chrome@0.0.319': resolution: {integrity: sha512-k+E1b3VrWEj7OqQ22cPxHs3o/jrYWEEDGaocwjlIGtul+WrAn1vaBANc0iurgm0aXkyIVs7aH3ce0lpr96XYhw==} @@ -8878,9 +8490,6 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/diff-match-patch@1.0.36': resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} @@ -9670,9 +9279,6 @@ packages: '@vitest/expect@3.1.4': resolution: {integrity: sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==} - '@vitest/expect@4.1.10': - resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} - '@vitest/mocker@3.1.4': resolution: {integrity: sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==} peerDependencies: @@ -9684,44 +9290,21 @@ packages: vite: optional: true - '@vitest/mocker@4.1.10': - resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - '@vitest/pretty-format@3.1.4': resolution: {integrity: sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==} '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@4.1.10': - resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} - '@vitest/runner@3.1.4': resolution: {integrity: sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==} - '@vitest/runner@4.1.10': - resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} - '@vitest/snapshot@3.1.4': resolution: {integrity: sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==} - '@vitest/snapshot@4.1.10': - resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} - '@vitest/spy@3.1.4': resolution: {integrity: sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==} - '@vitest/spy@4.1.10': - resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} - '@vitest/ui@1.6.0': resolution: {integrity: sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==} peerDependencies: @@ -9733,9 +9316,6 @@ packages: '@vitest/utils@3.1.4': resolution: {integrity: sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==} - '@vitest/utils@4.1.10': - resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@wallet-standard/app@1.1.0': resolution: {integrity: sha512-3CijvrO9utx598kjr45hTbbeeykQrQfKmSnxeWOgU25TOEpvcipD/bYDQWIqUv1Oc6KK4YStokSMu/FBNecGUQ==} engines: {node: '>=16'} @@ -10463,9 +10043,6 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - blake3-wasm@2.1.5: - resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} - bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} @@ -10673,10 +10250,6 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} - chalk-template@0.4.0: resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} engines: {node: '>=12'} @@ -10781,9 +10354,6 @@ packages: resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} engines: {node: '>= 0.10'} - cjs-module-lexer@1.2.3: - resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} - cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -11087,10 +10657,6 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - cookie@1.1.1: - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} - engines: {node: '>=18'} - copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} @@ -11690,9 +11256,6 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - error-stack-parser-es@1.0.5: - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} @@ -11769,11 +11332,6 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} - engines: {node: '>=18'} - hasBin: true - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -14836,10 +14394,6 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - miniflare@5.20260811.1-alpha: - resolution: {integrity: sha512-DtOG0BeanIxs2sH0smFvExZD89cBQwGckbHiFkRJrrNAUu3NGClZkUxqu+zy7HYfKBAgq935EMY49vIPm3JVdA==} - engines: {node: '>=22.0.0'} - minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} @@ -15286,10 +14840,6 @@ packages: resolution: {integrity: sha512-szyd0ou0T8nsAqHtprRcP3WidfsN1TnAR5yWXf2mFCEr5ek3LEOkT6EZ/92Xfs74HIdyhG5WkGxIssMU0jBaeg==} engines: {node: '>=16'} - obug@2.1.4: - resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} - engines: {node: '>=12.20.0'} - ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} @@ -15616,9 +15166,6 @@ packages: path-to-regexp@3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} - path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} @@ -16997,10 +16544,6 @@ packages: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - sharp@0.35.2: - resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} - engines: {node: '>=20.9.0'} - sharp@0.35.3: resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} engines: {node: '>=20.9.0'} @@ -17236,9 +16779,6 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - std-env@4.2.0: - resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} - stealthy-require@1.1.1: resolution: {integrity: sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==} engines: {node: '>=0.10.0'} @@ -17445,10 +16985,6 @@ packages: resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} engines: {node: '>=14.0.0'} - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -17620,10 +17156,6 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.3.0: - resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} - engines: {node: '>=18'} - tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -17640,10 +17172,6 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyrainbow@3.1.1: - resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} - engines: {node: '>=14.0.0'} - tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} @@ -18059,9 +17587,6 @@ packages: resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} - unenv@2.0.0-rc.24: - resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} - unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -18579,47 +18104,6 @@ packages: jsdom: optional: true - vitest@4.1.10: - resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.10 - '@vitest/browser-preview': 4.1.10 - '@vitest/browser-webdriverio': 4.1.10 - '@vitest/coverage-istanbul': 4.1.10 - '@vitest/coverage-v8': 4.1.10 - '@vitest/ui': 4.1.10 - happy-dom: '*' - jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/coverage-istanbul': - optional: true - '@vitest/coverage-v8': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - vlq@1.0.1: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} @@ -18811,21 +18295,6 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - workerd@1.20260811.1: - resolution: {integrity: sha512-kh+FFm55JQ4ssxhHZV9VPdMQq3D1nHxNJgwxMtWGD4dGppJvLySdguTRDKgeNTvgq6heSz+6TTXyPSDGj8Yllw==} - engines: {node: '>=16'} - hasBin: true - - wrangler@4.123.0: - resolution: {integrity: sha512-VXo2I1oa0x9aGAKIFPRSQPqTh0RBY5Ktl44YOhNmsJQFUdJKDA2vVTU6Xj+FC2koll6orJqWZN8jbXVIk9O67Q==} - engines: {node: '>=22.0.0'} - hasBin: true - peerDependencies: - '@cloudflare/workers-types': ^5.20260811.1 - peerDependenciesMeta: - '@cloudflare/workers-types': - optional: true - wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -19014,12 +18483,6 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} - youch-core@0.3.3: - resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} - - youch@4.1.0-beta.10: - resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - yup@1.7.1: resolution: {integrity: sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==} @@ -19049,9 +18512,6 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - zustand@5.0.11: resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} engines: {node: '>=12.20.0'} @@ -20954,46 +20414,6 @@ snapshots: fast-wrap-ansi: 0.1.6 sisteransi: 1.0.5 - '@cloudflare/kv-asset-handler@0.5.0': {} - - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260811.1)': - dependencies: - unenv: 2.0.0-rc.24 - optionalDependencies: - workerd: 1.20260811.1 - - '@cloudflare/vitest-pool-workers@0.21.3(@cloudflare/workers-types@5.20260813.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vitest@4.1.10(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(@vitest/coverage-v8@1.6.0(vitest@3.1.4))(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jsdom@29.1.0(@noble/hashes@2.0.1)(canvas@2.11.2))(vite@8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3)))': - dependencies: - '@vitest/runner': 4.1.10 - '@vitest/snapshot': 4.1.10 - cjs-module-lexer: 1.2.3 - esbuild: 0.28.1 - miniflare: 5.20260811.1-alpha(bufferutil@4.1.0)(utf-8-validate@6.0.6) - vitest: 4.1.10(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(@vitest/coverage-v8@1.6.0(vitest@3.1.4))(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jsdom@29.1.0(@noble/hashes@2.0.1)(canvas@2.11.2))(vite@8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3)) - wrangler: 4.123.0(@cloudflare/workers-types@5.20260813.1)(bufferutil@4.1.0)(utf-8-validate@6.0.6) - zod: 4.4.3 - transitivePeerDependencies: - - '@cloudflare/workers-types' - - bufferutil - - utf-8-validate - - '@cloudflare/workerd-darwin-64@1.20260811.1': - optional: true - - '@cloudflare/workerd-darwin-arm64@1.20260811.1': - optional: true - - '@cloudflare/workerd-linux-64@1.20260811.1': - optional: true - - '@cloudflare/workerd-linux-arm64@1.20260811.1': - optional: true - - '@cloudflare/workerd-windows-64@1.20260811.1': - optional: true - - '@cloudflare/workers-types@5.20260813.1': {} - '@colors/colors@1.5.0': optional: true @@ -21495,9 +20915,6 @@ snapshots: '@esbuild/aix-ppc64@0.27.7': optional: true - '@esbuild/aix-ppc64@0.28.1': - optional: true - '@esbuild/android-arm64@0.25.12': optional: true @@ -21507,9 +20924,6 @@ snapshots: '@esbuild/android-arm64@0.27.7': optional: true - '@esbuild/android-arm64@0.28.1': - optional: true - '@esbuild/android-arm@0.25.12': optional: true @@ -21519,9 +20933,6 @@ snapshots: '@esbuild/android-arm@0.27.7': optional: true - '@esbuild/android-arm@0.28.1': - optional: true - '@esbuild/android-x64@0.25.12': optional: true @@ -21531,9 +20942,6 @@ snapshots: '@esbuild/android-x64@0.27.7': optional: true - '@esbuild/android-x64@0.28.1': - optional: true - '@esbuild/darwin-arm64@0.25.12': optional: true @@ -21543,9 +20951,6 @@ snapshots: '@esbuild/darwin-arm64@0.27.7': optional: true - '@esbuild/darwin-arm64@0.28.1': - optional: true - '@esbuild/darwin-x64@0.25.12': optional: true @@ -21555,9 +20960,6 @@ snapshots: '@esbuild/darwin-x64@0.27.7': optional: true - '@esbuild/darwin-x64@0.28.1': - optional: true - '@esbuild/freebsd-arm64@0.25.12': optional: true @@ -21567,9 +20969,6 @@ snapshots: '@esbuild/freebsd-arm64@0.27.7': optional: true - '@esbuild/freebsd-arm64@0.28.1': - optional: true - '@esbuild/freebsd-x64@0.25.12': optional: true @@ -21579,9 +20978,6 @@ snapshots: '@esbuild/freebsd-x64@0.27.7': optional: true - '@esbuild/freebsd-x64@0.28.1': - optional: true - '@esbuild/linux-arm64@0.25.12': optional: true @@ -21591,9 +20987,6 @@ snapshots: '@esbuild/linux-arm64@0.27.7': optional: true - '@esbuild/linux-arm64@0.28.1': - optional: true - '@esbuild/linux-arm@0.25.12': optional: true @@ -21603,9 +20996,6 @@ snapshots: '@esbuild/linux-arm@0.27.7': optional: true - '@esbuild/linux-arm@0.28.1': - optional: true - '@esbuild/linux-ia32@0.25.12': optional: true @@ -21615,9 +21005,6 @@ snapshots: '@esbuild/linux-ia32@0.27.7': optional: true - '@esbuild/linux-ia32@0.28.1': - optional: true - '@esbuild/linux-loong64@0.25.12': optional: true @@ -21627,9 +21014,6 @@ snapshots: '@esbuild/linux-loong64@0.27.7': optional: true - '@esbuild/linux-loong64@0.28.1': - optional: true - '@esbuild/linux-mips64el@0.25.12': optional: true @@ -21639,9 +21023,6 @@ snapshots: '@esbuild/linux-mips64el@0.27.7': optional: true - '@esbuild/linux-mips64el@0.28.1': - optional: true - '@esbuild/linux-ppc64@0.25.12': optional: true @@ -21651,9 +21032,6 @@ snapshots: '@esbuild/linux-ppc64@0.27.7': optional: true - '@esbuild/linux-ppc64@0.28.1': - optional: true - '@esbuild/linux-riscv64@0.25.12': optional: true @@ -21663,9 +21041,6 @@ snapshots: '@esbuild/linux-riscv64@0.27.7': optional: true - '@esbuild/linux-riscv64@0.28.1': - optional: true - '@esbuild/linux-s390x@0.25.12': optional: true @@ -21675,9 +21050,6 @@ snapshots: '@esbuild/linux-s390x@0.27.7': optional: true - '@esbuild/linux-s390x@0.28.1': - optional: true - '@esbuild/linux-x64@0.25.12': optional: true @@ -21687,9 +21059,6 @@ snapshots: '@esbuild/linux-x64@0.27.7': optional: true - '@esbuild/linux-x64@0.28.1': - optional: true - '@esbuild/netbsd-arm64@0.25.12': optional: true @@ -21699,9 +21068,6 @@ snapshots: '@esbuild/netbsd-arm64@0.27.7': optional: true - '@esbuild/netbsd-arm64@0.28.1': - optional: true - '@esbuild/netbsd-x64@0.25.12': optional: true @@ -21711,9 +21077,6 @@ snapshots: '@esbuild/netbsd-x64@0.27.7': optional: true - '@esbuild/netbsd-x64@0.28.1': - optional: true - '@esbuild/openbsd-arm64@0.25.12': optional: true @@ -21723,9 +21086,6 @@ snapshots: '@esbuild/openbsd-arm64@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.28.1': - optional: true - '@esbuild/openbsd-x64@0.25.12': optional: true @@ -21735,9 +21095,6 @@ snapshots: '@esbuild/openbsd-x64@0.27.7': optional: true - '@esbuild/openbsd-x64@0.28.1': - optional: true - '@esbuild/openharmony-arm64@0.25.12': optional: true @@ -21747,9 +21104,6 @@ snapshots: '@esbuild/openharmony-arm64@0.27.7': optional: true - '@esbuild/openharmony-arm64@0.28.1': - optional: true - '@esbuild/sunos-x64@0.25.12': optional: true @@ -21759,9 +21113,6 @@ snapshots: '@esbuild/sunos-x64@0.27.7': optional: true - '@esbuild/sunos-x64@0.28.1': - optional: true - '@esbuild/win32-arm64@0.25.12': optional: true @@ -21771,9 +21122,6 @@ snapshots: '@esbuild/win32-arm64@0.27.7': optional: true - '@esbuild/win32-arm64@0.28.1': - optional: true - '@esbuild/win32-ia32@0.25.12': optional: true @@ -21783,9 +21131,6 @@ snapshots: '@esbuild/win32-ia32@0.27.7': optional: true - '@esbuild/win32-ia32@0.28.1': - optional: true - '@esbuild/win32-x64@0.25.12': optional: true @@ -21795,9 +21140,6 @@ snapshots: '@esbuild/win32-x64@0.27.7': optional: true - '@esbuild/win32-x64@0.28.1': - optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@8.57.0)': dependencies: eslint: 8.57.0 @@ -22062,18 +21404,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@img/colour@1.1.0': {} + '@img/colour@1.1.0': + optional: true '@img/sharp-darwin-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.0.4 optional: true - '@img/sharp-darwin-arm64@0.35.2': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.1 - optional: true - '@img/sharp-darwin-arm64@0.35.3': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.3.2 @@ -22084,21 +21422,11 @@ snapshots: '@img/sharp-libvips-darwin-x64': 1.0.4 optional: true - '@img/sharp-darwin-x64@0.35.2': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.1 - optional: true - '@img/sharp-darwin-x64@0.35.3': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.3.2 optional: true - '@img/sharp-freebsd-wasm32@0.35.2': - dependencies: - '@img/sharp-wasm32': 0.35.2 - optional: true - '@img/sharp-freebsd-wasm32@0.35.3': dependencies: '@img/sharp-wasm32': 0.35.3 @@ -22107,84 +21435,54 @@ snapshots: '@img/sharp-libvips-darwin-arm64@1.0.4': optional: true - '@img/sharp-libvips-darwin-arm64@1.3.1': - optional: true - '@img/sharp-libvips-darwin-arm64@1.3.2': optional: true '@img/sharp-libvips-darwin-x64@1.0.4': optional: true - '@img/sharp-libvips-darwin-x64@1.3.1': - optional: true - '@img/sharp-libvips-darwin-x64@1.3.2': optional: true '@img/sharp-libvips-linux-arm64@1.0.4': optional: true - '@img/sharp-libvips-linux-arm64@1.3.1': - optional: true - '@img/sharp-libvips-linux-arm64@1.3.2': optional: true '@img/sharp-libvips-linux-arm@1.0.5': optional: true - '@img/sharp-libvips-linux-arm@1.3.1': - optional: true - '@img/sharp-libvips-linux-arm@1.3.2': optional: true - '@img/sharp-libvips-linux-ppc64@1.3.1': - optional: true - '@img/sharp-libvips-linux-ppc64@1.3.2': optional: true - '@img/sharp-libvips-linux-riscv64@1.3.1': - optional: true - '@img/sharp-libvips-linux-riscv64@1.3.2': optional: true '@img/sharp-libvips-linux-s390x@1.0.4': optional: true - '@img/sharp-libvips-linux-s390x@1.3.1': - optional: true - '@img/sharp-libvips-linux-s390x@1.3.2': optional: true '@img/sharp-libvips-linux-x64@1.0.4': optional: true - '@img/sharp-libvips-linux-x64@1.3.1': - optional: true - '@img/sharp-libvips-linux-x64@1.3.2': optional: true '@img/sharp-libvips-linuxmusl-arm64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.3.1': - optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': optional: true '@img/sharp-libvips-linuxmusl-x64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.3.1': - optional: true - '@img/sharp-libvips-linuxmusl-x64@1.3.2': optional: true @@ -22193,11 +21491,6 @@ snapshots: '@img/sharp-libvips-linux-arm64': 1.0.4 optional: true - '@img/sharp-linux-arm64@0.35.2': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.1 - optional: true - '@img/sharp-linux-arm64@0.35.3': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.3.2 @@ -22208,31 +21501,16 @@ snapshots: '@img/sharp-libvips-linux-arm': 1.0.5 optional: true - '@img/sharp-linux-arm@0.35.2': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.1 - optional: true - '@img/sharp-linux-arm@0.35.3': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.3.2 optional: true - '@img/sharp-linux-ppc64@0.35.2': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.1 - optional: true - '@img/sharp-linux-ppc64@0.35.3': optionalDependencies: '@img/sharp-libvips-linux-ppc64': 1.3.2 optional: true - '@img/sharp-linux-riscv64@0.35.2': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.1 - optional: true - '@img/sharp-linux-riscv64@0.35.3': optionalDependencies: '@img/sharp-libvips-linux-riscv64': 1.3.2 @@ -22243,11 +21521,6 @@ snapshots: '@img/sharp-libvips-linux-s390x': 1.0.4 optional: true - '@img/sharp-linux-s390x@0.35.2': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.1 - optional: true - '@img/sharp-linux-s390x@0.35.3': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.3.2 @@ -22258,11 +21531,6 @@ snapshots: '@img/sharp-libvips-linux-x64': 1.0.4 optional: true - '@img/sharp-linux-x64@0.35.2': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.1 - optional: true - '@img/sharp-linux-x64@0.35.3': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.3.2 @@ -22273,11 +21541,6 @@ snapshots: '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 optional: true - '@img/sharp-linuxmusl-arm64@0.35.2': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 - optional: true - '@img/sharp-linuxmusl-arm64@0.35.3': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 @@ -22288,11 +21551,6 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64': 1.0.4 optional: true - '@img/sharp-linuxmusl-x64@0.35.2': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.1 - optional: true - '@img/sharp-linuxmusl-x64@0.35.3': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.3.2 @@ -22303,47 +21561,28 @@ snapshots: '@emnapi/runtime': 1.8.1 optional: true - '@img/sharp-wasm32@0.35.2': - dependencies: - '@emnapi/runtime': 1.11.3 - optional: true - '@img/sharp-wasm32@0.35.3': dependencies: '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-webcontainers-wasm32@0.35.2': - dependencies: - '@img/sharp-wasm32': 0.35.2 - optional: true - '@img/sharp-webcontainers-wasm32@0.35.3': dependencies: '@img/sharp-wasm32': 0.35.3 optional: true - '@img/sharp-win32-arm64@0.35.2': - optional: true - '@img/sharp-win32-arm64@0.35.3': optional: true '@img/sharp-win32-ia32@0.33.5': optional: true - '@img/sharp-win32-ia32@0.35.2': - optional: true - '@img/sharp-win32-ia32@0.35.3': optional: true '@img/sharp-win32-x64@0.33.5': optional: true - '@img/sharp-win32-x64@0.35.2': - optional: true - '@img/sharp-win32-x64@0.35.3': optional: true @@ -25361,18 +24600,6 @@ snapshots: '@popperjs/core@2.11.8': {} - '@poppinss/colors@4.1.6': - dependencies: - kleur: 4.1.5 - - '@poppinss/dumper@0.6.5': - dependencies: - '@poppinss/colors': 4.1.6 - '@sindresorhus/is': 7.2.0 - supports-color: 10.2.2 - - '@poppinss/exception@1.2.3': {} - '@posthog/core@1.23.2': dependencies: cross-spawn: 7.0.6 @@ -26742,8 +25969,6 @@ snapshots: '@sindresorhus/is@4.6.0': {} - '@sindresorhus/is@7.2.0': {} - '@sindresorhus/merge-streams@4.0.0': {} '@sindresorhus/slugify@2.2.1': @@ -27632,8 +26857,6 @@ snapshots: eventemitter3: 5.0.4 uuid: 9.0.1 - '@speed-highlight/core@1.2.24': {} - '@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)': dependencies: '@standard-schema/spec': 1.1.0 @@ -28422,11 +27645,6 @@ snapshots: '@types/caseless@0.12.5': {} - '@types/chai@5.2.3': - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - '@types/chrome@0.0.319': dependencies: '@types/filesystem': 0.0.36 @@ -28457,8 +27675,6 @@ snapshots: dependencies: '@types/ms': 2.1.0 - '@types/deep-eql@4.0.2': {} - '@types/diff-match-patch@1.0.36': {} '@types/dom-mediacapture-transform@0.1.11': @@ -29415,15 +28631,6 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/expect@4.1.10': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.10 - '@vitest/utils': 4.1.10 - chai: 6.2.2 - tinyrainbow: 3.1.1 - '@vitest/mocker@3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.33.0)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 3.1.4 @@ -29432,14 +28639,6 @@ snapshots: optionalDependencies: vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.33.0)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3) - '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3))': - dependencies: - '@vitest/spy': 4.1.10 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3) - '@vitest/pretty-format@3.1.4': dependencies: tinyrainbow: 2.0.0 @@ -29448,39 +28647,21 @@ snapshots: dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@4.1.10': - dependencies: - tinyrainbow: 3.1.1 - '@vitest/runner@3.1.4': dependencies: '@vitest/utils': 3.1.4 pathe: 2.0.3 - '@vitest/runner@4.1.10': - dependencies: - '@vitest/utils': 4.1.10 - pathe: 2.0.3 - '@vitest/snapshot@3.1.4': dependencies: '@vitest/pretty-format': 3.1.4 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/snapshot@4.1.10': - dependencies: - '@vitest/pretty-format': 4.1.10 - '@vitest/utils': 4.1.10 - magic-string: 0.30.21 - pathe: 2.0.3 - '@vitest/spy@3.1.4': dependencies: tinyspy: 3.0.2 - '@vitest/spy@4.1.10': {} - '@vitest/ui@1.6.0(vitest@3.1.4)': dependencies: '@vitest/utils': 1.6.0 @@ -29505,12 +28686,6 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@4.1.10': - dependencies: - '@vitest/pretty-format': 4.1.10 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.1 - '@wallet-standard/app@1.1.0': dependencies: '@wallet-standard/base': 1.1.0 @@ -30767,8 +29942,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - blake3-wasm@2.1.5: {} - bluebird@3.7.2: {} blueimp-canvas-to-blob@3.29.0: {} @@ -31043,8 +30216,6 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 - chai@6.2.2: {} - chalk-template@0.4.0: dependencies: chalk: 4.1.2 @@ -31178,8 +30349,6 @@ snapshots: safe-buffer: 5.2.1 to-buffer: 1.2.2 - cjs-module-lexer@1.2.3: {} - cjs-module-lexer@1.4.3: {} cjs-module-lexer@2.2.0: {} @@ -31454,8 +30623,6 @@ snapshots: cookie@0.7.2: {} - cookie@1.1.1: {} - copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 @@ -32080,8 +31247,6 @@ snapshots: dependencies: is-arrayish: 0.2.1 - error-stack-parser-es@1.0.5: {} - error-stack-parser@2.1.4: dependencies: stackframe: 1.3.4 @@ -32307,35 +31472,6 @@ snapshots: '@esbuild/win32-ia32': 0.27.7 '@esbuild/win32-x64': 0.27.7 - esbuild@0.28.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 - escalade@3.2.0: {} escape-html@1.0.3: {} @@ -36465,18 +35601,6 @@ snapshots: mimic-response@3.1.0: {} - miniflare@5.20260811.1-alpha(bufferutil@4.1.0)(utf-8-validate@6.0.6): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - sharp: 0.35.2 - undici: 7.29.0 - workerd: 1.20260811.1 - ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) - youch: 4.1.0-beta.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - minimalistic-assert@1.0.1: {} minimalistic-crypto-utils@1.0.1: {} @@ -36915,8 +36039,6 @@ snapshots: oblivious-set@1.4.0: {} - obug@2.1.4: {} - ofetch@1.5.1: dependencies: destr: 2.0.5 @@ -37286,8 +36408,6 @@ snapshots: path-to-regexp@3.3.0: {} - path-to-regexp@6.3.0: {} - path-to-regexp@8.3.0: {} path-to-regexp@8.4.2: {} @@ -39135,38 +38255,6 @@ snapshots: '@img/sharp-win32-ia32': 0.33.5 '@img/sharp-win32-x64': 0.33.5 - sharp@0.35.2: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.8.5 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.2 - '@img/sharp-darwin-x64': 0.35.2 - '@img/sharp-freebsd-wasm32': 0.35.2 - '@img/sharp-libvips-darwin-arm64': 1.3.1 - '@img/sharp-libvips-darwin-x64': 1.3.1 - '@img/sharp-libvips-linux-arm': 1.3.1 - '@img/sharp-libvips-linux-arm64': 1.3.1 - '@img/sharp-libvips-linux-ppc64': 1.3.1 - '@img/sharp-libvips-linux-riscv64': 1.3.1 - '@img/sharp-libvips-linux-s390x': 1.3.1 - '@img/sharp-libvips-linux-x64': 1.3.1 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 - '@img/sharp-libvips-linuxmusl-x64': 1.3.1 - '@img/sharp-linux-arm': 0.35.2 - '@img/sharp-linux-arm64': 0.35.2 - '@img/sharp-linux-ppc64': 0.35.2 - '@img/sharp-linux-riscv64': 0.35.2 - '@img/sharp-linux-s390x': 0.35.2 - '@img/sharp-linux-x64': 0.35.2 - '@img/sharp-linuxmusl-arm64': 0.35.2 - '@img/sharp-linuxmusl-x64': 0.35.2 - '@img/sharp-webcontainers-wasm32': 0.35.2 - '@img/sharp-win32-arm64': 0.35.2 - '@img/sharp-win32-ia32': 0.35.2 - '@img/sharp-win32-x64': 0.35.2 - sharp@0.35.3(@types/node@18.16.9): dependencies: '@img/colour': 1.1.0 @@ -39439,8 +38527,6 @@ snapshots: std-env@3.10.0: {} - std-env@4.2.0: {} - stealthy-require@1.1.1: {} stop-iteration-iterator@1.1.0: @@ -39663,8 +38749,6 @@ snapshots: superstruct@2.0.2: {} - supports-color@10.2.2: {} - supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -39850,8 +38934,6 @@ snapshots: tinyexec@0.3.2: {} - tinyexec@1.3.0: {} - tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.4) @@ -39866,8 +38948,6 @@ snapshots: tinyrainbow@2.0.0: {} - tinyrainbow@3.1.1: {} - tinyspy@3.0.2: {} tippy.js@6.3.7: @@ -40285,11 +39365,8 @@ snapshots: undici@7.25.0: {} - undici@7.29.0: {} - - unenv@2.0.0-rc.24: - dependencies: - pathe: 2.0.3 + undici@7.29.0: + optional: true unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -40772,22 +39849,6 @@ snapshots: terser: 5.46.0 yaml: 2.8.3 - vite@8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3): - dependencies: - lightningcss: 1.33.0 - picomatch: 4.0.5 - postcss: 8.5.26 - rolldown: 1.2.4 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 20.19.37 - esbuild: 0.28.1 - fsevents: 2.3.3 - jiti: 2.6.1 - sass: 1.97.3 - terser: 5.46.0 - yaml: 2.8.3 - vitest@3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.33.0)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3): dependencies: '@vitest/expect': 3.1.4 @@ -40831,38 +39892,6 @@ snapshots: - tsx - yaml - vitest@4.1.10(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(@vitest/coverage-v8@1.6.0(vitest@3.1.4))(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jsdom@29.1.0(@noble/hashes@2.0.1)(canvas@2.11.2))(vite@8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3)): - dependencies: - '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.10 - '@vitest/runner': 4.1.10 - '@vitest/snapshot': 4.1.10 - '@vitest/spy': 4.1.10 - '@vitest/utils': 4.1.10 - es-module-lexer: 2.0.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.4 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.2.0 - tinybench: 2.9.0 - tinyexec: 1.3.0 - tinyglobby: 0.2.15 - tinyrainbow: 3.1.1 - vite: 8.2.1(@types/node@20.19.37)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/node': 20.19.37 - '@vitest/coverage-v8': 1.6.0(vitest@3.1.4) - '@vitest/ui': 1.6.0(vitest@3.1.4) - happy-dom: 15.11.7 - jsdom: 29.1.0(@noble/hashes@2.0.1)(canvas@2.11.2) - transitivePeerDependencies: - - msw - vlq@1.0.1: {} void-elements@3.1.0: {} @@ -41112,31 +40141,6 @@ snapshots: wordwrap@1.0.0: {} - workerd@1.20260811.1: - optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260811.1 - '@cloudflare/workerd-darwin-arm64': 1.20260811.1 - '@cloudflare/workerd-linux-64': 1.20260811.1 - '@cloudflare/workerd-linux-arm64': 1.20260811.1 - '@cloudflare/workerd-windows-64': 1.20260811.1 - - wrangler@4.123.0(@cloudflare/workers-types@5.20260813.1)(bufferutil@4.1.0)(utf-8-validate@6.0.6): - dependencies: - '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260811.1) - blake3-wasm: 2.1.5 - esbuild: 0.28.1 - miniflare: 5.20260811.1-alpha(bufferutil@4.1.0)(utf-8-validate@6.0.6) - path-to-regexp: 6.3.0 - unenv: 2.0.0-rc.24 - workerd: 1.20260811.1 - optionalDependencies: - '@cloudflare/workers-types': 5.20260813.1 - fsevents: 2.3.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -41192,11 +40196,6 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 5.0.10 - ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): - optionalDependencies: - bufferutil: 4.1.0 - utf-8-validate: 6.0.6 - xml-name-validator@4.0.0: {} xml-name-validator@5.0.0: {} @@ -41283,19 +40282,6 @@ snapshots: yoctocolors@2.1.2: {} - youch-core@0.3.3: - dependencies: - '@poppinss/exception': 1.2.3 - error-stack-parser-es: 1.0.5 - - youch@4.1.0-beta.10: - dependencies: - '@poppinss/colors': 4.1.6 - '@poppinss/dumper': 0.6.5 - '@speed-highlight/core': 1.2.24 - cookie: 1.1.1 - youch-core: 0.3.3 - yup@1.7.1: dependencies: property-expr: 2.0.6 @@ -41325,8 +40311,6 @@ snapshots: zod@4.3.6: {} - zod@4.4.3: {} - zustand@5.0.11(@types/react@19.1.8)(immer@9.0.21)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): optionalDependencies: '@types/react': 19.1.8 From 9d7e74eff9b26b54ebe7941fc35d38a18c015700 Mon Sep 17 00:00:00 2001 From: JOY <5027251+JOY@users.noreply.github.com> Date: Sat, 19 Sep 2026 02:29:10 +0700 Subject: [PATCH 2/3] chore(sso): fix removal fallout in CI and scripts Review findings on PR #39: - build.yml: drop unconditional 'Test SSO Worker (vitest)' step that would fail every push after apps/crove-sso is removed - package.json: drop typecheck:sso / test:sso scripts - deploy-beta.ps1 / deploy-prod.ps1: rewrite without SSO steps (Branding Guard + compose contract + container instructions only) - build-all.ps1: drop SSO step, renumber to [1/3]..[3/3] - scripts/deploy-sso.ps1: delete - branding-guard.ts: drop dead apps/crove-sso/ strict prefix - docs/cicd.md: update deploy script purposes, drop Deploy SSO row - docs/README.md: drop crove-sso from repo tree - .github/copilot-instructions.md: drop crove-sso directory entry - docs/sso-integration.md: mark as historical design record, point to the api.dos.me PKCE bridge as the current implementation --- .github/copilot-instructions.md | 1 - .github/workflows/build.yml | 3 - docs/README.md | 1 - docs/cicd.md | 5 +- docs/sso-integration.md | 6 ++ package.json | 4 +- scripts/branding-guard.ts | 2 +- scripts/build-all.ps1 | 8 +-- scripts/deploy-beta.ps1 | 36 +++--------- scripts/deploy-prod.ps1 | 36 +++--------- scripts/deploy-sso.ps1 | 98 --------------------------------- 11 files changed, 31 insertions(+), 169 deletions(-) delete mode 100644 scripts/deploy-sso.ps1 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b71b1ff6b5..8d2dad77eb 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -11,7 +11,6 @@ - `apps/backend`: NestJS REST API Gateway, Controllers, OAuth & Webhook endpoints. - `apps/frontend`: Next.js 16 (App Router) + React 19 + Tailwind CSS 3 UI. - `apps/orchestrator`: Temporal Worker for scheduled posting workflows and background activities. -- `apps/crove-sso`: Cloudflare Worker SSO bridge (optional edge fallback). - `libraries/nestjs-libraries`: Database layer (Prisma), 34+ Social Providers, Email, Notifications, Mastra MCP Server. - `libraries/helpers`: Runtime branding engine, Fetch wrappers, Subdomain management, Auth helper utilities. - `libraries/react-shared-libraries`: Reusable React components, translations, UI forms. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ada6989534..f499105d7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,9 +78,6 @@ jobs: - name: Validate Beta deployment script and compose contract run: pnpm run validate:beta-deploy - - name: Test SSO Worker (vitest) - run: pnpm run test:sso - # The suites drive the real Prisma client, so the schema must exist in # the throwaway database first. --skip-generate because the postinstall # hook already generated the client. No --accept-data-loss: this is an diff --git a/docs/README.md b/docs/README.md index 1a2175c12c..ebbe1fca5c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -29,7 +29,6 @@ The project is structured as a **Monorepo (PNPM Workspaces)**: │ ├── orchestrator/ # Temporal Worker (Workflows & Activities for scheduled posting) │ ├── frontend/ # Next.js 16 (App Router) + React 19 + Tailwind CSS 3 │ ├── extension/ # Chrome Extension Manifest V3 (Cookie capture & social bridge) -│ └── crove-sso/ # Cloudflare Worker SSO Bridge (OAuth 2.1 PKCE) ├── libraries/ │ ├── helpers/ # Shared utilities, Brand Config Engine, Fetch wrappers │ ├── nestjs-libraries/# 34+ Social Providers, Email, Database Prisma, Mastra MCP diff --git a/docs/cicd.md b/docs/cicd.md index 299acec488..2625a634ea 100644 --- a/docs/cicd.md +++ b/docs/cicd.md @@ -51,9 +51,8 @@ The Crove Post CI/CD system provides end-to-end automation: source code verifica | Script | Path | Purpose | | :--- | :--- | :--- | -| **Deploy Beta** | `scripts/deploy-beta.ps1` | Runs tests, deploys SSO Beta, and outputs container instructions | -| **Deploy Prod** | `scripts/deploy-prod.ps1` | Runs tests, deploys SSO Prod, and outputs container instructions | -| **Deploy SSO** | `scripts/deploy-sso.ps1` | Independent SSO deployment with `-Environment beta` or `prod` | +| **Deploy Beta** | `scripts/deploy-beta.ps1` | Runs Branding Guard, validates beta contract, and outputs container instructions | +| **Deploy Prod** | `scripts/deploy-prod.ps1` | Runs Branding Guard and outputs container instructions | | **Branding Guard** | `scripts/branding-guard.ts` | Comprehensive contract validation suite for runtime branding | | **Build All** | `scripts/build-all.ps1` | Full parallel/sequential workspace build verification | | **Dev Environment** | `scripts/dev.ps1` | Starts development server on Windows PowerShell | diff --git a/docs/sso-integration.md b/docs/sso-integration.md index 13e5175630..7b13aeab2e 100644 --- a/docs/sso-integration.md +++ b/docs/sso-integration.md @@ -1,5 +1,11 @@ # Kiến Trúc Single Sign-On (SSO) & OAuth 2.1 PKCE Bridge +> **STATUS 2026-09: Triển khai bridge đã dời vào `api.dos.me` (PKCE bridge central, +> `POSTIZ_OAUTH_*` trỏ tới `https://api.dos.me/sso/*`). Cloudflare Worker +> `apps/crove-sso` (`sso.crove.com` / `beta-sso.crove.com`) đã bị gỡ bỏ - xem +> `docs/README.md` và CHANGELOG. Tài liệu này giữ lại làm bản ghi thiết kế lịch sử +> cho bài toán bridge; các đoạn hướng dẫn `cd apps/crove-sso` bên dưới không còn +> áp dụng. ## 1. Tổng Quan Hệ thống cho phép người dùng **Crove** đăng nhập thông qua tài khoản **DOS ID** (sử dụng Supabase OAuth 2.1) mà **không làm thay đổi hoặc can thiệp trực tiếp vào mã nguồn OAuth mặc định của Postiz**. diff --git a/package.json b/package.json index 7743c39366..7cd17040b1 100644 --- a/package.json +++ b/package.json @@ -34,14 +34,12 @@ "start:prod:web": "pnpm --filter ./apps/web run start", "dev:docker": "docker compose -f ./docker-compose.dev.yaml up -d", "validate:beta-deploy": "node scripts/validate-beta-compose.mjs", - "typecheck:sso": "pnpm --filter @crove/sso run typecheck", "commands:build:development": "pnpm --filter ./apps/commands run build", "prisma-generate": "pnpm exec prisma generate --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "prisma-db-push": "pnpm exec prisma db push --accept-data-loss --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "prisma-db-pull": "pnpm exec prisma db pull --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "postinstall": "pnpm run prisma-generate", - "test": "jest --coverage --detectOpenHandles --reporters=default --reporters=jest-junit", - "test:sso": "pnpm --filter @crove/sso test" + "test": "jest --coverage --detectOpenHandles --reporters=default --reporters=jest-junit" }, "dependencies": { "@ag-ui/mastra": "^1.0.1", diff --git a/scripts/branding-guard.ts b/scripts/branding-guard.ts index 05cda5cba2..68ef513649 100644 --- a/scripts/branding-guard.ts +++ b/scripts/branding-guard.ts @@ -215,7 +215,7 @@ console.log('=== Running Branding Guard Validations ===\n'); ]; // Directories we own and actively edit — leaks here are strict. const STRICT_PREFIXES = [ - 'apps/backend/src/', 'apps/frontend/src/', 'apps/crove-sso/', + 'apps/backend/src/', 'apps/frontend/src/', 'apps/sdk/', 'libraries/nestjs-libraries/src/', 'libraries/helpers/src/', 'libraries/react-shared-libraries/src/', diff --git a/scripts/build-all.ps1 b/scripts/build-all.ps1 index 138ecffc39..26cc37a523 100644 --- a/scripts/build-all.ps1 +++ b/scripts/build-all.ps1 @@ -19,22 +19,20 @@ Write-Host " CROVE MONOREPO - FULL BUILD & VALIDATION" -ForegroundColor Yellow Write-Host "==========================================================" -ForegroundColor Cyan # 1. Build & Test SSO Worker -Write-Host "`n[1/4] Build & Test Cloudflare Worker SSO (@crove/sso)..." -ForegroundColor Green -pnpm --filter @crove/sso test if ($LASTEXITCODE -ne 0) { Write-Error "SSO Test failed!"; exit 1 } # 2. Build Landing Page -Write-Host "`n[2/4] Build Landing Page (@crove/web)..." -ForegroundColor Green +Write-Host "`n[1/3] Build Landing Page (@crove/web)..." -ForegroundColor Green pnpm --filter @crove/web run build if ($LASTEXITCODE -ne 0) { Write-Error "Web Build failed!"; exit 1 } # 3. Prisma Generate -Write-Host "`n[3/4] Generate Prisma Client..." -ForegroundColor Green +Write-Host "`n[2/3] Generate Prisma Client..." -ForegroundColor Green pnpm run prisma-generate if ($LASTEXITCODE -ne 0) { Write-Error "Prisma generate failed!"; exit 1 } # 4. Build Core Apps -Write-Host "`n[4/4] Build Core Backend & Frontend..." -ForegroundColor Green +Write-Host "`n[3/3] Build Core Backend & Frontend..." -ForegroundColor Green pnpm run build if ($LASTEXITCODE -ne 0) { Write-Error "Core build failed!"; exit 1 } diff --git a/scripts/deploy-beta.ps1 b/scripts/deploy-beta.ps1 index ad1fbc19b3..c1a42d863f 100644 --- a/scripts/deploy-beta.ps1 +++ b/scripts/deploy-beta.ps1 @@ -3,9 +3,8 @@ Automated deployment pipeline for the Crove Beta environment (beta-post.crove.com). .DESCRIPTION Script workflow: - 1. Run SSO Worker tests (@crove/sso) and Branding Guard validation - 2. Deploy Cloudflare Worker Crove SSO for Beta (beta-sso.crove.com) - 3. Output Docker compose stack commands for GCP server + 1. Run Branding Guard validation + 2. Output Docker compose stack commands for GCP server .EXAMPLE .\scripts\deploy-beta.ps1 #> @@ -25,25 +24,18 @@ Write-Host "==========================================================" -Foregro Write-Host " CROVE BETA ENVIRONMENT - AUTOMATED DEPLOYMENT PIPELINE" -ForegroundColor Yellow Write-Host "==========================================================" -ForegroundColor Cyan -# 1. Run tests & Branding Guard +# 1. Branding Guard if (-not $SkipTests) { - Write-Host "`n[1/3] Running Vitest unit tests for @crove/sso..." -ForegroundColor Green - pnpm --filter @crove/sso test - if ($LASTEXITCODE -ne 0) { - Write-Error "SSO tests failed! Aborting deployment." - exit 1 - } - - Write-Host "`nChecking Branding Guard validation..." -ForegroundColor Green + Write-Host "`n[1/2] Checking Branding Guard validation..." -ForegroundColor Green pnpm dlx tsx scripts/branding-guard.ts if ($LASTEXITCODE -ne 0) { Write-Error "Branding Guard validation failed! Aborting deployment." exit 1 } - Write-Host "-> All tests and branding validation passed!" -ForegroundColor Green + Write-Host "-> Branding validation passed!" -ForegroundColor Green } else { - Write-Host "`n[1/3] Skipping tests (-SkipTests)." -ForegroundColor Yellow + Write-Host "`n[1/2] Skipping tests (-SkipTests)." -ForegroundColor Yellow } Write-Host "`nChecking Beta deployment contract..." -ForegroundColor Green @@ -53,18 +45,8 @@ if ($LASTEXITCODE -ne 0) { exit 1 } -# 2. Deploy Cloudflare Worker for Beta -Write-Host "`n[2/3] Deploying Cloudflare Worker Crove SSO (Beta)..." -ForegroundColor Green -pnpm --filter @crove/sso run deploy:beta - -if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to deploy SSO Beta to Cloudflare." - exit 1 -} -Write-Host "-> SSO Beta Worker deployed successfully to https://beta-sso.crove.com" -ForegroundColor Green - -# 3. Docker Stack Beta deployment instructions -Write-Host "`n[3/3] Configure Docker Stack Beta on GCP Server..." -ForegroundColor Green +# 2. Docker Stack Beta deployment instructions +Write-Host "`n[2/2] Configure Docker Stack Beta on GCP Server..." -ForegroundColor Green Write-Host "Beta environment configuration file:" -ForegroundColor Cyan Write-Host " - scripts/crove-server.beta.env" -ForegroundColor White Write-Host " - scripts/docker-compose.beta.yaml" -ForegroundColor White @@ -75,5 +57,5 @@ Write-Host " docker compose -f scripts/docker-compose.beta.yaml up -d --no-deps Write-Host "`n==========================================================" -ForegroundColor Green Write-Host " BETA DEPLOYMENT READY!" -ForegroundColor Green Write-Host " - App Dashboard: https://beta-post.crove.com" -ForegroundColor Cyan -Write-Host " - SSO Endpoint: https://beta-sso.crove.com" -ForegroundColor Cyan +Write-Host " - SSO: via api.dos.me PKCE bridge (sso.crove.com worker removed)" -ForegroundColor Cyan Write-Host "==========================================================" -ForegroundColor Green diff --git a/scripts/deploy-prod.ps1 b/scripts/deploy-prod.ps1 index cb026a3c8a..8aaaac5ea7 100644 --- a/scripts/deploy-prod.ps1 +++ b/scripts/deploy-prod.ps1 @@ -3,9 +3,8 @@ Automated deployment pipeline for the Crove Production environment (crove.com & post.crove.com). .DESCRIPTION Script workflow: - 1. Comprehensive testing for SSO Worker and Branding Guard - 2. Deploy Cloudflare Worker Crove SSO for Production (sso.crove.com) - 3. Output Docker compose stack commands for production server + 1. Run Branding Guard validation + 2. Output Docker compose stack commands for production server .EXAMPLE .\scripts\deploy-prod.ps1 #> @@ -22,38 +21,21 @@ Write-Host "==========================================================" -Foregro Write-Host " CROVE PRODUCTION ENVIRONMENT - DEPLOYMENT PIPELINE" -ForegroundColor Yellow Write-Host "==========================================================" -ForegroundColor Cyan -# 1. Run tests & Branding Guard +# 1. Branding Guard if (-not $SkipTests) { - Write-Host "`n[1/3] Running Vitest unit tests for @crove/sso..." -ForegroundColor Green - pnpm --filter @crove/sso test - if ($LASTEXITCODE -ne 0) { - Write-Error "SSO tests failed! Aborting deployment." - exit 1 - } - - Write-Host "`nChecking Branding Guard validation..." -ForegroundColor Green + Write-Host "`n[1/2] Checking Branding Guard validation..." -ForegroundColor Green pnpm dlx tsx scripts/branding-guard.ts if ($LASTEXITCODE -ne 0) { Write-Error "Branding Guard validation failed! Aborting deployment." exit 1 } - Write-Host "-> All tests and branding validation passed!" -ForegroundColor Green + Write-Host "-> Branding validation passed!" -ForegroundColor Green } else { - Write-Host "`n[1/3] Skipping tests (-SkipTests)." -ForegroundColor Yellow -} - -# 2. Deploy Cloudflare Worker for Production -Write-Host "`n[2/3] Deploying Cloudflare Worker Crove SSO (Production)..." -ForegroundColor Green -pnpm --filter @crove/sso run deploy:prod - -if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to deploy SSO Production to Cloudflare." - exit 1 + Write-Host "`n[1/2] Skipping tests (-SkipTests)." -ForegroundColor Yellow } -Write-Host "-> SSO Production Worker deployed successfully to https://sso.crove.com" -ForegroundColor Green -# 3. Docker Stack Production deployment instructions -Write-Host "`n[3/3] Configure Docker Stack Production on GCP Server..." -ForegroundColor Green +# 2. Docker Stack Production deployment instructions +Write-Host "`n[2/2] Configure Docker Stack Production on GCP Server..." -ForegroundColor Green Write-Host "Production environment configuration files:" -ForegroundColor Cyan Write-Host " - scripts/crove-server.env" -ForegroundColor White Write-Host " - scripts/docker-compose.prod.yaml" -ForegroundColor White @@ -65,5 +47,5 @@ Write-Host "`n==========================================================" -Foreg Write-Host " PRODUCTION DEPLOYMENT READY!" -ForegroundColor Green Write-Host " - Landing Page: https://crove.com (and https://www.crove.com)" -ForegroundColor Cyan Write-Host " - App Dashboard: https://post.crove.com" -ForegroundColor Cyan -Write-Host " - SSO Endpoint: https://sso.crove.com" -ForegroundColor Cyan +Write-Host " - SSO: via api.dos.me PKCE bridge (sso.crove.com worker removed)" -ForegroundColor Cyan Write-Host "==========================================================" -ForegroundColor Green diff --git a/scripts/deploy-sso.ps1 b/scripts/deploy-sso.ps1 deleted file mode 100644 index 822e63218a..0000000000 --- a/scripts/deploy-sso.ps1 +++ /dev/null @@ -1,98 +0,0 @@ -<# -.SYNOPSIS - Automated build, test and deployment for Cloudflare Worker Crove SSO (Beta & Prod). -.DESCRIPTION - Script workflow: - 1. Verify Node.js & pnpm environment - 2. Run SSO unit test suite (31/31 unit tests) - 3. Deploy worker to Cloudflare via Wrangler CLI for the specified environment -.PARAMETER Environment - Deployment target: 'beta' (default) or 'prod' -.EXAMPLE - .\scripts\deploy-sso.ps1 -Environment beta -.EXAMPLE - .\scripts\deploy-sso.ps1 -Environment prod -#> - -[CmdletBinding()] -param ( - [Parameter(Position = 0)] - [ValidateSet("beta", "prod")] - [string]$Environment = "beta", - - [Parameter()] - [string]$UpstreamClientId = "", - - [Parameter()] - [string]$UpstreamClientSecret = "", - - [Parameter()] - [string]$DownstreamClientId = "", - - [Parameter()] - [string]$DownstreamClientSecret = "" -) - -$ErrorActionPreference = "Stop" - -Write-Host "==========================================================" -ForegroundColor Cyan -Write-Host " CROVE SSO WORKER - AUTOMATED DEPLOYMENT ($($Environment.ToUpper()))" -ForegroundColor Yellow -Write-Host "==========================================================" -ForegroundColor Cyan - -# 1. Run SSO unit tests -Write-Host "`n[1/3] Running Vitest unit tests for @crove/sso..." -ForegroundColor Green -$testResult = pnpm --filter @crove/sso test -if ($LASTEXITCODE -ne 0) { - Write-Error "Tests failed! Aborting deployment." - exit 1 -} -Write-Host "-> All tests passed successfully!" -ForegroundColor Green - -# 2. Configure Secrets if provided -if ($UpstreamClientId -or $UpstreamClientSecret) { - Write-Host "`n[2/3] Configuring Cloudflare Secrets..." -ForegroundColor Green - $envFlag = if ($Environment -eq "beta") { "-e beta" } else { "" } - - if ($UpstreamClientId) { - Write-Host "-> Setting UPSTREAM_CLIENT_ID..." - $UpstreamClientId | pnpm --filter @crove/sso exec wrangler secret put UPSTREAM_CLIENT_ID $envFlag - } - if ($UpstreamClientSecret) { - Write-Host "-> Setting UPSTREAM_CLIENT_SECRET..." - $UpstreamClientSecret | pnpm --filter @crove/sso exec wrangler secret put UPSTREAM_CLIENT_SECRET $envFlag - } - if ($DownstreamClientId) { - Write-Host "-> Setting DOWNSTREAM_CLIENT_ID..." - $DownstreamClientId | pnpm --filter @crove/sso exec wrangler secret put DOWNSTREAM_CLIENT_ID $envFlag - } - if ($DownstreamClientSecret) { - Write-Host "-> Setting DOWNSTREAM_CLIENT_SECRET..." - $DownstreamClientSecret | pnpm --filter @crove/sso exec wrangler secret put DOWNSTREAM_CLIENT_SECRET $envFlag - } -} else { - Write-Host "`n[2/3] Skipping secrets upload (using pre-configured dashboard/env secrets)." -ForegroundColor Gray -} - -# 3. Deploy Worker -Write-Host "`n[3/3] Deploying Cloudflare Worker ($Environment)..." -ForegroundColor Green -if ($Environment -eq "beta") { - pnpm --filter @crove/sso run deploy:beta -} else { - pnpm --filter @crove/sso run deploy:prod -} - -if ($LASTEXITCODE -eq 0) { - Write-Host "`n==========================================================" -ForegroundColor Green - Write-Host " DEPLOYMENT SUCCEEDED FOR $($Environment.ToUpper())!" -ForegroundColor Green - if ($Environment -eq "beta") { - Write-Host " SSO Endpoint: https://beta-sso.crove.com" -ForegroundColor Cyan - Write-Host " App Callback: https://beta-post.crove.com/settings" -ForegroundColor Cyan - } else { - Write-Host " SSO Endpoint: https://sso.crove.com" -ForegroundColor Cyan - Write-Host " App Callback: https://post.crove.com/settings" -ForegroundColor Cyan - } - Write-Host "==========================================================" -ForegroundColor Green -} else { - Write-Error "Deployment failed. Please check Cloudflare authentication or API token." - exit 1 -} From 9d17f86b66d5776f93a3231af6754cb081c2b2a0 Mon Sep 17 00:00:00 2001 From: JOY <5027251+JOY@users.noreply.github.com> Date: Sat, 19 Sep 2026 02:31:49 +0700 Subject: [PATCH 3/3] chore(sso): drop orphaned SSO guard in build-all and sso mode in dev script - build-all.ps1: remove stale LASTEXITCODE guard from the deleted SSO step (stale non-zero exit from a prior native command in the same PowerShell session would abort the build with 'SSO Test failed') - dev.ps1: remove the dead -Mode sso branch and its ValidateSet entry --- scripts/build-all.ps1 | 6 +----- scripts/dev.ps1 | 8 ++------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/scripts/build-all.ps1 b/scripts/build-all.ps1 index 26cc37a523..a831a1d0cc 100644 --- a/scripts/build-all.ps1 +++ b/scripts/build-all.ps1 @@ -7,7 +7,6 @@ - apps/frontend (App Dashboard Next.js) - apps/backend (API NestJS) - apps/orchestrator (Temporal Background Jobs) - - apps/crove-sso (Cloudflare Worker SSO Bridge) .EXAMPLE .\scripts\build-all.ps1 #> @@ -18,10 +17,7 @@ Write-Host "==========================================================" -Foregro Write-Host " CROVE MONOREPO - FULL BUILD & VALIDATION" -ForegroundColor Yellow Write-Host "==========================================================" -ForegroundColor Cyan -# 1. Build & Test SSO Worker -if ($LASTEXITCODE -ne 0) { Write-Error "SSO Test failed!"; exit 1 } - -# 2. Build Landing Page +# 1. Build Landing Page Write-Host "`n[1/3] Build Landing Page (@crove/web)..." -ForegroundColor Green pnpm --filter @crove/web run build if ($LASTEXITCODE -ne 0) { Write-Error "Web Build failed!"; exit 1 } diff --git a/scripts/dev.ps1 b/scripts/dev.ps1 index dd5321af0d..0c966b70cc 100644 --- a/scripts/dev.ps1 +++ b/scripts/dev.ps1 @@ -7,7 +7,7 @@ - App Frontend Dashboard (Port 4200) - Backend API & Orchestrator (Port 3000 internal / background) .PARAMETER Mode - 'all' (Full stack: Web + App + Backend), 'web' (Landing Page only), 'app' (App Frontend only), 'sso' (SSO Worker only) + 'all' (Full stack: Web + App + Backend), 'web' (Landing Page only), 'app' (App Frontend only) .EXAMPLE .\scripts\dev.ps1 -Mode all .EXAMPLE @@ -17,7 +17,7 @@ [CmdletBinding()] param ( [Parameter(Position = 0)] - [ValidateSet("all", "web", "app", "sso")] + [ValidateSet("all", "web", "app")] [string]$Mode = "all" ) @@ -34,10 +34,6 @@ switch ($Mode) { Write-Host "Starting App Frontend (apps/frontend)..." -ForegroundColor Green pnpm --filter @crove/frontend run dev } - "sso" { - Write-Host "Starting local SSO Worker..." -ForegroundColor Green - pnpm --filter @crove/sso run dev - } "all" { Write-Host "Starting Landing Page, Frontend & Backend in parallel..." -ForegroundColor Green pnpm run dev