diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index aef3430..69f7b91 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -23,6 +23,11 @@ jobs: - run: pnpm install --frozen-lockfile - run: pnpm check - run: pnpm build:cloudflare + - name: Apply D1 migrations + run: pnpm db:migrate:remote + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - uses: cloudflare/wrangler-action@v4 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/README.md b/README.md index ce35e77..0f35eb6 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,94 @@ Pushes to `main`, including merged PRs, deploy to `internal.ctcusc.com` after ch Same-repo PRs build and deploy to `pr-.internal.ctcusc.com` independently of CI checks. Find the preview in the PR's deployment status, updated bot comment, or **Preview** workflow summary. Closing the PR deletes its preview and marks its deployments inactive. Production and previews share four GitHub Actions secrets: `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_API_TOKEN`, `SITE_PASSWORD` (12+ characters), and `SESSION_SECRET` (32+ characters). The first production deployment registers the custom domain for production and previews. Previews use `workers.dev` until the custom domain is active. + +## Project data starter + +D1 is bound as `DB` to `ctc-internal` (`dded22aa-c431-40e7-bae4-3d62f0540c16`). +This starter supplies storage and basic reads/writes. Developers implement the +short-code generation, redirect behavior, QR rendering, and attendance UI/workflows. +There is no public redirect or unauthenticated attendance endpoint. + +### Database setup + +```sh +pnpm db:migrate:local +pnpm dev +``` + +Local development uses a local D1 database, not production. To create the tables +in the existing remote database, authenticate Wrangler to its owning Cloudflare +account (set `CLOUDFLARE_ACCOUNT_ID` if necessary), then run: + +```sh +pnpm db:migrate:remote +``` + +The production deployment workflow applies migrations before deploying, using the +existing Cloudflare account/token secrets. That token needs D1 edit access. +PR previews intentionally have no D1 binding; add a separate preview database and +apply its migrations before testing data features there. Do not bind previews to +production attendance data. + +### Tables + +- `short_links`: `code` (primary key), `url`, optional `title`, `created_at`. + A code must be supplied by the developer's app. QR codes can encode the eventual + short URL; image generation and storage are left to the project. +- `attendance_events`: `id` (UUID), `name`, `starts_at` (UTC), optional `location`, + `created_at`. +- `attendance_check_ins`: `event_id` (foreign key), `email`, `name`, `checked_in_at`. + One row per event/email. Repeating a write updates the name and preserves the + original timestamp. Email is normalized to lowercase. + +### Endpoints + +All endpoints require the existing club login cookie. They are intended for +same-origin pages in this app. All signed-in members share access; this is not +individual identity verification or per-role authorization. + +| Method | Path | Read/write | +| ------ | ---------------------------- | ------------------------------------- | +| GET | `/api/links` | List saved links | +| GET | `/api/links/:code` | Read one link | +| POST | `/api/links` | Save `{ code, url, title? }` | +| GET | `/api/events` | List events | +| GET | `/api/events/:id` | Read one event | +| POST | `/api/events` | Save `{ name, starts_at, location? }` | +| GET | `/api/events/:id/attendance` | List check-ins | +| POST | `/api/events/:id/attendance` | Save `{ name, email }` | + +List endpoints accept `?limit=50&offset=0` (maximum limit 100). Responses wrap +results in `links`, `link`, `events`, `event`, `attendance`, or `checkIn`. +Dates are ISO 8601 strings. Send writes as `application/json` (maximum 16 KiB). +Errors return `{ error: "..." }`: 400 invalid input, 401 sign-in required, +403 cross-origin write, 404 missing record, 409 duplicate code, +413 oversized body, 415 incorrect content type, or 503 unavailable database. + +Example from a signed-in page: + +```ts +const response = await fetch("/api/links", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ code: "workshop", url: "https://example.com" }), +}); +if (!response.ok) throw new Error("Could not save link"); +const { link } = await response.json(); +``` + +Server components and server actions can call the same authenticated helpers: + +```ts +import { createLink, getLink, listLinks } from "@/server/data/links"; +import { + createEvent, + getEvent, + listEvents, + checkIn, + listAttendance, +} from "@/server/data/attendance"; +``` + +Keep these imports server-side. Validate project-specific rules in your own code; +the starter validates basic fields and uses parameterized SQL. diff --git a/cloudflare-env.d.ts b/cloudflare-env.d.ts index 047d865..3735e74 100644 --- a/cloudflare-env.d.ts +++ b/cloudflare-env.d.ts @@ -1,7 +1,12 @@ -interface CloudflareEnv { - LOGIN_RATE_LIMITER: { - limit(options: { key: string }): Promise<{ success: boolean }>; - }; - SITE_PASSWORD: string; - SESSION_SECRET: string; +import type { Database } from "./src/server/data/db"; + +declare global { + interface CloudflareEnv { + DB: Database; + LOGIN_RATE_LIMITER: { + limit(options: { key: string }): Promise<{ success: boolean }>; + }; + SITE_PASSWORD: string; + SESSION_SECRET: string; + } } diff --git a/eslint.config.js b/eslint.config.js index 0d1f97a..b2754e9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,15 +1,11 @@ -import { FlatCompat } from "@eslint/eslintrc"; +import nextVitals from "eslint-config-next/core-web-vitals"; import tseslint from "typescript-eslint"; -const compat = new FlatCompat({ - baseDirectory: import.meta.dirname, -}); - export default tseslint.config( { ignores: [".next/**", ".open-next/**", ".wrangler/**", "next-env.d.ts"], }, - ...compat.extends("next/core-web-vitals"), + ...nextVitals, { files: ["**/*.ts", "**/*.tsx"], extends: [ diff --git a/migrations/0001_links_and_attendance.sql b/migrations/0001_links_and_attendance.sql new file mode 100644 index 0000000..ce47ff2 --- /dev/null +++ b/migrations/0001_links_and_attendance.sql @@ -0,0 +1,25 @@ +CREATE TABLE short_links ( + code TEXT PRIMARY KEY NOT NULL, + url TEXT NOT NULL, + title TEXT, + created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX short_links_created_at ON short_links(created_at DESC, code); + +CREATE TABLE attendance_events ( + id TEXT PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + starts_at TEXT NOT NULL, + location TEXT, + created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX attendance_events_starts_at ON attendance_events(starts_at DESC, id); + +CREATE TABLE attendance_check_ins ( + event_id TEXT NOT NULL REFERENCES attendance_events(id), + email TEXT NOT NULL COLLATE NOCASE, + name TEXT NOT NULL, + checked_in_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + PRIMARY KEY (event_id, email) +); +CREATE INDEX attendance_check_ins_time ON attendance_check_ins(event_id, checked_in_at, email); diff --git a/next.config.js b/next.config.js index 1af9b0c..c18f0f8 100644 --- a/next.config.js +++ b/next.config.js @@ -1,11 +1,13 @@ import "./src/env.js"; +import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; + +initOpenNextCloudflareForDev(); const isPreviewBuild = process.env.PREVIEW_BUILD === "true"; /** @type {import("next").NextConfig} */ const config = { poweredByHeader: false, - eslint: { ignoreDuringBuilds: isPreviewBuild }, typescript: { ignoreBuildErrors: isPreviewBuild }, async headers() { return [ diff --git a/package.json b/package.json index 80c75d1..dc8ba6e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "next dev --turbo", - "build": "next build", + "build": "next build --webpack", "build:cloudflare": "opennextjs-cloudflare build", "preview": "pnpm build:cloudflare && opennextjs-cloudflare preview", "deploy": "pnpm build:cloudflare && opennextjs-cloudflare deploy", @@ -15,16 +15,19 @@ "test": "vitest run", "format:check": "prettier --check .", "format:write": "prettier --write .", - "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test" + "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test", + "db:migrate:local": "wrangler d1 migrations apply ctc-internal --local", + "db:migrate:remote": "wrangler d1 migrations apply ctc-internal --remote", + "test:worker": "node scripts/smoke-worker.js" }, "dependencies": { - "@opennextjs/cloudflare": "1.20.6", + "@opennextjs/cloudflare": "1.20.13", "@t3-oss/env-nextjs": "^0.12.0", "class-variance-authority": "^0.7.1", "cn": "^0.4.0", "jose": "^6.2.12", "lucide-react": "^1.51.0", - "next": "^15.2.3", + "next": "^16.4.2", "radix-ui": "^1.6.7", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -34,13 +37,12 @@ "zod": "^3.24.2" }, "devDependencies": { - "@eslint/eslintrc": "^3.3.1", "@tailwindcss/postcss": "^4.0.15", "@types/node": "^20.14.10", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "eslint": "^9.23.0", - "eslint-config-next": "^15.2.3", + "eslint-config-next": "^16.4.2", "postcss": "^8.5.3", "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7cb1f9..9aa2af2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,8 +167,8 @@ importers: .: dependencies: '@opennextjs/cloudflare': - specifier: 1.20.6 - version: 1.20.6(next@15.5.27(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(supports-color@7.2.0)(wrangler@4.147.0(@types/node@20.19.43)) + specifier: 1.20.13 + version: 1.20.13(next@16.4.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(supports-color@7.2.0)(wrangler@4.147.0(@types/node@20.19.43)) '@t3-oss/env-nextjs': specifier: ^0.12.0 version: 0.12.0(typescript@5.9.3)(zod@3.25.76) @@ -185,8 +185,8 @@ importers: specifier: ^1.51.0 version: 1.51.0(@types/react@19.3.0)(react@19.3.0) next: - specifier: ^15.2.3 - version: 15.5.27(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + specifier: ^16.4.2 + version: 16.4.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) radix-ui: specifier: ^1.6.7 version: 1.6.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -209,9 +209,6 @@ importers: specifier: ^3.24.2 version: 3.25.76 devDependencies: - '@eslint/eslintrc': - specifier: ^3.3.1 - version: 3.3.7(supports-color@7.2.0) '@tailwindcss/postcss': specifier: ^4.0.15 version: 4.3.3 @@ -228,8 +225,8 @@ importers: specifier: ^9.23.0 version: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) eslint-config-next: - specifier: ^15.2.3 - version: 15.5.27(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + specifier: ^16.4.2 + version: 16.4.2(@typescript-eslint/parser@8.71.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) postcss: specifier: ^8.5.3 version: 8.5.28 @@ -261,66 +258,66 @@ packages: resolution: {integrity: sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==} engines: {node: '>=10'} - '@ast-grep/napi-darwin-arm64@0.40.5': - resolution: {integrity: sha512-2F072fGN0WTq7KI3okuEnkGJVEHLbi56Bw1H6NAMf7j2mJJeQWsRyGOMcyNnUXZDeNdvoMH0OB2a5wwUegY/nQ==} + '@ast-grep/napi-darwin-arm64@0.45.3': + resolution: {integrity: sha512-rMZJ6frSpR/xSbeV4hiA9WR5hHqk3rVYX8lOqEoUCaUnjEuFXvHdGN4WawJ8pVDq/IqM1UnTtsDSYgUdVmO5DA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@ast-grep/napi-darwin-x64@0.40.5': - resolution: {integrity: sha512-dJMidHZhhxuLBYNi6/FKI812jQ7wcFPSKkVPwviez2D+KvYagapUMAV/4dJ7FCORfguVk8Y0jpPAlYmWRT5nvA==} + '@ast-grep/napi-darwin-x64@0.45.3': + resolution: {integrity: sha512-tIclZ5pM+YSgk8NNTBoTkaclbS39cU5lbVfSrFWX4LUnxXfUPG+F+ppxyFjKwJTBELsGHEO4LdX3/Lc+FDwPNA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@ast-grep/napi-linux-arm64-gnu@0.40.5': - resolution: {integrity: sha512-nBRCbyoS87uqkaw4Oyfe5VO+SRm2B+0g0T8ME69Qry9ShMf41a2bTdpcQx9e8scZPogq+CTwDHo3THyBV71l9w==} + '@ast-grep/napi-linux-arm64-gnu@0.45.3': + resolution: {integrity: sha512-Bm8F7/oRHQkDXbjH+gBi9oB5huJ0q12r/AIOMIypf5PWlAVvh206Tsw468phw4Oh6kd4Ad1Z+gflNW1MutgK2g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@ast-grep/napi-linux-arm64-musl@0.40.5': - resolution: {integrity: sha512-/qKsmds5FMoaEj6FdNzepbmLMtlFuBLdrAn9GIWCqOIcVcYvM1Nka8+mncfeXB/MFZKOrzQsQdPTWqrrQzXLrA==} + '@ast-grep/napi-linux-arm64-musl@0.45.3': + resolution: {integrity: sha512-yr5xcRerV41GeRxmZ8SKbfGeyZRhQePoNNCgTAb4OrWMelWwbMI80aY7/IlF2bo1sN40BSW4hZdGIO/akTd1RQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@ast-grep/napi-linux-x64-gnu@0.40.5': - resolution: {integrity: sha512-DP4oDbq7f/1A2hRTFLhJfDFR6aI5mRWdEfKfHzRItmlKsR9WlcEl1qDJs/zX9R2EEtIDsSKRzuJNfJllY3/W8Q==} + '@ast-grep/napi-linux-x64-gnu@0.45.3': + resolution: {integrity: sha512-lluRK3hxfHMQdFuVID6seAKVx4FH0kEwF6rCh5/I40Sf3EtNR2UCBXAqd6RBA6hqlSckktvBxCfw+h4aescDmg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@ast-grep/napi-linux-x64-musl@0.40.5': - resolution: {integrity: sha512-BRZUvVBPUNpWPo6Ns8chXVzxHPY+k9gpsubGTHy92Q26ecZULd/dTkWWdnvfhRqttsSQ9Pe/XQdi5+hDQ6RYcg==} + '@ast-grep/napi-linux-x64-musl@0.45.3': + resolution: {integrity: sha512-09wJQKq86LqILqQtw0+X09SzhcPwUnMVFKyia7S3XLbLqSeQpAMF55Y+zll53JRQ1dCzFEZJyvTT/7j9vwfE3w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@ast-grep/napi-win32-arm64-msvc@0.40.5': - resolution: {integrity: sha512-y95zSEwc7vhxmcrcH0GnK4ZHEBQrmrszRBNQovzaciF9GUqEcCACNLoBesn4V47IaOp4fYgD2/EhGRTIBFb2Ug==} + '@ast-grep/napi-win32-arm64-msvc@0.45.3': + resolution: {integrity: sha512-/HiVgw2cdtzXLDLoG4eBBcauekNXewfagy7qiVbjkVu/KcCH8dJ9lpj8DzeWwuBNtZxuD8jpiwP0M1pz3Bc3uw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@ast-grep/napi-win32-ia32-msvc@0.40.5': - resolution: {integrity: sha512-K/u8De62iUnFCzVUs7FBdTZ2Jrgc5/DLHqjpup66KxZ7GIM9/HGME/O8aSoPkpcAeCD4TiTZ11C1i5p5H98hTg==} + '@ast-grep/napi-win32-ia32-msvc@0.45.3': + resolution: {integrity: sha512-/gwJrJQUw65uNtAFcG2rtSTr117Gqqy3bfjaljCPKsa65dTeRcMMnjKLlZ6V9/g8z+4Xf78kdhp5ECbir4yfSw==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@ast-grep/napi-win32-x64-msvc@0.40.5': - resolution: {integrity: sha512-dqm5zg/o4Nh4VOQPEpMS23ot8HVd22gG0eg01t4CFcZeuzyuSgBlOL3N7xLbz3iH2sVkk7keuBwAzOIpTqziNQ==} + '@ast-grep/napi-win32-x64-msvc@0.45.3': + resolution: {integrity: sha512-CEZAMAsaDoQoVE3MT2c/ry3/ydfyaqlTjxv8DK5PG355SrdAYPgevGC2Me6tz4dhphJin89Y20VsnrS5UrYV5Q==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@ast-grep/napi@0.40.5': - resolution: {integrity: sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A==} + '@ast-grep/napi@0.45.3': + resolution: {integrity: sha512-bjJsUt0g3UCwbn9bjv7MnoR15rszSm79yPCXn4DWWK0NuYUUqBU/srtNMR4WnpfR7TnfBRdYFoqSNx0pGCNWJg==} engines: {node: '>= 10'} '@aws-crypto/sha1-browser@5.2.0': @@ -678,22 +675,25 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@dotenvx/dotenvx@1.31.0': - resolution: {integrity: sha512-GeDxvtjiRuoyWVU9nQneId879zIyNdL05bS7RKiqMkfBSKpHMWHLoRyRqjYWLaXmX/llKO1hTlqHDmatkQAjPA==} - hasBin: true - '@dotenvx/dotenvx@1.75.1': resolution: {integrity: sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==} hasBin: true + '@dotenvx/dotenvx@2.38.2': + resolution: {integrity: sha512-+tjy8o//H+ZkRfRWLh08UkLHCw/e74uIV27QOZ3RxKwXvGrxjEcQMfUvQJzkvK/qNTk/OsE1MlGiDB6iL+1X0g==} + hasBin: true + '@dotenvx/primitives@0.8.0': resolution: {integrity: sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg==} - '@ecies/ciphers@0.2.6': - resolution: {integrity: sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==} - engines: {bun: '>=1', deno: '>=2.7.10', node: '>=16'} - peerDependencies: - '@noble/ciphers': ^1.0.0 + '@dotenvx/primitives@3.3.1': + resolution: {integrity: sha512-ykX29rVnHwwV6m3AMmvR+niFSwhOG2TYttJjURz3n/3iHtpWC8gEptojYY+eHgBAV4fkaGRM1Rf0wgQ6/DeUYQ==} + + '@dotenvx/providers@0.3.11': + resolution: {integrity: sha512-5H5kAr5ps4shR7ZtA3mtDphh9Fbhp21yY77jCBCJLX2pbdEse0bcCBn7M1Ex1YrKF2O9SLKJB8ttJ2P5JsPidQ==} + + '@dotenvx/tooling@1.0.6': + resolution: {integrity: sha512-d1AhKqlprzjR7+/7YTYbf9lTjeIUxr2WN+gYL5jf9O25wymlVWzuwOujq9IWJOt33sGWcLjr1eswSOmqm4SRMg==} '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} @@ -707,23 +707,17 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/aix-ppc64@0.25.4': - resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.28.1': resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.4': - resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} - cpu: [arm64] - os: [android] + cpu: [ppc64] + os: [aix] '@esbuild/android-arm64@0.28.1': resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} @@ -731,10 +725,10 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.4': - resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [android] '@esbuild/android-arm@0.28.1': @@ -743,10 +737,10 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.4': - resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm] os: [android] '@esbuild/android-x64@0.28.1': @@ -755,11 +749,11 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.4': - resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] '@esbuild/darwin-arm64@0.28.1': resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} @@ -767,10 +761,10 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.4': - resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.28.1': @@ -779,11 +773,11 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.4': - resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + cpu: [x64] + os: [darwin] '@esbuild/freebsd-arm64@0.28.1': resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} @@ -791,10 +785,10 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.4': - resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.28.1': @@ -803,11 +797,11 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.4': - resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} engines: {node: '>=18'} - cpu: [arm64] - os: [linux] + cpu: [x64] + os: [freebsd] '@esbuild/linux-arm64@0.28.1': resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} @@ -815,10 +809,10 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.4': - resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.28.1': @@ -827,10 +821,10 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.4': - resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.28.1': @@ -839,10 +833,10 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.4': - resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} engines: {node: '>=18'} - cpu: [loong64] + cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.28.1': @@ -851,10 +845,10 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.4': - resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} engines: {node: '>=18'} - cpu: [mips64el] + cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.28.1': @@ -863,10 +857,10 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.4': - resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} engines: {node: '>=18'} - cpu: [ppc64] + cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.28.1': @@ -875,10 +869,10 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.4': - resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} engines: {node: '>=18'} - cpu: [riscv64] + cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.28.1': @@ -887,10 +881,10 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.4': - resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} engines: {node: '>=18'} - cpu: [s390x] + cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.28.1': @@ -899,10 +893,10 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.4': - resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} engines: {node: '>=18'} - cpu: [x64] + cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.28.1': @@ -911,11 +905,11 @@ packages: cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.4': - resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] + cpu: [x64] + os: [linux] '@esbuild/netbsd-arm64@0.28.1': resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} @@ -923,10 +917,10 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.4': - resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.28.1': @@ -935,11 +929,11 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.4': - resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] + cpu: [x64] + os: [netbsd] '@esbuild/openbsd-arm64@0.28.1': resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} @@ -947,10 +941,10 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.4': - resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.28.1': @@ -959,17 +953,23 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.28.1': resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.4': - resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} engines: {node: '>=18'} - cpu: [x64] - os: [sunos] + cpu: [arm64] + os: [openharmony] '@esbuild/sunos-x64@0.28.1': resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} @@ -977,11 +977,11 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.4': - resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} engines: {node: '>=18'} - cpu: [arm64] - os: [win32] + cpu: [x64] + os: [sunos] '@esbuild/win32-arm64@0.28.1': resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} @@ -989,10 +989,10 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.4': - resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.28.1': @@ -1001,10 +1001,10 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.4': - resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} engines: {node: '>=18'} - cpu: [x64] + cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.28.1': @@ -1013,12 +1013,24 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -1412,9 +1424,9 @@ packages: cpu: [x64] os: [win32] - '@isaacs/cliui@9.0.0': - resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} - engines: {node: '>=18'} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1455,87 +1467,75 @@ packages: '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 - '@next/env@15.5.27': - resolution: {integrity: sha512-WC5hvVqiuFOcRyz6gKPeJkiVvSMf3scNeAV49CdWtVqxmUlzh8UKR6YHt9yiNkh8LngtzqSlVz07/Ma3ZAzchA==} + '@next/env@16.4.2': + resolution: {integrity: sha512-nTUTFH7G5/J/fmpkjVLegOkqyKOP0CEsJcMsyO4UCYeN1su+YMW/MfaSErnJskL7XM+UDK6aTvslxsRCyya/sA==} - '@next/eslint-plugin-next@15.5.27': - resolution: {integrity: sha512-a94xlzvOLaULh4Fzcvj9B2hwIOtPHJW75gghkXiJ8s1Jk0eo00UknrDVhx04Y+8ARu9ds13vy/jHg5u/PdVGqw==} + '@next/eslint-plugin-next@16.4.2': + resolution: {integrity: sha512-gCTWlw0e+Y6kajpFyKKgA195jUJCRhlzJE0lV7ufe8a+ghIsANWZ0/FiJwg6DuOG1+OvRbcZDcFBoRh3mNJx0g==} - '@next/swc-darwin-arm64@15.5.27': - resolution: {integrity: sha512-fBA9QurmK4+uj4wNhmRTuO5zMVRaBHCvp6H93zTG/5i6Yi6TewbvbesXB8YQNj8T99Cwrow87hHiFV6wL/hyMA==} + '@next/swc-darwin-arm64@16.4.2': + resolution: {integrity: sha512-ONkhFvZNFWlsEHOM/81AtxdyuJP2KRYKYR0JxPwvcu1AuPMMMgcL+wW2luldJvMtZwKtUxoSkCychW5wIeoYSw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.5.27': - resolution: {integrity: sha512-FGZwJwry8qZNHxD/nfwrCwCHHrqx6epqItkkxbYcBQEQ2uw5S43bY83fQB5zbAsZE7TELMjoYkbhsQ4oKM7wwg==} + '@next/swc-darwin-x64@16.4.2': + resolution: {integrity: sha512-luPCMbOsCoaF+kXJEUjmjmOZg1lC3BflhmUu+ZmW4tBu2vW/4AXak8JBFvePizGkxVmNiAw/r9ra+6UviCWtWQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.5.27': - resolution: {integrity: sha512-GQulAaJYNF1RYuatlsw9/uzbKz4if2V7Ic9nVO+P0ljYPm4yBRTFIl6g5YkKTw40lMLbpy9edLoH44g3jSHMYA==} + '@next/swc-linux-arm64-gnu@16.4.2': + resolution: {integrity: sha512-xhwFTAncJyaCNy5KqKKuKaIIdCy1/1ZV1s5S1G0hhT7wGMkDTlfJ6aQbfRFnIG/s3/kDrxQ8aycVe+c5QnP8ow==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@15.5.27': - resolution: {integrity: sha512-URFFsUmu8Pi0/1qdnUI7tx7keZw1Mt5zHfrIX1RfDG9Nih8DYxK54WZsDJUX2ZTlii1jgotL7qcbwKnSAES6xQ==} + '@next/swc-linux-arm64-musl@16.4.2': + resolution: {integrity: sha512-T1PNmfbPcxr5ndtWUL/3wQtLXsz808lq6sc2GrNT5k2oquF7UyqZddHSuAewHVsWsGXe6nsl9HoN1YNriVjuSg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@15.5.27': - resolution: {integrity: sha512-aJp74z7uvU5jQhyH7lyk+4qSRcTdKbKT9NQE3jiO9kWYkoHZjKi0zzHDbCoZ+v2sJDEiJ/z58R0UlWMGWNF3HA==} + '@next/swc-linux-x64-gnu@16.4.2': + resolution: {integrity: sha512-N9cDUOCGtkJG5PNKDFZUF3xXndrJJcpLp121uAGjIGvO/U2p9tfdNOM9htj7+5f72l0m8rMw/cvYacUJBQoudg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@15.5.27': - resolution: {integrity: sha512-ILl2bQ8MbpvyMpY8AXljb3BXs0MJSC8HXZ6QV5AWpVS7ZELqNPtrHvhUk+SNarvjvICXX8bCS287TCi+gUFVYg==} + '@next/swc-linux-x64-musl@16.4.2': + resolution: {integrity: sha512-yY2uh1YhX6ufuYy2FCbw1dqblbhI57GLYAJfCkqD9auTv6pLrH67Uccjc3QMKBeCYAG6lkgu08ExmX1bbjejjQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@15.5.27': - resolution: {integrity: sha512-1MB9Rh5y8TOPN2x7naNTJNkCZQav9c70AI7EBYXxsEewwOffC4DjjwBwKf20NBvcA+u+5jDv7nVmHxunlP2d7Q==} + '@next/swc-win32-arm64-msvc@16.4.2': + resolution: {integrity: sha512-j/ArWRIHpMMB6wYLMhr2puTguBt70lNU/Cmjl9p6dTDE8DGknYJMP9PdwPk2dqpCNL/KIX9yfngDvIesxJPmsg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.5.27': - resolution: {integrity: sha512-XtYgpGOswgnyBWXzOEG6q9RhvnSw0CEZ0MbWqCsl5PISgt9EzvJvNW5mU5n1PsphX/lnamzxjgQXH2GUJL8nqg==} + '@next/swc-win32-x64-msvc@16.4.2': + resolution: {integrity: sha512-AA4edyWHDko3NCSo0x6GomsdgFb0oR1J3MXU1b5xt6FRFg4AucrMPW3S0hCe/9onU0O1BbpGQBsqo1r0Gneu1g==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@noble/ciphers@1.3.0': - resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} - engines: {node: ^14.21.3 || >=16} - - '@noble/curves@1.9.7': - resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} - engines: {node: ^14.21.3 || >=16} - - '@noble/hashes@1.8.0': - resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} - engines: {node: ^14.21.3 || >=16} - - '@node-minify/core@8.0.6': - resolution: {integrity: sha512-/vxN46ieWDLU67CmgbArEvOb41zlYFOkOtr9QW9CnTrBLuTyGgkyNWC2y5+khvRw3Br58p2B5ZVSx/PxCTru6g==} - engines: {node: '>=16.0.0'} + '@node-minify/core@9.0.2': + resolution: {integrity: sha512-FNhv29Wom6wKrrFKaeAfmZqz7TX5A1E6P+bpd0VIc+DYWMLUIhAViS8riaZg3A1oD0s06s+5BG2Fg7RqMKiKHw==} + engines: {node: '>=18.0.0'} - '@node-minify/terser@8.0.6': - resolution: {integrity: sha512-grQ1ipham743ch2c3++C8Isk6toJnxJSyDiwUI/IWUCh4CZFD6aYVw6UAY40IpCnjrq5aXGwiv5OZJn6Pr0hvg==} - engines: {node: '>=16.0.0'} + '@node-minify/terser@9.0.1': + resolution: {integrity: sha512-WF78Ex+/xNZZDYvzwB7+sLUYQbJzyyS36ZjMhVqhORejHOsoOcTjQ9TdOgKZoY7wlfxjnLxeMKkO8R/R1KL9aQ==} + engines: {node: '>=18.0.0'} - '@node-minify/utils@8.0.6': - resolution: {integrity: sha512-csY4qcR7jUwiZmkreNTJhcypQfts2aY2CK+a+rXgXUImZiZiySh0FvwHjRnlqWKvg+y6ae9lHFzDRjBTmqlTIQ==} - engines: {node: '>=16.0.0'} + '@node-minify/utils@9.0.1': + resolution: {integrity: sha512-aC1+mhKTP3IMa2VcuGl3ui92LO/7CPQWldNGzu3BVGKiMNJ70AKJW/R6huuYCSuQyHDGM9oFwiVClsZnFxn67g==} + engines: {node: '>=18.0.0'} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -1553,19 +1553,19 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@opennextjs/aws@4.1.4': - resolution: {integrity: sha512-L5Yle38qg6p1DY3yS8FXO2840pXW5XlAR6XM71TtIYs0Kh7eQE/a+j4T/DR+VgUQSxbAWw3J2Wnar08xPhOS+w==} + '@opennextjs/aws@4.1.12': + resolution: {integrity: sha512-tZm1rBTRY6MVOOPCxvx0oZIg3b2Z5u0TzDm9D4Wy1cZpMp8Rjfex20gRQeEn0o6dsXpVQCE8q65TjsCFJA5Fpw==} hasBin: true peerDependencies: - next: '>=15.5.24 <16 || >=16.3.3' + next: '>=15.5.28 <16 || >=16.4.1' - '@opennextjs/cloudflare@1.20.6': - resolution: {integrity: sha512-Jxa+Uv72uBMp4JHNMDFuZ5C0ypcnQUmpsp0+aNGfSw6ssb2Hqosk2ebadyvYyh7BkDwcxMkHXGdyJen3Sq0UAw==} + '@opennextjs/cloudflare@1.20.13': + resolution: {integrity: sha512-Rhcvsx/PlQzOslleCBttG+r2o16lOFazKbULxiN33CYexm8S1Fd00mn3tU2w76PIaqO9evzgVKInOpR/fTMc0Q==} hasBin: true peerDependencies: - next: '>=15.5.24 <16 || >=16.3.3' + next: '>=15.5.28 <16 || >=16.4.1' rclone.js: ^0.6.6 - wrangler: ^4.125.0 + wrangler: ^4.148.0 peerDependenciesMeta: rclone.js: optional: true @@ -1573,6 +1573,10 @@ packages: '@oxc-project/types@0.152.0': resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@poppinss/colors@4.1.6': resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} @@ -2374,9 +2378,6 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@rushstack/eslint-patch@1.16.1': - resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} - '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -2547,8 +2548,8 @@ packages: '@speed-highlight/core@1.2.24': resolution: {integrity: sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==} - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} '@t3-oss/env-core@0.12.0': resolution: {integrity: sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw==} @@ -2673,8 +2674,8 @@ packages: '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} - '@tsconfig/node18@1.0.3': - resolution: {integrity: sha512-RbwvSJQsuN9TB04AQbGULYfOGE/RnSFk/FLQ5b0NmDf5Kx2q/lABZbHQPKCO1vZ6Fiwkplu+yb9pGdLy1iGseQ==} + '@tsconfig/node18@18.2.7': + resolution: {integrity: sha512-UyenTORreCUh8MKh2qbLGGHTRrxjBbSwvX0ZoWs6Jx1jrNS62PXEA3+VaLvmb0YDQrgGU3v3h7zB9Xs/BN1ozg==} '@tybys/wasm-util@0.10.4': resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} @@ -3366,10 +3367,6 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - dotenv@17.4.2: resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} engines: {node: '>=12'} @@ -3381,9 +3378,8 @@ packages: duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - eciesjs@0.4.18: - resolution: {integrity: sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==} - engines: {bun: '>=1', deno: '>=2', node: '>=16'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -3394,6 +3390,9 @@ packages: emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -3458,13 +3457,13 @@ packages: resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} engines: {node: '>= 0.4'} - esbuild@0.25.4: - resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} hasBin: true - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} engines: {node: '>=18'} hasBin: true @@ -3479,10 +3478,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@15.5.27: - resolution: {integrity: sha512-SlpZ5Ch1bM+GNHxahIoCy5OVgbpIHzHoGQDjHo6vEhxvYMukWYVo++AEh9RWYaIxH1i38PO7ArzSdmTruswOew==} + eslint-config-next@16.4.2: + resolution: {integrity: sha512-P33vjcETMXc+qBjpF77TeJHZLzk6cPcTy8KjpxLucYB3mCIFIORMcZ+cBXcAiIYjE5RM7jyknEEOwMNdnx/ADQ==} peerDependencies: - eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 + eslint: '>=9.0.0' typescript: '>=3.3.1' peerDependenciesMeta: typescript: @@ -3541,11 +3540,11 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 eslint-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} @@ -3740,9 +3739,6 @@ packages: resolution: {integrity: sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==} engines: {node: '>=14.14'} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -3816,20 +3812,24 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@12.0.0: - resolution: {integrity: sha512-5Qcll1z7IKgHr5g485ePDdHcNQY0k2dtv/bjYy0iuyGxQw2qSOiiXUXJ+AYQpg3HNoUMHqAruX478Jeev7UULw==} - engines: {node: 20 || >=22} - hasBin: true - - glob@9.3.5: - resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + glob@10.3.3: + resolution: {integrity: sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==} engines: {node: '>=16 || 14 >=14.17'} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -3872,6 +3872,12 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hono@4.13.13: resolution: {integrity: sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA==} engines: {node: '>=16.9.0'} @@ -3986,6 +3992,10 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -4116,9 +4126,9 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} - jackspeak@4.2.3: - resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} - engines: {node: 20 || >=22} + jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} @@ -4455,23 +4465,19 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@8.0.7: - resolution: {integrity: sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@4.2.8: - resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} - engines: {node: '>=8'} - minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} engines: {node: '>=10'} hasBin: true @@ -4498,9 +4504,9 @@ packages: resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} engines: {node: '>=18'} - next@15.5.27: - resolution: {integrity: sha512-F82CrlPZ8GRxBH9RIIoR3qU1t5RT3dv1K8moe8SjlDEquiFipvEI13R1Om90TaUOEwShkWCMlg8+gvTNzfMhvg==} - engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + next@16.4.2: + resolution: {integrity: sha512-r+oBJojfefUjOiWkBhXgLS+igxX/7BWyK59uHnyOyplMSLtBZ+QLP3okrdYTbrfrbrmNTVWLBPaHDX9KRaIt3w==} + engines: {node: '>=20.9.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -4647,9 +4653,6 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4732,8 +4735,8 @@ packages: resolution: {integrity: sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==} engines: {node: '>=4'} - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} engines: {node: ^10 || ^12 || >=14} postcss@8.5.28: @@ -5102,6 +5105,14 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} @@ -5204,6 +5215,11 @@ packages: engines: {node: '>=10'} hasBin: true + terser@5.36.0: + resolution: {integrity: sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==} + engines: {node: '>=10'} + hasBin: true + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -5305,6 +5321,10 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici@6.29.1: + resolution: {integrity: sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==} + engines: {node: '>=18.17'} + undici@7.29.1: resolution: {integrity: sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==} engines: {node: '>=20.18.1'} @@ -5518,6 +5538,14 @@ packages: '@cloudflare/workers-types': optional: true + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrap-ansi@9.0.2: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} @@ -5525,6 +5553,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ws@8.21.0: resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} @@ -5584,6 +5616,12 @@ packages: peerDependencies: zod: ^3.25.28 || ^4 + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -5591,44 +5629,44 @@ snapshots: '@alloc/quick-lru@5.3.0': {} - '@ast-grep/napi-darwin-arm64@0.40.5': + '@ast-grep/napi-darwin-arm64@0.45.3': optional: true - '@ast-grep/napi-darwin-x64@0.40.5': + '@ast-grep/napi-darwin-x64@0.45.3': optional: true - '@ast-grep/napi-linux-arm64-gnu@0.40.5': + '@ast-grep/napi-linux-arm64-gnu@0.45.3': optional: true - '@ast-grep/napi-linux-arm64-musl@0.40.5': + '@ast-grep/napi-linux-arm64-musl@0.45.3': optional: true - '@ast-grep/napi-linux-x64-gnu@0.40.5': + '@ast-grep/napi-linux-x64-gnu@0.45.3': optional: true - '@ast-grep/napi-linux-x64-musl@0.40.5': + '@ast-grep/napi-linux-x64-musl@0.45.3': optional: true - '@ast-grep/napi-win32-arm64-msvc@0.40.5': + '@ast-grep/napi-win32-arm64-msvc@0.45.3': optional: true - '@ast-grep/napi-win32-ia32-msvc@0.40.5': + '@ast-grep/napi-win32-ia32-msvc@0.45.3': optional: true - '@ast-grep/napi-win32-x64-msvc@0.40.5': + '@ast-grep/napi-win32-x64-msvc@0.45.3': optional: true - '@ast-grep/napi@0.40.5': + '@ast-grep/napi@0.45.3': optionalDependencies: - '@ast-grep/napi-darwin-arm64': 0.40.5 - '@ast-grep/napi-darwin-x64': 0.40.5 - '@ast-grep/napi-linux-arm64-gnu': 0.40.5 - '@ast-grep/napi-linux-arm64-musl': 0.40.5 - '@ast-grep/napi-linux-x64-gnu': 0.40.5 - '@ast-grep/napi-linux-x64-musl': 0.40.5 - '@ast-grep/napi-win32-arm64-msvc': 0.40.5 - '@ast-grep/napi-win32-ia32-msvc': 0.40.5 - '@ast-grep/napi-win32-x64-msvc': 0.40.5 + '@ast-grep/napi-darwin-arm64': 0.45.3 + '@ast-grep/napi-darwin-x64': 0.45.3 + '@ast-grep/napi-linux-arm64-gnu': 0.45.3 + '@ast-grep/napi-linux-arm64-musl': 0.45.3 + '@ast-grep/napi-linux-x64-gnu': 0.45.3 + '@ast-grep/napi-linux-x64-musl': 0.45.3 + '@ast-grep/napi-win32-arm64-msvc': 0.45.3 + '@ast-grep/napi-win32-ia32-msvc': 0.45.3 + '@ast-grep/napi-win32-x64-msvc': 0.45.3 '@aws-crypto/sha1-browser@5.2.0': dependencies: @@ -6375,18 +6413,6 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@dotenvx/dotenvx@1.31.0': - dependencies: - commander: 11.1.0 - dotenv: 16.6.1 - eciesjs: 0.4.18 - execa: 5.1.1 - fdir: 6.5.0(picomatch@4.0.7) - ignore: 5.3.2 - object-treeify: 1.1.33 - picomatch: 4.0.7 - which: 4.0.0 - '@dotenvx/dotenvx@1.75.1': dependencies: '@dotenvx/primitives': 0.8.0 @@ -6406,11 +6432,25 @@ snapshots: which: 4.0.0 yocto-spinner: 1.2.2 + '@dotenvx/dotenvx@2.38.2': + dependencies: + '@dotenvx/primitives': 3.3.1 + '@dotenvx/providers': 0.3.11 + '@dotenvx/tooling': 1.0.6 + undici: 6.29.1 + write-file-atomic: 5.0.1 + yocto-spinner: 1.2.2 + '@dotenvx/primitives@0.8.0': {} - '@ecies/ciphers@0.2.6(@noble/ciphers@1.3.0)': + '@dotenvx/primitives@3.3.1': {} + + '@dotenvx/providers@0.3.11': dependencies: - '@noble/ciphers': 1.3.0 + '@dotenvx/primitives': 3.3.1 + '@dotenvx/tooling': 1.0.6 + + '@dotenvx/tooling@1.0.6': {} '@emnapi/core@1.10.0': dependencies: @@ -6433,164 +6473,172 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.4': - optional: true - '@esbuild/aix-ppc64@0.28.1': optional: true - '@esbuild/android-arm64@0.25.4': + '@esbuild/aix-ppc64@0.28.2': optional: true '@esbuild/android-arm64@0.28.1': optional: true - '@esbuild/android-arm@0.25.4': + '@esbuild/android-arm64@0.28.2': optional: true '@esbuild/android-arm@0.28.1': optional: true - '@esbuild/android-x64@0.25.4': + '@esbuild/android-arm@0.28.2': optional: true '@esbuild/android-x64@0.28.1': optional: true - '@esbuild/darwin-arm64@0.25.4': + '@esbuild/android-x64@0.28.2': optional: true '@esbuild/darwin-arm64@0.28.1': optional: true - '@esbuild/darwin-x64@0.25.4': + '@esbuild/darwin-arm64@0.28.2': optional: true '@esbuild/darwin-x64@0.28.1': optional: true - '@esbuild/freebsd-arm64@0.25.4': + '@esbuild/darwin-x64@0.28.2': optional: true '@esbuild/freebsd-arm64@0.28.1': optional: true - '@esbuild/freebsd-x64@0.25.4': + '@esbuild/freebsd-arm64@0.28.2': optional: true '@esbuild/freebsd-x64@0.28.1': optional: true - '@esbuild/linux-arm64@0.25.4': + '@esbuild/freebsd-x64@0.28.2': optional: true '@esbuild/linux-arm64@0.28.1': optional: true - '@esbuild/linux-arm@0.25.4': + '@esbuild/linux-arm64@0.28.2': optional: true '@esbuild/linux-arm@0.28.1': optional: true - '@esbuild/linux-ia32@0.25.4': + '@esbuild/linux-arm@0.28.2': optional: true '@esbuild/linux-ia32@0.28.1': optional: true - '@esbuild/linux-loong64@0.25.4': + '@esbuild/linux-ia32@0.28.2': optional: true '@esbuild/linux-loong64@0.28.1': optional: true - '@esbuild/linux-mips64el@0.25.4': + '@esbuild/linux-loong64@0.28.2': optional: true '@esbuild/linux-mips64el@0.28.1': optional: true - '@esbuild/linux-ppc64@0.25.4': + '@esbuild/linux-mips64el@0.28.2': optional: true '@esbuild/linux-ppc64@0.28.1': optional: true - '@esbuild/linux-riscv64@0.25.4': + '@esbuild/linux-ppc64@0.28.2': optional: true '@esbuild/linux-riscv64@0.28.1': optional: true - '@esbuild/linux-s390x@0.25.4': + '@esbuild/linux-riscv64@0.28.2': optional: true '@esbuild/linux-s390x@0.28.1': optional: true - '@esbuild/linux-x64@0.25.4': + '@esbuild/linux-s390x@0.28.2': optional: true '@esbuild/linux-x64@0.28.1': optional: true - '@esbuild/netbsd-arm64@0.25.4': + '@esbuild/linux-x64@0.28.2': optional: true '@esbuild/netbsd-arm64@0.28.1': optional: true - '@esbuild/netbsd-x64@0.25.4': + '@esbuild/netbsd-arm64@0.28.2': optional: true '@esbuild/netbsd-x64@0.28.1': optional: true - '@esbuild/openbsd-arm64@0.25.4': + '@esbuild/netbsd-x64@0.28.2': optional: true '@esbuild/openbsd-arm64@0.28.1': optional: true - '@esbuild/openbsd-x64@0.25.4': + '@esbuild/openbsd-arm64@0.28.2': optional: true '@esbuild/openbsd-x64@0.28.1': optional: true + '@esbuild/openbsd-x64@0.28.2': + optional: true + '@esbuild/openharmony-arm64@0.28.1': optional: true - '@esbuild/sunos-x64@0.25.4': + '@esbuild/openharmony-arm64@0.28.2': optional: true '@esbuild/sunos-x64@0.28.1': optional: true - '@esbuild/win32-arm64@0.25.4': + '@esbuild/sunos-x64@0.28.2': optional: true '@esbuild/win32-arm64@0.28.1': optional: true - '@esbuild/win32-ia32@0.25.4': + '@esbuild/win32-arm64@0.28.2': optional: true '@esbuild/win32-ia32@0.28.1': optional: true - '@esbuild/win32-x64@0.25.4': + '@esbuild/win32-ia32@0.28.2': optional: true '@esbuild/win32-x64@0.28.1': optional: true + '@esbuild/win32-x64@0.28.2': + optional: true + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))': dependencies: eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))': + dependencies: + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} '@eslint/config-array@0.21.2(supports-color@7.2.0)': @@ -6879,7 +6927,14 @@ snapshots: '@img/sharp-win32-x64@0.35.5': optional: true - '@isaacs/cliui@9.0.0': {} + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -6939,56 +6994,51 @@ snapshots: '@tybys/wasm-util': 0.10.4 optional: true - '@next/env@15.5.27': {} + '@next/env@16.4.2': {} - '@next/eslint-plugin-next@15.5.27': + '@next/eslint-plugin-next@16.4.2(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))': dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) fast-glob: 3.3.1 + transitivePeerDependencies: + - eslint - '@next/swc-darwin-arm64@15.5.27': + '@next/swc-darwin-arm64@16.4.2': optional: true - '@next/swc-darwin-x64@15.5.27': + '@next/swc-darwin-x64@16.4.2': optional: true - '@next/swc-linux-arm64-gnu@15.5.27': + '@next/swc-linux-arm64-gnu@16.4.2': optional: true - '@next/swc-linux-arm64-musl@15.5.27': + '@next/swc-linux-arm64-musl@16.4.2': optional: true - '@next/swc-linux-x64-gnu@15.5.27': + '@next/swc-linux-x64-gnu@16.4.2': optional: true - '@next/swc-linux-x64-musl@15.5.27': + '@next/swc-linux-x64-musl@16.4.2': optional: true - '@next/swc-win32-arm64-msvc@15.5.27': + '@next/swc-win32-arm64-msvc@16.4.2': optional: true - '@next/swc-win32-x64-msvc@15.5.27': + '@next/swc-win32-x64-msvc@16.4.2': optional: true - '@noble/ciphers@1.3.0': {} - - '@noble/curves@1.9.7': + '@node-minify/core@9.0.2': dependencies: - '@noble/hashes': 1.8.0 - - '@noble/hashes@1.8.0': {} + '@node-minify/utils': 9.0.1 + glob: 10.3.3 + mkdirp: 3.0.1 - '@node-minify/core@8.0.6': + '@node-minify/terser@9.0.1': dependencies: - '@node-minify/utils': 8.0.6 - glob: 9.3.5 - mkdirp: 1.0.4 + '@node-minify/utils': 9.0.1 + terser: 5.36.0 - '@node-minify/terser@8.0.6': - dependencies: - '@node-minify/utils': 8.0.6 - terser: 5.16.9 - - '@node-minify/utils@8.0.6': + '@node-minify/utils@9.0.1': dependencies: gzip-size: 6.0.0 @@ -7006,40 +7056,40 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} - '@opennextjs/aws@4.1.4(next@15.5.27(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(supports-color@7.2.0)': + '@opennextjs/aws@4.1.12(next@16.4.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(supports-color@7.2.0)': dependencies: - '@ast-grep/napi': 0.40.5 + '@ast-grep/napi': 0.45.3 '@aws-sdk/client-cloudfront': 3.984.0 '@aws-sdk/client-dynamodb': 3.984.0 '@aws-sdk/client-lambda': 3.984.0 '@aws-sdk/client-s3': 3.984.0 '@aws-sdk/client-sqs': 3.984.0 - '@node-minify/core': 8.0.6 - '@node-minify/terser': 8.0.6 - '@tsconfig/node18': 1.0.3 + '@node-minify/core': 9.0.2 + '@node-minify/terser': 9.0.1 + '@tsconfig/node18': 18.2.7 aws4fetch: 1.0.20 chalk: 5.6.2 cookie: 1.1.1 - esbuild: 0.25.4 + esbuild: 0.28.2 express: 5.2.1(supports-color@7.2.0) - next: 15.5.27(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + next: 16.4.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) path-to-regexp: 6.3.0 urlpattern-polyfill: 10.1.0 yaml: 2.9.1 transitivePeerDependencies: - supports-color - '@opennextjs/cloudflare@1.20.6(next@15.5.27(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(supports-color@7.2.0)(wrangler@4.147.0(@types/node@20.19.43))': + '@opennextjs/cloudflare@1.20.13(next@16.4.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(supports-color@7.2.0)(wrangler@4.147.0(@types/node@20.19.43))': dependencies: - '@ast-grep/napi': 0.40.5 - '@dotenvx/dotenvx': 1.31.0 - '@opennextjs/aws': 4.1.4(next@15.5.27(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(supports-color@7.2.0) + '@ast-grep/napi': 0.45.3 + '@dotenvx/dotenvx': 2.38.2 + '@opennextjs/aws': 4.1.12(next@16.4.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(supports-color@7.2.0) ci-info: 4.4.0 cloudflare: 4.5.0 comment-json: 4.6.2 enquirer: 2.4.1 - glob: 12.0.0 - next: 15.5.27(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + glob: 13.0.6 + next: 16.4.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) ts-tqdm: 0.8.6 wrangler: 4.147.0(@types/node@20.19.43) yargs: 18.2.0 @@ -7049,6 +7099,9 @@ snapshots: '@oxc-project/types@0.152.0': {} + '@pkgjs/parseargs@0.11.0': + optional: true + '@poppinss/colors@4.1.6': dependencies: kleur: 4.1.5 @@ -7857,8 +7910,6 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@rushstack/eslint-patch@1.16.1': {} - '@sec-ant/readable-stream@0.4.1': {} '@shadcn/registry@0.1.0(supports-color@7.2.0)(typescript@5.9.3)': @@ -8090,7 +8141,7 @@ snapshots: '@speed-highlight/core@1.2.24': {} - '@swc/helpers@0.5.15': + '@swc/helpers@0.5.23': dependencies: tslib: 2.8.1 @@ -8181,7 +8232,7 @@ snapshots: minimatch: 10.2.6 path-browserify: 1.0.1 - '@tsconfig/node18@1.0.3': {} + '@tsconfig/node18@18.2.7': {} '@tybys/wasm-util@0.10.4': dependencies: @@ -8834,8 +8885,6 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@16.6.1: {} - dotenv@17.4.2: {} dunder-proto@1.0.1: @@ -8846,12 +8895,7 @@ snapshots: duplexer@0.1.2: {} - eciesjs@0.4.18: - dependencies: - '@ecies/ciphers': 0.2.6(@noble/ciphers@1.3.0) - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 + eastasianwidth@0.2.0: {} ee-first@1.1.1: {} @@ -8859,6 +8903,8 @@ snapshots: emoji-regex@10.6.0: {} + emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} encodeurl@2.0.0: {} @@ -8994,34 +9040,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.25.4: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.4 - '@esbuild/android-arm': 0.25.4 - '@esbuild/android-arm64': 0.25.4 - '@esbuild/android-x64': 0.25.4 - '@esbuild/darwin-arm64': 0.25.4 - '@esbuild/darwin-x64': 0.25.4 - '@esbuild/freebsd-arm64': 0.25.4 - '@esbuild/freebsd-x64': 0.25.4 - '@esbuild/linux-arm': 0.25.4 - '@esbuild/linux-arm64': 0.25.4 - '@esbuild/linux-ia32': 0.25.4 - '@esbuild/linux-loong64': 0.25.4 - '@esbuild/linux-mips64el': 0.25.4 - '@esbuild/linux-ppc64': 0.25.4 - '@esbuild/linux-riscv64': 0.25.4 - '@esbuild/linux-s390x': 0.25.4 - '@esbuild/linux-x64': 0.25.4 - '@esbuild/netbsd-arm64': 0.25.4 - '@esbuild/netbsd-x64': 0.25.4 - '@esbuild/openbsd-arm64': 0.25.4 - '@esbuild/openbsd-x64': 0.25.4 - '@esbuild/sunos-x64': 0.25.4 - '@esbuild/win32-arm64': 0.25.4 - '@esbuild/win32-ia32': 0.25.4 - '@esbuild/win32-x64': 0.25.4 - esbuild@0.28.1: optionalDependencies: '@esbuild/aix-ppc64': 0.28.1 @@ -9051,28 +9069,57 @@ snapshots: '@esbuild/win32-ia32': 0.28.1 '@esbuild/win32-x64': 0.28.1 + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + escalade@3.2.0: {} escape-html@1.0.3: {} escape-string-regexp@4.0.0: {} - eslint-config-next@15.5.27(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3): + eslint-config-next@16.4.2(@typescript-eslint/parser@8.71.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3): dependencies: - '@next/eslint-plugin-next': 15.5.27 - '@rushstack/eslint-patch': 1.16.1 - '@typescript-eslint/eslint-plugin': 8.71.0(@typescript-eslint/parser@8.71.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) - '@typescript-eslint/parser': 8.71.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@next/eslint-plugin-next': 16.4.2(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) eslint-import-resolver-node: 0.3.10(supports-color@7.2.0) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.71.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) - eslint-plugin-react-hooks: 5.2.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0) + globals: 16.4.0 + typescript-eslint: 8.71.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: + - '@typescript-eslint/parser' - eslint-import-resolver-webpack - eslint-plugin-import-x - supports-color @@ -9159,9 +9206,16 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@5.2.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)): + eslint-plugin-react-hooks@7.1.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0): dependencies: + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/parser': 7.29.9 eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + hermes-parser: 0.25.1 + zod: 3.25.76 + zod-validation-error: 4.0.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color eslint-plugin-react@7.37.5(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)): dependencies: @@ -9446,8 +9500,6 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true @@ -9524,24 +9576,24 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@12.0.0: + glob@10.3.3: dependencies: foreground-child: 3.3.1 - jackspeak: 4.2.3 - minimatch: 10.2.6 + jackspeak: 2.3.6 + minimatch: 9.0.9 minipass: 7.1.3 - package-json-from-dist: 1.0.1 - path-scurry: 2.0.2 + path-scurry: 1.11.1 - glob@9.3.5: + glob@13.0.6: dependencies: - fs.realpath: 1.0.0 - minimatch: 8.0.7 - minipass: 4.2.8 - path-scurry: 1.11.1 + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 globals@14.0.0: {} + globals@16.4.0: {} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -9577,6 +9629,12 @@ snapshots: dependencies: function-bind: 1.1.2 + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + hono@4.13.13: {} http-errors@2.0.1: @@ -9682,6 +9740,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -9793,9 +9853,11 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 - jackspeak@4.2.3: + jackspeak@2.3.6: dependencies: - '@isaacs/cliui': 9.0.0 + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 jiti@2.7.0: {} @@ -10057,17 +10119,15 @@ snapshots: dependencies: brace-expansion: 1.1.21 - minimatch@8.0.7: + minimatch@9.0.9: dependencies: brace-expansion: 2.1.7 minimist@1.2.8: {} - minipass@4.2.8: {} - minipass@7.1.3: {} - mkdirp@1.0.4: {} + mkdirp@3.0.1: {} mnemonist@0.38.3: dependencies: @@ -10085,24 +10145,25 @@ snapshots: dependencies: content-type: 2.1.0 - next@15.5.27(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + next@16.4.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: - '@next/env': 15.5.27 - '@swc/helpers': 0.5.15 + '@next/env': 16.4.2 + '@swc/helpers': 0.5.23 + baseline-browser-mapping: 2.11.27 caniuse-lite: 1.0.30001814 - postcss: 8.4.31 + postcss: 8.5.23 react: 19.3.0 react-dom: 19.3.0(react@19.3.0) styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.3.0) optionalDependencies: - '@next/swc-darwin-arm64': 15.5.27 - '@next/swc-darwin-x64': 15.5.27 - '@next/swc-linux-arm64-gnu': 15.5.27 - '@next/swc-linux-arm64-musl': 15.5.27 - '@next/swc-linux-x64-gnu': 15.5.27 - '@next/swc-linux-x64-musl': 15.5.27 - '@next/swc-win32-arm64-msvc': 15.5.27 - '@next/swc-win32-x64-msvc': 15.5.27 + '@next/swc-darwin-arm64': 16.4.2 + '@next/swc-darwin-x64': 16.4.2 + '@next/swc-linux-arm64-gnu': 16.4.2 + '@next/swc-linux-arm64-musl': 16.4.2 + '@next/swc-linux-x64-gnu': 16.4.2 + '@next/swc-linux-x64-musl': 16.4.2 + '@next/swc-win32-arm64-msvc': 16.4.2 + '@next/swc-win32-x64-msvc': 16.4.2 sharp: 0.35.5(@types/node@20.19.43) transitivePeerDependencies: - '@babel/core' @@ -10258,8 +10319,6 @@ snapshots: p-try@2.2.0: {} - package-json-from-dist@1.0.1: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -10322,7 +10381,7 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss@8.4.31: + postcss@8.5.23: dependencies: nanoid: 3.3.19 picocolors: 1.1.1 @@ -10824,6 +10883,18 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + string-width@7.2.0: dependencies: emoji-regex: 10.6.0 @@ -10935,6 +11006,14 @@ snapshots: acorn: 8.18.0 commander: 2.20.3 source-map-support: 0.5.21 + optional: true + + terser@5.36.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.18.0 + commander: 2.20.3 + source-map-support: 0.5.21 tiny-invariant@1.3.3: {} @@ -11051,6 +11130,8 @@ snapshots: undici-types@6.21.0: {} + undici@6.29.1: {} + undici@7.29.1: {} undici@7.30.0: {} @@ -11248,6 +11329,18 @@ snapshots: - bufferutil - utf-8-validate + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 @@ -11256,6 +11349,11 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + ws@8.21.0: {} wsl-utils@1.0.1: @@ -11305,4 +11403,8 @@ snapshots: dependencies: zod: 3.25.76 + zod-validation-error@4.0.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4afb325..0df6b8d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,3 +6,7 @@ allowBuilds: sharp: true unrs-resolver: true workerd: true + +minimumReleaseAgeExclude: + - "@opennextjs/aws@4.1.12" + - "@opennextjs/cloudflare@1.20.13" diff --git a/src/app/api/events/[id]/attendance/route.ts b/src/app/api/events/[id]/attendance/route.ts new file mode 100644 index 0000000..14c2561 --- /dev/null +++ b/src/app/api/events/[id]/attendance/route.ts @@ -0,0 +1,30 @@ +import { + checkIn, + listAttendance, + type Attendee, +} from "@/server/data/attendance"; +import { authorize, pageFromRequest } from "@/server/data/shared"; +import { dataError, json, readData } from "@/server/data/api-utils"; + +type Context = { params: Promise<{ id: string }> }; +export async function GET(request: Request, context: Context) { + try { + const { id } = await context.params; + return json({ + attendance: await listAttendance(id, pageFromRequest(request)), + }); + } catch (error) { + return dataError(error); + } +} +export async function POST(request: Request, context: Context) { + try { + await authorize(); + const { id } = await context.params; + return json({ + checkIn: await checkIn(id, (await readData(request)) as Attendee), + }); + } catch (error) { + return dataError(error); + } +} diff --git a/src/app/api/events/[id]/route.ts b/src/app/api/events/[id]/route.ts new file mode 100644 index 0000000..23e0eb1 --- /dev/null +++ b/src/app/api/events/[id]/route.ts @@ -0,0 +1,15 @@ +import { getEvent } from "@/server/data/attendance"; +import { dataError, json } from "@/server/data/api-utils"; + +export async function GET( + _request: Request, + context: { params: Promise<{ id: string }> }, +) { + try { + const { id } = await context.params; + const event = await getEvent(id); + return event ? json({ event }) : json({ error: "Event not found." }, 404); + } catch (error) { + return dataError(error); + } +} diff --git a/src/app/api/events/route.ts b/src/app/api/events/route.ts new file mode 100644 index 0000000..9385fb4 --- /dev/null +++ b/src/app/api/events/route.ts @@ -0,0 +1,26 @@ +import { + createEvent, + listEvents, + type NewEvent, +} from "@/server/data/attendance"; +import { authorize, pageFromRequest } from "@/server/data/shared"; +import { dataError, json, readData } from "@/server/data/api-utils"; + +export async function GET(request: Request) { + try { + return json({ events: await listEvents(pageFromRequest(request)) }); + } catch (error) { + return dataError(error); + } +} +export async function POST(request: Request) { + try { + await authorize(); + return json( + { event: await createEvent((await readData(request)) as NewEvent) }, + 201, + ); + } catch (error) { + return dataError(error); + } +} diff --git a/src/app/api/links/[code]/route.ts b/src/app/api/links/[code]/route.ts new file mode 100644 index 0000000..10fc7ee --- /dev/null +++ b/src/app/api/links/[code]/route.ts @@ -0,0 +1,15 @@ +import { getLink } from "@/server/data/links"; +import { dataError, json } from "@/server/data/api-utils"; + +export async function GET( + _request: Request, + context: { params: Promise<{ code: string }> }, +) { + try { + const { code } = await context.params; + const link = await getLink(code); + return link ? json({ link }) : json({ error: "Link not found." }, 404); + } catch (error) { + return dataError(error); + } +} diff --git a/src/app/api/links/route.ts b/src/app/api/links/route.ts new file mode 100644 index 0000000..e0c2c67 --- /dev/null +++ b/src/app/api/links/route.ts @@ -0,0 +1,22 @@ +import { createLink, listLinks, type NewLink } from "@/server/data/links"; +import { authorize, pageFromRequest } from "@/server/data/shared"; +import { dataError, json, readData } from "@/server/data/api-utils"; + +export async function GET(request: Request) { + try { + return json({ links: await listLinks(pageFromRequest(request)) }); + } catch (error) { + return dataError(error); + } +} +export async function POST(request: Request) { + try { + await authorize(); + return json( + { link: await createLink((await readData(request)) as NewLink) }, + 201, + ); + } catch (error) { + return dataError(error); + } +} diff --git a/src/server/data/api-utils.ts b/src/server/data/api-utils.ts new file mode 100644 index 0000000..9ae95e5 --- /dev/null +++ b/src/server/data/api-utils.ts @@ -0,0 +1,71 @@ +import "server-only"; + +import { DataError } from "./shared"; + +const MAX_RECORD_BYTES = 16_384; + +export function json(data: unknown, status = 200) { + return Response.json(data, { + status, + headers: { "Cache-Control": "no-store" }, + }); +} + +export function dataError(error: unknown) { + if (error instanceof DataError) + return json({ error: error.message }, error.status); + console.error("Project data request failed."); + return json({ error: "Data is temporarily unavailable." }, 503); +} + +export async function readData( + request: Request, +): Promise> { + const origin = request.headers.get("origin"); + if ( + (origin && origin !== new URL(request.url).origin) || + request.headers.get("sec-fetch-site") === "cross-site" + ) { + throw new DataError(403, "Cross-origin writes are not allowed."); + } + if ( + request.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== + "application/json" + ) { + throw new DataError(415, "Use Content-Type: application/json."); + } + const reader = request.body?.getReader(); + if (!reader) throw new DataError(400, "Provide a JSON object."); + const chunks: Uint8Array[] = []; + let size = 0; + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + size += value.byteLength; + if (size > MAX_RECORD_BYTES) { + await reader.cancel(); + throw new DataError(413, "Data must be at most 16 KiB."); + } + chunks.push(value); + } + } finally { + reader.releaseLock(); + } + const body = new Uint8Array(size); + let offset = 0; + for (const chunk of chunks) { + body.set(chunk, offset); + offset += chunk.length; + } + try { + const data: unknown = JSON.parse( + new TextDecoder("utf-8", { fatal: true }).decode(body), + ); + if (!data || typeof data !== "object" || Array.isArray(data)) + throw new Error(); + return data as Record; + } catch { + throw new DataError(400, "Provide a valid JSON object."); + } +} diff --git a/src/server/data/attendance.ts b/src/server/data/attendance.ts new file mode 100644 index 0000000..d3b4648 --- /dev/null +++ b/src/server/data/attendance.ts @@ -0,0 +1,117 @@ +import "server-only"; + +import { z } from "zod"; +import { getDatabase } from "./db"; +import { authorize, DataError, pagination, parse, type Page } from "./shared"; + +const eventIdSchema = z.string().uuid("Use a valid event ID."); +const eventSchema = z + .object({ + name: z.string().trim().min(1).max(200), + starts_at: z + .string() + .datetime({ offset: true }) + .transform((value) => new Date(value).toISOString()), + location: z.string().trim().max(200).optional(), + }) + .strict(); +const attendeeSchema = z + .object({ + name: z.string().trim().min(1).max(200), + email: z + .string() + .trim() + .email() + .max(254) + .transform((value) => value.toLowerCase()), + }) + .strict(); +export type NewEvent = z.input; +export type Attendee = z.input; +export type AttendanceEvent = { + id: string; + name: string; + starts_at: string; + location: string | null; + created_at: string; +}; +export type CheckIn = { + event_id: string; + name: string; + email: string; + checked_in_at: string; +}; + +export async function createEvent(input: NewEvent): Promise { + await authorize(); + const data = parse(eventSchema, input); + const db = await getDatabase(); + const row = await db + .prepare( + "INSERT INTO attendance_events (id, name, starts_at, location) VALUES (?, ?, ?, ?) RETURNING *", + ) + .bind(crypto.randomUUID(), data.name, data.starts_at, data.location ?? null) + .first(); + if (!row) throw new Error("D1 did not return the event."); + return row; +} + +export async function listEvents( + options: Page = {}, +): Promise { + await authorize(); + const { limit, offset } = parse(pagination, options); + const db = await getDatabase(); + const { results } = await db + .prepare( + "SELECT * FROM attendance_events ORDER BY starts_at DESC, id LIMIT ? OFFSET ?", + ) + .bind(limit, offset) + .all(); + return results; +} + +export async function getEvent(id: string): Promise { + await authorize(); + parse(eventIdSchema, id); + const db = await getDatabase(); + return db + .prepare("SELECT * FROM attendance_events WHERE id = ?") + .bind(id) + .first(); +} + +export async function listAttendance( + eventId: string, + options: Page = {}, +): Promise { + if (!(await getEvent(eventId))) throw new DataError(404, "Event not found."); + const { limit, offset } = parse(pagination, options); + const db = await getDatabase(); + const { results } = await db + .prepare( + "SELECT * FROM attendance_check_ins WHERE event_id = ? ORDER BY checked_in_at, email LIMIT ? OFFSET ?", + ) + .bind(eventId, limit, offset) + .all(); + return results; +} + +// Repeated check-ins update the name while preserving the original check-in time. +export async function checkIn( + eventId: string, + input: Attendee, +): Promise { + if (!(await getEvent(eventId))) throw new DataError(404, "Event not found."); + const data = parse(attendeeSchema, input); + const db = await getDatabase(); + const row = await db + .prepare( + `INSERT INTO attendance_check_ins (event_id, name, email) VALUES (?, ?, ?) + ON CONFLICT (event_id, email) DO UPDATE SET name = excluded.name RETURNING *`, + ) + .bind(eventId, data.name, data.email) + .first(); + if (!row) throw new Error("D1 did not return the check-in."); + return row; +} diff --git a/src/server/data/db.ts b/src/server/data/db.ts new file mode 100644 index 0000000..079f554 --- /dev/null +++ b/src/server/data/db.ts @@ -0,0 +1,19 @@ +import "server-only"; + +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +// The small subset of the D1 binding used by this app. +export interface Database { + prepare(sql: string): { + bind(...values: (string | number | null)[]): { + first(): Promise; + all(): Promise<{ results: T[] }>; + }; + }; +} + +export async function getDatabase(): Promise { + const { env } = await getCloudflareContext({ async: true }); + if (!env.DB) throw new Error("D1 binding DB is not configured."); + return env.DB; +} diff --git a/src/server/data/links.ts b/src/server/data/links.ts new file mode 100644 index 0000000..856fc52 --- /dev/null +++ b/src/server/data/links.ts @@ -0,0 +1,78 @@ +import "server-only"; + +import { z } from "zod"; +import { getDatabase } from "./db"; +import { authorize, DataError, pagination, parse, type Page } from "./shared"; + +const codeSchema = z + .string() + .regex( + /^[a-zA-Z0-9_-]{3,64}$/, + "Code must be 3–64 letters, numbers, underscores, or hyphens.", + ); +const linkSchema = z + .object({ + code: codeSchema, + title: z.string().trim().min(1).max(200).optional(), + url: z + .string() + .url() + .max(2048) + .refine((value) => { + const url = URL.parse(value); + return ( + url !== null && + ["http:", "https:"].includes(url.protocol) && + !url.username && + !url.password + ); + }, "Use an HTTP or HTTPS URL without embedded credentials."), + }) + .strict(); +export type NewLink = z.infer; +export type ShortLink = { + code: string; + url: string; + title: string | null; + created_at: string; +}; + +export async function createLink(input: NewLink): Promise { + await authorize(); + const data = parse(linkSchema, input); + const db = await getDatabase(); + const row = await db + .prepare( + `INSERT INTO short_links (code, url, title) VALUES (?, ?, ?) + ON CONFLICT(code) DO NOTHING RETURNING code, url, title, created_at`, + ) + .bind(data.code, data.url, data.title ?? null) + .first(); + if (!row) throw new DataError(409, "That short code is already taken."); + return row; +} + +export async function listLinks(options: Page = {}): Promise { + await authorize(); + const { limit, offset } = parse(pagination, options); + const db = await getDatabase(); + const { results } = await db + .prepare( + "SELECT code, url, title, created_at FROM short_links ORDER BY created_at DESC, code LIMIT ? OFFSET ?", + ) + .bind(limit, offset) + .all(); + return results; +} + +export async function getLink(code: string): Promise { + await authorize(); + parse(codeSchema, code); + const db = await getDatabase(); + return db + .prepare( + "SELECT code, url, title, created_at FROM short_links WHERE code = ?", + ) + .bind(code) + .first(); +} diff --git a/src/server/data/shared.ts b/src/server/data/shared.ts new file mode 100644 index 0000000..9c3a244 --- /dev/null +++ b/src/server/data/shared.ts @@ -0,0 +1,44 @@ +import "server-only"; + +import { z } from "zod"; +import { getClubSession } from "@/server/auth/server"; + +export class DataError extends Error { + constructor( + public status: number, + message: string, + ) { + super(message); + } +} + +export async function authorize() { + if (!(await getClubSession())) throw new DataError(401, "Sign in first."); +} + +export function parse( + schema: z.ZodType, + input: unknown, +): T { + const result = schema.safeParse(input); + if (!result.success) + throw new DataError( + 400, + result.error.issues[0]?.message ?? "Invalid input.", + ); + return result.data; +} + +export const pagination = z.object({ + limit: z.number().int().min(1).max(100).default(50), + offset: z.number().int().min(0).max(1_000_000).default(0), +}); +export type Page = { limit?: number; offset?: number }; + +export function pageFromRequest(request: Request): Page { + const query = new URL(request.url).searchParams; + return { + limit: query.has("limit") ? Number(query.get("limit")) : undefined, + offset: query.has("offset") ? Number(query.get("offset")) : undefined, + }; +} diff --git a/tests/data.test.ts b/tests/data.test.ts new file mode 100644 index 0000000..1ffc361 --- /dev/null +++ b/tests/data.test.ts @@ -0,0 +1,169 @@ +import { readFile } from "node:fs/promises"; +import { afterAll, beforeAll, beforeEach, expect, it, vi } from "vitest"; +import { getPlatformProxy } from "wrangler"; +import type { Database } from "@/server/data/db"; +import { createLink, getLink, listLinks } from "@/server/data/links"; +import { + checkIn, + createEvent, + getEvent, + listAttendance, + listEvents, +} from "@/server/data/attendance"; +import { POST as postLink } from "@/app/api/links/route"; + +const state = vi.hoisted(() => ({ + signedIn: true, + db: null as Database | null, +})); +vi.mock("@/server/auth/server", () => ({ + getClubSession: async () => (state.signedIn ? {} : null), +})); +vi.mock("@/server/data/db", () => ({ getDatabase: async () => state.db })); +let dispose: () => Promise; + +beforeAll(async () => { + const proxy = await getPlatformProxy<{ + DB: Database & { exec(sql: string): Promise }; + }>({ persist: false }); + dispose = proxy.dispose; + state.db = proxy.env.DB; + const migration = await readFile( + new URL("../migrations/0001_links_and_attendance.sql", import.meta.url), + "utf8", + ); + await proxy.env.DB.exec(migration.replaceAll("\n", " ")); +}, 30_000); +afterAll(async () => { + await dispose?.(); +}); +beforeEach(async () => { + state.signedIn = true; + for (const table of [ + "attendance_check_ins", + "attendance_events", + "short_links", + ]) { + await state.db!.prepare(`DELETE FROM ${table}`).bind().first(); + } +}); + +it("persists links and rejects duplicate codes", async () => { + const link = await createLink({ + url: "https://example.com/path?q=1", + code: "workshop", + }); + expect(await getLink("workshop")).toEqual(link); + expect(await listLinks()).toEqual([link]); + await expect( + createLink({ url: "https://example.org", code: "workshop" }), + ).rejects.toMatchObject({ status: 409 }); + expect(await getLink("unknown")).toBeNull(); +}); +it("paginates links", async () => { + const first = await createLink({ + url: "https://example.com", + code: "example-one", + }); + const second = await createLink({ + url: "https://example.org", + code: "example-two", + }); + expect(first.code).not.toBe(second.code); + expect(await listLinks({ limit: 1 })).toHaveLength(1); + expect(await listLinks({ limit: 1, offset: 2 })).toEqual([]); + await expect(listLinks({ limit: 101 })).rejects.toMatchObject({ + status: 400, + }); +}); +it.each([ + "javascript:alert(1)", + "data:text/html,hi", + "https://user:pass@example.com", + "not a url", +])("rejects unsafe or invalid URL %s", async (url) => { + await expect(createLink({ url, code: "unsafe" })).rejects.toMatchObject({ + status: 400, + }); +}); +it("keeps attendance per event and deduplicates email case-insensitively", async () => { + const event = await createEvent({ + name: "Workshop", + starts_at: "2026-10-25T17:00:00-07:00", + }); + const other = await createEvent({ + name: "Meeting", + starts_at: "2026-10-26T00:00:00Z", + }); + expect((await getEvent(event.id))?.starts_at).toBe( + "2026-10-26T00:00:00.000Z", + ); + expect(await listEvents()).toHaveLength(2); + const first = await checkIn(event.id, { + name: "Jane", + email: "JANE@example.com", + }); + const repeated = await checkIn(event.id, { + name: "Jane Doe", + email: "jane@example.com", + }); + expect(repeated.checked_in_at).toBe(first.checked_in_at); + expect(repeated.name).toBe("Jane Doe"); + expect(await listAttendance(event.id)).toEqual([repeated]); + expect(await listAttendance(other.id)).toEqual([]); + await checkIn(other.id, { name: "Jane", email: "jane@example.com" }); + expect(await listAttendance(other.id)).toHaveLength(1); +}); +it("rejects invalid attendance and unknown events", async () => { + await expect( + checkIn(crypto.randomUUID(), { name: "Jane", email: "jane@example.com" }), + ).rejects.toMatchObject({ status: 404 }); + await expect( + createEvent({ name: "", starts_at: "bad date" }), + ).rejects.toMatchObject({ status: 400 }); + const event = await createEvent({ + name: "Meeting", + starts_at: "2026-10-26T00:00:00Z", + }); + await expect( + checkIn(event.id, { name: "Jane", email: "bad" }), + ).rejects.toMatchObject({ status: 400 }); +}); +it("requires login for every private read and write helper", async () => { + state.signedIn = false; + for (const call of [ + () => createLink({ url: "https://example.com", code: "example-one" }), + () => listLinks(), + () => getLink("abc"), + () => createEvent({ name: "Event", starts_at: "2026-10-26T00:00:00Z" }), + () => listEvents(), + () => getEvent(crypto.randomUUID()), + () => listAttendance(crypto.randomUUID()), + () => + checkIn(crypto.randomUUID(), { name: "Jane", email: "jane@example.com" }), + ]) + await expect(call()).rejects.toMatchObject({ status: 401 }); +}); +it("handles JSON, auth, cross-origin writes, and oversized requests", async () => { + const request = (body: string, headers: Record = {}) => + new Request("https://internal.ctcusc.com/api/links", { + method: "POST", + headers: { "Content-Type": "application/json", ...headers }, + body, + }); + expect( + (await postLink(request('{"url":"https://example.com","code":"example"}'))) + .status, + ).toBe(201); + expect((await postLink(request("{"))).status).toBe(400); + expect((await postLink(request("[]"))).status).toBe(400); + expect( + (await postLink(request("{}", { Origin: "https://other.example" }))).status, + ).toBe(403); + expect( + (await postLink(request("{}", { "Content-Type": "text/plain" }))).status, + ).toBe(415); + expect((await postLink(request(" ".repeat(16_385)))).status).toBe(413); + state.signedIn = false; + expect((await postLink(request("{}"))).status).toBe(401); +}); diff --git a/tsconfig.json b/tsconfig.json index 4b9fd9c..b037df3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,19 +8,20 @@ "moduleDetection": "force", "isolatedModules": true, "verbatimModuleSyntax": true, - "strict": true, "noUncheckedIndexedAccess": true, "checkJs": true, - "lib": ["dom", "dom.iterable", "ES2022"], "noEmit": true, "module": "ESNext", "moduleResolution": "Bundler", - "jsx": "preserve", - "plugins": [{ "name": "next" }], + "jsx": "react-jsx", + "plugins": [ + { + "name": "next" + } + ], "incremental": true, - "baseUrl": ".", "paths": { "@/*": ["./src/*"] @@ -32,7 +33,8 @@ "**/*.tsx", "**/*.cjs", "**/*.js", - ".next/types/**/*.ts" + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" ], "exclude": ["node_modules", "generated"] } diff --git a/wrangler.jsonc b/wrangler.jsonc index 3044d76..f96f2a0 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -15,6 +15,14 @@ "simple": { "limit": 100, "period": 60 }, }, ], + "d1_databases": [ + { + "binding": "DB", + "database_name": "ctc-internal", + "database_id": "dded22aa-c431-40e7-bae4-3d62f0540c16", + "migrations_dir": "migrations", + }, + ], "observability": { "enabled": true }, "secrets": { "required": ["SITE_PASSWORD", "SESSION_SECRET"] }, "workers_dev": true, @@ -29,6 +37,8 @@ "previews": { // Service bindings in native previews resolve to production deployments. "services": [], + // Add a dedicated preview database before enabling data features in previews. + "d1_databases": [], "ratelimits": [ { "name": "LOGIN_RATE_LIMITER",