From 4b7ac6ddf300a9b845227a797523ddc61f392b9b Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:39:31 -0400 Subject: [PATCH 1/9] chore: configure production metadata --- src/app/layout.tsx | 9 +++++++++ src/app/robots.ts | 9 +++++++++ src/app/sitemap.ts | 10 ++++++++++ src/config/product.ts | 1 + 4 files changed, 29 insertions(+) create mode 100644 src/app/robots.ts create mode 100644 src/app/sitemap.ts diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c06d413..e45bd8b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,11 +5,20 @@ import { ThemeProvider } from '@/components/theme-provider'; import './globals.css'; export const metadata: Metadata = { + metadataBase: new URL(productConfig.siteUrl), title: { default: `${productConfig.name} — A calmer way to wait`, template: `%s — ${productConfig.name}`, }, description: productConfig.statement, + alternates: { canonical: '/' }, + openGraph: { + type: 'website', + url: '/', + siteName: productConfig.name, + title: `${productConfig.name} — A calmer way to wait`, + description: productConfig.statement, + }, }; export default function RootLayout({ children }: LayoutProps<'/'>) { diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 0000000..0b0fe92 --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,9 @@ +import type { MetadataRoute } from 'next'; +import { productConfig } from '@/config/product'; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { userAgent: '*', allow: '/' }, + sitemap: `${productConfig.siteUrl}/sitemap.xml`, + }; +} diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..ca77dab --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,10 @@ +import type { MetadataRoute } from 'next'; +import { productConfig } from '@/config/product'; + +export default function sitemap(): MetadataRoute.Sitemap { + return ['/', '/demo', '/about'].map((path) => ({ + url: `${productConfig.siteUrl}${path}`, + changeFrequency: 'monthly', + priority: path === '/' ? 1 : 0.7, + })); +} diff --git a/src/config/product.ts b/src/config/product.ts index cd24339..4b28d55 100644 --- a/src/config/product.ts +++ b/src/config/product.ts @@ -4,6 +4,7 @@ export const productConfig = { demoQueueSlug: 'north-star-cafe', demoQueueName: 'North Star Coffee', defaultQueuePrefix: 'A', + siteUrl: 'https://next-queue-omega.vercel.app', } as const; export const demoRoutes = { From 49d683826d985b82ccd29bb121458c3ac064367d Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:52:00 -0400 Subject: [PATCH 2/9] fix: restore connected state after resync --- src/lib/realtime/realtime-controller.test.ts | 1 + src/lib/realtime/supabase-adapter.ts | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lib/realtime/realtime-controller.test.ts b/src/lib/realtime/realtime-controller.test.ts index 052ead4..2eb2afa 100644 --- a/src/lib/realtime/realtime-controller.test.ts +++ b/src/lib/realtime/realtime-controller.test.ts @@ -203,6 +203,7 @@ describe('Realtime invalidation and convergence', () => { expect(onSnapshot.mock.calls.at(-1)?.[0].queue.revision).toBe(4), ); expect(states).toContain('reconnecting'); + await waitFor(() => expect(states.at(-1)).toBe('connected')); await cleanup(); }); diff --git a/src/lib/realtime/supabase-adapter.ts b/src/lib/realtime/supabase-adapter.ts index 205af7b..409e8fe 100644 --- a/src/lib/realtime/supabase-adapter.ts +++ b/src/lib/realtime/supabase-adapter.ts @@ -224,12 +224,12 @@ export class SupabaseQueueAdapter implements QueueRealtimeAdapter { const gate = new RevisionGate(); let stopped = false; let timer: ReturnType | undefined; - let refreshInFlight: Promise | undefined; + let refreshInFlight: Promise | undefined; let refreshQueued = false; let hiddenAt = 0; - const refresh = (reason: string, force = false): Promise => { - if (stopped) return Promise.resolve(); + const refresh = (reason: string, force = false): Promise => { + if (stopped) return Promise.resolve(false); if (refreshInFlight) { refreshQueued = true; return refreshInFlight; @@ -243,14 +243,16 @@ export class SupabaseQueueAdapter implements QueueRealtimeAdapter { revision: snapshot.queue.revision, }); } + return true; }) - .catch((error: unknown) => + .catch((error: unknown) => { callbacks.onError( error instanceof Error ? error : new Error('Snapshot refresh failed.'), - ), - ) + ); + return false; + }) .finally(() => { refreshInFlight = undefined; if (refreshQueued) { @@ -307,7 +309,9 @@ export class SupabaseQueueAdapter implements QueueRealtimeAdapter { const online = () => { callbacks.onConnectionState('reconnecting'); - void refresh('browser-online', true); + void refresh('browser-online', true).then((synchronized) => { + if (synchronized && !stopped) callbacks.onConnectionState('connected'); + }); }; const offline = () => callbacks.onConnectionState('offline'); const visibility = () => { From 74c0d9905dd2e361f8d9f41f0a0489a0cf0b7c0f Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:00:07 -0400 Subject: [PATCH 3/9] test: add production smoke coverage --- package.json | 1 + playwright.production.config.ts | 14 ++++++ tests/production/smoke.spec.ts | 78 +++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 playwright.production.config.ts create mode 100644 tests/production/smoke.spec.ts diff --git a/package.json b/package.json index 00f5e8b..fe50eb0 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "test": "vitest run", "test:watch": "vitest", "test:e2e": "playwright test", + "test:production": "playwright test --config playwright.production.config.ts", "db:start": "supabase start", "db:stop": "supabase stop", "db:status": "supabase status", diff --git a/playwright.production.config.ts b/playwright.production.config.ts new file mode 100644 index 0000000..cca82eb --- /dev/null +++ b/playwright.production.config.ts @@ -0,0 +1,14 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests/production', + fullyParallel: true, + reporter: 'list', + use: { + baseURL: + process.env.PRODUCTION_BASE_URL ?? 'https://next-queue-omega.vercel.app', + trace: 'retain-on-failure', + screenshot: 'only-on-failure', + }, + projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }], +}); diff --git a/tests/production/smoke.spec.ts b/tests/production/smoke.spec.ts new file mode 100644 index 0000000..06eb9fd --- /dev/null +++ b/tests/production/smoke.spec.ts @@ -0,0 +1,78 @@ +import AxeBuilder from '@axe-core/playwright'; +import { expect, test } from '@playwright/test'; + +const queueSlug = process.env.PRODUCTION_QUEUE_SLUG; +const applicationRoutes = [ + '/', + '/demo', + '/about', + ...(queueSlug + ? [`/q/${queueSlug}`, `/q/${queueSlug}/staff`, `/q/${queueSlug}/display`] + : []), +]; + +test('public routes and metadata endpoints are healthy', async ({ + request, +}) => { + for (const route of [...applicationRoutes, '/robots.txt', '/sitemap.xml']) { + expect((await request.get(route)).status(), route).toBe(200); + } + expect(await (await request.get('/robots.txt')).text()).toContain( + 'https://next-queue-omega.vercel.app/sitemap.xml', + ); + expect(await (await request.get('/sitemap.xml')).text()).toContain( + 'https://next-queue-omega.vercel.app/', + ); +}); + +for (const route of applicationRoutes) { + test(`${route} has clean serious accessibility and console results`, async ({ + page, + }) => { + const errors: string[] = []; + page.on('console', (message) => { + if (message.type() === 'error') errors.push(message.text()); + }); + page.on('pageerror', (error) => errors.push(error.message)); + await page.goto(route); + await expect(page.locator('h1')).toHaveCount(1); + const results = await new AxeBuilder({ page }).analyze(); + expect( + results.violations.filter((finding) => + ['serious', 'critical'].includes(finding.impact ?? ''), + ), + ).toEqual([]); + expect(errors).toEqual([]); + }); +} + +for (const width of [320, 375, 768, 1024, 1440]) { + test(`primary routes avoid horizontal overflow at ${width}px`, async ({ + page, + }) => { + await page.setViewportSize({ width, height: 900 }); + for (const route of applicationRoutes) { + await page.goto(route); + await expect + .poll(() => + page.evaluate( + () => + document.documentElement.scrollWidth <= + document.documentElement.clientWidth, + ), + ) + .toBe(true); + } + }); +} + +test('public display remains legible at 1920x1080 and reduced motion', async ({ + page, +}) => { + test.skip(!queueSlug, 'A temporary production QA queue is required.'); + await page.setViewportSize({ width: 1920, height: 1080 }); + await page.emulateMedia({ reducedMotion: 'reduce' }); + await page.goto(`/q/${queueSlug}/display`); + await expect(page.getByRole('heading', { level: 1 })).toBeVisible(); + await expect(page.getByText('Queue is clear')).toBeVisible(); +}); From 44c53de171044417dd9de8003aefdcdc0da0f590 Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:00:27 -0400 Subject: [PATCH 4/9] docs: document production validation --- README.md | 29 +++++++- docs/architecture/realtime-protocol.md | 2 + .../releases/story-3-production-validation.md | 69 +++++++++++++++++++ 3 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 docs/releases/story-3-production-validation.md diff --git a/README.md b/README.md index 2943ca4..ff00a6a 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ > A calm, persistent real-time queue for small service teams. -Story 2 replaces the local visual prototype state with a transactional Supabase PostgreSQL engine. Anonymous browser identities can create or join queues; a one-time queue capability grants staff membership; customer, staff, and public-display clients converge through filtered Realtime invalidations followed by authoritative revisioned snapshots. +Next uses a transactional Supabase PostgreSQL engine. Anonymous browser identities can create or join queues; a one-time queue capability grants staff membership; customer, staff, and public-display clients converge through filtered Realtime invalidations followed by authoritative revisioned snapshots. -The application is not publicly deployed. +The production application is live at [next-queue-omega.vercel.app](https://next-queue-omega.vercel.app). It runs on Vercel Hobby with one Supabase Free project in `us-east-1`; no paid service, trial, analytics, storage add-on, or custom domain is part of the deployment. ## Product surfaces @@ -42,6 +42,12 @@ Open [http://localhost:3000/demo](http://localhost:3000/demo). Create a queue, s The local stack is development-only, uses default local credentials, and must not be exposed to public traffic. +## Live demo + +Open the [production demo](https://next-queue-omega.vercel.app/demo), create a queue, and save the staff code when it appears: the raw code is shown once and cannot be recovered. The creator is immediately authorized. To test synchronization, open the customer, staff, and display links in separate tabs or isolated browser profiles. A second staff profile must claim access with the code. Queue state persists remotely, while staff membership and customer ownership follow each profile's anonymous session. + +Clearing site data, signing out, or changing browsers loses that anonymous identity. Losing both the creating staff session and the one-time code means staff access cannot be recovered through the product. Do not enter sensitive or regulated personal information; a display name is optional. + ## Commands ```bash @@ -56,6 +62,7 @@ npm run db:types:check npm run test:integration npm run test:realtime npm run test:e2e +npm run test:production npm run format npm run format:check npm run lint @@ -65,6 +72,8 @@ npm run build npm audit ``` +`npm run test:production` is a read-only smoke check against the public URL. Set `PRODUCTION_BASE_URL` to test another deployment. Queue-route coverage additionally accepts `PRODUCTION_QUEUE_SLUG` for a temporary synthetic queue; the suite never creates or mutates remote records. + `db:reset` drops only the local database, replays every migration, and reapplies safe seed data. `db:types` regenerates `src/lib/supabase/database.types.ts`; do not edit that file manually. ## Command and authorization model @@ -79,7 +88,20 @@ Only `queues` and display-safe `queue_entries` are in `supabase_realtime`. Each ## Cost boundary -The intended hosted validation target is one Supabase Free project only: no card, trial, compute upgrade, paid backup, PITR, log drain, support plan, custom domain, or usage-based add-on. Current Free projects are limited to two active projects, 500 MB database size, and may pause after roughly one week of low activity. No keep-alive is used to evade pausing. Re-check [official pricing](https://supabase.com/pricing) before provisioning. +Production uses one Supabase Free project and one Vercel Hobby project only: no card, trial, compute upgrade, paid backup, PITR, log drain, paid analytics, paid storage, support plan, custom domain, or usage-based add-on. Free projects have finite database, bandwidth, build, function, and connection quotas and Supabase may pause an inactive project. No keep-alive is used to evade pausing. Re-check provider limits before relying on the service. + +This portfolio deployment is provided as-is, without a commercial SLA. It has database-enforced authorization and basic access-attempt throttling, but not enterprise abuse protection, guaranteed recovery, or capacity for unbounded traffic. + +## Production deployment + +Vercel is connected to `XonkelX/next-queue`, uses `main` as the production branch, Node.js 22.x, and the standard Next.js build. Only these Production variables are configured: + +```text +NEXT_PUBLIC_SUPABASE_URL +NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY +``` + +Deployments are produced by pushing reviewed commits to `main`; a manual equivalent is `npx vercel deploy --prod`. The browser uses only public Supabase configuration—never a service-role key or database password. See the [Story 3 production validation](docs/releases/story-3-production-validation.md) for the release evidence and remaining risks. ## Documentation @@ -89,3 +111,4 @@ The intended hosted validation target is one Supabase Free project only: no card - [Authorization and security](docs/security/authorization.md) - [Motion system](docs/design/motion-system.md) - [Product scope](docs/product/scope.md) +- [Story 3 production validation](docs/releases/story-3-production-validation.md) diff --git a/docs/architecture/realtime-protocol.md b/docs/architecture/realtime-protocol.md index cff46f4..297b1d0 100644 --- a/docs/architecture/realtime-protocol.md +++ b/docs/architecture/realtime-protocol.md @@ -30,6 +30,8 @@ Queue and entry messages from one transaction can arrive rapidly, so a 75 ms deb There is no healthy-connection polling, Presence, global schema channel, custom heartbeat write, or private-table subscription. +An `online` event performs an authoritative refresh before restoring the visible `CONNECTED` state. A failed refresh remains reconnecting or enters the adapter's error path; network reachability alone is not treated as successful convergence. + ## Commands and retry A new user intent gets a UUID request ID. Automatic retry must retain that UUID. The RPC validates actor/type reuse, performs its transaction, and returns a snapshot. Buttons show local pending feedback but do not invent an authoritative result. Typed conflicts restore the control and explain the recoverable state. Realtime is confirmation/invalidation; the RPC result may update the UI immediately. diff --git a/docs/releases/story-3-production-validation.md b/docs/releases/story-3-production-validation.md new file mode 100644 index 0000000..5ab897a --- /dev/null +++ b/docs/releases/story-3-production-validation.md @@ -0,0 +1,69 @@ +# Story 3 production validation + +Validated on 2026-07-17 against [next-queue-omega.vercel.app](https://next-queue-omega.vercel.app). + +## Deployment architecture + +- Vercel Hobby project `next-queue`, connected only to `XonkelX/next-queue` +- production branch `main`, repository root, Next.js preset, Node.js 22.x, standard build +- one existing Supabase Free project in `us-east-1` +- filtered Realtime publication containing only `queues` and `queue_entries` +- no custom domain, paid analytics, paid storage, add-on, trial, or payment method + +Production has only the following public client configuration, scoped to Production (values intentionally omitted): + +```text +NEXT_PUBLIC_SUPABASE_URL +NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY +``` + +The application runtime has no service-role key, database password, Supabase access token, or other server credential. + +## Production QA + +One synthetic queue, `North Star Café QA`, was used. Separate browser contexts represented two staff members, five customers, and a public display; nine contexts were connected at the busiest point. Queue creation and anonymous sign-in persisted across refresh. A second staff identity was denied before authorization, received a generic invalid-code error, then successfully claimed membership; the code disappeared from the form and never appeared in a URL. + +Customer numbers were unique and ordered. Staff, customers, and display converged without refresh for join, call, complete, skip, pause, reopen, and close. Pausing denied new joins while allowing active service to finish; reopening restored joins; closing denied joins and calls. Terminal entries did not return after every surface was reloaded. + +Two authorized staff issued call-next actions nearly concurrently. Exactly one waiting entry became serving, the other command received the expected conflict, and both screens converged without violating the one-serving invariant. Remote same-request-ID validation produced one side effect and one revision increment, and mismatched command reuse was rejected. + +While a display was offline, staff changed queue state. Restoring connectivity triggered an authoritative snapshot, returned the indicator to connected, showed the latest revision, and introduced no duplicate entries. This exercise exposed and fixed a stale reconnect indicator; a regression test now covers the successful online refresh transition. + +## Privacy and security inspection + +Public snapshots and rendered display state contained queue numbers, not customer UUIDs or private names. The display had no private-table subscription. Page HTML contained no authentication token, private name, or staff code. Eleven production JavaScript bundles were scanned for service credentials and QA secrets with zero matches. No raw code appeared in a URL, report, commit, or PR text. Browser console and page-error collection found zero errors or warnings relevant to hydration, subscriptions, promises, or logging. + +Database validation reconfirmed RLS, RPC-only writes, cross-queue staff isolation, staff-code hashing, private-name ownership, idempotency, and exact Realtime publication. The public client cannot query internal command receipts or security-sensitive tables. + +## Accessibility, responsive, and motion results + +Keyboard-only navigation, skip link, visible focus, mobile navigation, labeled access-code and join forms, command pending states, status announcements, focus retention, offline/reconnect messaging, dark theme, and 200% zoom were manually checked. No focus theft or focus loss occurred during Realtime changes. Queue status does not rely on color alone. + +Automated Axe checks covered home, demo, about, customer, staff, and display routes: zero serious and zero critical findings. Widths 320, 375, 768, 1024, and 1440 px had no horizontal overflow or clipped controls. The public display was also checked at 1920×1080. Reduced-motion mode removed translation/stagger behavior while keeping state visible and interactive. New items animated once; unchanged snapshots and reconnect did not replay the queue. + +## Automated and release validation + +- Vitest: 33 passed +- pgTAP: 62 passed +- integration: 11 passed +- Realtime: 9 passed +- Playwright application suite: 15 passed +- production smoke with temporary queue: 13 passed, 0 skipped +- formatting, lint, typecheck, clean database reset, database lint, generated types, production build, audit (0 vulnerabilities), and diff checks passed +- Story 2 post-merge GitHub Actions: quality, database, and browser jobs passed + +The production smoke suite is read-only and is not part of ordinary PR CI. Without a temporary queue slug, queue-specific display coverage is skipped by design. + +## Cleanup + +The synthetic queue and all related private entries, memberships, access records, attempts, commands, and events were removed. Final counts were zero for all eight application tables: `queues`, `queue_entries`, `queue_entry_private`, `queue_staff_memberships`, `queue_staff_access`, `queue_staff_access_attempts`, `queue_commands`, and `queue_events`. Twenty-three temporary anonymous users were removed, leaving zero Auth users. All QA browser sessions were closed and the one-time staff credential was discarded. + +## Free-tier limitations and remaining risks + +- Supabase may pause an inactive Free project, causing a cold start or temporary unavailability. +- Supabase Free and Vercel Hobby impose finite database, bandwidth, connection, build, and function quotas. +- Anonymous ownership and staff membership are lost when the browser session or site data is cleared. +- A lost one-time staff code cannot be recovered; access depends on an existing authorized session or the saved code. +- Access-attempt throttling is not a substitute for a WAF, bot management, or advanced abuse protection. +- The architecture is suitable for small queues, not unbounded concurrent clients or enterprise-scale workloads without capacity planning. +- The portfolio deployment has no commercial uptime, support, backup, or recovery SLA. From ce001d152aa7c9a9f3fc34b5200f21f74727042b Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Sat, 25 Jul 2026 19:21:05 -0400 Subject: [PATCH 5/9] chore: patch production dependencies --- package-lock.json | 653 ++++++++++++++++++++++++---------------------- package.json | 8 +- 2 files changed, 339 insertions(+), 322 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30ca137..c511223 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@supabase/supabase-js": "2.110.7", "lucide-react": "^1.25.0", "motion": "^12.42.2", - "next": "16.2.10", + "next": "16.2.12", "next-themes": "^0.4.6", "react": "19.2.4", "react-dom": "19.2.4", @@ -30,7 +30,7 @@ "@types/react-dom": "^19", "@vitejs/plugin-react": "^6.0.3", "eslint": "^9", - "eslint-config-next": "16.2.10", + "eslint-config-next": "16.2.12", "jsdom": "^29.1.1", "prettier": "^3.9.5", "supabase": "2.109.1", @@ -813,9 +813,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", "cpu": [ "arm64" ], @@ -825,19 +825,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", "cpu": [ "x64" ], @@ -847,19 +847,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", "cpu": [ "arm64" ], @@ -873,9 +892,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", "cpu": [ "x64" ], @@ -889,9 +908,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", "cpu": [ "arm" ], @@ -905,9 +924,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", "cpu": [ "arm64" ], @@ -921,9 +940,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", "cpu": [ "ppc64" ], @@ -937,9 +956,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", "cpu": [ "riscv64" ], @@ -953,9 +972,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", "cpu": [ "s390x" ], @@ -969,9 +988,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", "cpu": [ "x64" ], @@ -985,9 +1004,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", "cpu": [ "arm64" ], @@ -1001,9 +1020,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", "cpu": [ "x64" ], @@ -1017,9 +1036,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", "cpu": [ "arm" ], @@ -1029,19 +1048,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", "cpu": [ "arm64" ], @@ -1051,19 +1070,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", "cpu": [ "ppc64" ], @@ -1073,19 +1092,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", "cpu": [ "riscv64" ], @@ -1095,19 +1114,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", "cpu": [ "s390x" ], @@ -1117,19 +1136,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", "cpu": [ "x64" ], @@ -1139,19 +1158,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", "cpu": [ "arm64" ], @@ -1161,19 +1180,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", "cpu": [ "x64" ], @@ -1183,38 +1202,54 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", "cpu": [ "arm64" ], @@ -1224,16 +1259,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", "cpu": [ "ia32" ], @@ -1243,16 +1278,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", "cpu": [ "x64" ], @@ -1262,7 +1297,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -1338,15 +1373,15 @@ } }, "node_modules/@next/env": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz", - "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.12.tgz", + "integrity": "sha512-d0Z5Bc13Fa4nR8pFAKx2jay2yhJM16vlfHbTzYnUQAxlNb6B6lmn4hjt69lYNt4kRtyYP6gEM49lPRHNbIyneg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.10.tgz", - "integrity": "sha512-Gs8D2m21VnJeFo9qvYIIqJH94frWerWYu41BprU1pLtRVF7PCQNLiFZZ3fG+iPuj3K83Cwv/rt+msLOy8Qgu3Q==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.12.tgz", + "integrity": "sha512-uF2z/qAK2q7B5/6CpnFcBRX6jOq5iCO+Uqh1UkJhXljX1JwLarLYhhoJadO6dPb6moTprOKewMXheBcbIoSbug==", "dev": true, "license": "MIT", "dependencies": { @@ -1354,9 +1389,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz", - "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.12.tgz", + "integrity": "sha512-0W1R0teHWJrqKX0FH20IzzIWAOuGtBxPGuObrxy1lE8hQvCFj49KE8a3WUg0D7sq6rn6zkM4c7YGUnhudBS6oA==", "cpu": [ "arm64" ], @@ -1370,9 +1405,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz", - "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.12.tgz", + "integrity": "sha512-Hy5Ls099+aFUmOLmIgPfLqNi6iCwhL3uQCssz5rWk+5Nkc6TUKCE83DY5BbNylfm3+mfwcSFnLRfrZDJhVxdtw==", "cpu": [ "x64" ], @@ -1386,9 +1421,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz", - "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.12.tgz", + "integrity": "sha512-+YqU2h1cQkHsGfvjAsrSmst8UIFBibBGm5x3Xgel8NLMiDQtNOM4sM2GOEMvG5YiOBNeN/Ykk8cQC2S0Xrqljg==", "cpu": [ "arm64" ], @@ -1402,9 +1437,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz", - "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.12.tgz", + "integrity": "sha512-0qjhiYBaKAqF63LA1ZWAAnKTzFUguAaZiRa5etMLGGPj/B6uEVjtIZldIzFEp3wHlB0koK6aTzqPtSdplTCjoA==", "cpu": [ "arm64" ], @@ -1418,9 +1453,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz", - "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.12.tgz", + "integrity": "sha512-7A3q26W+h7gnA15uqBToNuDqBEFZZcqh0mW2mn4AJh/G5pdg2RVE3n4slzLEliASZFG3NmsbEzng/x2Sh09mBg==", "cpu": [ "x64" ], @@ -1434,9 +1469,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz", - "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.12.tgz", + "integrity": "sha512-qSjL/uppm+cbh21s72Ss8gkiOhQ4dExWHNGOWy6eZV7STj5WsKehgxT61beSsOj+YYQuTplL376lOCdMQU5T8w==", "cpu": [ "x64" ], @@ -1450,9 +1485,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz", - "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.12.tgz", + "integrity": "sha512-X6hzsOUJac/e7AWSbn9gQ9nzHld1xWP5iyjHpYWvud8pufB679O1xg4JDyKr8Xd69Jvd+kM2Der6uftiZCmjYA==", "cpu": [ "arm64" ], @@ -1466,9 +1501,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz", - "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.12.tgz", + "integrity": "sha512-F6fakeHuFTLOPt0bslQJdf+xtT+WIP9DVn/m4y1w1mRnVPyh3D/cNvzlRkxM444xfm+IvvYNSOrKiA2CDJ0Uxw==", "cpu": [ "x64" ], @@ -2774,45 +2809,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -2868,19 +2864,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", @@ -3652,11 +3635,14 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/baseline-browser-mapping": { "version": "2.10.43", @@ -3681,14 +3667,16 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/braces": { @@ -3871,13 +3859,6 @@ "dev": true, "license": "MIT" }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4490,13 +4471,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.10.tgz", - "integrity": "sha512-HSybLOY0QKf39i4FWUqPN0xWiNDi6A6UqJmZtgDkS3zMqjXTqULvj/sueXx3cdCG0mVG+qH6k5/qdegklH1d1w==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.12.tgz", + "integrity": "sha512-iaaf4vvKo5h2LBdGt0JuRv7t0Ysqr9FMCiFxbptDg8LqOE//mIKR80DdpOnSVM7qjLH3jT8P0aFiwXxBEGZRXw==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.2.10", + "@next/eslint-plugin-next": "16.2.12", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -4516,42 +4497,7 @@ } } }, - "node_modules/eslint-config-next/node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", - "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.16.1", - "resolve": "^2.0.0-next.6" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { + "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", @@ -4586,35 +4532,7 @@ } } }, - "node_modules/eslint-module-utils": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", - "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { + "node_modules/eslint-config-next/node_modules/eslint-plugin-import": { "version": "2.32.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", @@ -4648,7 +4566,7 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { + "node_modules/eslint-config-next/node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", @@ -4658,7 +4576,7 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-jsx-a11y": { + "node_modules/eslint-config-next/node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", @@ -4688,7 +4606,7 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/eslint-plugin-react": { + "node_modules/eslint-config-next/node_modules/eslint-plugin-react": { "version": "7.37.5", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", @@ -4721,6 +4639,69 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", @@ -4759,6 +4740,19 @@ } }, "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", @@ -4789,6 +4783,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", @@ -6496,16 +6503,19 @@ } }, "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -6608,12 +6618,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.10.tgz", - "integrity": "sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.12.tgz", + "integrity": "sha512-iD59eYQWmbFcEbX7v/acG5DRym9iw1DdaPoD0WTA920naWsE25wShzJW4+UvAs8MK9EC2kBfIH6vtto1H1PHGw==", "license": "MIT", "dependencies": { - "@next/env": "16.2.10", + "@next/env": "16.2.12", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -6627,14 +6637,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.10", - "@next/swc-darwin-x64": "16.2.10", - "@next/swc-linux-arm64-gnu": "16.2.10", - "@next/swc-linux-arm64-musl": "16.2.10", - "@next/swc-linux-x64-gnu": "16.2.10", - "@next/swc-linux-x64-musl": "16.2.10", - "@next/swc-win32-arm64-msvc": "16.2.10", - "@next/swc-win32-x64-msvc": "16.2.10", + "@next/swc-darwin-arm64": "16.2.12", + "@next/swc-darwin-x64": "16.2.12", + "@next/swc-linux-arm64-gnu": "16.2.12", + "@next/swc-linux-arm64-musl": "16.2.12", + "@next/swc-linux-x64-gnu": "16.2.12", + "@next/swc-linux-x64-musl": "16.2.12", + "@next/swc-win32-arm64-msvc": "16.2.12", + "@next/swc-win32-x64-msvc": "16.2.12", "sharp": "^0.34.5" }, "peerDependencies": { @@ -7503,48 +7513,53 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/sharp/node_modules/semver": { diff --git a/package.json b/package.json index fe50eb0..ca49acf 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@supabase/supabase-js": "2.110.7", "lucide-react": "^1.25.0", "motion": "^12.42.2", - "next": "16.2.10", + "next": "16.2.12", "next-themes": "^0.4.6", "react": "19.2.4", "react-dom": "19.2.4", @@ -48,7 +48,7 @@ "@types/react-dom": "^19", "@vitejs/plugin-react": "^6.0.3", "eslint": "^9", - "eslint-config-next": "16.2.10", + "eslint-config-next": "16.2.12", "jsdom": "^29.1.1", "prettier": "^3.9.5", "supabase": "2.109.1", @@ -57,6 +57,8 @@ "vitest": "^4.1.10" }, "overrides": { - "postcss": "8.5.19" + "minimatch": "10.2.5", + "postcss": "8.5.19", + "sharp": "0.35.3" } } From c991dfa91f98fc7cefdd29b4396453d3828a5f03 Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Sat, 25 Jul 2026 19:21:05 -0400 Subject: [PATCH 6/9] test: disambiguate reconnect status --- tests/e2e/realtime.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/e2e/realtime.spec.ts b/tests/e2e/realtime.spec.ts index 0258f37..ae2966c 100644 --- a/tests/e2e/realtime.spec.ts +++ b/tests/e2e/realtime.spec.ts @@ -44,7 +44,11 @@ test('customer, staff, and display converge without refresh and resync after off await created.page.getByRole('button', { name: 'Call next' }).click(); await second.context.setOffline(false); await expect(second.page.getByText('It’s your turn.')).toBeVisible(); - await expect(second.page.getByText('Connected')).toBeVisible(); + await expect( + second.page + .getByRole('region', { name: 'Keep your place. Keep your day.' }) + .getByText('Connected', { exact: true }), + ).toBeVisible(); await expect(second.page.getByLabel('Queue number R-002')).toHaveCount(1); await Promise.all([ From 06bdcd95d432632d5677f6e8319233b912530f62 Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Sat, 25 Jul 2026 19:35:11 -0400 Subject: [PATCH 7/9] test: cover invalid production queue --- tests/production/smoke.spec.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/production/smoke.spec.ts b/tests/production/smoke.spec.ts index 06eb9fd..ee5d01c 100644 --- a/tests/production/smoke.spec.ts +++ b/tests/production/smoke.spec.ts @@ -25,6 +25,15 @@ test('public routes and metadata endpoints are healthy', async ({ ); }); +test('an unknown queue shows the intended unavailable state', async ({ + page, +}) => { + await page.goto(`/q/unknown-${crypto.randomUUID()}`); + await expect( + page.getByRole('heading', { name: 'This queue could not be found.' }), + ).toBeVisible(); +}); + for (const route of applicationRoutes) { test(`${route} has clean serious accessibility and console results`, async ({ page, From 8c9ecd69135ccd35b2ad6d22cb5f5f140672ccde Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Sat, 25 Jul 2026 19:35:12 -0400 Subject: [PATCH 8/9] docs: record final production revalidation --- docs/releases/story-3-production-validation.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/releases/story-3-production-validation.md b/docs/releases/story-3-production-validation.md index 5ab897a..7cd75bc 100644 --- a/docs/releases/story-3-production-validation.md +++ b/docs/releases/story-3-production-validation.md @@ -8,7 +8,7 @@ Validated on 2026-07-17 against [next-queue-omega.vercel.app](https://next-queue - production branch `main`, repository root, Next.js preset, Node.js 22.x, standard build - one existing Supabase Free project in `us-east-1` - filtered Realtime publication containing only `queues` and `queue_entries` -- no custom domain, paid analytics, paid storage, add-on, trial, or payment method +- Web Analytics and Speed Insights disabled; no custom domain, paid analytics, paid storage, add-on, trial, or payment method Production has only the following public client configuration, scoped to Production (values intentionally omitted): @@ -56,7 +56,11 @@ The production smoke suite is read-only and is not part of ordinary PR CI. Witho ## Cleanup -The synthetic queue and all related private entries, memberships, access records, attempts, commands, and events were removed. Final counts were zero for all eight application tables: `queues`, `queue_entries`, `queue_entry_private`, `queue_staff_memberships`, `queue_staff_access`, `queue_staff_access_attempts`, `queue_commands`, and `queue_events`. Twenty-three temporary anonymous users were removed, leaving zero Auth users. All QA browser sessions were closed and the one-time staff credential was discarded. +The synthetic queue and all related private entries, memberships, access records, attempts, commands, and events were removed. Final counts were zero for all eight application tables: `queues`, `queue_entries`, `queue_entry_private`, `queue_staff_memberships`, `queue_staff_access`, `queue_staff_access_attempts`, `queue_commands`, and `queue_events`. Twenty-three temporary anonymous users were removed during full QA and two more during final post-restore smoke validation, leaving zero Auth users. All QA browser sessions were closed and the one-time staff credential was discarded. + +## Final revalidation + +On 2026-07-25, the complete suite was rerun before opening the Story 3 PR. Newly published advisories were resolved by updating Next.js to 16.2.12 and pinning patched transitive Sharp and Minimatch versions; `npm audit` returned zero vulnerabilities. Supabase had automatically paused the inactive Free project as documented. The existing project was restored without an upgrade or payment, returned to `ACTIVE_HEALTHY`, and passed anonymous Auth and production route validation. The read-only production smoke suite then passed 10 tests; one display-only test was skipped because the temporary QA queue had already been deleted. Final cleanup again confirmed zero rows in all eight application tables and zero Auth users. ## Free-tier limitations and remaining risks From 4f405717db387c815ef465fded12bc4d76230d9a Mon Sep 17 00:00:00 2001 From: Oniel Alejo Feliz <197416079+XonkelX@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:32:57 -0400 Subject: [PATCH 9/9] test: keep production smoke read-only --- README.md | 2 +- .../releases/story-3-production-validation.md | 6 +-- tests/production/smoke.spec.ts | 40 +++++-------------- 3 files changed, 15 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index ff00a6a..ae80904 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ npm run build npm audit ``` -`npm run test:production` is a read-only smoke check against the public URL. Set `PRODUCTION_BASE_URL` to test another deployment. Queue-route coverage additionally accepts `PRODUCTION_QUEUE_SLUG` for a temporary synthetic queue; the suite never creates or mutates remote records. +`npm run test:production` is a read-only smoke check against the public URL. Set `PRODUCTION_BASE_URL` to test another deployment. `PRODUCTION_QUEUE_SLUG` optionally adds HTTP health checks for an existing synthetic queue; browser checks remain on public pages so the suite never creates an anonymous identity or mutates remote records. `db:reset` drops only the local database, replays every migration, and reapplies safe seed data. `db:types` regenerates `src/lib/supabase/database.types.ts`; do not edit that file manually. diff --git a/docs/releases/story-3-production-validation.md b/docs/releases/story-3-production-validation.md index 7cd75bc..42e8282 100644 --- a/docs/releases/story-3-production-validation.md +++ b/docs/releases/story-3-production-validation.md @@ -48,11 +48,11 @@ Automated Axe checks covered home, demo, about, customer, staff, and display rou - integration: 11 passed - Realtime: 9 passed - Playwright application suite: 15 passed -- production smoke with temporary queue: 13 passed, 0 skipped +- production QA smoke with temporary queue: 13 passed, 0 skipped; its temporary anonymous identities were removed during cleanup - formatting, lint, typecheck, clean database reset, database lint, generated types, production build, audit (0 vulnerabilities), and diff checks passed - Story 2 post-merge GitHub Actions: quality, database, and browser jobs passed -The production smoke suite is read-only and is not part of ordinary PR CI. Without a temporary queue slug, queue-specific display coverage is skipped by design. +The ongoing production smoke suite is read-only and is not part of ordinary PR CI. Browser checks cover only public pages; an optional temporary queue slug adds server-rendered HTTP health checks without starting an anonymous browser session. ## Cleanup @@ -60,7 +60,7 @@ The synthetic queue and all related private entries, memberships, access records ## Final revalidation -On 2026-07-25, the complete suite was rerun before opening the Story 3 PR. Newly published advisories were resolved by updating Next.js to 16.2.12 and pinning patched transitive Sharp and Minimatch versions; `npm audit` returned zero vulnerabilities. Supabase had automatically paused the inactive Free project as documented. The existing project was restored without an upgrade or payment, returned to `ACTIVE_HEALTHY`, and passed anonymous Auth and production route validation. The read-only production smoke suite then passed 10 tests; one display-only test was skipped because the temporary QA queue had already been deleted. Final cleanup again confirmed zero rows in all eight application tables and zero Auth users. +On 2026-07-25, the complete suite was rerun before opening the Story 3 PR. Newly published advisories were resolved by updating Next.js to 16.2.12 and pinning patched transitive Sharp and Minimatch versions; `npm audit` returned zero vulnerabilities. Supabase had automatically paused the inactive Free project as documented. The existing project was restored without an upgrade or payment, returned to `ACTIVE_HEALTHY`, and passed anonymous Auth and production route validation. The production smoke suite then passed 10 tests; one display-only test was skipped because the temporary QA queue had already been deleted. Final cleanup again confirmed zero rows in all eight application tables and zero Auth users. Final review hardened the ongoing smoke suite to avoid browser navigation to queue routes, which creates anonymous identities by design; the current read-only suite passes 9 tests and leaves no remote records. ## Free-tier limitations and remaining risks diff --git a/tests/production/smoke.spec.ts b/tests/production/smoke.spec.ts index ee5d01c..8f13418 100644 --- a/tests/production/smoke.spec.ts +++ b/tests/production/smoke.spec.ts @@ -2,19 +2,21 @@ import AxeBuilder from '@axe-core/playwright'; import { expect, test } from '@playwright/test'; const queueSlug = process.env.PRODUCTION_QUEUE_SLUG; -const applicationRoutes = [ - '/', - '/demo', - '/about', - ...(queueSlug - ? [`/q/${queueSlug}`, `/q/${queueSlug}/staff`, `/q/${queueSlug}/display`] - : []), -]; +const applicationRoutes = ['/', '/demo', '/about']; +const queueRoutes = queueSlug + ? [`/q/${queueSlug}`, `/q/${queueSlug}/staff`, `/q/${queueSlug}/display`] + : []; test('public routes and metadata endpoints are healthy', async ({ request, }) => { - for (const route of [...applicationRoutes, '/robots.txt', '/sitemap.xml']) { + for (const route of [ + ...applicationRoutes, + ...queueRoutes, + `/q/unknown-${crypto.randomUUID()}`, + '/robots.txt', + '/sitemap.xml', + ]) { expect((await request.get(route)).status(), route).toBe(200); } expect(await (await request.get('/robots.txt')).text()).toContain( @@ -25,15 +27,6 @@ test('public routes and metadata endpoints are healthy', async ({ ); }); -test('an unknown queue shows the intended unavailable state', async ({ - page, -}) => { - await page.goto(`/q/unknown-${crypto.randomUUID()}`); - await expect( - page.getByRole('heading', { name: 'This queue could not be found.' }), - ).toBeVisible(); -}); - for (const route of applicationRoutes) { test(`${route} has clean serious accessibility and console results`, async ({ page, @@ -74,14 +67,3 @@ for (const width of [320, 375, 768, 1024, 1440]) { } }); } - -test('public display remains legible at 1920x1080 and reduced motion', async ({ - page, -}) => { - test.skip(!queueSlug, 'A temporary production QA queue is required.'); - await page.setViewportSize({ width: 1920, height: 1080 }); - await page.emulateMedia({ reducedMotion: 'reduce' }); - await page.goto(`/q/${queueSlug}/display`); - await expect(page.getByRole('heading', { level: 1 })).toBeVisible(); - await expect(page.getByText('Queue is clear')).toBeVisible(); -});