diff --git a/AGENTS.md b/AGENTS.md index aa856915..a538d2da 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,13 +58,17 @@ Document what you ruled out in PR comments so reviewers do not chase the wrong d ## Project overview -VMP (Video Monetization Platform) is a subscription-gated HLS video streaming platform. npm workspaces monorepo with three packages: +VMP (Video Monetization Platform) is a subscription-gated HLS video streaming platform. npm workspaces monorepo — see [README.md](README.md) for the full package table. Core packages: | Package | Path | Runtime | |---|---|---| -| `@vmp/api` | `packages/api` | Cloudflare Worker (JS) — REST API, auth, Stripe, push, thumbnails | -| `@vmp/web` | `packages/web` | Nuxt 4 / Vue 3 frontend (TypeScript) — Cloudflare **Worker** SSR (`wrangler.workers.toml`) | -| `@vmp/shared` | `packages/shared` | Shared TS types | +| `@vmp/api` | `packages/api` | Cloudflare Worker (TypeScript) — REST API, auth, Stripe, push, thumbnails | +| `@vmp/web` | `packages/web` | Nuxt 4 / Vue 3 frontend (TypeScript) — Cloudflare **Worker** SSR (`packages/web/wrangler.workers.toml`) | +| `@vmp/shared` | `packages/shared` | Shared TypeScript types | +| `@vmp/storage` | `packages/storage` | Pluggable object storage (R2 / S3-compatible) | +| `@vmp/payments` | `packages/payments` | Payment provider registry (Stripe, legacy Qerko) | +| `@vmp/api-node` | `packages/api-node` | Deno Deploy backup API (Postgres + S3 adapters) | +| `@vmp/media-pipeline` | `packages/media-pipeline` | Media VM: SVT Encore + Shaka HLS → R2 | ### Infrastructure @@ -73,7 +77,7 @@ VMP (Video Monetization Platform) is a subscription-gated HLS video streaming pl | Video/asset storage | Cloudflare R2 | | API + auth backend | Cloudflare Workers | | Database | Cloudflare D1 (SQLite) | -| Config format | `wrangler.json` (not `.toml`) | +| Config format | `wrangler.json` for `@vmp/api` (exception: `@vmp/web` uses `wrangler.workers.toml`) | | Frontend | Nuxt 4 on Cloudflare Workers (`vmp-web-worker-dev` / `vmp-web-worker-prod`) + `@vmp/api` API Worker | | Email | Brevo Transactional API | | Payments | Stripe (card, PayPal, SEPA via Checkout); optional legacy provider for grandfathered subs | @@ -104,7 +108,7 @@ Migrations live in `packages/api/migrations/` — always add a new numbered file ### Auth system (DO NOT rewrite) -Fully implemented in `packages/api/src/auth.js`. Key exports: +Fully implemented in `packages/api/src/auth.ts`. Key exports: - `handleRequestMagicLink` — `POST /api/auth/magic-link` - `handleVerifyMagicLink` — `GET /api/auth/verify?token=` - `handleRefreshToken` — `POST /api/auth/refresh` diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 41811613..8ac6acf1 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,5 +1,16 @@ # Deployment and Environment Strategy +Operational deploy guide for VMP. For product architecture and package overview, see [README.md](README.md). For agent/dev workflow, see [AGENTS.md](AGENTS.md). + +## Contents + +- [Unified environment variables](#unified-environment-variables) +- [Multi-domain setup](#multi-domain-setup) +- [CI/CD flow](#cicd-flow) +- [Fresh infrastructure bootstrap runbook](#fresh-infrastructure-bootstrap-runbook) +- [Rollback notes (staging/production)](#rollback-notes-stagingproduction) +- [Livestream notes (Media over QUIC)](#livestream-notes-media-over-quic) + ## Unified environment variables Use the root `.env.example` as the single source template for all runtime values. @@ -128,7 +139,7 @@ Use this when staging/production D1, KV, and/or R2 were intentionally reset. - D1 database - KV namespace(s) - R2 bucket(s) -- Update Worker/Page bindings to point to recreated resources. +- Update Worker bindings to point to recreated resources. 1. Restore required secrets (per environment) diff --git a/README.md b/README.md index e15ce6ca..b810bca5 100644 --- a/README.md +++ b/README.md @@ -1,147 +1,139 @@ # VMP (Video Monetization Platform) -VMP is a Cloudflare-based video subscription platform with a Worker API, Nuxt web app, and shared TypeScript types. +Subscription-gated HLS video streaming: Cloudflare Worker API + Nuxt 4 web app, with optional Deno backup API and a media VM for transcoding. ## Contents +- [Architecture](#architecture) - [Monorepo packages](#monorepo-packages) -- [Documentation map](#documentation-map) -- [Deployment model (high level)](#deployment-model-high-level) +- [Documentation](#documentation) - [Prerequisites](#prerequisites) -- [Local setup](#local-setup) -- [Manual deploy commands](#manual-deploy-commands) -- [Runtime secrets/vars](#runtime-secretsvars) - - [Optional MediaConvert pipeline](#optional-mediaconvert-pipeline-additive-to-local-rclone-flow) -- [API docs (`docs/`)](#api-docs-docs) -- [Notes](#notes) +- [Local development](#local-development) +- [Deploy](#deploy) +- [Secrets and configuration](#secrets-and-configuration) +- [Media encoding](#media-encoding) + +## Architecture + +| Layer | Technology | +| --- | --- | +| API | Cloudflare Worker (`@vmp/api`) — REST, auth, Stripe, push, RSS | +| Web | Nuxt 4 / Vue 3 (`@vmp/web`) — Cloudflare **Workers** SSR (`vmp-web-worker-dev` / `vmp-web-worker-prod`) | +| Database | Cloudflare D1 (SQLite); Postgres shim on Deno backup | +| Object storage | Cloudflare R2 (pluggable via `@vmp/storage`) | +| Payments | Stripe (+ optional legacy Qerko via `@vmp/payments`) | +| Email / push | Brevo transactional, Web Push (VAPID) | +| Transcoding | `@vmp/media-pipeline` on a media VM (SVT Encore + Shaka → R2) | +| Backup API | `@vmp/api-node` on Deno Deploy (same handlers, Postgres + S3) | + +Cloudflare **Pages** (`vmp-fe`) is deprecated. Do not attach production hostnames to Pages. + +```text +Browser ──► @vmp/web (Worker SSR) + │ + ▼ + @vmp/api (Worker) ──► D1 / R2 / KV + │ + ├── Stripe webhooks, Brevo, Web Push + └── pipeline-status ◄── @vmp/media-pipeline (VM) +``` ## Monorepo packages | Package | Path | Role | | --- | --- | --- | -| `@vmp/api` | [`packages/api`](packages/api) | Cloudflare Worker API + D1/R2/KV integrations | -| `@vmp/web` | [`packages/web`](packages/web) | Nuxt 4 frontend deployed to Cloudflare Pages | -| `@vmp/shared` | [`packages/shared`](packages/shared) | Shared TypeScript contracts | -| `@vmp/api-node` | [`packages/api-node`](packages/api-node) | Deno Deploy backup API (Postgres + S3 adapters) — see [README](packages/api-node/README.md) | -| `@vmp/media-pipeline` | [`packages/media-pipeline`](packages/media-pipeline) | Media VM: SVT Encore transcoding + Shaka HLS + R2 — see [README](packages/media-pipeline/README.md) | -| `@vmp/offloading` | [`packages/offloading`](packages/offloading) | R2↔Garage hot/cold tier orchestration — see [README](packages/offloading/README.md) | -| `@vmp/moq-probe` | [`packages/moq-probe`](packages/moq-probe) | MoQ broadcast diagnostic probe — see [README](packages/moq-probe/README.md) | +| `@vmp/api` | [`packages/api`](packages/api) | Primary Cloudflare Worker API | +| `@vmp/web` | [`packages/web`](packages/web) | Nuxt 4 frontend (Workers SSR) | +| `@vmp/shared` | [`packages/shared`](packages/shared) | Shared TypeScript types | +| `@vmp/storage` | [`packages/storage`](packages/storage) | Pluggable object storage (R2 / S3-compatible) — [README](packages/storage/README.md) | +| `@vmp/payments` | [`packages/payments`](packages/payments) | Payment provider registry (Stripe, legacy Qerko) — [README](packages/payments/README.md) | +| `@vmp/api-node` | [`packages/api-node`](packages/api-node) | Deno Deploy backup API — [README](packages/api-node/README.md) | +| `@vmp/media-pipeline` | [`packages/media-pipeline`](packages/media-pipeline) | Media VM: Encore + Shaka HLS + R2 — [README](packages/media-pipeline/README.md) | +| `@vmp/offloading` | [`packages/offloading`](packages/offloading) | R2 ↔ Garage hot/cold tiering — [README](packages/offloading/README.md) | +| `@vmp/moq-probe` | [`packages/moq-probe`](packages/moq-probe) | MoQ live broadcast diagnostic probe — [README](packages/moq-probe/README.md) | -Core API and web packages do not ship separate READMEs; see [AGENTS.md](AGENTS.md) for architecture, auth, schema, and local dev. +Core API/web packages do not ship separate READMEs; see [AGENTS.md](AGENTS.md) for auth, D1 schema, roles, and agent workflow. -## Documentation map +## Documentation -| Document | Description | +| Document | Audience | | --- | --- | -| [AGENTS.md](AGENTS.md) | Canonical agent/dev guide: git workflow, D1 schema, auth, secrets, roadmap | -| [DEPLOYMENT.md](DEPLOYMENT.md) | Environment variables, CI/CD, multi-domain deploy | -| [packages/api-node/README.md](packages/api-node/README.md) | Deno Deploy API backup, Postgres replication ingest | -| [packages/media-pipeline/README.md](packages/media-pipeline/README.md) | Encore transcoding orchestration, supervisor, webhooks, TTP logging | -| [packages/media-pipeline/MIGRATION.md](packages/media-pipeline/MIGRATION.md) | Cutover guide from legacy `podcast-host` | -| [packages/media-pipeline/systemd/README.md](packages/media-pipeline/systemd/README.md) | `vmp-supervisor` systemd unit install and ops | -| [packages/offloading/README.md](packages/offloading/README.md) | Garage compose, demote/promote scripts | -| [packages/offloading/DEPLOYMENT.md](packages/offloading/DEPLOYMENT.md) | Docker/Compose deployment for offloading | -| [packages/moq-probe/README.md](packages/moq-probe/README.md) | Live MoQ probe usage and recorder recommendations | -| [.cursor/README.md](.cursor/README.md) | Cursor workspace git/deploy policy for agents | -| [docs/pills-external-update-api.md](docs/pills-external-update-api.md) | Pills external update API | - -## Deployment model (high level) - -- Pushes to `main` run staging deploy in [`.github/workflows/deploy.yml`](.github/workflows/deploy.yml): API Worker + web Worker (`vmp-web-worker-dev`). -- Version tags (`v*.*.*`) run production deploy: API Worker + web Worker (`vmp-web-worker-prod`). -- Frontend is **Cloudflare Workers only** (Nuxt SSR). Pages is deprecated. -- Deploy pipeline fails fast on type-checking before build/deploy: - - `@vmp/shared` `tsc --noEmit` - - `@vmp/api` `tsc --noEmit` - - `@vmp/web` `nuxi prepare && nuxi typecheck` - -See [DEPLOYMENT.md](DEPLOYMENT.md) for env templates, secrets, and domain overrides. +| [AGENTS.md](AGENTS.md) | **Canonical** architecture, git workflow, secrets, local Cloud setup, roadmap | +| [DEPLOYMENT.md](DEPLOYMENT.md) | CI/CD, env vars, smoke checks, bootstrap / rollback | +| [docs/README.md](docs/README.md) | Index of API notes and historical design docs | +| [packages/web/docs/workers-deploy-env.md](packages/web/docs/workers-deploy-env.md) | Web Worker build-time env vars | +| Package READMEs under `packages/*` | Package-specific ops (pipeline, api-node, storage, …) | ## Prerequisites -1. Node.js 20+ and npm 10+. -2. Cloudflare account with: - - Two Workers per environment (API + Nuxt web frontend) - - D1 database - - R2 bucket - - KV namespaces -3. Repository secrets configured for CI deploy: - - `CLOUDFLARE_API_TOKEN_STAGING` - - `CLOUDFLARE_ACCOUNT_ID_STAGING` - - `CLOUDFLARE_API_TOKEN_PROD` - - `CLOUDFLARE_ACCOUNT_ID_PROD` +1. Node.js 20+ and npm 10+ (`packageManager` in root `package.json`). +2. Cloudflare account with Workers, D1, R2, and KV (per environment). +3. CI deploy secrets: `CLOUDFLARE_API_TOKEN_{STAGING,PROD}` and `CLOUDFLARE_ACCOUNT_ID_{STAGING,PROD}`. -## Local setup +## Local development -1. Install dependencies: - - `npm ci` -2. Build service worker helper from TS source: - - `npm run build:sw-push --workspace=@vmp/web` -3. Type-check everything: - - `npm run typecheck` +```bash +npm ci +npm run typecheck -Full local dev (API on `:8787`, web on `:3000`, D1 migrations): [AGENTS.md → Cursor Cloud-specific instructions](AGENTS.md#cursor-cloud-specific-instructions). +# API (Wrangler, port 8787) — needs packages/api/.dev.vars +npm run dev --workspace=@vmp/api -## Manual deploy commands +# Web (Nuxt, port 3000) +API_URL=http://localhost:8787 npm run dev --workspace=@vmp/web +``` -These are useful for controlled/manual rollouts outside GitHub Actions. +Apply D1 migrations locally before serving data: -1. API deploy: - - `npm run deploy:api` -2. Web deploy (from `packages/web` after `npm run build`): - - Staging Worker: `npm run deploy --workspace=@vmp/web` - - Production Worker: `npm run deploy:prod --workspace=@vmp/web` +```bash +cd packages/api +for f in $(ls -1 migrations/*.sql | sort); do + npx wrangler d1 execute video-subscription-db --local --file="$f" +done +``` -## Runtime secrets/vars +Seed videos start as drafts. To publish them for the homepage: -Use Wrangler secrets for sensitive values (never commit secrets). Required values are documented in [AGENTS.md](AGENTS.md) and [DEPLOYMENT.md](DEPLOYMENT.md) (JWT, Stripe, Brevo, VAPID, RSS, TOTP encryption, etc.). +```bash +npx wrangler d1 execute video-subscription-db --local \ + --command="UPDATE videos SET publish_status = 'published', published_at = CURRENT_TIMESTAMP WHERE publish_status = 'draft';" +``` -### Optional MediaConvert pipeline (additive to local rclone flow) +Full Cloud-agent local notes (secrets, gotchas): [AGENTS.md → Cursor Cloud-specific instructions](AGENTS.md#cursor-cloud-specific-instructions). -The existing local/watchfolder/rclone pipeline remains the default and is unchanged. -An optional cloud path can be enabled for direct source uploads + AWS Elemental MediaConvert. +## Deploy + +| Trigger | What deploys | +| --- | --- | +| Push to `main` | Staging: API Worker + web Worker (`vmp-web-worker-dev`) via [`.github/workflows/deploy.yml`](.github/workflows/deploy.yml) | +| Tag `v*.*.*` | Production: API Worker + web Worker (`vmp-web-worker-prod`) | +| Push / PR (Deno git integration) | `@vmp/api-node` preview/production build on Deno Deploy — **not** from `deploy.yml`. PR check status is **pending until** `deploy/tjm/vmp` clears; maintainer log review on [console.deno.com](https://console.deno.com) is required when that check stays red. | -Required API env vars (Worker): +Manual commands: -- `MEDIA_CONVERT_ENABLED=1` -- `AWS_REGION` (for S3 + MediaConvert, e.g. `eu-central-1`) -- `AWS_ACCESS_KEY_ID` -- `AWS_SECRET_ACCESS_KEY` -- `AWS_SESSION_TOKEN` (optional, only when using temporary credentials) -- `MEDIA_CONVERT_ENDPOINT` (account-specific endpoint from MediaConvert console) -- `MEDIA_CONVERT_ROLE_ARN` (IAM role MediaConvert assumes) -- `MEDIA_CONVERT_INPUT_BUCKET` (S3 source uploads) -- `MEDIA_CONVERT_OUTPUT_BUCKET` (S3 transcode outputs) -- `MEDIA_CONVERT_INPUT_PREFIX` (optional, default `mediaconvert-input`) -- `MEDIA_CONVERT_OUTPUT_PREFIX` (optional, default `mediaconvert-output`) -- `MEDIA_CONVERT_MAX_UPLOAD_MB` (optional, default `4096`) -- `MEDIA_CONVERT_PRICE_HD_PER_MIN` (optional, default `0.015`; rough estimator) +```bash +npm run deploy:api +npm run deploy --workspace=@vmp/web # staging Worker +npm run deploy:prod --workspace=@vmp/web # production Worker +``` -AWS setup checklist: +Deploy gates typecheck `@vmp/shared`, `@vmp/storage`, `@vmp/api`, and `@vmp/web` before build. Details: [DEPLOYMENT.md](DEPLOYMENT.md). -1. Create/choose two S3 prefixes/buckets for input and output. -2. Create a MediaConvert IAM service role (`MEDIA_CONVERT_ROLE_ARN`) with read access to input and write access to output. -3. Create an IAM principal for the Worker credentials with: - - `mediaconvert:CreateJob`, `mediaconvert:GetJob` - - `iam:PassRole` scoped to `MEDIA_CONVERT_ROLE_ARN` - - `s3:PutObject` on input prefix - - `s3:GetObject` on output object prefix - - `s3:ListBucket` on output bucket (bucket-level action), scoped by `s3:prefix` condition to the output prefix -4. In MediaConvert console, copy the account endpoint into `MEDIA_CONVERT_ENDPOINT`. -5. Run migration `packages/api/migrations/0017_media_convert_jobs.sql`. +**Never push feature work directly to `main`** — use a branch + pull request (autodeploy + CodeRabbit). See [AGENTS.md → Git workflow](AGENTS.md#git-workflow-mandatory--read-first). -Notes: +## Secrets and configuration -- Current cloud profile is H.264/HLS with 720p only, fps capped at 30. -- Architecture is rendition-based and supports future expansion (480p/1080p/4K, alternate codecs). -- Usage/cost values are approximate normalized-minute estimates, not billing-grade accounting. +- Template: root [`.env.example`](.env.example). Copy to `.env.staging` / `.env.production` as needed. +- Local API secrets: `packages/api/.dev.vars` (never commit). +- Required Worker secrets (JWT, Stripe, Brevo, VAPID, RSS, TOTP, …): listed in [AGENTS.md](AGENTS.md) and [DEPLOYMENT.md](DEPLOYMENT.md). +- Prices, plan names, and limits live in D1 `admin_settings` — not hardcoded. -## API docs (`docs/`) +## Media encoding -- [Pills external update API](docs/pills-external-update-api.md) +Transcoding runs on a **media VM** via [`@vmp/media-pipeline`](packages/media-pipeline/README.md): -## Notes +1. Watchfolder intake → SVT Encore encode +2. encore-packager (Shaka) → fMP4 HLS ladder uploaded to R2 +3. HMAC callback to `POST /api/admin/videos/:id/pipeline-status` -- API entrypoint is TypeScript (`packages/api/src/index.ts`) referenced by `packages/api/wrangler.json`. -- Worker/service scripts that must remain JavaScript at runtime (for browser/service-worker execution) are generated from TypeScript sources during build. -- Media encoding on a VM is handled by [`@vmp/media-pipeline`](packages/media-pipeline/README.md) (SVT Encore + orchestrator); optional Deno backup API by [`@vmp/api-node`](packages/api-node/README.md). +There is **no** AWS Elemental MediaConvert admin upload/transcode UI in this repo anymore. The historical `media_convert_jobs` D1 table remains: playback and offline-download code may still read completed **Bunny Stream** rows (`provider = 'bunnystream'`, `bunny_playback_url`) as an alternate HLS entrypoint. Do not drop that table without a migration that replaces those reads. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..f144db9d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,29 @@ +# Documentation index + +Start with the [repository README](../README.md) and [AGENTS.md](../AGENTS.md). This folder holds API notes and historical design write-ups. + +## Current + +| Document | Description | +| --- | --- | +| [pills-external-update-api.md](pills-external-update-api.md) | `POST /api/pills/update` external API contract | +| [console-errors.md](console-errors.md) | Common browser console messages and how to interpret them | +| [i18n-prep.md](i18n-prep.md) | Per-instance UI locale (`NUXT_PUBLIC_UI_LOCALE`) and translation workflow | + +Related (outside `docs/`): + +| Document | Description | +| --- | --- | +| [../DEPLOYMENT.md](../DEPLOYMENT.md) | CI/CD, env vars, smoke checks | +| [../packages/web/docs/workers-deploy-env.md](../packages/web/docs/workers-deploy-env.md) | Web Worker build-time environment | +| [../packages/web/docs/workers-pages-compatibility.md](../packages/web/docs/workers-pages-compatibility.md) | Workers vs deprecated Pages routing notes | + +## Archive (historical / planning) + +These are kept for context. They are **not** the source of truth for current behavior. + +| Document | Notes | +| --- | --- | +| [archive/admin-homescreen-layout-redesign.md](archive/admin-homescreen-layout-redesign.md) | Homescreen editor redesign plan | +| [archive/offline-downloads-roadmap.md](archive/offline-downloads-roadmap.md) | Offline downloads milestones (M1–M6 shipped) | +| [archive/stripe-express-checkout-investigation.md](archive/stripe-express-checkout-investigation.md) | Stripe Express Checkout investigation notes | diff --git a/docs/admin-homescreen-layout-redesign.md b/docs/archive/admin-homescreen-layout-redesign.md similarity index 96% rename from docs/admin-homescreen-layout-redesign.md rename to docs/archive/admin-homescreen-layout-redesign.md index a6e034a0..f7746f64 100644 --- a/docs/admin-homescreen-layout-redesign.md +++ b/docs/archive/admin-homescreen-layout-redesign.md @@ -1,6 +1,8 @@ # Admin homescreen layout — redesign plan -This document is a **plan only** (no implementation in the current PR). It captures the target experience described for the admin “homescreen layout” editor. +> **Archived.** Historical planning note. Not the source of truth for current admin UI behavior. See [docs/README.md](../README.md). + +This document is a **plan only**. It captures the target experience described for the admin “homescreen layout” editor. ## Problem with the current admin UI diff --git a/docs/offline-downloads-roadmap.md b/docs/archive/offline-downloads-roadmap.md similarity index 96% rename from docs/offline-downloads-roadmap.md rename to docs/archive/offline-downloads-roadmap.md index c15bb26c..92731922 100644 --- a/docs/offline-downloads-roadmap.md +++ b/docs/archive/offline-downloads-roadmap.md @@ -1,5 +1,7 @@ # Offline Downloads — Architecture & Roadmap +> **Archived.** Milestones below were used during implementation. For current behavior, prefer code under `packages/api` / `packages/web` and [docs/README.md](../README.md). + Status: **M1–M6 implemented** (API + client storage, playback, UI, revalidation). ## Goals diff --git a/docs/stripe-express-checkout-investigation.md b/docs/archive/stripe-express-checkout-investigation.md similarity index 98% rename from docs/stripe-express-checkout-investigation.md rename to docs/archive/stripe-express-checkout-investigation.md index 67b47bc9..097024c0 100644 --- a/docs/stripe-express-checkout-investigation.md +++ b/docs/archive/stripe-express-checkout-investigation.md @@ -1,5 +1,7 @@ # Stripe Express Checkout (Apple Pay / Google Pay) — Investigation +> **Archived.** Historical investigation. Not current product docs. See [docs/README.md](../README.md). + **Status:** Investigation only (no implementation in this PR). **Date:** 2026-05-29 **Context:** Add a wallet option beside card payments in the premium checkout UI, using Stripe’s [Express Checkout Element](https://docs.stripe.com/elements/express-checkout-element). diff --git a/docs/console-errors.md b/docs/console-errors.md index c89aab21..2db660cc 100644 --- a/docs/console-errors.md +++ b/docs/console-errors.md @@ -17,7 +17,7 @@ the browser is running an **older JS bundle**, not current `main`: 1. **Service worker cache** — PWA uses Workbox with `registerType: 'prompt'`. Hard-refresh or unregister the service worker (DevTools → Application → Service workers → Unregister), then reload. 2. **Stale deployment** — Confirm staging/production was deployed after the #384 merge (push to `main` triggers staging CD). -3. **Backup hostname** — Failover to a Vercel-hosted build would 404 that path on Cloudflare Pages; primary analytics are Umami (GTM gateway) and Sentry. +3. **Wrong hostname / stale Worker** — Confirm the URL is served by `vmp-web-worker-dev` or `vmp-web-worker-prod` (not a deprecated Pages project). Primary analytics are Umami (GTM gateway) and Sentry. No code change is required once clients pick up a post-#384 build. diff --git a/docs/i18n-prep.md b/docs/i18n-prep.md index bc17760e..2a12d9d3 100644 --- a/docs/i18n-prep.md +++ b/docs/i18n-prep.md @@ -34,13 +34,13 @@ Admin dashboard copy is listed under `strings.admin` as an **inventory** for tra VMP is designed for **one language per deployment** — e.g. a Slovak client instance with `NUXT_PUBLIC_UI_LOCALE=sk`, a Czech client with `NUXT_PUBLIC_UI_LOCALE=cs`. -Set at build time in Cloudflare Pages / local dev: +Set at **build time** for the web Worker (or local `nuxi` / `npm run build`): ```bash NUXT_PUBLIC_UI_LOCALE=sk API_URL=https://api.example.sk npm run build --workspace=@vmp/web ``` -Staging deploys via `.github/workflows/deploy.yml` on push to `main`. Set `NUXT_PUBLIC_UI_LOCALE=sk` in the Cloudflare Pages project env (or workflow `env:` block) before flipping a Slovak instance. +Staging deploys via `.github/workflows/deploy.yml` on push to `main`. Set `NUXT_PUBLIC_UI_LOCALE=sk` in the GitHub Actions / repository variables used by that workflow before flipping a Slovak instance. Runtime Worker vars alone do not change language — rebuild and redeploy. See [packages/web/docs/workers-deploy-env.md](../packages/web/docs/workers-deploy-env.md). `runtimeConfig.public.uiLocale` and `` follow this value. There is no language picker in the header by default. diff --git a/package-lock.json b/package-lock.json index 308fbfaf..f6cb6ce7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2521,9 +2521,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2540,9 +2537,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2559,9 +2553,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2578,9 +2569,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2597,9 +2585,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2616,9 +2601,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2635,9 +2617,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2654,9 +2633,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3620,9 +3596,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3640,9 +3613,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3660,9 +3630,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3680,9 +3647,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3700,9 +3664,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3720,9 +3681,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3740,9 +3698,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3760,9 +3715,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3780,9 +3732,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3806,9 +3755,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3832,9 +3778,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3858,9 +3801,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3884,9 +3824,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3910,9 +3847,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3936,9 +3870,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3962,9 +3893,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -4697,8 +4625,9 @@ "version": "1.11.2", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" @@ -4708,8 +4637,9 @@ "version": "1.11.2", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -4718,8 +4648,9 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -4728,8 +4659,9 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, @@ -4749,11 +4681,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "android" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4765,11 +4698,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "android" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4781,11 +4715,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4797,11 +4732,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4813,11 +4749,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "freebsd" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4829,11 +4766,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4845,11 +4783,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4861,14 +4800,12 @@ "cpu": [ "arm64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4880,14 +4817,12 @@ "cpu": [ "arm64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4899,14 +4834,12 @@ "cpu": [ "ppc64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4918,14 +4851,12 @@ "cpu": [ "riscv64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4937,14 +4868,12 @@ "cpu": [ "riscv64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4956,14 +4885,12 @@ "cpu": [ "s390x" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4975,14 +4902,12 @@ "cpu": [ "x64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -4994,14 +4919,12 @@ "cpu": [ "x64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5013,11 +4936,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "openharmony" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5029,8 +4953,9 @@ "cpu": [ "wasm32" ], - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@emnapi/core": "1.11.2", "@emnapi/runtime": "1.11.2", @@ -5047,11 +4972,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5063,11 +4989,12 @@ "cpu": [ "ia32" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5079,11 +5006,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5092,8 +5020,9 @@ "version": "0.140.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.140.0.tgz", "integrity": "sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/Boshen" } @@ -5102,8 +5031,9 @@ "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -5112,8 +5042,9 @@ "version": "0.140.0", "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.140.0.tgz", "integrity": "sha512-h6QFWd6lBMfjESqgQ27GjzrSDb0qbznp7VDQqp2zvgsrWut4vcchyMIzOVXvGQ2GMZgKw9RWrFNWv9WqGL0p7Q==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@oxc-project/types": "^0.140.0" }, @@ -5232,8 +5163,9 @@ "version": "1.11.2", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" @@ -5243,8 +5175,9 @@ "version": "1.11.2", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -5253,8 +5186,9 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -5263,8 +5197,9 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, @@ -5284,11 +5219,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "android" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5300,11 +5236,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "android" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5316,11 +5253,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5332,11 +5270,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5348,11 +5287,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "freebsd" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5364,11 +5304,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5380,11 +5321,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5396,14 +5338,12 @@ "cpu": [ "arm64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5415,14 +5355,12 @@ "cpu": [ "arm64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5434,14 +5372,12 @@ "cpu": [ "ppc64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5453,14 +5389,12 @@ "cpu": [ "riscv64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5472,14 +5406,12 @@ "cpu": [ "riscv64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5491,14 +5423,12 @@ "cpu": [ "s390x" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5510,14 +5440,12 @@ "cpu": [ "x64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5529,14 +5457,12 @@ "cpu": [ "x64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5548,11 +5474,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "openharmony" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5564,8 +5491,9 @@ "cpu": [ "wasm32" ], - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@emnapi/core": "1.11.2", "@emnapi/runtime": "1.11.2", @@ -5582,11 +5510,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5598,11 +5527,12 @@ "cpu": [ "ia32" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5614,11 +5544,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -5627,8 +5558,9 @@ "version": "0.140.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.140.0.tgz", "integrity": "sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/Boshen" } @@ -5637,8 +5569,9 @@ "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -5659,8 +5592,9 @@ "version": "0.140.0", "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.140.0.tgz", "integrity": "sha512-h6QFWd6lBMfjESqgQ27GjzrSDb0qbznp7VDQqp2zvgsrWut4vcchyMIzOVXvGQ2GMZgKw9RWrFNWv9WqGL0p7Q==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@oxc-project/types": "^0.140.0" }, @@ -6105,9 +6039,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6122,9 +6053,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6139,9 +6067,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6156,9 +6081,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6591,9 +6513,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6610,9 +6529,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6629,9 +6545,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6648,9 +6561,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6667,9 +6577,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6686,9 +6593,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6705,9 +6609,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6724,9 +6625,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6993,9 +6891,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7010,9 +6905,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -7027,9 +6919,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7044,9 +6933,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7061,9 +6947,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -7078,9 +6961,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7095,9 +6975,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7112,9 +6989,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -7508,9 +7382,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7527,9 +7398,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -7546,9 +7414,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7565,9 +7430,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7584,9 +7446,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7603,9 +7462,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -8058,9 +7914,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -8074,9 +7927,6 @@ "cpu": [ "arm" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -8090,9 +7940,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -8106,9 +7953,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -8122,9 +7966,6 @@ "cpu": [ "loong64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -8138,9 +7979,6 @@ "cpu": [ "loong64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -8154,9 +7992,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -8170,9 +8005,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -8186,9 +8018,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -8202,9 +8031,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -8218,9 +8044,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -8234,9 +8057,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -8250,9 +8070,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -9996,9 +9813,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -10015,9 +9829,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -10034,9 +9845,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -10053,9 +9861,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -10072,9 +9877,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -10091,9 +9893,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -10110,9 +9909,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -10129,9 +9925,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -10578,8 +10371,9 @@ "version": "2.4.15", "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz", "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@volar/language-core": "2.4.15", "path-browserify": "^1.0.1", @@ -10590,8 +10384,9 @@ "version": "2.4.15", "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@volar/source-map": "2.4.15" } @@ -10600,8 +10395,9 @@ "version": "2.4.15", "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@vue-macros/common": { "version": "3.1.4", @@ -10743,8 +10539,9 @@ "version": "2.7.16", "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" @@ -10754,8 +10551,10 @@ "version": "6.6.4", "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@vue/devtools-core": { "version": "8.1.5", @@ -11899,8 +11698,9 @@ "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -12216,8 +12016,9 @@ "version": "15.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=22.12.0" } @@ -12764,8 +12565,9 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/debug": { "version": "4.4.3", @@ -13184,9 +12986,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -13203,9 +13002,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -13222,9 +13018,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -13241,9 +13034,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -13260,9 +13050,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -13279,9 +13066,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -13298,9 +13082,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -13317,9 +13098,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -13443,8 +13221,9 @@ "version": "0.4.10", "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.4.10.tgz", "integrity": "sha512-pz3oubH/dt12KjqsUB0IuXW4nwRDQ583iDsP4555Cpdqx0NoU7pGlWBcayyFI8f/l/idRpgjMEfwuOxSWJYlIA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "peerDependencies": { "srvx": ">=0.11.5" }, @@ -16867,9 +16646,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -16890,9 +16666,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -16913,9 +16686,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -16936,9 +16706,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -18105,8 +17872,9 @@ "version": "1.11.2", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" @@ -18116,8 +17884,9 @@ "version": "1.11.2", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -18126,8 +17895,9 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -18136,8 +17906,9 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, @@ -18157,11 +17928,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "android" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18173,11 +17945,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "android" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18189,11 +17962,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18205,11 +17979,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18221,11 +17996,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "freebsd" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18237,11 +18013,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18253,11 +18030,12 @@ "cpu": [ "arm" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18269,14 +18047,12 @@ "cpu": [ "arm64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18288,14 +18064,12 @@ "cpu": [ "arm64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18307,14 +18081,12 @@ "cpu": [ "ppc64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18326,14 +18098,12 @@ "cpu": [ "riscv64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18345,14 +18115,12 @@ "cpu": [ "riscv64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18364,14 +18132,12 @@ "cpu": [ "s390x" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18383,14 +18149,12 @@ "cpu": [ "x64" ], - "extraneous": true, - "libc": [ - "glibc" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18402,14 +18166,12 @@ "cpu": [ "x64" ], - "extraneous": true, - "libc": [ - "musl" - ], "license": "MIT", + "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18421,11 +18183,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "openharmony" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18437,8 +18200,9 @@ "cpu": [ "wasm32" ], - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@emnapi/core": "1.11.2", "@emnapi/runtime": "1.11.2", @@ -18455,11 +18219,12 @@ "cpu": [ "arm64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18471,11 +18236,12 @@ "cpu": [ "ia32" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18487,11 +18253,12 @@ "cpu": [ "x64" ], - "extraneous": true, "license": "MIT", + "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -18500,8 +18267,9 @@ "version": "0.140.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.140.0.tgz", "integrity": "sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/Boshen" } @@ -18510,8 +18278,9 @@ "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -18541,8 +18310,9 @@ "version": "0.140.0", "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.140.0.tgz", "integrity": "sha512-h6QFWd6lBMfjESqgQ27GjzrSDb0qbznp7VDQqp2zvgsrWut4vcchyMIzOVXvGQ2GMZgKw9RWrFNWv9WqGL0p7Q==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@oxc-project/types": "^0.140.0" }, @@ -24556,9 +24326,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -24575,9 +24342,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -24594,9 +24358,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -24613,9 +24374,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -24632,9 +24390,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -24651,9 +24406,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -24855,8 +24607,10 @@ "version": "4.6.4", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@vue/devtools-api": "^6.6.4" }, @@ -24871,8 +24625,9 @@ "version": "2.2.12", "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz", "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@volar/typescript": "2.4.15", "@vue/language-core": "2.2.12" @@ -24888,8 +24643,9 @@ "version": "2.4.15", "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@volar/source-map": "2.4.15" } @@ -24898,15 +24654,17 @@ "version": "2.4.15", "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/vue-tsc/node_modules/@vue/language-core": { "version": "2.2.12", "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", - "extraneous": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@volar/language-core": "2.4.15", "@vue/compiler-dom": "^3.5.0", @@ -24930,15 +24688,17 @@ "version": "1.0.13", "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/vue-tsc/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "extraneous": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^2.0.2" }, @@ -25723,7 +25483,9 @@ "version": "1.0.0", "dependencies": { "@aws-sdk/client-s3": "^3.1090.0", - "@vmp/storage": "*", + "@aws-sdk/s3-request-presigner": "^3.1090.0", + "@smithy/fetch-http-handler": "^5.6.6", + "@vmp/storage": "file:../storage", "postgres": "^3.4.7" }, "devDependencies": { diff --git a/packages/api-node/.env.example b/packages/api-node/.env.example index 4e40c976..85c30e50 100644 --- a/packages/api-node/.env.example +++ b/packages/api-node/.env.example @@ -35,12 +35,12 @@ SENDER_EMAIL=vmp@example.com SENDER_NAME=VMP STRIPE_PUBLISHABLE_KEY= -# ── S3 bucket (MediaConvert output — same assets as R2 in production) ─────── +# ── S3-compatible bucket (same HLS assets as R2 in production) ────────────── S3_BUCKET_NAME= AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= -# Optional custom endpoint (MinIO, etc.) +# Optional custom endpoint (MinIO, Garage, etc.) # S3_ENDPOINT= # S3_FORCE_PATH_STYLE=1 @@ -54,10 +54,3 @@ REPLICATION_INGEST_TOKEN= # R2_ACCESS_KEY_ID= # R2_SECRET_ACCESS_KEY= # R2_BUCKET_NAME= - -# ── MediaConvert vars (optional, if admin transcode UI is used) ───────────── -MEDIA_CONVERT_ENABLED=0 -MEDIA_CONVERT_INPUT_PREFIX=mediaconvert-input -MEDIA_CONVERT_OUTPUT_PREFIX=mediaconvert-output -MEDIA_CONVERT_MAX_UPLOAD_MB=4096 -MEDIA_CONVERT_PRICE_HD_PER_MIN=0.015 diff --git a/packages/api-node/README.md b/packages/api-node/README.md index 8203e40d..7aa8fe0c 100644 --- a/packages/api-node/README.md +++ b/packages/api-node/README.md @@ -33,6 +33,8 @@ Run locally before opening a PR: npm run verify:api-node ``` +That gate typechecks, unit-tests, esbuild-bundles `dist/server.js`, and smoke-loads the bundle (expects a clean `DATABASE_URL` boot error — catches ESM/`node:https` require regressions that break Deno Deploy warmup). + Required GitHub configuration: none for Deno Deploy upload (git integration handles deploy). Optional repo variable `API_URL_BACKUP` for manual smoke scripts. ## Contents @@ -67,8 +69,9 @@ The Worker `fetch()` entry in `packages/api/src/index.ts` is invoked unchanged. 1. Provision **Prisma Postgres** (or attach external Postgres) in the [Deno Deploy dashboard](https://console.deno.com) and assign it to the app — `DATABASE_URL` is injected automatically per environment. 2. Link the GitHub repository in Deno Deploy (git builds run on every push; PR previews and `main` → production). -3. Copy Worker secrets into Deno Deploy env vars (see `.env.example`). -4. `GET /api/health` should return `"mode": "deno-deploy"` and `"checks.database": { "ok": true, "backend": "postgres" }`. +3. Set the app directory to `packages/api-node`. Dashboard install/build/runtime options are documented in [`deploy.json`](deploy.json) (lean package-local install via `node scripts/deno-deploy-install.mjs` — do **not** run root `npm ci`; the monorepo lockfile includes optional native platform packages that fail on Linux). +4. Copy Worker secrets into Deno Deploy env vars (see `.env.example`). +5. `GET /api/health` should return `"mode": "deno-deploy"` and `"checks.database": { "ok": true, "backend": "postgres" }`. ## CI verify (same as local release) diff --git a/packages/api-node/deploy.json b/packages/api-node/deploy.json index 8f5c4343..11898343 100644 --- a/packages/api-node/deploy.json +++ b/packages/api-node/deploy.json @@ -4,5 +4,5 @@ "nodeVersion": "24.11.0", "rootDirectory": "packages/api-node", "runtimeEntrypoint": "dist/server.js", - "notes": "Deno Deploy builds from the linked repo on every push (PR previews and production on main). GitHub Actions does not run deno deploy CLI uploads — those fail consistently while git-triggered builds succeed. PR gate: npm run verify:api-node in ci.yml. Deploy gate: Deno Deploy build status on the PR." + "notes": "Deno Deploy builds from the linked repo on every push (PR previews and production on main). packages/api-node/deno.json keeps deploy.org/app only so dashboard install/build/runtime remain authoritative (putting install/build/runtime in source overrides the dashboard entirely). Bundle via scripts/build.mjs (createRequire banner for AWS SDK ESM, workspace aliases). Optional lean installer for the dashboard install command: node scripts/deno-deploy-install.mjs. PR gate: npm run verify:api-node (includes smoke:bundle). Deploy gate: Deno Deploy build status — check console.deno.com logs if red." } diff --git a/packages/api-node/package.json b/packages/api-node/package.json index 27d91557..6de93af2 100644 --- a/packages/api-node/package.json +++ b/packages/api-node/package.json @@ -7,18 +7,21 @@ "description": "VMP API on Deno Deploy (Postgres D1 shim, S3/R2, SQL-backed KV for @vmp/api Worker code)", "scripts": { "build": "node scripts/build.mjs", + "smoke:bundle": "node scripts/smoke-bundle.mjs", "start": "node dist/server.js", "dev": "npm run build && node --watch dist/server.js", "typecheck": "tsc -p tsconfig.json --noEmit", "test": "tsx --test test/*.test.ts", - "verify:deploy": "npm run typecheck && npm run test && npm run build" + "verify:deploy": "npm run typecheck && npm run test && npm run build && npm run smoke:bundle" }, "engines": { "node": "^22.12.0 || ^24.11.0 || >=26.0.0" }, "dependencies": { "@aws-sdk/client-s3": "^3.1090.0", - "@vmp/storage": "*", + "@aws-sdk/s3-request-presigner": "^3.1090.0", + "@smithy/fetch-http-handler": "^5.6.6", + "@vmp/storage": "file:../storage", "postgres": "^3.4.7" }, "devDependencies": { diff --git a/packages/api-node/scripts/build.mjs b/packages/api-node/scripts/build.mjs index f619020d..fa49d3b8 100644 --- a/packages/api-node/scripts/build.mjs +++ b/packages/api-node/scripts/build.mjs @@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url' const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') const apiEntry = path.join(packageRoot, '../api/src/index.ts') const sharedEntry = path.join(packageRoot, '../shared/src/index.ts') +const paymentsEntry = path.join(packageRoot, '../payments/src/index.ts') const storageRoot = path.join(packageRoot, '../storage/src') const storageIndex = path.join(storageRoot, 'index.ts') const storageNode = path.join(storageRoot, 'node.ts') @@ -14,11 +15,12 @@ const storageWorker = path.join(storageRoot, 'worker.ts') for (const required of [ ['Worker sources', apiEntry], ['Shared types', sharedEntry], + ['Payments package', paymentsEntry], ['Storage package', storageIndex], ]) { if (!existsSync(required[1])) { throw new Error( - `[build] Missing ${required[0]} at ${required[1]}. Deploy checkout must include packages/api, packages/shared, and packages/storage (esbuild bundles them into dist/).`, + `[build] Missing ${required[0]} at ${required[1]}. Deploy checkout must include packages/api, packages/shared, packages/payments, and packages/storage (esbuild bundles them into dist/).`, ) } } @@ -34,12 +36,22 @@ await esbuild.build({ outdir: 'dist', outExtension: { '.js': '.js' }, packages: 'bundle', + // Sibling sources (../storage, ../api) resolve npm deps from this package's + // node_modules during Deno Deploy's package-local install. + nodePaths: [path.join(packageRoot, 'node_modules')], + // AWS SDK / Smithy ship CJS that does `require('node:https')`. esbuild's default + // ESM require shim throws ("Dynamic require … is not supported"), which breaks + // Deno Deploy preview warmup. Provide a real createRequire for those calls. + banner: { + js: "import { createRequire as __apiNodeCreateRequire } from 'node:module'; const require = __apiNodeCreateRequire(import.meta.url);", + }, alias: { - // CI can invoke api-node build without full workspace link metadata. + // CI / Deno Deploy can invoke this build without full workspace link metadata. // Resolve workspace packages to source directly; runtime npm dependencies are // bundled too so Deno Deploy does not need a package-local node_modules. // Prefer source over @vmp/storage package.json exports (which point at dist/). '@vmp/shared': sharedEntry, + '@vmp/payments': paymentsEntry, '@vmp/storage/node': storageNode, '@vmp/storage/worker': storageWorker, '@vmp/storage': storageIndex, diff --git a/packages/api-node/scripts/deno-deploy-install.mjs b/packages/api-node/scripts/deno-deploy-install.mjs new file mode 100644 index 00000000..16fa98c6 --- /dev/null +++ b/packages/api-node/scripts/deno-deploy-install.mjs @@ -0,0 +1,76 @@ +/** + * Deno Deploy install step for packages/api-node. + * + * Avoids root `npm ci` (fails on optional native platform packages in the + * monorepo lockfile, e.g. @oxc-parser/binding-android-*) and avoids installing + * the full Nuxt/Nx tree on the 3GB Deno builder. + * + * Workspace packages are resolved from sibling sources by scripts/build.mjs + * aliases. `@vmp/storage` is a file: dependency so this package-local install + * can link it without publishing. + * + * Sibling packages import npm deps (AWS SDK, Sentry) that live in this + * package's node_modules — symlink that directory into each sibling so esbuild + * can resolve them when bundling ../api and ../storage sources. + */ +import { spawnSync } from 'node:child_process' +import { existsSync, mkdirSync, readdirSync, rmSync, symlinkSync } from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') +const siblingNames = ['api', 'shared', 'storage', 'payments'] + +for (const name of siblingNames) { + const entry = path.join(packageRoot, '..', name, 'src', 'index.ts') + if (!existsSync(entry)) { + console.error( + `[deno-deploy-install] Missing packages/${name} at ${entry}. ` + + 'Deno Deploy needs a full monorepo checkout (app directory = repo root, or ' + + 'packages/api-node with sibling packages present).', + ) + console.error('[deno-deploy-install] packageRoot=', packageRoot) + console.error('[deno-deploy-install] parent listing:') + try { + const { readdirSync } = await import('node:fs') + console.error(readdirSync(path.join(packageRoot, '..')).join('\n')) + } catch (err) { + console.error(String(err)) + } + process.exit(1) + } +} + +console.log('[deno-deploy-install] npm install --no-workspaces (package-local + file:../storage)') +const result = spawnSync( + 'npm', + [ + 'install', + '--no-workspaces', + '--ignore-scripts', + '--no-audit', + '--no-fund', + '--no-package-lock', + ], + { cwd: packageRoot, stdio: 'inherit', env: process.env }, +) + +if ((result.status ?? 1) !== 0) { + console.error('[deno-deploy-install] npm install failed') + process.exit(result.status ?? 1) +} + +const nodeModules = path.join(packageRoot, 'node_modules') +for (const name of siblingNames) { + const linkPath = path.join(packageRoot, '..', name, 'node_modules') + try { + rmSync(linkPath, { recursive: true, force: true }) + } catch { + // ignore + } + mkdirSync(path.dirname(linkPath), { recursive: true }) + symlinkSync(nodeModules, linkPath, 'dir') + console.log(`[deno-deploy-install] linked packages/${name}/node_modules -> api-node/node_modules`) +} + +console.log('[deno-deploy-install] OK') diff --git a/packages/api-node/scripts/smoke-bundle.mjs b/packages/api-node/scripts/smoke-bundle.mjs new file mode 100644 index 00000000..48c468ce --- /dev/null +++ b/packages/api-node/scripts/smoke-bundle.mjs @@ -0,0 +1,85 @@ +/** + * Smoke-test the esbuild bundle can load under Node (Deno Deploy warmup path). + * Expects a clean failure for missing DATABASE_URL — not a module-init crash + * (e.g. esbuild ESM "Dynamic require of node:https is not supported"). + */ +import { spawn } from 'node:child_process' +import { existsSync } from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') +const serverEntry = path.join(packageRoot, 'dist/server.js') + +if (!existsSync(serverEntry)) { + console.error('[smoke-bundle] missing dist/server.js — run npm run build first') + process.exit(1) +} + +const child = spawn(process.execPath, [serverEntry], { + cwd: packageRoot, + env: { + ...process.env, + // Force the expected missing-config path (do not inherit a local DATABASE_URL). + DATABASE_URL: '', + }, + stdio: ['ignore', 'pipe', 'pipe'], +}) + +let stdout = '' +let stderr = '' +child.stdout.on('data', (chunk) => { + stdout += chunk +}) +child.stderr.on('data', (chunk) => { + stderr += chunk +}) + +let timedOut = false +const timeout = setTimeout(() => { + timedOut = true + child.kill('SIGKILL') +}, 10_000) + +const exitCode = await new Promise((resolve) => { + child.on('close', (code) => resolve(code ?? 1)) +}) +clearTimeout(timeout) + +const combined = `${stdout}\n${stderr}` + +if (timedOut) { + console.error( + '[smoke-bundle] FAIL: process did not exit within 10s (killed by timeout).\n' + + combined, + ) + process.exit(1) +} + +if (/Dynamic require of ["']node:https["'] is not supported/i.test(combined)) { + console.error( + '[smoke-bundle] FAIL: bundle crashes on AWS SDK CJS require under ESM.\n' + + 'Fix packages/api-node/scripts/build.mjs (createRequire banner) and rebuild.\n' + + combined, + ) + process.exit(1) +} + +if (exitCode === 0) { + console.error( + '[smoke-bundle] FAIL: expected non-zero exit without DATABASE_URL; got 0.\n' + + combined, + ) + process.exit(1) +} + +if (!/DATABASE_URL is required/i.test(combined)) { + console.error( + '[smoke-bundle] FAIL: expected DATABASE_URL boot error; got exit ' + + `${exitCode}\n${combined}`, + ) + process.exit(1) +} + +console.log('[smoke-bundle] OK: bundle loads; fails cleanly without DATABASE_URL') +process.exit(0) diff --git a/packages/api-node/src/env.ts b/packages/api-node/src/env.ts index d12eb9ac..f79fadbe 100644 --- a/packages/api-node/src/env.ts +++ b/packages/api-node/src/env.ts @@ -59,11 +59,6 @@ export async function buildEnv(): Promise { SENDER_EMAIL: process.env.SENDER_EMAIL, SENDER_NAME: process.env.SENDER_NAME, STRIPE_PUBLISHABLE_KEY: process.env.STRIPE_PUBLISHABLE_KEY, - MEDIA_CONVERT_ENABLED: process.env.MEDIA_CONVERT_ENABLED, - MEDIA_CONVERT_INPUT_PREFIX: process.env.MEDIA_CONVERT_INPUT_PREFIX, - MEDIA_CONVERT_OUTPUT_PREFIX: process.env.MEDIA_CONVERT_OUTPUT_PREFIX, - MEDIA_CONVERT_MAX_UPLOAD_MB: process.env.MEDIA_CONVERT_MAX_UPLOAD_MB, - MEDIA_CONVERT_PRICE_HD_PER_MIN: process.env.MEDIA_CONVERT_PRICE_HD_PER_MIN, DB: db as unknown as D1Database, video_subscription_db: db as unknown as D1Database, diff --git a/packages/api-node/src/types.ts b/packages/api-node/src/types.ts index c31c6e01..c41f5311 100644 --- a/packages/api-node/src/types.ts +++ b/packages/api-node/src/types.ts @@ -18,11 +18,6 @@ export interface CFEnvShape { SENDER_EMAIL?: string SENDER_NAME?: string STRIPE_PUBLISHABLE_KEY?: string - MEDIA_CONVERT_ENABLED?: string - MEDIA_CONVERT_INPUT_PREFIX?: string - MEDIA_CONVERT_OUTPUT_PREFIX?: string - MEDIA_CONVERT_MAX_UPLOAD_MB?: string - MEDIA_CONVERT_PRICE_HD_PER_MIN?: string DB?: D1Database video_subscription_db?: D1Database diff --git a/packages/api/src/offlineDownloads.ts b/packages/api/src/offlineDownloads.ts index 7a369eca..943530e7 100644 --- a/packages/api/src/offlineDownloads.ts +++ b/packages/api/src/offlineDownloads.ts @@ -2,7 +2,7 @@ * Offline downloads API — device registration (M1) and download licensing (M2). * * V1 uses Store & License: plaintext CMAF in OPFS on the client; server gates - * authorize/asset access. See docs/offline-downloads-roadmap.md. + * authorize/asset access. See docs/archive/offline-downloads-roadmap.md. */ import { requireAuth } from './auth.js'