From 5c7893f878168b045f6f225812160aa4addbbe20 Mon Sep 17 00:00:00 2001 From: Levi Wilkerson Date: Tue, 13 Oct 2026 13:25:20 -0400 Subject: [PATCH] feat: self-host app updates on cloudflare --- .github/workflows/deploy.yml | 26 + AGENTS.md | 16 +- README.md | 6 + docs/ota-updates.md | 82 + migrations/updates/0001_hot-updater_1.0.0.sql | 121 ++ package.json | 12 +- pnpm-lock.yaml | 1519 ++++++++++++++++- src/updates/worker.test.ts | 68 + src/updates/worker.ts | 36 + wrangler-updates.json | 82 + 10 files changed, 1956 insertions(+), 12 deletions(-) create mode 100644 docs/ota-updates.md create mode 100644 migrations/updates/0001_hot-updater_1.0.0.sql create mode 100644 src/updates/worker.test.ts create mode 100644 src/updates/worker.ts create mode 100644 wrangler-updates.json diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 86a5f6e..fdf906d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,6 +6,32 @@ on: - "v*" jobs: + updates: + runs-on: ubuntu-latest + name: Deploy updates (${{ matrix.environment }}) + strategy: + fail-fast: false + matrix: + environment: [dev, beta, prod] + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: pnpm + - run: pnpm install --frozen-lockfile + - name: Apply update schema + run: pnpm run updates:migrate:${{ matrix.environment }} + env: + CI: "true" + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + - name: Deploy update service + run: pnpm run updates:deploy:${{ matrix.environment }} + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} deploy: runs-on: ubuntu-latest name: Deploy diff --git a/AGENTS.md b/AGENTS.md index ac09c65..8c1ee5c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,13 +8,27 @@ details, primarily the **environments**. ## Environments -There are two deployed Workers, defined in `wrangler.toml`: +There are two deployed API Workers, defined in `wrangler.toml`: | Env | Worker name | URL | Deploy command | | ---- | -------------- | -------------------------------------------- | --------------------- | | prod | `ww-proxy` | `https://ww-proxy.leviwilkerson.com` | `pnpm run deploy` | | dev | `ww-proxy-dev` | `https://ww-proxy-dev..workers.dev` | `pnpm run deploy:dev` | +Three app-update Workers (Hot Updater) are defined separately in +`wrangler-updates.json`, one per app channel, each with its own D1 database and +private R2 bucket of the same name: + +| Env | Worker / D1 / R2 | URL | Deploy command | +| ---- | ----------------- | ---------------------------------------------------- | ------------------------------------------------------- | +| dev | `ww-updates-dev` | `https://ww-updates-dev.levi-wilkerson.workers.dev` | `pnpm updates:migrate:dev && pnpm updates:deploy:dev` | +| beta | `ww-updates-beta` | `https://ww-updates-beta.levi-wilkerson.workers.dev` | `pnpm updates:migrate:beta && pnpm updates:deploy:beta` | +| prod | `ww-updates-prod` | `https://ww-updates-prod.levi-wilkerson.workers.dev` | `pnpm updates:migrate:prod && pnpm updates:deploy:prod` | + +They share no bindings with ww-proxy, and plain `wrangler deploy` never touches +them. The tag deploy migrates and deploys all three alongside ww-proxy. Setup, +credentials and verification are in [`docs/ota-updates.md`](./docs/ota-updates.md). + Prefer the `pnpm run deploy*` scripts over raw `wrangler deploy` — they also inject the `SENTRY_RELEASE` var and upload source maps to Sentry (see [Sentry source maps](#sentry-source-maps) below). CI (`.github/workflows/deploy.yml`) diff --git a/README.md b/README.md index a2ab9e6..b0a7127 100644 --- a/README.md +++ b/README.md @@ -405,3 +405,9 @@ Response to Client - **Package Manager**: pnpm - **Monitoring**: Sentry - **Wrangler**: v4.42.2 + +## App OTA updates + +Hot Updater runs in separate dev, beta and prod Workers, each with its own D1 +catalog and private R2 bucket. See [the OTA runbook](docs/ota-updates.md) for +resource identities, migration/deployment commands and credential setup. diff --git a/docs/ota-updates.md b/docs/ota-updates.md new file mode 100644 index 0000000..a852619 --- /dev/null +++ b/docs/ota-updates.md @@ -0,0 +1,82 @@ +# Hot Updater + +The update services are independent of the ww-proxy API Worker. Each has its +own private R2 bucket, D1 database, client credential and app signing key: + +| Environment | Worker / bucket / database | App channel | +| --- | --- | --- | +| Development | `ww-updates-dev` | `dev` | +| Beta | `ww-updates-beta` | `beta` | +| Production | `ww-updates-prod` | `production` | + +Their URLs are `https://.levi-wilkerson.workers.dev`. The database IDs +and resource bindings are in `wrangler-updates.json`. Plain `wrangler deploy` +still deploys only ww-proxy; update commands always select their own config and +an explicit environment. + +## Deploy + +From this repository, for each of `dev`, `beta` and `prod`: + +```sh +pnpm updates:migrate:dev +pnpm updates:deploy:dev +``` + +Apply migrations before deploying the runtime. `0001_hot-updater_1.0.0.sql` is +the unmodified Cloudflare schema supplied by Hot Updater 1.0.2, including API +keys, Insights and Remote Config. Only `handlers.client` is mounted; the admin +protocol and console are not exposed publicly. CLI writes use Cloudflare D1 +and R2 credentials in the app repository. + +The tag deployment workflow also migrates and deploys these Workers, retaining +their existing secrets. This changes update infrastructure, not installed app +code; publishing an OTA is a separate operation in the app repository. + +## Credentials + +Each Worker needs `R2_ACCESS_KEY_ID` and `R2_SECRET_ACCESS_KEY` secrets to sign +download URLs. Use an R2 Object Read & Write token scoped to that Worker's +bucket. Save it in the 1Password Agents vault and upload the two values with +`wrangler secret put --config wrangler-updates.json --env ` via +stdin. Do not commit credentials or put them in command arguments or logs. + +The app's CLI also needs a D1 Edit API token for this account. A Wrangler OAuth +session can migrate D1 and register client keys, but cannot mint API tokens or +supply persistent R2 S3 credentials. + +Register client keys through the `apiKeys()` plugin's provisioning API or +`hot-updater api-key create` in a matching server/config context. Only the +SHA-256 digest is stored in D1. A client key can read updates and submit +operational reports; it cannot deploy or administer updates. Each environment +has a separate client key. Rotation must ship a new client credential before +revoking the old one. + +## Verify + +Use the official CLI's scoped scaffold and infrastructure doctor checks against +the version-matched generated scaffold. For both iOS and Android, the exact +catalog route must reject an absent/wrong key with 401 and accept the matching +key with 200, or 404 with `x-hot-updater-catalog: none` for an empty catalog. +Keys from another environment must fail. `/version` alone does not prove OTA. + +Separately verify D1 migrations, bound bucket identities, a bounded R2 S3 list +with the CLI's credentials, signed artifact downloads, and a native Release +app's application/rollback and Insights reports. See WitnessWork's +`docs/ota-updates.md`. Keep all generated scaffolds, credentials and evidence +under ignored `.verify/` folders. + +## Rollout + +Before enabling an environment, create its resources, apply the schema, +register its distinct client key and install its bucket-scoped R2 secrets. +Save credentials in Agents as `WitnessWork OTA R2 ()`; the app's +publisher also needs a persistent account-scoped D1 Edit token. Confirm that +the CI account token can migrate D1 and deploy each configured Worker. + +Use development and Beta Release apps on both platforms to verify signed +artifact downloads, activation, disabled-release rollback, invalid-signature +rejection and Insights reports. Record the current setup and proof in the +paired PRs or release record. Preparing production infrastructure does not +release an app bundle; production app rollout follows its separate typed +release gate in WitnessWork. diff --git a/migrations/updates/0001_hot-updater_1.0.0.sql b/migrations/updates/0001_hot-updater_1.0.0.sql new file mode 100644 index 0000000..fa38766 --- /dev/null +++ b/migrations/updates/0001_hot-updater_1.0.0.sql @@ -0,0 +1,121 @@ +-- HotUpdater.schema + +CREATE TABLE IF NOT EXISTS "_hu_write" ("id" TEXT NOT NULL, "failed_op" INTEGER, PRIMARY KEY ("id")); + +CREATE TABLE IF NOT EXISTS "bundles" ("id" TEXT NOT NULL, "platform" TEXT NOT NULL, "git_commit_hash" TEXT, "metadata" TEXT NOT NULL, "manifest_storage_uri" TEXT NOT NULL, "manifest_file_hash" TEXT NOT NULL, "asset_base_storage_uri" TEXT NOT NULL, "_refs_bundle_patches_bundle_id" INTEGER NOT NULL DEFAULT 0, "_refs_bundle_patches_base_bundle_id" INTEGER NOT NULL DEFAULT 0, "_refs_releases_bundle_id" INTEGER NOT NULL DEFAULT 0, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id")); + +CREATE INDEX IF NOT EXISTS "bundles_byPlatform" ON "bundles" ("platform", "id"); + +CREATE TABLE IF NOT EXISTS "bundle_patches" ("id" TEXT NOT NULL, "bundle_id" TEXT NOT NULL, "base_bundle_id" TEXT NOT NULL, "base_file_hash" TEXT NOT NULL, "patch_file_hash" TEXT NOT NULL, "patch_storage_uri" TEXT NOT NULL, "byte_size" INTEGER NOT NULL, "order_index" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id")); + +CREATE UNIQUE INDEX IF NOT EXISTS "bundle_patches_pair" ON "bundle_patches" ("bundle_id", "base_bundle_id"); + +CREATE INDEX IF NOT EXISTS "bundle_patches_byBundle" ON "bundle_patches" ("bundle_id", "order_index", "id"); + +CREATE INDEX IF NOT EXISTS "bundle_patches_byBase" ON "bundle_patches" ("base_bundle_id", "bundle_id", "id"); + +CREATE TABLE IF NOT EXISTS "releases" ("id" TEXT NOT NULL, "revision" INTEGER NOT NULL, "scope_key" TEXT NOT NULL, "channel_id" TEXT NOT NULL, "platform" TEXT NOT NULL, "kind" TEXT NOT NULL, "bundle_id" TEXT, "strategy" TEXT NOT NULL, "target_app_version" TEXT, "fingerprint_hash" TEXT, "enabled" INTEGER NOT NULL, "should_force_update" INTEGER NOT NULL, "message" TEXT, "rollout_cohort_count" INTEGER NOT NULL, "target_cohorts" TEXT NOT NULL, "operation" TEXT NOT NULL, "source_release_id" TEXT, "created_at_ms" INTEGER NOT NULL, "updated_at_ms" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id")); + +CREATE INDEX IF NOT EXISTS "releases_byScope" ON "releases" ("scope_key", "id"); + +CREATE INDEX IF NOT EXISTS "releases_byScopeEnabled" ON "releases" ("scope_key", "enabled", "id"); + +CREATE INDEX IF NOT EXISTS "releases_byChannelPlatform" ON "releases" ("channel_id", "platform", "id"); + +CREATE INDEX IF NOT EXISTS "releases_byChannelPlatformEnabled" ON "releases" ("channel_id", "platform", "enabled", "id"); + +CREATE INDEX IF NOT EXISTS "releases_byBundle" ON "releases" ("bundle_id", "id"); + +CREATE TABLE IF NOT EXISTS "release_catalogs" ("scope_key" TEXT NOT NULL, "catalog_id" TEXT NOT NULL, "strategy" TEXT NOT NULL, "channel_id" TEXT NOT NULL, "channel_key" TEXT NOT NULL, "platform" TEXT NOT NULL, "fingerprint_hash" TEXT, "generation" INTEGER NOT NULL, "payload" TEXT NOT NULL, "catalog_hash" TEXT NOT NULL, "byte_size" INTEGER NOT NULL, "is_tombstone" INTEGER NOT NULL, "updated_at_ms" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("scope_key")); + +CREATE TABLE IF NOT EXISTS "channels" ("id" TEXT NOT NULL, "name" TEXT NOT NULL, "_refs_releases_channel_id" INTEGER NOT NULL DEFAULT 0, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id")); + +CREATE INDEX IF NOT EXISTS "channels_all" ON "channels" ("name", "id"); + +CREATE UNIQUE INDEX IF NOT EXISTS "channels_name" ON "channels" ("name"); + +CREATE TABLE IF NOT EXISTS "bundle_totals" ("platform_key" TEXT NOT NULL, "_shard" INTEGER NOT NULL, "bundles" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("platform_key", "_shard")); + +CREATE TABLE IF NOT EXISTS "bundle_events" ("id" TEXT NOT NULL, "type" TEXT NOT NULL, "install_id" TEXT NOT NULL, "user_id" TEXT, "from_release_id" TEXT, "from_bundle_id" TEXT, "to_release_id" TEXT, "to_bundle_id" TEXT NOT NULL, "platform" TEXT NOT NULL, "app_version" TEXT NOT NULL, "channel" TEXT NOT NULL, "metadata" TEXT NOT NULL, "received_at_ms" INTEGER NOT NULL, "day" INTEGER, "movement_install_id" TEXT, "bundle_ref" TEXT, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id")); + +CREATE INDEX IF NOT EXISTS "bundle_events_movementsByInstall" ON "bundle_events" ("movement_install_id", "received_at_ms", "id"); + +CREATE TABLE IF NOT EXISTS "bundle_events__byBundle" ("platform" TEXT NOT NULL, "channel" TEXT NOT NULL, "type" TEXT NOT NULL, "bundle_ref" TEXT NOT NULL, "day" INTEGER NOT NULL, "received_at_ms" INTEGER NOT NULL, "id" TEXT NOT NULL, PRIMARY KEY ("platform", "channel", "type", "bundle_ref", "day", "received_at_ms", "id")); + +CREATE INDEX IF NOT EXISTS "bundle_events_byDay" ON "bundle_events" ("day", "received_at_ms", "id"); + +CREATE TABLE IF NOT EXISTS "bundle_event_heads" ("install_id" TEXT NOT NULL, "id" TEXT NOT NULL, "type" TEXT NOT NULL, "user_id" TEXT, "from_release_id" TEXT, "from_bundle_id" TEXT, "to_release_id" TEXT, "to_bundle_id" TEXT NOT NULL, "platform" TEXT NOT NULL, "app_version" TEXT NOT NULL, "channel" TEXT NOT NULL, "metadata" TEXT NOT NULL, "received_at_ms" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("install_id")); + +CREATE INDEX IF NOT EXISTS "bundle_event_heads_byUser" ON "bundle_event_heads" ("user_id", "install_id"); + +CREATE INDEX IF NOT EXISTS "bundle_event_heads__retention" ON "bundle_event_heads" ("received_at_ms", "install_id"); + +CREATE TABLE IF NOT EXISTS "insights_overview" ("identity" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "downloads" INTEGER NOT NULL, "failed_launches" INTEGER NOT NULL, "patch_downloads" INTEGER NOT NULL, "patch_fallbacks" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_overview__retention" ON "insights_overview" ("bucket_start_ms", "identity", "_shard"); + +CREATE TABLE IF NOT EXISTS "insights_sketches" ("identity" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "activity_users" TEXT, "failed_users" TEXT, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_sketches__retention" ON "insights_sketches" ("bucket_start_ms", "identity", "_shard"); + +CREATE TABLE IF NOT EXISTS "insights_overview_daily" ("identity" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "downloads" INTEGER NOT NULL, "failed_launches" INTEGER NOT NULL, "patch_downloads" INTEGER NOT NULL, "patch_fallbacks" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_overview_daily__retention" ON "insights_overview_daily" ("bucket_start_ms", "identity", "_shard"); + +CREATE TABLE IF NOT EXISTS "insights_sketches_daily" ("identity" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "activity_users" TEXT, "failed_users" TEXT, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_sketches_daily__retention" ON "insights_sketches_daily" ("bucket_start_ms", "identity", "_shard"); + +CREATE TABLE IF NOT EXISTS "insights_overview_lifetime" ("identity" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "downloads" INTEGER NOT NULL, "failed_launches" INTEGER NOT NULL, "patch_downloads" INTEGER NOT NULL, "patch_fallbacks" INTEGER NOT NULL, "applies" INTEGER NOT NULL, "failed_updates" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard")); + +CREATE TABLE IF NOT EXISTS "insights_sketches_lifetime" ("identity" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "failed_users" TEXT, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("identity", "bucket_start_ms", "_shard")); + +CREATE TABLE IF NOT EXISTS "insights_distribution" ("channel" TEXT NOT NULL, "platform" TEXT NOT NULL, "app_version" TEXT NOT NULL, "release_id" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "latest_installations" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("channel", "platform", "app_version", "release_id", "bucket_start_ms", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_distribution_byScope" ON "insights_distribution" ("channel", "platform", "bucket_start_ms", "app_version", "release_id", "_shard"); + +CREATE INDEX IF NOT EXISTS "insights_distribution_byVersion" ON "insights_distribution" ("channel", "platform", "app_version", "bucket_start_ms", "release_id", "_shard"); + +CREATE INDEX IF NOT EXISTS "insights_distribution__retention" ON "insights_distribution" ("bucket_start_ms", "channel", "platform", "app_version", "release_id", "_shard"); + +CREATE TABLE IF NOT EXISTS "insights_builtin_distribution" ("channel" TEXT NOT NULL, "platform" TEXT NOT NULL, "app_version" TEXT NOT NULL, "release_id" TEXT NOT NULL, "builtin_bundle_id" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "latest_installations" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("channel", "platform", "app_version", "release_id", "builtin_bundle_id", "bucket_start_ms", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_builtin_distribution_byScope" ON "insights_builtin_distribution" ("channel", "platform", "bucket_start_ms", "app_version", "release_id", "builtin_bundle_id", "_shard"); + +CREATE INDEX IF NOT EXISTS "insights_builtin_distribution_byVersion" ON "insights_builtin_distribution" ("channel", "platform", "app_version", "bucket_start_ms", "release_id", "builtin_bundle_id", "_shard"); + +CREATE INDEX IF NOT EXISTS "insights_builtin_distribution__retention" ON "insights_builtin_distribution" ("bucket_start_ms", "channel", "platform", "app_version", "release_id", "builtin_bundle_id", "_shard"); + +CREATE TABLE IF NOT EXISTS "insights_latest_by_bundle" ("platform" TEXT NOT NULL, "channel" TEXT NOT NULL, "bundle_field" TEXT NOT NULL, "bundle_id" TEXT NOT NULL, "type" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "installations" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("platform", "channel", "bundle_field", "bundle_id", "type", "bucket_start_ms", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_latest_by_bundle__retention" ON "insights_latest_by_bundle" ("bucket_start_ms", "platform", "channel", "bundle_field", "bundle_id", "type", "_shard"); + +CREATE TABLE IF NOT EXISTS "insights_outcomes" ("platform" TEXT NOT NULL, "channel" TEXT NOT NULL, "type" TEXT NOT NULL, "bundle_ref" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "_shard" INTEGER NOT NULL, "events" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("platform", "channel", "type", "bundle_ref", "bucket_start_ms", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_outcomes__retention" ON "insights_outcomes" ("bucket_start_ms", "platform", "channel", "type", "bundle_ref", "_shard"); + +CREATE TABLE IF NOT EXISTS "insights_failures" ("platform" TEXT NOT NULL, "channel" TEXT NOT NULL, "bucket_start_ms" INTEGER NOT NULL, "release_id" TEXT NOT NULL, "stage" TEXT NOT NULL, "reason" TEXT NOT NULL, "detail" TEXT NOT NULL, "_shard" INTEGER NOT NULL, "events" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("platform", "channel", "bucket_start_ms", "release_id", "stage", "reason", "detail", "_shard")); + +CREATE INDEX IF NOT EXISTS "insights_failures__retention" ON "insights_failures" ("bucket_start_ms", "platform", "channel", "release_id", "stage", "reason", "detail", "_shard"); + +CREATE TABLE IF NOT EXISTS "api_keys" ("id" TEXT NOT NULL, "hash" TEXT NOT NULL, "name" TEXT NOT NULL, "prefix" TEXT NOT NULL, "role" TEXT NOT NULL, "created_at_ms" INTEGER NOT NULL, "revoked_at_ms" INTEGER, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id")); + +CREATE INDEX IF NOT EXISTS "api_keys_byCreated" ON "api_keys" ("created_at_ms", "id"); + +CREATE UNIQUE INDEX IF NOT EXISTS "api_keys_hash" ON "api_keys" ("hash"); + +CREATE TABLE IF NOT EXISTS "remote_config_active" ("id" TEXT NOT NULL, "version" INTEGER NOT NULL, "template" TEXT NOT NULL, "updated_at_ms" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("id")); + +CREATE TABLE IF NOT EXISTS "remote_config_versions" ("version" INTEGER NOT NULL, "template" TEXT NOT NULL, "description" TEXT, "update_type" TEXT NOT NULL, "rollback_source" INTEGER, "created_at_ms" INTEGER NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("version")); + +CREATE TABLE IF NOT EXISTS "private_hot_updater_settings" ("key" TEXT NOT NULL, "value" TEXT NOT NULL, "_v" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY ("key")); + +INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.engine', '1', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value"; + +INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.core', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value"; + +INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.insights', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value"; + +INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.apiKeys', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value"; + +INSERT INTO "private_hot_updater_settings" ("key", "value", "_v") VALUES ('schema.remoteConfig', '1.0.0', 0) ON CONFLICT ("key") DO UPDATE SET "value" = excluded."value"; diff --git a/package.json b/package.json index 8f5d0a1..57e505a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,13 @@ "verify": "node scripts/verify/dev.mjs", "typecheck": "tsc --noEmit", "admin:reset-usage": "node --env-file-if-exists=.env scripts/reset-notes-import-usage.mjs", - "db:generate": "drizzle-kit generate" + "db:generate": "drizzle-kit generate", + "updates:deploy:dev": "wrangler deploy --config wrangler-updates.json --env dev", + "updates:migrate:dev": "wrangler d1 migrations apply ww-updates-dev --config wrangler-updates.json --env dev --remote", + "updates:deploy:beta": "wrangler deploy --config wrangler-updates.json --env beta", + "updates:migrate:beta": "wrangler d1 migrations apply ww-updates-beta --config wrangler-updates.json --env beta --remote", + "updates:deploy:prod": "wrangler deploy --config wrangler-updates.json --env prod", + "updates:migrate:prod": "wrangler d1 migrations apply ww-updates-prod --config wrangler-updates.json --env prod --remote" }, "packageManager": "pnpm@10.33.0", "dependencies": { @@ -26,7 +32,9 @@ "drizzle-orm": "0.45.4", "hono": "^4.12.12", "reflect-metadata": "^0.2.2", - "zod": "^4.4.3" + "zod": "^4.4.3", + "@hot-updater/cloudflare": "1.0.2", + "@hot-updater/server": "1.0.2" }, "devDependencies": { "@cloudflare/workers-types": "^4.20260411.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69b1ff4..83c297e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + '@hot-updater/cloudflare': + specifier: 1.0.2 + version: 1.0.2(drizzle-orm@0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1)) + '@hot-updater/server': + specifier: 1.0.2 + version: 1.0.2(drizzle-orm@0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1)) '@openrouter/ai-sdk-provider': specifier: ^2.9.1 version: 2.9.1(ai@6.0.208(zod@4.4.3))(zod@4.4.3) @@ -50,7 +56,7 @@ importers: version: 7.0.1-rc vitest: specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(vite@8.0.16(esbuild@0.28.1)(tsx@4.23.15)) + version: 4.1.9(@opentelemetry/api@1.9.1)(vite@8.0.16(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.15)) wrangler: specifier: ^4.103.0 version: 4.103.0(@cloudflare/workers-types@4.20260411.1) @@ -73,6 +79,112 @@ packages: resolution: {integrity: sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==} engines: {node: '>=18'} + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/checksums@3.1001.3': + resolution: {integrity: sha512-CcAkVyEUKGngbEqJdVdSc53Iy52+XzVL5NIcTen2BlK8UByNC1DThVtf5VKSrNzyA+SqyTvs+3S+knVBbOUizQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.1066.0': + resolution: {integrity: sha512-jfJTg6Xbyws8+YF5sVQXgCA01elkenGEYeX6+HQOovu9m/Td1Ln2xcY3lHKetVNltdArp5rykjNd56z7bnA9dw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.978.3': + resolution: {integrity: sha512-nslpzUCTI9b2IQMWSd6mSAXldBexPc8rr0d3dXUutXYFLFaTdsHa2GD7sJdsn3PhyjK0O5K8ypd6zi+RXXPVlg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.74': + resolution: {integrity: sha512-rsRJcW2iFUhCgclyD6OSbMEaPl8Eu0qSeRZb2uuCK1ViZ66hl5wOe/p6Vr6MO3b+ydyKgFeqza2p6tZThvILlw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.76': + resolution: {integrity: sha512-pnKdh0Br7OJPj+7qKjzgTfDTFW0RhEMKXNDR2jv9CAM4oHuG/4z3hRh8KtutLfpmItuRtDbKBmwWkDqHwwAWXw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.973.19': + resolution: {integrity: sha512-kcjvAxqQEV8yuwkX58RelNn3ne65o7JgRWf3Z/gVvbi7Xuw1qSDLJlatrLD76+40cmRxmNkLxBoyp/vAQChQ5g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.81': + resolution: {integrity: sha512-CeQHSbE2CnVdaHIJ/NayRaCecIER+Pm9faUPkmRATO54rCoMTEPSbVieGHzEwvm1dZM2HIs6qQZCznhayNTWJQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.86': + resolution: {integrity: sha512-WNttSyr6e8innPtBSty//CdnqJcgZN6eAbtw3v/CR2IObz6MYZEo8eusdtuCHJ2ujwFqC2bSPHVyhXGHx8HruQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.74': + resolution: {integrity: sha512-Yj6kZFJQdbR+PygTkqdVLPHHpdDpOS9GCYVcDCuweCRRig9LF24ELQBMfyUw1vspvrZJUfnOGVyMjhaL094ahw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.973.18': + resolution: {integrity: sha512-FnpPhdY4qUGtX/8X6eKxh+uD+9Y19i9FYibgk4SBe0M935s9vocHkHzaxaMNk+YkHmvzgdu8cHA5SCoeU+kkew==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.80': + resolution: {integrity: sha512-TJF25Ndwy2HxcVj6oxMgp8qVlJAawlExHYBXqhuC6Zt2m+A7WpdkjBD6QglNbiArlteYljZHEY3R7KKmno6ISA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/lib-storage@3.1008.0': + resolution: {integrity: sha512-SWIzixPt9VG6jz8+tR8VklOi3FXY1J6WMfd1JEtY9gVtJaLGF5tWexIkjUnGHP8B6M6Uk+qC5zdr2Caome5Gfg==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@aws-sdk/client-s3': ^3.1008.0 + + '@aws-sdk/middleware-flexible-checksums@3.974.58': + resolution: {integrity: sha512-pI1n9zuAT6Dubdk2fp8UBn/xtRYmzZejMweMRcol0zqE5TSfGG1cs6fFW9jkoVWh9GU1W0HqHixirdoOlC+Fzw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.79': + resolution: {integrity: sha512-UfgAQosf4IEXldMsyntVuHsJnaQzlH6pmkOr6dQy7CaOYSlGz3mWiW4Di7XfQ/kPJP6xE7oMshz1V0E8RqGdRg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.997.48': + resolution: {integrity: sha512-B01jdXJtAW/XOiUzGdI2mz67WyEHbGEc9X6Euzw3bjLAa+txTqdZDER4X2FsIEcXzV2ABqudVZ5thali8tCCzQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/s3-request-presigner@3.1066.0': + resolution: {integrity: sha512-hgjGUVKggCKcwS+e7HOoHlElQrhz4nlZkMWGKez/Ycjv2qZaQeTZ8Ps7HGOrpqR3KBPWt7iSrOBcOCVoUYYRWw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.48': + resolution: {integrity: sha512-cFDXfEbXaGwbnpvR/DyqfxfjxpjXVRVthQLgkwvcfDS5u3+8G9WSj1X1Ag/pTRg/QOeOno1Tq3ycEdrKsCUcOA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1150.0': + resolution: {integrity: sha512-wDU36ofhabZYu+d2KSriyDbwzxNoaOp+4dG0p3qkabk7HeALR5nIWyQbjoyJZLn3iqWkv/xNdyUfbfevDVDzzg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.974.6': + resolution: {integrity: sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.10': + resolution: {integrity: sha512-ycwH6Zd2GhuSqdXX9ihbCjeGTB6xOJs+O3+Jb8/zDG9978XU80qs75dfkPJRMNKe5MvBZPuNeFpd4JZKPoUF4g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.41': + resolution: {integrity: sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.3.0': + resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} + engines: {node: '>=18.0.0'} + '@cloudflare/kv-asset-handler@0.5.0': resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} engines: {node: '>=22.0.0'} @@ -133,15 +245,24 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + '@emnapi/core@1.11.2': + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/runtime@1.9.2': resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} deprecated: 'Merged into tsx: https://tsx.hirok.io' @@ -594,6 +715,57 @@ packages: cpu: [x64] os: [win32] + '@hot-updater/cli-tools@1.0.2': + resolution: {integrity: sha512-32odueqPPlfvlG/BMBzM1mhu7sfZJBqY5l5842j2nbVOj+Nq6T/jSTOSB7FRsYzibUMa9em8NDYqZyC8IK5dDA==} + engines: {node: '>=20.19.0'} + + '@hot-updater/cloudflare@1.0.2': + resolution: {integrity: sha512-7lARbB2vqhdyxS7wgv7t0TFV8Z8CWXCsJc9AOVN3FU+NLR6uO0U9aLgpsFkvbLcc5CtOVavRInVSCiJZCgWX8w==} + + '@hot-updater/plugin-api-keys@1.0.2': + resolution: {integrity: sha512-YUqLDwX0+NSzQ5YptO+oa1HGetGZGIVJHbQ2chSSW6APthRpaKpJ07Dg6CYhVnV3sG9sWH8dILH/0RduhRaAeA==} + peerDependencies: + '@hot-updater/plugin-core': ^1.0.2 + + '@hot-updater/plugin-core@1.0.2': + resolution: {integrity: sha512-zIjXgLHcwRwKGyzOcdr4CHwMNg6yyQZ0T9cC8u532d6GOzNCdAZl5bOb3NO8TPmCM/ai6n5AMjKSjJx1nQeoDQ==} + + '@hot-updater/plugin-insights@1.0.2': + resolution: {integrity: sha512-hXhRlZgS2gDUJa4Zsa1yQnEsLUfwjWMuvsgRcvvY0UX/r9Z3PMIZ+o2yBcEPf1mKta0dPhV4WJiP/gGKCCgmjg==} + peerDependencies: + '@hot-updater/plugin-core': ^1.0.2 + peerDependenciesMeta: + '@hot-updater/plugin-core': + optional: true + + '@hot-updater/plugin-remote-config@1.0.2': + resolution: {integrity: sha512-Wt7X+pQ6aGrR9BAM1M082xrtdPNrbnvAuqyfgZz9hKdqhJT7e0eCYTMOIRitrZWnvq03ralvT30I7tc0DxbKSg==} + peerDependencies: + '@hot-updater/plugin-core': ^1.0.2 + peerDependenciesMeta: + '@hot-updater/plugin-core': + optional: true + + '@hot-updater/protocol@1.0.2': + resolution: {integrity: sha512-14mbcKsWG6YSJAULLDwqmeolBoHo1n+VR0DKs/KtdC8NZJe8yPk89IznCfAEVlp2QkkqE56Sr4Hu/WfICXrIQw==} + + '@hot-updater/server@1.0.2': + resolution: {integrity: sha512-l44qF/+q+nQMmoLl5VTehrbnGspS70yJiwDtYPi9K6zOi95nrWoIhVtLF5CHb17B4UxA6K42SGbkWkOPogiP9Q==} + peerDependencies: + '@prisma/client': '*' + drizzle-orm: '*' + kysely: '*' + mongodb: '*' + peerDependenciesMeta: + '@prisma/client': + optional: true + drizzle-orm: + optional: true + kysely: + optional: true + mongodb: + optional: true + '@img/colour@1.1.0': resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} @@ -763,6 +935,13 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@napi-rs/wasm-runtime@1.2.5': + resolution: {integrity: sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + '@openrouter/ai-sdk-provider@2.9.1': resolution: {integrity: sha512-okgq07Vdkro4CB5INbfhwa0e6VR1HS7sidNcfHN/MeXLJvX1JmQCff/vem6tcxwT9r1avyFrXSlfv9B28D/Pag==} engines: {node: '>=18'} @@ -774,9 +953,266 @@ packages: resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} + '@oxc-parser/binding-android-arm-eabi@0.141.0': + resolution: {integrity: sha512-jk7086MFvR/T4DG9IY7MKBVt1PMxvSZoz/TvnifodvS0pjghVwJHRttnAExhlwdMOgHv1TmLdENnbNpYk2zjvA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.141.0': + resolution: {integrity: sha512-a4XDQ27ZT7e7zwAlxJDTiCA7IBGWDuy2+MhFq85Of7XlBSmpkfcBFml11q0Zx6f7RMuI0B4xCtt2ytBS4yOptg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.141.0': + resolution: {integrity: sha512-m/kVk6rzYmBeHYnz+1Y5fod00AVTTxMbC71azFfm/zjx1j9XxwKtA0+VfkKuVMC8rbghb9TtfevnuWZa9OuPEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.141.0': + resolution: {integrity: sha512-o0X+6KZlfucWU/v5oKRQPwdFXsXAjW8jmpo/Gpw/qyKsbKtlfkHoeH9Bjp/m13TwjewvJnCkwF0DWzgpC4HjTQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.141.0': + resolution: {integrity: sha512-W5KbTnNkTMMMylqj6dYqnsXvkmESVPodPKYLJ5zdzIPdl9fUJtolkpUeSzYEbGGYB4a4A4avl3EePnZ/wLIdJg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': + resolution: {integrity: sha512-g3dtbJa8zeOGK36Sr9cQavsdi5H/ie2hVjrSjIxsNAR1qZA40ZYVXnfdfoMAlq8CmB9qFL1yhsSCUHeNmdmt8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': + resolution: {integrity: sha512-e6hwQqd+3lvP13G2jxvFpoA7dzHcFLN+Mq47JCVMtdNHbbyBRo756JCtbbJH6ca8inTfyqZoqBmS3vhQlzAK2w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.141.0': + resolution: {integrity: sha512-vXz2BLAuypA+4MLyBg94pzEo6THVnzYnCtAjXoihIIQo0t2pnp/AmW+SH1EI+4VbuJnC//KplIJ5yyaCGua4jA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.141.0': + resolution: {integrity: sha512-jMkS/EztNW34HKsXIaT/SoHcmtocq/vWhwFOVduF9kduuuRIVwfwQ6uxzIO+qPKSXdd2TXt54of0BJ2zFMXnmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': + resolution: {integrity: sha512-vo+MR+n3zQJ6Mq92hiP084NZcgDv5iJlVR02gMf28neMvVT1tKVm7VeiW/DxhdqOi3QLeaXIk9cUcLL1qrkngw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': + resolution: {integrity: sha512-oh80w+7RuiO5gBp9Jnoa/H8Qlt3JsHL2MkW+0dwEdlDMdslVZX/YsekSK6EeyEenY66/mhCfypsNATQ7Ph3qlQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.141.0': + resolution: {integrity: sha512-LOyEmFA8sCnYbEXP1+iQvCC/P1YXHMA/t6x1Ksp0Y9VwhLFsiBJFzV1zIxrOIE2LKaGGhDjQ29xq9cbq6omDXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.141.0': + resolution: {integrity: sha512-3wnwk/l1CvszVE5TJR1wSl/zSEfydRqrNhn6s7Vr9IzSJpUQIroqVsIoPARHRFA+FQwkxAFDAHDAasa7v8OobQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.141.0': + resolution: {integrity: sha512-qtyQVAAebFq57B2tifTlel3TgGqUtsYNI/e+p6aya9rN9lOZVTDvr215fGYSA9XWooxzMxDiVxkBLk2jQHbsOQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.141.0': + resolution: {integrity: sha512-SkGV1nKw40roEc94pv5EaaeH2ay14G6+roe8Q0wIUC1LcEKxzKW921h7+ZuZX0D3q2Mb/7aSFmxEVqnko3lPRw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.141.0': + resolution: {integrity: sha512-cVgDM7n8QziQqOaP5hNgUYfMG7S/ZeuPxFWXnnHRv7rh025COk0rfQ6eEdKG3j/GaUuyvNZN4ifF1J8KmuXLLA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.141.0': + resolution: {integrity: sha512-HggH++Fkn3OilBn+bs3jpgIFQa34oMAyUUHy0vpGum+gt1Eb5nyLc8dNU/RAPSw6lsLrx7ncKtHSZE+3Sp0l2g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.141.0': + resolution: {integrity: sha512-KLSEH9GwgbrqbJOjtGHt9STw96s+78yDzp7IDN8Lno+7Ut9sNBfZ4jYZIz4mD50qmWUjoOI7i9I6UENbhNbMZQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.141.0': + resolution: {integrity: sha512-9UVWUOOCI/1YkiSSNjg2zyBJYM9E/t1A/8GNobd48JDn/fQ6mzxcVO3H08jb3rAaW/B1VBf8eCORTvSsO9T08g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.141.0': + resolution: {integrity: sha512-HI/wsvbWT5RHHw5c37D0fEgeTd8/1Q4OJs5jUmEBc17VZFG6SsCIe4barq7NsAPPks/JW+3ayi3Rp+PQI5h4Kg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxc-project/types@0.133.0': resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + '@oxc-project/types@0.141.0': + resolution: {integrity: sha512-S4as7z0j0xQkXcJlyY5ehntwK8/wRkQb9Cyqw+J/N2rkWGQGK0SxD6X6DhQTc7qsxVTBxXbxZtBJh3mr3PtIzQ==} + + '@oxc-transform/binding-android-arm-eabi@0.141.0': + resolution: {integrity: sha512-FOdseb5KpV3JtkM+7TN4u3sW3aQkUSRy0bWWiKgT/qrIGqZHqzKEUpwaMybutsfwEglWBXuJgnrT9loWisPkrw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-transform/binding-android-arm64@0.141.0': + resolution: {integrity: sha512-jglqGwEnSuldt1nfFSpDBHQZ/RzjCjWQEMDatPOEOWIA0ZCCYFj/9ILUOTVllZz79FI9/c0p/bctVVlQqQxxYg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-transform/binding-darwin-arm64@0.141.0': + resolution: {integrity: sha512-81jBeodJH0IRJ3/OnAgW2lBPm/kcoz603d8eGnnwgW2HbxAUs8rfw+YWaKLrWaKjT0oSIPOrGMJu9DeaKD68sQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-transform/binding-darwin-x64@0.141.0': + resolution: {integrity: sha512-o2jD9E7CyPxhb3CuYbaYsnFH0Mo1hKa2R+Hfo6xJxOlyfcM8U4R73W3YXb9w6lRPUc0PBFUbCmJjtGR/k5SIYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-transform/binding-freebsd-x64@0.141.0': + resolution: {integrity: sha512-DNF+Of7nckfwqu9fZin4vR8bGmF/3sWPvtw9Jc4cjeQoRB9vUuC6w9C/GxW+0Vr+PCH+gk4tR4Ygvjfx3LYOzQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-transform/binding-linux-arm-gnueabihf@0.141.0': + resolution: {integrity: sha512-nolqi5WqP68qVQJdnPFCF50JT5KzqlcpvwGFyIV+IRCLcv5Hh11gIu4InMs3Uswcf0BJnV/oLIjzb7Sycd3zJg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-transform/binding-linux-arm-musleabihf@0.141.0': + resolution: {integrity: sha512-ytbO89DQl6KxjKCRlyCEtABgfs8njm623ambDkZZBer5J9dbTwbaV5hseZsPkVzZPxwXobu43/XS0jVZnrmCTA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-transform/binding-linux-arm64-gnu@0.141.0': + resolution: {integrity: sha512-Tjj3pobBAzbzUJE8ljbS61om6yfVnarSZzS8LaFCSn0rB+hs0IZRE3jC9IzpsVPHSoJl4uhbIGebuDY2G3B6gg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-transform/binding-linux-arm64-musl@0.141.0': + resolution: {integrity: sha512-aUUptri02E4nqkUvyA+2oYkmU20pYnHIpNLPZOgom0kAa0Fx9X6QFKqKm1MopyucWolQ9XTgCK2PrupSSBdCdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-transform/binding-linux-ppc64-gnu@0.141.0': + resolution: {integrity: sha512-d28AT+SfcAYFnulI2C8HJ5OeoUa5w+eMpwy0uMzbU/3zXJpFjjFOvJlEL8FNvx7HBEENj5IfgD+aXdNzEEn1Tg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-transform/binding-linux-riscv64-gnu@0.141.0': + resolution: {integrity: sha512-P6XVhw+MJsjeliWJ6BBKdC0HU1VEiwMRHtqUADK+jK5wiqgzt3/JhIcMuRLzBPYfnOujLpe+fm63yEzKUIAcSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-transform/binding-linux-riscv64-musl@0.141.0': + resolution: {integrity: sha512-6QUicErqoLpCVsXPYI+OEYJ95TjiV+trvX9VeCuyVcyL5p0Opi7DFQL2c1DcGHi1c9cqz1h09mUKCZ7Y52kxfw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-transform/binding-linux-s390x-gnu@0.141.0': + resolution: {integrity: sha512-r2Jk0vfcnHnkPOjLnWvpGVTFcYDirGICBJYraCkWeplhPZ2Sgg9GvDVgah9VFOHRZVtK6XQynELP9A8EB/Ne0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-transform/binding-linux-x64-gnu@0.141.0': + resolution: {integrity: sha512-lIsdGq4LA1IlbrZlmrhP/p0pCo4+5jrAlLp9BMzIHaV4mILBSr+ZtuTDkML4117cLHdzoWOqw0s3Rb7igOWtxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-transform/binding-linux-x64-musl@0.141.0': + resolution: {integrity: sha512-y+9FbVRBhUtHBVn0xHiVcaNHkoRudIhlKhRk1+CclE1uGD6af3xznTjCOrKGB7HZ2SzlCWLZwQGm/KERpUjX1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-transform/binding-openharmony-arm64@0.141.0': + resolution: {integrity: sha512-Ul5Fu6zPL7kjTbtBU30HaRFHPL3bjjpdieHmAXJlJDuFukuOXqVD6dS70b314IjFy/rbqkD8OK4MQ4408eEjyg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-transform/binding-wasm32-wasi@0.141.0': + resolution: {integrity: sha512-GaQJvcyFJyle5lll67+iYAgv+G21bQXvWPNhD6YIYoR/bV8kS0Qm8YSL22Y/YZC/8RwsneVFWpP2y6VZJfcM/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-transform/binding-win32-arm64-msvc@0.141.0': + resolution: {integrity: sha512-Fbi/hC64tNa56mXEitjaWsxQoMvtJDbOe2MxO+BA1v1Ev28L9n1yMJwhKyOCA+Rm8lYK91oOb5CRv3Ct5RuvJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-transform/binding-win32-ia32-msvc@0.141.0': + resolution: {integrity: sha512-2HlslqwOXQ2nxAZjhqZIpvPxENhQGvA6b4cGwmiVD6Uh5x+dZAopOeEJRxRPpKcrr/W5KXfwwbttwx4OWbg9TA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-transform/binding-win32-x64-msvc@0.141.0': + resolution: {integrity: sha512-JEgTv+y56FbwinH1/kqpNyD+bWRWDpbbPdSY7Ta7rCKFiRsYkUHZd1v+XWxda08cS8GAmlKS4WKcVwtrAbDUHA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@peculiar/asn1-cms@2.8.0': resolution: {integrity: sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==} @@ -823,6 +1259,9 @@ packages: '@poppinss/exception@1.2.3': resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@quansync/fs@1.1.0': + resolution: {integrity: sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==} + '@rolldown/binding-android-arm64@1.0.3': resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -985,6 +1424,54 @@ packages: resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} + '@smithy/abort-controller@4.4.2': + resolution: {integrity: sha512-IINJV4TQAvI1+upIy3YaDtlFcLsWQ5Uw6Nos7CNwiJtpRFw98L1XOn38467Pw54pcYuDi8HGnc2CNbkZBEZq6Q==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.36.0': + resolution: {integrity: sha512-3hJiwG1F5t1SoXqp/yOCmYXziyx7ZVqF63S5W6YU201e/qwVaHPex+E/3vvyY9a3ORq/b+BydeNZpP2saQ9X2g==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.5.2': + resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.8.0': + resolution: {integrity: sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-endpoint@4.7.2': + resolution: {integrity: sha512-7HgK3/pQQHcD5w9lOPtK53/eDMKDp324Nd4KZ8XvxlcKHiWytuM9VwVOB2iy3rutsz/N1WNEWBkaRBayrGnuog==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.12.1': + resolution: {integrity: sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.7.4': + resolution: {integrity: sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.15.2': + resolution: {integrity: sha512-qweA7dHwcWmq8IN2mIGo7nAfeji1nwWBra+P3nz4R8W8yoQYE6Iy2bN33I0W3d50MN/V5EiDY/nElr4EHITsrg==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.19.0': + resolution: {integrity: sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + '@speed-highlight/core@1.2.15': resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} @@ -994,6 +1481,9 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1003,6 +1493,12 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + '@typescript/typescript-aix-ppc64@7.0.1-rc': resolution: {integrity: sha512-oqq2ZfEJ7BQuufcC3QBQndZLPNyamYNHLao8lKRBeeSkZKypBqxPSgkzrcFZtbYcIaBvpiyUnQP9MT7DEYHWbw==} engines: {node: '>=16.20.0'} @@ -1156,6 +1652,14 @@ packages: '@vitest/utils@4.1.9': resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + ai@6.0.208: resolution: {integrity: sha512-STz+AaZqJ4ZjH7UkpXkbHx+bjgIDOsE8fIUoZjkZ2whoZcfVmG9K/TqEKouJZ03SuZuD7lagntlU3zBhAEkRpQ==} engines: {node: '>=18'} @@ -1170,12 +1674,28 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.6.0: + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + cbor2@2.3.0: resolution: {integrity: sha512-76WB3hq8BoaGkMkBVJ27fW5LJU+qqDLEpgRNCG/SYKhODWXpVPOTD4UcUto3IEzYLA52nsvbhb0wabhHDn3qXg==} engines: {node: '>=20'} @@ -1184,6 +1704,13 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} + cloudflare@4.2.0: + resolution: {integrity: sha512-L9IainZq+7PN3NG/Mg7T9oRm7SB7AskNe6QLD8j7FrJY+2Dn1qpeLqXF8Im04ANnssezf0KAOtSuxNAAyNEgkg==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1191,6 +1718,13 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1294,12 +1828,35 @@ packages: sqlite3: optional: true + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-module-lexer@2.1.0: resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-toolkit@1.52.0: + resolution: {integrity: sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==} + esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'} @@ -1318,6 +1875,14 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + eventsource-parser@3.1.0: resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} engines: {node: '>=18.0.0'} @@ -1335,21 +1900,76 @@ packages: picomatch: optional: true + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-tsconfig@4.14.3: resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + hono@4.12.12: resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} engines: {node: '>=16.9.0'} + hono@4.12.34: + resolution: {integrity: sha512-GqXJqY/xJkJmuloTrnV1ZEXG3fqte+VjkUqoRNZXcrUidiUOP4fMSIHHY4tsqZBK++kVyWmt/AAfSUuy57/eSA==} + engines: {node: '>=16.9.0'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -1434,20 +2054,62 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@4.1.1: + resolution: {integrity: sha512-YAApRsTpM2+gnUFHhRNGfkX8NK0V5xj1nyQLufGlwEI+eX4xRsTcel2IXDuMEGSGjBvzykNW4VuoEACgwYe7BQ==} + engines: {node: '>=16'} + hasBin: true + miniflare@4.20260617.1: resolution: {integrity: sha512-Go3/gzStm99QHptsSgU+q1S+xDfLoRgwjJNY80kaTVi0ENhTyqKq+sc4xZiWBSbM7uUcJwmzm8+QFKtcYLJ9nw==} engines: {node: '>=22.0.0'} hasBin: true + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.14: resolution: {integrity: sha512-U9kYi5bpVMEI31yC8iw4bJJp0avcHXA0W8/wNfLfnvJYzihQo2ZRPYPvpAAd570HAcCBjCTN7vnr+v4StKl1IQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + obug@2.1.3: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} + oxc-parser@0.141.0: + resolution: {integrity: sha512-uFkGGr1KMWd6aWv9UAqooYrN78trw8MWWmoPvgWokfBEUq1+eiIQ+qfj3wokhy0fxtZWZk+0dHoS7/yRTJtd6w==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-transform@0.141.0: + resolution: {integrity: sha512-CrMPblXfPl57WIvGjynrUThcyEGcS3E3YUHkPYAwXYYla23gLlaIYifVFTMQsFr5uxA/2k/XMG5n6c/XsuAvaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -1479,6 +2141,13 @@ packages: resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} engines: {node: '>=16.0.0'} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} @@ -1490,6 +2159,12 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rou3@0.7.9: + resolution: {integrity: sha512-+JM7c8swGkoXkWRkIz7qpYN9Bls7Rj/vuSGaxtoKHIe8Ba1ci+mXnqzqtJFrXSbvEazNL9v83P2RiXae9NSbfQ==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} @@ -1519,6 +2194,12 @@ packages: std-env@4.1.0: resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -1538,6 +2219,9 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -1558,6 +2242,15 @@ packages: engines: {node: '>=16.20.0'} hasBin: true + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + + unconfig@7.5.0: + resolution: {integrity: sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici@6.27.0: resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} engines: {node: '>=18.17'} @@ -1569,6 +2262,17 @@ packages: unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuidv7@1.2.1: + resolution: {integrity: sha512-4kPkK3/XTQW9Hbm4CaqfICn+kY9LJtDVEOfgsRRra/+n2Ofg4NqzRFceAkxvQ/Ud/6BpHOPzj8cirqM7TzTN5Q==} + hasBin: true + + verkit@0.4.0: + resolution: {integrity: sha512-sXMwN6DMHeouPfCxkxWkKAmxphWKEenHYY5H1nIBzU3PmDsmJp6kBXJdshjVdpMZuWCmL9SH7KFRx29AylpP6g==} + engines: {node: '>=18.12.0'} + vite@8.0.16: resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1653,6 +2357,16 @@ packages: jsdom: optional: true + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1715,9 +2429,241 @@ snapshots: eventsource-parser: 3.1.0 zod: 4.4.3 - '@ai-sdk/provider@3.0.10': + '@ai-sdk/provider@3.0.10': + dependencies: + json-schema: 0.4.0 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.974.6 + '@aws-sdk/util-locate-window': 3.965.10 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.974.6 + '@aws-sdk/util-locate-window': 3.965.10 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.974.6 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.974.6 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/checksums@3.1001.3': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1066.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.978.3 + '@aws-sdk/credential-provider-node': 3.972.86 + '@aws-sdk/middleware-flexible-checksums': 3.974.58 + '@aws-sdk/middleware-sdk-s3': 3.972.79 + '@aws-sdk/signature-v4-multi-region': 3.996.48 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.12.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/core@3.978.3': + dependencies: + '@aws-sdk/types': 3.974.6 + '@aws-sdk/xml-builder': 3.972.41 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.36.0 + '@smithy/signature-v4': 5.7.4 + '@smithy/types': 4.19.0 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.74': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.76': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.12.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.973.19': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/credential-provider-env': 3.972.74 + '@aws-sdk/credential-provider-http': 3.972.76 + '@aws-sdk/credential-provider-login': 3.972.81 + '@aws-sdk/credential-provider-process': 3.972.74 + '@aws-sdk/credential-provider-sso': 3.973.18 + '@aws-sdk/credential-provider-web-identity': 3.972.80 + '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.81': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.86': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.74 + '@aws-sdk/credential-provider-http': 3.972.76 + '@aws-sdk/credential-provider-ini': 3.973.19 + '@aws-sdk/credential-provider-process': 3.972.74 + '@aws-sdk/credential-provider-sso': 3.973.18 + '@aws-sdk/credential-provider-web-identity': 3.972.80 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.74': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.973.18': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/token-providers': 3.1150.0 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.80': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/lib-storage@3.1008.0(@aws-sdk/client-s3@3.1066.0)': + dependencies: + '@aws-sdk/client-s3': 3.1066.0 + '@smithy/abort-controller': 4.4.2 + '@smithy/middleware-endpoint': 4.7.2 + '@smithy/smithy-client': 4.15.2 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.974.58': + dependencies: + '@aws-sdk/checksums': 3.1001.3 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.79': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/signature-v4-multi-region': 3.996.48 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.48': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/signature-v4-multi-region': 3.996.48 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.12.1 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/s3-request-presigner@3.1066.0': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/signature-v4-multi-region': 3.996.48 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.48': + dependencies: + '@aws-sdk/types': 3.974.6 + '@smithy/signature-v4': 5.7.4 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1150.0': + dependencies: + '@aws-sdk/core': 3.978.3 + '@aws-sdk/nested-clients': 3.997.48 + '@aws-sdk/types': 3.974.6 + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/types@3.974.6': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.10': dependencies: - json-schema: 0.4.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.41': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.3.0': {} '@cloudflare/kv-asset-handler@0.5.0': {} @@ -1758,11 +2704,22 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.2': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.9.2': dependencies: tslib: 2.8.1 @@ -1773,6 +2730,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild-kit/core-utils@3.3.2': dependencies: esbuild: 0.18.20 @@ -2005,6 +2967,77 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true + '@hot-updater/cli-tools@1.0.2(drizzle-orm@0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1))': + dependencies: + '@hot-updater/plugin-core': 1.0.2 + '@hot-updater/protocol': 1.0.2 + '@hot-updater/server': 1.0.2(drizzle-orm@0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1)) + oxc-parser: 0.141.0 + oxc-transform: 0.141.0 + unconfig: 7.5.0 + transitivePeerDependencies: + - '@prisma/client' + - drizzle-orm + - kysely + - mongodb + + '@hot-updater/cloudflare@1.0.2(drizzle-orm@0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1))': + dependencies: + '@aws-sdk/client-s3': 3.1066.0 + '@aws-sdk/lib-storage': 3.1008.0(@aws-sdk/client-s3@3.1066.0) + '@aws-sdk/s3-request-presigner': 3.1066.0 + '@hot-updater/cli-tools': 1.0.2(drizzle-orm@0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1)) + '@hot-updater/plugin-core': 1.0.2 + '@hot-updater/protocol': 1.0.2 + '@hot-updater/server': 1.0.2(drizzle-orm@0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1)) + cloudflare: 4.2.0 + hono: 4.12.34 + uuidv7: 1.2.1 + transitivePeerDependencies: + - '@prisma/client' + - drizzle-orm + - encoding + - kysely + - mongodb + + '@hot-updater/plugin-api-keys@1.0.2(@hot-updater/plugin-core@1.0.2)': + dependencies: + '@hot-updater/plugin-core': 1.0.2 + + '@hot-updater/plugin-core@1.0.2': + dependencies: + '@hot-updater/protocol': 1.0.2 + es-toolkit: 1.52.0 + mime: 4.1.1 + verkit: 0.4.0 + + '@hot-updater/plugin-insights@1.0.2(@hot-updater/plugin-core@1.0.2)': + dependencies: + '@hot-updater/protocol': 1.0.2 + optionalDependencies: + '@hot-updater/plugin-core': 1.0.2 + + '@hot-updater/plugin-remote-config@1.0.2(@hot-updater/plugin-core@1.0.2)': + dependencies: + '@hot-updater/protocol': 1.0.2 + verkit: 0.4.0 + optionalDependencies: + '@hot-updater/plugin-core': 1.0.2 + + '@hot-updater/protocol@1.0.2': {} + + '@hot-updater/server@1.0.2(drizzle-orm@0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1))': + dependencies: + '@hot-updater/plugin-api-keys': 1.0.2(@hot-updater/plugin-core@1.0.2) + '@hot-updater/plugin-core': 1.0.2 + '@hot-updater/plugin-insights': 1.0.2(@hot-updater/plugin-core@1.0.2) + '@hot-updater/plugin-remote-config': 1.0.2(@hot-updater/plugin-core@1.0.2) + '@hot-updater/protocol': 1.0.2 + rou3: 0.7.9 + verkit: 0.4.0 + optionalDependencies: + drizzle-orm: 0.45.4(@cloudflare/workers-types@4.20260411.1)(@opentelemetry/api@1.9.1) + '@img/colour@1.1.0': {} '@img/sharp-darwin-arm64@0.34.5': @@ -2117,6 +3150,13 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@napi-rs/wasm-runtime@1.2.5(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@tybys/wasm-util': 0.10.4 + optional: true + '@openrouter/ai-sdk-provider@2.9.1(ai@6.0.208(zod@4.4.3))(zod@4.4.3)': dependencies: ai: 6.0.208(zod@4.4.3) @@ -2124,8 +3164,138 @@ snapshots: '@opentelemetry/api@1.9.1': {} + '@oxc-parser/binding-android-arm-eabi@0.141.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.141.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.141.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.141.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.141.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.141.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.141.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.141.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.141.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.141.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.141.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.141.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.141.0': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.5(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.141.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.141.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.141.0': + optional: true + '@oxc-project/types@0.133.0': {} + '@oxc-project/types@0.141.0': {} + + '@oxc-transform/binding-android-arm-eabi@0.141.0': + optional: true + + '@oxc-transform/binding-android-arm64@0.141.0': + optional: true + + '@oxc-transform/binding-darwin-arm64@0.141.0': + optional: true + + '@oxc-transform/binding-darwin-x64@0.141.0': + optional: true + + '@oxc-transform/binding-freebsd-x64@0.141.0': + optional: true + + '@oxc-transform/binding-linux-arm-gnueabihf@0.141.0': + optional: true + + '@oxc-transform/binding-linux-arm-musleabihf@0.141.0': + optional: true + + '@oxc-transform/binding-linux-arm64-gnu@0.141.0': + optional: true + + '@oxc-transform/binding-linux-arm64-musl@0.141.0': + optional: true + + '@oxc-transform/binding-linux-ppc64-gnu@0.141.0': + optional: true + + '@oxc-transform/binding-linux-riscv64-gnu@0.141.0': + optional: true + + '@oxc-transform/binding-linux-riscv64-musl@0.141.0': + optional: true + + '@oxc-transform/binding-linux-s390x-gnu@0.141.0': + optional: true + + '@oxc-transform/binding-linux-x64-gnu@0.141.0': + optional: true + + '@oxc-transform/binding-linux-x64-musl@0.141.0': + optional: true + + '@oxc-transform/binding-openharmony-arm64@0.141.0': + optional: true + + '@oxc-transform/binding-wasm32-wasi@0.141.0': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.5(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@oxc-transform/binding-win32-arm64-msvc@0.141.0': + optional: true + + '@oxc-transform/binding-win32-ia32-msvc@0.141.0': + optional: true + + '@oxc-transform/binding-win32-x64-msvc@0.141.0': + optional: true + '@peculiar/asn1-cms@2.8.0': dependencies: '@peculiar/asn1-schema': 2.8.0 @@ -2231,6 +3401,10 @@ snapshots: '@poppinss/exception@1.2.3': {} + '@quansync/fs@1.1.0': + dependencies: + quansync: 1.0.0 + '@rolldown/binding-android-arm64@1.0.3': optional: true @@ -2332,6 +3506,69 @@ snapshots: '@sindresorhus/is@7.2.0': {} + '@smithy/abort-controller@4.4.2': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/core@3.36.0': + dependencies: + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.5.2': + dependencies: + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.8.0': + dependencies: + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.7.2': + dependencies: + '@smithy/core': 3.36.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.12.1': + dependencies: + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.7.4': + dependencies: + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/smithy-client@4.15.2': + dependencies: + '@smithy/core': 3.36.0 + '@smithy/types': 4.19.0 + tslib: 2.8.1 + + '@smithy/types@4.19.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + '@speed-highlight/core@1.2.15': {} '@standard-schema/spec@1.1.0': {} @@ -2341,6 +3578,11 @@ snapshots: tslib: 2.8.1 optional: true + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -2350,6 +3592,15 @@ snapshots: '@types/estree@1.0.9': {} + '@types/node-fetch@2.6.13': + dependencies: + '@types/node': 18.19.130 + form-data: 4.0.6 + + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + '@typescript/typescript-aix-ppc64@7.0.1-rc': optional: true @@ -2421,13 +3672,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.9(vite@8.0.16(esbuild@0.28.1)(tsx@4.23.15))': + '@vitest/mocker@4.1.9(vite@8.0.16(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.15))': dependencies: '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.16(esbuild@0.28.1)(tsx@4.23.15) + vite: 8.0.16(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.15) '@vitest/pretty-format@4.1.9': dependencies: @@ -2453,6 +3704,14 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + ai@6.0.208(zod@4.4.3): dependencies: '@ai-sdk/gateway': 3.0.133(zod@4.4.3) @@ -2469,20 +3728,56 @@ snapshots: assertion-error@2.0.1: {} + asynckit@0.4.0: {} + + base64-js@1.5.1: {} + blake3-wasm@2.1.5: {} + bowser@2.14.1: {} + buffer-from@1.1.2: {} + buffer@5.6.0: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + cbor2@2.3.0: dependencies: '@cto.af/wtf8': 0.0.5 chai@6.2.2: {} + cloudflare@4.2.0: + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + convert-source-map@2.0.0: {} cookie@1.1.1: {} + defu@6.1.7: {} + + delayed-stream@1.0.0: {} + detect-libc@2.1.2: {} drizzle-kit@0.31.11: @@ -2497,10 +3792,33 @@ snapshots: '@cloudflare/workers-types': 4.20260411.1 '@opentelemetry/api': 1.9.1 + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + error-stack-parser-es@1.0.5: {} + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + es-module-lexer@2.1.0: {} + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-toolkit@1.52.0: {} + esbuild@0.18.20: optionalDependencies: '@esbuild/android-arm': 0.18.20 @@ -2588,6 +3906,10 @@ snapshots: dependencies: '@types/estree': 1.0.9 + event-target-shim@5.0.1: {} + + events@3.3.0: {} + eventsource-parser@3.1.0: {} expect-type@1.3.0: {} @@ -2596,17 +3918,76 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + form-data-encoder@1.7.2: {} + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + get-tsconfig@4.14.3: dependencies: resolve-pkg-maps: 1.0.0 + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + hono@4.12.12: {} + hono@4.12.34: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + ieee754@1.2.1: {} + + inherits@2.0.4: {} + isexe@2.0.0: {} + jiti@2.7.0: {} + json-schema@0.4.0: {} kleur@4.1.5: {} @@ -2664,6 +4045,16 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + math-intrinsics@1.1.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@4.1.1: {} + miniflare@4.20260617.1: dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -2676,10 +4067,66 @@ snapshots: - bufferutil - utf-8-validate + ms@2.1.3: {} + nanoid@3.3.14: {} + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + obug@2.1.3: {} + oxc-parser@0.141.0: + dependencies: + '@oxc-project/types': 0.141.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.141.0 + '@oxc-parser/binding-android-arm64': 0.141.0 + '@oxc-parser/binding-darwin-arm64': 0.141.0 + '@oxc-parser/binding-darwin-x64': 0.141.0 + '@oxc-parser/binding-freebsd-x64': 0.141.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.141.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.141.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.141.0 + '@oxc-parser/binding-linux-arm64-musl': 0.141.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.141.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.141.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.141.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.141.0 + '@oxc-parser/binding-linux-x64-gnu': 0.141.0 + '@oxc-parser/binding-linux-x64-musl': 0.141.0 + '@oxc-parser/binding-openharmony-arm64': 0.141.0 + '@oxc-parser/binding-wasm32-wasi': 0.141.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.141.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.141.0 + '@oxc-parser/binding-win32-x64-msvc': 0.141.0 + + oxc-transform@0.141.0: + optionalDependencies: + '@oxc-transform/binding-android-arm-eabi': 0.141.0 + '@oxc-transform/binding-android-arm64': 0.141.0 + '@oxc-transform/binding-darwin-arm64': 0.141.0 + '@oxc-transform/binding-darwin-x64': 0.141.0 + '@oxc-transform/binding-freebsd-x64': 0.141.0 + '@oxc-transform/binding-linux-arm-gnueabihf': 0.141.0 + '@oxc-transform/binding-linux-arm-musleabihf': 0.141.0 + '@oxc-transform/binding-linux-arm64-gnu': 0.141.0 + '@oxc-transform/binding-linux-arm64-musl': 0.141.0 + '@oxc-transform/binding-linux-ppc64-gnu': 0.141.0 + '@oxc-transform/binding-linux-riscv64-gnu': 0.141.0 + '@oxc-transform/binding-linux-riscv64-musl': 0.141.0 + '@oxc-transform/binding-linux-s390x-gnu': 0.141.0 + '@oxc-transform/binding-linux-x64-gnu': 0.141.0 + '@oxc-transform/binding-linux-x64-musl': 0.141.0 + '@oxc-transform/binding-openharmony-arm64': 0.141.0 + '@oxc-transform/binding-wasm32-wasi': 0.141.0 + '@oxc-transform/binding-win32-arm64-msvc': 0.141.0 + '@oxc-transform/binding-win32-ia32-msvc': 0.141.0 + '@oxc-transform/binding-win32-x64-msvc': 0.141.0 + path-to-regexp@6.3.0: {} pathe@2.0.3: {} @@ -2704,6 +4151,14 @@ snapshots: pvutils@1.1.5: {} + quansync@1.0.0: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + reflect-metadata@0.2.2: {} resolve-pkg-maps@1.0.0: {} @@ -2729,6 +4184,10 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.3 '@rolldown/binding-win32-x64-msvc': 1.0.3 + rou3@0.7.9: {} + + safe-buffer@5.2.1: {} + semver@7.7.4: {} sharp@0.34.5: @@ -2777,6 +4236,15 @@ snapshots: std-env@4.1.0: {} + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + supports-color@10.2.2: {} tinybench@2.9.0: {} @@ -2790,6 +4258,8 @@ snapshots: tinyrainbow@3.1.0: {} + tr46@0.0.3: {} + tslib@1.14.1: {} tslib@2.8.1: {} @@ -2827,6 +4297,21 @@ snapshots: '@typescript/typescript-win32-arm64': 7.0.1-rc '@typescript/typescript-win32-x64': 7.0.1-rc + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.1.0 + quansync: 1.0.0 + + unconfig@7.5.0: + dependencies: + '@quansync/fs': 1.1.0 + defu: 6.1.7 + jiti: 2.7.0 + quansync: 1.0.0 + unconfig-core: 7.5.0 + + undici-types@5.26.5: {} + undici@6.27.0: {} undici@7.28.0: {} @@ -2835,7 +4320,13 @@ snapshots: dependencies: pathe: 2.0.3 - vite@8.0.16(esbuild@0.28.1)(tsx@4.23.15): + util-deprecate@1.0.2: {} + + uuidv7@1.2.1: {} + + verkit@0.4.0: {} + + vite@8.0.16(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.15): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -2845,12 +4336,13 @@ snapshots: optionalDependencies: esbuild: 0.28.1 fsevents: 2.3.3 + jiti: 2.7.0 tsx: 4.23.15 - vitest@4.1.9(@opentelemetry/api@1.9.1)(vite@8.0.16(esbuild@0.28.1)(tsx@4.23.15)): + vitest@4.1.9(@opentelemetry/api@1.9.1)(vite@8.0.16(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.15)): dependencies: '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@8.0.16(esbuild@0.28.1)(tsx@4.23.15)) + '@vitest/mocker': 4.1.9(vite@8.0.16(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.15)) '@vitest/pretty-format': 4.1.9 '@vitest/runner': 4.1.9 '@vitest/snapshot': 4.1.9 @@ -2867,13 +4359,22 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.16(esbuild@0.28.1)(tsx@4.23.15) + vite: 8.0.16(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.15) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 transitivePeerDependencies: - msw + web-streams-polyfill@4.0.0-beta.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/src/updates/worker.test.ts b/src/updates/worker.test.ts new file mode 100644 index 0000000..f47c0d6 --- /dev/null +++ b/src/updates/worker.test.ts @@ -0,0 +1,68 @@ +import { DatabaseSync } from 'node:sqlite'; +import { readFileSync } from 'node:fs'; +import { afterEach, expect, it } from 'vitest'; +import { createUpdateService } from './worker'; + +const databases: DatabaseSync[] = []; +afterEach(() => databases.splice(0).forEach((db) => db.close())); + +function service() { + const db = new DatabaseSync(':memory:'); + databases.push(db); + db.exec(readFileSync(new URL('../../migrations/updates/0001_hot-updater_1.0.0.sql', import.meta.url), 'utf8')); + // Exercise the real D1 adapter and supplied schema with the binding's API. + const binding = { + prepare(sql: string) { + const statement = db.prepare(sql); + const bound = (values: unknown[]) => ({ + async all() { + if (statement.columns().length) + return { success: true, results: statement.all(...values), meta: {} }; + const result = statement.run(...values); + return { success: true, results: [], meta: { changes: Number(result.changes) } }; + }, + }); + return { ...bound([]), bind: (...values: unknown[]) => bound(values) }; + }, + batch(statements: { all(): Promise }[]) { + return Promise.all(statements.map((statement) => statement.all())); + }, + }; + return createUpdateService({ + DB: binding as unknown as D1Database, + BUCKET: {} as R2Bucket, + BUCKET_NAME: 'test-updates', + ACCOUNT_ID: 'test-account', + R2_ACCESS_KEY_ID: 'test-access', + R2_SECRET_ACCESS_KEY: 'test-secret', + }); +} + +const catalog = '/release-catalogs/fingerprint/ios/ZGV2/test-fingerprint'; + +it('serves the v1 protocol and rejects unauthorized catalog reads', async () => { + const updater = service(); + const version = await updater.handlers.client(new Request('https://updates.test/version')); + expect(version.status).toBe(200); + expect(await version.json()).toMatchObject({ version: '1.0.2', infrastructureGeneration: 1 }); + for (const key of [undefined, 'invalid']) { + const response = await updater.handlers.client(new Request('https://updates.test' + catalog, { headers: key ? { 'x-api-key': key } : {} })); + expect(response.status).toBe(401); + } +}); + +it('keeps clients in their environment and supports revocation', async () => { + const dev = service(); + const beta = service(); + const { apiKey, record } = await dev.api.apiKeys.create({ name: 'Development test client' }); + const request = () => new Request('https://updates.test' + catalog, { headers: { 'x-api-key': apiKey } }); + const allowed = await dev.handlers.client(request()); + expect(allowed.status).toBe(404); + expect(allowed.headers.get('x-hot-updater-catalog')).toBe('none'); + expect((await beta.handlers.client(request())).status).toBe(401); + await dev.api.apiKeys.revoke({ id: record.id }); + expect((await dev.handlers.client(request())).status).toBe(401); + // Only client routes are exposed by the Worker. + const admin = await dev.handlers.client(new Request('https://updates.test/admin/bundles', { headers: { 'x-api-key': apiKey } })); + expect(admin.status).not.toBe(200); +}); diff --git a/src/updates/worker.ts b/src/updates/worker.ts new file mode 100644 index 0000000..edf4dad --- /dev/null +++ b/src/updates/worker.ts @@ -0,0 +1,36 @@ +import { d1Database, r2Storage } from '@hot-updater/cloudflare/worker'; +import { createHotUpdater } from '@hot-updater/server'; +import { apiKeys, insights, remoteConfig } from '@hot-updater/server/plugins'; + +export interface UpdateEnvironment { + DB: D1Database; + BUCKET: R2Bucket; + ACCOUNT_ID: string; + BUCKET_NAME: string; + R2_ACCESS_KEY_ID: string; + R2_SECRET_ACCESS_KEY: string; +} + +// A separate Worker, database and private bucket for each app environment. +// Mount only the authenticated client protocol; no admin API is public. +export function createUpdateService(env: UpdateEnvironment) { + return createHotUpdater({ + database: d1Database(env.DB), + storage: r2Storage({ + bucket: env.BUCKET, + bucketName: env.BUCKET_NAME, + accountId: env.ACCOUNT_ID, + credentials: { + accessKeyId: env.R2_ACCESS_KEY_ID, + secretAccessKey: env.R2_SECRET_ACCESS_KEY, + }, + }), + plugins: [insights(), apiKeys(), remoteConfig()], + }); +} + +export default { + fetch(request: Request, env: UpdateEnvironment) { + return createUpdateService(env).handlers.client(request); + }, +}; diff --git a/wrangler-updates.json b/wrangler-updates.json new file mode 100644 index 0000000..99d7c0c --- /dev/null +++ b/wrangler-updates.json @@ -0,0 +1,82 @@ +{ + "name": "ww-updates", + "main": "src/updates/worker.ts", + "account_id": "f73da039376f712ea9a5637a58baca09", + "compatibility_date": "2026-08-13", + "compatibility_flags": [ + "nodejs_compat" + ], + "workers_dev": true, + "preview_urls": false, + "observability": { + "enabled": true + }, + "env": { + "dev": { + "name": "ww-updates-dev", + "d1_databases": [ + { + "binding": "DB", + "database_id": "5ff7125f-f92b-40ef-b829-c2c408b742b7", + "database_name": "ww-updates-dev", + "migrations_dir": "migrations/updates" + } + ], + "r2_buckets": [ + { + "binding": "BUCKET", + "bucket_name": "ww-updates-dev" + } + ], + "vars": { + "ACCOUNT_ID": "f73da039376f712ea9a5637a58baca09", + "BUCKET_NAME": "ww-updates-dev" + } + }, + "beta": { + "name": "ww-updates-beta", + "d1_databases": [ + { + "binding": "DB", + "database_id": "684a53de-2b48-46b7-8bb9-e9f8a12a4ba6", + "database_name": "ww-updates-beta", + "migrations_dir": "migrations/updates" + } + ], + "r2_buckets": [ + { + "binding": "BUCKET", + "bucket_name": "ww-updates-beta" + } + ], + "vars": { + "ACCOUNT_ID": "f73da039376f712ea9a5637a58baca09", + "BUCKET_NAME": "ww-updates-beta" + } + }, + "prod": { + "name": "ww-updates-prod", + "d1_databases": [ + { + "binding": "DB", + "database_id": "eaf373cf-3bf5-4301-b4f5-f321f025e880", + "database_name": "ww-updates-prod", + "migrations_dir": "migrations/updates" + } + ], + "r2_buckets": [ + { + "binding": "BUCKET", + "bucket_name": "ww-updates-prod" + } + ], + "vars": { + "ACCOUNT_ID": "f73da039376f712ea9a5637a58baca09", + "BUCKET_NAME": "ww-updates-prod" + } + } + }, + "cache": { + "enabled": true + } +}