diff --git a/compute/room-booking-prisma-template/AGENTS.md b/compute/room-booking-prisma-template/AGENTS.md index e87c9c07111e..4045662be1c7 100644 --- a/compute/room-booking-prisma-template/AGENTS.md +++ b/compute/room-booking-prisma-template/AGENTS.md @@ -12,7 +12,7 @@ This block is written and re-added by `next dev` — verify at `node_modules/nex A room booking template: Next.js 16 App Router, Resource Scheduler (from npm), Prisma ORM 8, Prisma Postgres, Prisma Compute via Prisma Composer. See README.md. -- Node.js 24 (`.nvmrc`), the version the template is tested on; the Prisma CLI needs 22.18+. Bun must be installed for `npm run dev:composer`, and it does not run on Windows (`prisma dev` returns `DEV.PLATFORM_UNSUPPORTED`): use macOS, Linux, WSL or a Linux container. `prisma dev` runs in the foreground (Ctrl+C stops it); there is no `prisma dev stop`. +- Node.js 24.11.1 or newer (`engines` in `package.json`; `.nvmrc` selects Node 24); the Prisma CLI needs 22.18+. Bun must be installed for `npm run dev:composer`, and it does not run on Windows (`prisma dev` returns `DEV.PLATFORM_UNSUPPORTED`): use macOS, Linux, WSL or a Linux container. `prisma dev` runs in the foreground (Ctrl+C stops it); there is no `prisma dev stop`. - Deploy is `npm run deploy` (`next build`, then `prisma deploy module.ts`). There is no `prisma.compute.ts`, `defineComputeConfig` or `compute:deploy`: that flow was removed. Do not add them. A deploy without `--stage` is production; ask first. `npx prisma auth login` needs a person at a browser. - Booking rules live in one place, `src/lib/rules.ts`, used by the browser (``) and the server (`isPlacementAllowed` in `src/server/bookings.ts`). Keep them independent of the machine's time zone (`utcToZoned(date, OFFICE_TIME_ZONE)`). - Every booking write locks the room row(s) inside `db.transaction` before checking; keep the lock order (booking, then rooms sorted by id). Prisma ORM 8 has no isolation-level option. diff --git a/compute/room-booking-prisma-template/README.md b/compute/room-booking-prisma-template/README.md index 8513fecd6297..e7a0f2e3c633 100644 --- a/compute/room-booking-prisma-template/README.md +++ b/compute/room-booking-prisma-template/README.md @@ -12,20 +12,22 @@ production-ready starter built end to end on the Prisma platform: - Six room plates saying which rooms are free right now, above a timeline of the day's bookings colored by floor + The booking board in its glass theme: a Find a time panel beside a day timeline of rooms grouped by floor, each with its status and a utilization bar, above cards for the stack it is built with ## What it does -- A plate per room, like the sign by a meeting room door, says whether it is free right now and until when. **Book now** takes the current half hour. -- Rooms are rows, half hours from 08:00 to 18:00 are columns, grouped by floor. Each floor has a color, on its plates and its bookings, and every room shows how much of its day is booked. +- Rooms are rows, grouped by floor, and half hours from 08:00 to 18:00 are columns, in a day view or a week view. Each room says, like the sign by a meeting room door, whether it is free right now and until when (`Free until 6:00 PM`, `In use until 3:30 PM`), and a utilization bar shows how much of its day is booked. +- **Find a time** sits beside the timeline: pick a length (30 minutes to 2 hours) and a group size, and it suggests the first free time of every room that fits. Picking one opens the booking form. - Drag across free time and your own booking form opens (`onSlotSelect`). Drag a booking to move it, drag its edge to resize it, click it to cancel it. +- Export the bookings on screen, or a single booking, as a calendar file (`.ics`), or print the page. +- A glass theme in light and dark: frosted layers over soft fields of color, built from the design tokens at the top of `src/app/globals.css`, with the scheduler's `--rs-*` variables mapped onto them. It follows the system setting, and the theme button switches it. - A taken slot, a time outside opening hours, or one that has already started turns red before you drop (`eventOverlap`, `isValidDrop`), so most mistakes never reach the server. - The server checks again inside a transaction, with the same rules, so two people booking the same slot at the same moment cannot both win. The loser's change rolls back on screen with a message that says what is in the way. - Times are shown and checked on the office's clock (`Europe/Berlin` by default), whatever zone the visitor or the server is in. -- An empty database gets six demo rooms and a day of bookings on the first visit. +- An empty database gets six demo rooms and a day of bookings on the first visit. After that, a day the board opens on with no bookings at all gets the same day of demo bookings, so the demo never shows an empty timeline; a day with any booking is left alone. ## How double booking is prevented @@ -49,7 +51,7 @@ The tests prove each layer separately, including a race where 25 requests book t ## Run it locally -You need **Node.js 24** (`.nvmrc`, the version this template is tested on; the Prisma CLI itself needs 22.18 or newer) and **[Bun](https://bun.sh)**, because Prisma Compute runs your app on Bun and so does its local emulator. +You need **Node.js 24.11.1 or newer** (`engines` in `package.json`; `.nvmrc` selects Node 24 for `nvm use`; the Prisma CLI itself needs 22.18 or newer) and **[Bun](https://bun.sh)**, because Prisma Compute runs your app on Bun and so does its local emulator. The local emulator runs on macOS and Linux only: `prisma dev` stops on Windows with `local dev is not supported on Windows yet`. On Windows, run it in WSL or a Linux container, or use [hot reload with your own database](#with-hot-reload-and-your-own-database) below. @@ -107,7 +109,7 @@ There is no `prisma.compute.ts` or `compute:deploy` script. That was the Prisma | --- | --- | --- | | `DATABASE_URL` | `.env.local`, local only | The database for `npm run dev`, `npm run db:*`. Not used on Prisma Compute, where the app gets its database from Composer (`service.load()`). | | `TEST_DATABASE_URL` | shell or `.env.local` | A disposable PostgreSQL database for the integration tests. | -| `PRISMA_SERVICE_TOKEN`, `PRISMA_WORKSPACE_ID` | CI secrets only | To run `prisma deploy` in CI without a connected repository: another CI system, or the `prisma-deploy` workflow before the repository is connected (see Troubleshooting). | +| `PRISMA_SERVICE_TOKEN` | CI secrets only | To run `prisma deploy` in CI without a connected repository: another CI system, or the `prisma-deploy` workflow before the repository is connected (see Troubleshooting). Add `PRISMA_WORKSPACE_ID` only when the token can see more than one workspace. | **Host and port:** the app needs no settings. Prisma Compute sets `PORT`, and the Next.js standalone server it runs listens on `PORT` and on `HOSTNAME`, which defaults to `0.0.0.0` (all interfaces), as Compute requires. Do not add `HOSTNAME` or `HOST` to the project's environment variables: a server bound to `localhost` deploys fine but cannot be reached. @@ -136,14 +138,20 @@ next.config.ts output: "standalone", which Prisma Compute runs migrations/ planned migrations, applied by every deploy (init adds the exclusion constraint) src/ app/ - page.tsx server component: rooms and the day's bookings, seeds an empty database + page.tsx server component: rooms and the day's bookings, seeds an empty database or day board.tsx client component: , optimistic updates and rollback booking-dialog.tsx the booking form opened by onSlotSelect + find-time.tsx "Find a time": the first free slot of each room that fits + room-status.tsx what each room's row says: free until, in use until + theme-toggle.tsx the light and dark switch + about.tsx the "Built with" cards + globals.css the glass theme: design tokens, then components api/bookings/route.ts GET (a date range) and POST api/bookings/[id]/route.ts PATCH (move, resize) and DELETE lib/ shared by browser and server office.ts office time zone, opening hours, slot length rules.ts bookingRules(): the placement rules both sides run + free-slots.ts the free-time search behind "Find a time" validation.ts zod schemas for every request api-types.ts, api-client.ts what crosses the network server/ @@ -169,7 +177,7 @@ docker run -d --name rooms-test -p 5433:5432 -e POSTGRES_PASSWORD=postgres postg TEST_DATABASE_URL="postgresql://postgres:postgres@localhost:5433/postgres" npm test ``` -They apply the migrations, then check create, move and cancel, 25 simultaneous bookings of one slot (exactly one wins), a create and a move racing, bookings swapping rooms without deadlocks, the room lock alone (constraint dropped), the constraint alone, and concurrent seeding. +They apply the migrations, then check create, move and cancel, 25 simultaneous bookings of one slot (exactly one wins), a create and a move racing, bookings swapping rooms without deadlocks, the room lock alone (constraint dropped), the constraint alone, and concurrent seeding of the database and of an empty day. ## Make it yours @@ -185,7 +193,7 @@ They apply the migrations, then check create, move and cancel, 25 simultaneous b - **`alchemy: node is not supported (module.registerHooks needs node 22.15+, 23.5+, or 24+)`** from `npm run dev:composer` or `npm run deploy`: switch to Node 24 (`nvm use`, the repository has an `.nvmrc`). - **`local dev is not supported on Windows yet`** (`DEV.PLATFORM_UNSUPPORTED`) from `npm run dev:composer`: Prisma's local emulator does not run on Windows. Run it in WSL or a Linux container, or use `npm run dev` with a `DATABASE_URL`. -- **The `prisma-deploy` run is green but nothing deployed** (its log shows `skipped-no-credential` and "This repository is not connected to a Prisma workspace"): the repository is not connected to a Prisma project, so the workflow gets no credential. Copies made with the Deploy button currently end up like this. Connect the repository as in [Deploy to Prisma Compute](#deploy-to-prisma-compute) and push again, or deploy with a service token: create one in the Prisma Console, add it and the workspace ID (from the workspace settings) as the Actions secrets `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID`, pass them to the deploy step in `.github/workflows/prisma-deploy.yml`, and push: +- **The `prisma-deploy` run is green but nothing deployed** (its log shows `skipped-no-credential` and "This repository is not connected to a Prisma workspace"): the repository is not connected to a Prisma project, so the workflow gets no credential. Connect the repository as in [Deploy to Prisma Compute](#deploy-to-prisma-compute) and push again, or deploy with a service token: create one in the Prisma Console, add it as the Actions secret `PRISMA_SERVICE_TOKEN`, pass it to the deploy step in `.github/workflows/prisma-deploy.yml` (the secret alone is not enough: a re-run without the `env:` lines still skips), and push: ```yaml - uses: prisma/cloud-deploy-action@v1 @@ -193,10 +201,9 @@ They apply the migrations, then check create, move and cancel, 25 simultaneous b build-command: npm run build env: PRISMA_SERVICE_TOKEN: ${{ secrets.PRISMA_SERVICE_TOKEN }} - PRISMA_WORKSPACE_ID: ${{ secrets.PRISMA_WORKSPACE_ID }} ``` - The token can deploy anything in the workspace, so keep it in secrets only. Once that deploy has created the project, `npx prisma project link room-booking-prisma-template` and `npx prisma git connect` switch the workflow to deploys without a token; then remove the `env:` lines and the two secrets. + The token can deploy anything in the workspace, so keep it in secrets only. Once that deploy has created the project, `npx prisma project link room-booking-prisma-template` and `npx prisma git connect` switch the workflow to deploys without a token; then remove the `env:` lines and the secret. - **The deployed URL answers 504:** `next.config.ts` must keep `output: "standalone"`. - **`HostedStateBootstrapError` on deploy:** a project with the module's name already exists in your workspace and was created from another checkout. Rename the module in `module.ts`, or deploy with `--name `. - **The integration tests are skipped:** set `TEST_DATABASE_URL` (see Tests). diff --git a/compute/room-booking-prisma-template/docs/screenshot-dark.png b/compute/room-booking-prisma-template/docs/screenshot-dark.png index ef7faf6a6c14..b431904226f2 100644 Binary files a/compute/room-booking-prisma-template/docs/screenshot-dark.png and b/compute/room-booking-prisma-template/docs/screenshot-dark.png differ diff --git a/compute/room-booking-prisma-template/docs/screenshot-light.png b/compute/room-booking-prisma-template/docs/screenshot-light.png index 5747fbb14a6c..d7fc929d1fc1 100644 Binary files a/compute/room-booking-prisma-template/docs/screenshot-light.png and b/compute/room-booking-prisma-template/docs/screenshot-light.png differ diff --git a/compute/room-booking-prisma-template/package-lock.json b/compute/room-booking-prisma-template/package-lock.json index 6846b847200f..56c8c833a4a7 100644 --- a/compute/room-booking-prisma-template/package-lock.json +++ b/compute/room-booking-prisma-template/package-lock.json @@ -9,11 +9,10 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@prisma/composer": "0.29.1", - "@prisma/composer-prisma-cloud": "0.29.1", - "@prisma/orm-postgres": "8.0.0-rc.17", - "alchemy": "2.0.0-beta.78", - "effect": "4.0.0-rc.115", + "@prisma/composer": "0.35.0", + "@prisma/composer-prisma-cloud": "0.35.0", + "@prisma/orm-postgres": "8.0.0-rc.20", + "alchemy": "2.0.0-beta.83", "next": "16.4.2", "react": "19.3.1", "react-dom": "19.3.1", @@ -22,13 +21,16 @@ "zod": "^4.6.7" }, "devDependencies": { + "@effect/platform-bun": "4.0.3", + "@effect/platform-node": "4.0.3", + "@effect/platform-node-shared": "4.0.3", "@prisma/dev": "0.25.2", "@types/node": "^26.6.5", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", "eslint": "^10.13.0", "eslint-config-next": "16.4.2", - "prisma": "8.0.0-rc.22", + "prisma": "8.0.0-rc.28", "tsx": "^4.23.15", "typescript": "^6.0.3", "vitest": "^5.0.5" @@ -38,26 +40,27 @@ } }, "node_modules/@alchemy.run/cloudflare-runtime": { - "version": "2.0.0-beta.78", - "resolved": "https://registry.npmjs.org/@alchemy.run/cloudflare-runtime/-/cloudflare-runtime-2.0.0-beta.78.tgz", - "integrity": "sha512-fsNiicISo7gOFPwrfAbRQAfB8RUSwfsFtgb7n54NSinqARxiULJ4vCyn7RuLrZ9d32S0wt6/QwuNAeliGvulfw==", + "version": "2.0.0-beta.83", + "resolved": "https://registry.npmjs.org/@alchemy.run/cloudflare-runtime/-/cloudflare-runtime-2.0.0-beta.83.tgz", + "integrity": "sha512-tchwc16KccR3Q8fmEZSqJcXRPJ0mV0Y3LEu7VbdE6R3e0VqKFNnz5xYiCfQWUAahB+QtJDtTu3/nkjOajE3ggQ==", "license": "Apache-2.0", "dependencies": { - "@alchemy.run/node-utils": "2.0.0-beta.78", + "@alchemy.run/node-utils": "2.0.0-beta.83", "@cloudflare/unenv-preset": "^2.16.1", "@puppeteer/browsers": "^3.2.0", "capnp-es": "^0.0.16", "magic-string": "^0.30.21", + "mime": "^4.0.7", "sharp": "^0.35.3", "unenv": "^2.0.0-rc.24", - "workerd": "1.20260901.1", + "workerd": "1.20261002.1", "yauzl": "^3.4.0" }, "peerDependencies": { - "@distilled.cloud/cloudflare": "1.0.0-rc.12", - "@effect/platform-bun": ">=4.0.0-rc.115 || >=4.0.0", - "@effect/platform-node": ">=4.0.0-rc.115 || >=4.0.0", - "effect": ">=4.0.0-rc.115 || >=4.0.0", + "@distilled.cloud/cloudflare": "1.0.0-rc.15", + "@effect/platform-bun": "^4.0.0", + "@effect/platform-node": "^4.0.0", + "effect": "^4.0.0", "rolldown": "1.2.5", "vite": "^7.0.0 || ^8.0.0" }, @@ -77,18 +80,18 @@ } }, "node_modules/@alchemy.run/floci": { - "version": "2.0.0-beta.78", - "resolved": "https://registry.npmjs.org/@alchemy.run/floci/-/floci-2.0.0-beta.78.tgz", - "integrity": "sha512-f+XDNleOUF/OU2GKMGGz8x674Ejg/cUf+xKeNByrTNok8OYFN8kO1Cev9gFiFB3JZjW6o6hARBb3MDEuYRAyDg==", + "version": "2.0.0-beta.83", + "resolved": "https://registry.npmjs.org/@alchemy.run/floci/-/floci-2.0.0-beta.83.tgz", + "integrity": "sha512-QVWGFvko0QYEMsR298PW8OQ/TkyRoEnLlz5iGNXar/wdBC1FNrBHHMmfG7pY9Vc5BbCR1WtggD/ZYUwtyyUgQw==", "license": "Apache-2.0", "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@alchemy.run/node-utils": { - "version": "2.0.0-beta.78", - "resolved": "https://registry.npmjs.org/@alchemy.run/node-utils/-/node-utils-2.0.0-beta.78.tgz", - "integrity": "sha512-tNp202VlDbN7ONlx7Zv7dDbicZerZUOzyfi1RNtTH/POXe+BaDA+Tl5CvhNCsn3l/adWpqNc/3jkQIh47K1Q0w==", + "version": "2.0.0-beta.83", + "resolved": "https://registry.npmjs.org/@alchemy.run/node-utils/-/node-utils-2.0.0-beta.83.tgz", + "integrity": "sha512-UiRTmZN95URcMgQQtuatgaV2YcYZ5TDoZtRgnd7Kz7R7pCeFCUhJXM2ZYwM32j3wqnObThpiFKFDJmqDsrosIA==", "license": "Apache-2.0", "dependencies": { "chokidar": "^5.0.0", @@ -96,15 +99,10 @@ } }, "node_modules/@alchemy.run/sigil": { - "version": "0.0.0-alpha.11", - "resolved": "https://registry.npmjs.org/@alchemy.run/sigil/-/sigil-0.0.0-alpha.11.tgz", - "integrity": "sha512-JzaCBeRu9kY0qWC8JKkrierstImkrK77dl+7l0RvCTR+7uhOJECL2VxmjhJ1blea+mmbYUVob8OXOH5r8Mqfpw==", + "version": "0.1.0-alpha.1", + "resolved": "https://registry.npmjs.org/@alchemy.run/sigil/-/sigil-0.1.0-alpha.1.tgz", + "integrity": "sha512-RtHELG2w+GluVewRsuKm66idtPzC+4STptMr/61X1Vy5uBkDzcCzjzV8G6jUa4iBv6Il0hHV7UL6wvv//9nD/Q==", "license": "MIT", - "dependencies": { - "react": "^19.3.0", - "react-reconciler": "^0.34.0", - "scheduler": "^0.28.0" - }, "engines": { "node": ">=22" }, @@ -136,302 +134,6 @@ "integrity": "sha512-XLiQyGfpJcws1zCtp2Cva8s1oheCdFn1Rt6JSyC4NnkDrpwCJnlaiawuuOc1Z97vMH+jEzW5vyyf0xOju5wUQA==", "license": "MIT" }, - "node_modules/@aws-sdk/core": { - "version": "3.978.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.6.tgz", - "integrity": "sha512-ykF/MiYoM15dgZwF/WLgjJXVbAK2wU6vjPigeEnOHwyUJ0rdlraO5uekecKNVPkvOnz+oju/5zljViQs1nI1FQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.975.0", - "@aws-sdk/xml-builder": "^3.972.41", - "@aws/lambda-invoke-store": "^0.3.0", - "@smithy/core": "^3.37.0", - "@smithy/signature-v4": "^5.7.4", - "@smithy/types": "^4.19.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.972.76", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.76.tgz", - "integrity": "sha512-p5RrwE/jGpCJ+JmXy6hv2ULpvLYIe2648tK29uKaCa5sM2NmaM7iCS9Z/ZG2jq6qCvNbdl/bBT1/AME+mAJaJw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/nested-clients": "^3.997.51", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.77", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.77.tgz", - "integrity": "sha512-CN6dQCDo32a8n2gvm2HBeqGV5BNI9noG96nTj5bGO2HWtt0Rucu/A8pcnJIqkzILOHSuilnCk0Do8/7h6SOXhQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.79", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.79.tgz", - "integrity": "sha512-Netsu0nnmvP2HiN1ZFQBeUu+JehQIuubqz+j2kHDgeSE4HDzniCpIZ7RzoWABq+LTcbCi9W03hywifUagoVIWQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/fetch-http-handler": "^5.8.0", - "@smithy/node-http-handler": "^4.13.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.973.22", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.22.tgz", - "integrity": "sha512-x7/5h58cqOoZR0RWUZC7v/cJOADQR42iSIQkvU+00HVHR4jh8ZpqEgRPGf2zei59iH5qP40lL3bkq49PuWl71g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/credential-provider-env": "^3.972.77", - "@aws-sdk/credential-provider-http": "^3.972.79", - "@aws-sdk/credential-provider-login": "^3.972.84", - "@aws-sdk/credential-provider-process": "^3.972.77", - "@aws-sdk/credential-provider-sso": "^3.973.21", - "@aws-sdk/credential-provider-web-identity": "^3.972.83", - "@aws-sdk/nested-clients": "^3.997.51", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/credential-provider-imds": "^4.5.2", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.84", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.84.tgz", - "integrity": "sha512-G9Ty1uIV0laEvEp2KiMbEhsuBAjgaD3KYrwVjAM5VUxpcIuPBqQI5riA7/qzrnSgYiAKQbubbgkwcqCICKCIxg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/nested-clients": "^3.997.51", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.89", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.89.tgz", - "integrity": "sha512-O8xj7/sanrnXfFHu/LUujVBiiwn5mknYenU7xt1w6+yvhR1QoDZWXLFh1gLITaFVPtb1jWgEirC+kFNw+//tyw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.77", - "@aws-sdk/credential-provider-http": "^3.972.79", - "@aws-sdk/credential-provider-ini": "^3.973.22", - "@aws-sdk/credential-provider-process": "^3.972.77", - "@aws-sdk/credential-provider-sso": "^3.973.21", - "@aws-sdk/credential-provider-web-identity": "^3.972.83", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/credential-provider-imds": "^4.5.2", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.77", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.77.tgz", - "integrity": "sha512-sWA8Jm+ASzkTxoqLizHWRbt8Gj3yZCmZ0hYdvg0uIxuLzWseYcfrDFzaSnkRWlEWSJV38rAWFimdrN0J4NR7Pg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.973.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.21.tgz", - "integrity": "sha512-KV2LM8fbYUhDzGEBnPwb+ISNfNlhmnpLS+y6A6VsJpg1EI9aUJ0+dRTxh2GrGdneFH0T4TiuboHxz8i6+M38JA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/nested-clients": "^3.997.51", - "@aws-sdk/token-providers": "3.1157.0", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.83", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.83.tgz", - "integrity": "sha512-iekYilmo2Re51fC7gMkEfZ7jFDXwehSCmXjDESaYQsdgYhsLQFG7rTYr3GBvRIyj+Fkda2NxbZX7hAZDaFD0JQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/nested-clients": "^3.997.51", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers": { - "version": "3.1157.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1157.0.tgz", - "integrity": "sha512-9VQagtAZ/5HZfjxaOBponZTQ3yScvkHe93yDUROBVGnB4PM0nZZofwtDdyZqzb4jSHa5m03c7NmGSqniB8n5nQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/credential-provider-cognito-identity": "^3.972.76", - "@aws-sdk/credential-provider-env": "^3.972.77", - "@aws-sdk/credential-provider-http": "^3.972.79", - "@aws-sdk/credential-provider-ini": "^3.973.22", - "@aws-sdk/credential-provider-login": "^3.972.84", - "@aws-sdk/credential-provider-node": "^3.972.89", - "@aws-sdk/credential-provider-process": "^3.972.77", - "@aws-sdk/credential-provider-sso": "^3.973.21", - "@aws-sdk/credential-provider-web-identity": "^3.972.83", - "@aws-sdk/nested-clients": "^3.997.51", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/credential-provider-imds": "^4.5.2", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.51", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.51.tgz", - "integrity": "sha512-DJK4iGIHWU8Jh8ODMvqFYcX9yGBMXV4tSZAvqlHF5EDd8hbmF3eFO9KIWOnA7wcQjPzEkyMMW1/FgZ9RU8oimQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/signature-v4-multi-region": "^3.996.49", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/fetch-http-handler": "^5.8.0", - "@smithy/node-http-handler": "^4.13.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.49", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.49.tgz", - "integrity": "sha512-nvuAZNRrRRVCiARRKsYlQ0VnXiEQiq7vXPH+UA+LSo+kN3Djl9eo/V2r0udkw85PnsNshmv2Em1clld5M3j67w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.975.0", - "@smithy/signature-v4": "^5.7.4", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.1157.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1157.0.tgz", - "integrity": "sha512-AeAzcwWRP2uANO2TjFpKjRdvOCzgfIu6YQl9Ceibd6E0gnq5dfOzujFnq2aeqgbLg4wRtXbUZE4gekCCPrz/OQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.978.6", - "@aws-sdk/nested-clients": "^3.997.51", - "@aws-sdk/types": "^3.975.0", - "@smithy/core": "^3.37.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.975.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.975.0.tgz", - "integrity": "sha512-WQlhs6juByzWOzj5fwxOx0mPJaEqMcG/sDpl3Id61B3XhKE63xY+0QWcnG+sWR/sCfprmreyUo1lM7IKZmHMzw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.41.tgz", - "integrity": "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws/lambda-invoke-store": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", - "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -773,9 +475,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260901.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260901.1.tgz", - "integrity": "sha512-5nJCpnclV6cc5v3aZOewu+djFvNvm9A/owQ+WJ2G1450roeFHfZGsyW7vVjSRWx3v2iykfkviyJfGm9ApeFDGg==", + "version": "1.20261002.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20261002.1.tgz", + "integrity": "sha512-PW3AYxxO6W2cCYCPrx0ZOqivb/2/R2zGtPgNiUgzZBGDl2MtVxWKvzDDXvTGVCmuL4aiTCziNcZktDpCcR7NnQ==", "cpu": [ "x64" ], @@ -789,9 +491,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260901.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260901.1.tgz", - "integrity": "sha512-nLwfUVoYmFNW1Ntp0BfZ2K1UWIll4zTTYP8Ni7NQnyrNkTnFeU/prrllWtnw7OG8Z/jyaEAAFaDFEBobrNTU3A==", + "version": "1.20261002.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20261002.1.tgz", + "integrity": "sha512-vv6t7a11NBEX9eS9mfMRvUymJszXVnnWCH8VEq1f65sxsFli3O4sI4pzFcIvG+P1BKVZZDBv4gwvxWLQXuM37g==", "cpu": [ "arm64" ], @@ -805,9 +507,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260901.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260901.1.tgz", - "integrity": "sha512-wzCO0cMrFLRRs6bG5KXDpLSe70GpzpsxPjxwBzQngQOfyI2cxs5i4Rb5rDB0GG8E19feZEhjGXcHgY9+5Lq8YA==", + "version": "1.20261002.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20261002.1.tgz", + "integrity": "sha512-uOU1eCBoDnR4CQLkm4tqBlM1lHMgzW14VNEWWVOohi/qvmbLAwapwyLDCytxpb4U55+mXgcMFc91y+Ibh1EssA==", "cpu": [ "x64" ], @@ -821,9 +523,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260901.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260901.1.tgz", - "integrity": "sha512-aBk82+sSphMa6uR6h/CFN/GXmELJ2YOgQKYSsOsCTvbVLvHjclmyoEdLTY5FEImvo1ch1uXpGQgTjI8/suHGEg==", + "version": "1.20261002.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20261002.1.tgz", + "integrity": "sha512-p3Zcrj4EAYc1+yDUjRZ3ygjPUj/8Dg03ux+QyfL0UT5d1OG0BJnBSto2OVJuGQ3J3cCRsjhjYs7gzB727eKBTw==", "cpu": [ "arm64" ], @@ -837,9 +539,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260901.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260901.1.tgz", - "integrity": "sha512-3oJnCV7mWwzqmqz+3E+KYLbw1FylH+8n+p84srWv9MEsY9II1QuKiRKVf3CQTIdTDb6rkr5fCuRtkmLgRpAsNg==", + "version": "1.20261002.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20261002.1.tgz", + "integrity": "sha512-S9lnFnKRUNZ6umIBjCCbdfo5cTk1fR7SKi2BcQuCUKTmOzAjLsIFV49yiwPLSH33c0TDUJn8eavX4S7bORwfBA==", "cpu": [ "x64" ], @@ -853,222 +555,288 @@ } }, "node_modules/@cloudflare/workers-types": { - "version": "5.20261027.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-5.20261027.1.tgz", - "integrity": "sha512-ZYhpdIJbEzQL68kwHRTq/B0hyBsxB7wXR17LmUln7ReWE2kxQ1LD//aTM1K8Jk/MuNBeQY66QLc9izUIOLWWyg==", + "version": "5.20261102.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-5.20261102.1.tgz", + "integrity": "sha512-rRNznLGla+8vRdToTN/5M+AzlopILSLLZ3qo2L5Ku/dY792iOlX/K9ZLkLnLYji1jycv7+FB12hiE+fFjVgMrw==", "license": "MIT OR Apache-2.0" }, + "node_modules/@distilled.cloud/acme": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/acme/-/acme-1.0.0-rc.15.tgz", + "integrity": "sha512-SzQZoPTonWolW6yHartHKIc1sEOlrnq3XdCfCT6RUXb1iAK0YyboRE0BgROzhg4FfxqSypb1BBB0D0vwhm+qjQ==", + "license": "Apache-2.0", + "dependencies": { + "@distilled.cloud/core": "1.0.0-rc.15" + }, + "peerDependencies": { + "effect": "^4.0.0" + } + }, "node_modules/@distilled.cloud/aws": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/aws/-/aws-1.0.0-rc.12.tgz", - "integrity": "sha512-L4TTEwMKB1ymXbYUqvYeoy5UWgmJtqQSzry2qWtAvD2ccwVGy0F7cFViEIYQk7boF4ZIP6SfhAls4+y1fVIi/g==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/aws/-/aws-1.0.0-rc.15.tgz", + "integrity": "sha512-5VBbmBxTLrTe0/MhcDqul0+rsptUc5rpPR2ymI6v+I//FOLE/x/2YlX3utm6ziGacTEeZaVpwdxhkt6SdJ28Pw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.974.3", - "@distilled.cloud/core": "1.0.0-rc.12", - "@smithy/shared-ini-file-loader": "^4.7.0", - "@smithy/types": "^4.17.2", - "@smithy/util-base64": "^4.6.0", - "fast-xml-parser": "^5.10.1" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/axiom": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/axiom/-/axiom-1.0.0-rc.12.tgz", - "integrity": "sha512-W/Kx0qKXh/RF5bJeLNr73BTLV51AFf7zsP8pG9wv2yGrA8sPj4Xu2z72sBG+TeQZIhNwA8Rh4J8GotNqH/380g==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/axiom/-/axiom-1.0.0-rc.15.tgz", + "integrity": "sha512-R4jcq23GdjcNVwJXwSqKmT13ME6k0Zz14i7VOiqj8dYa2jIMyNIDXqHY3bHiq3+x2vimCJ3B2VlgstoXd8G8sw==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/cloudflare": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/cloudflare/-/cloudflare-1.0.0-rc.12.tgz", - "integrity": "sha512-ddcL4RnAx6L8gRWCnE5T1W35ZlerI7SlaRLidX726iG/K/aA+bkU0ND39y7ljJ2Eyx3zR03TfpNrgXAXc9E3ng==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/cloudflare/-/cloudflare-1.0.0-rc.15.tgz", + "integrity": "sha512-UBnEpZGkf+lnMF33IJXcu7DD99vC+3314N01pnWNuKxLtsVNNLF/DxFbPxBU1Cb+aLE3Cs35aSiHZrYO7EiR0g==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/core": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/core/-/core-1.0.0-rc.12.tgz", - "integrity": "sha512-YeH/uPohGXp2qIctbqVzBtQV+bUs+K5Zdac8qIid7/t6VlwY964SALme5znRdFu/rQgrloXnaej8iTSK9RIQdA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/core/-/core-1.0.0-rc.15.tgz", + "integrity": "sha512-TPXxuwcxQ85Pvm0g7ZQSLTQkPc1Wmcb6p6KOk+Be2fP14Xpj5a+7HOSUyPNwbqOP+vPK5Sj0ZLN7M6spKnO2LA==", + "license": "Apache-2.0", + "peerDependencies": { + "effect": "^4.0.0" + } + }, + "node_modules/@distilled.cloud/doppler": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/doppler/-/doppler-1.0.0-rc.15.tgz", + "integrity": "sha512-w72a8B07IQhGEG09Cg9OZuJbCEV/Qjf7DNRI6r5+ATN88575gv8xOFyAi7DuwfbR245BLN8VGEgZ7I0exMMEmw==", "license": "Apache-2.0", "dependencies": { - "graphql": "16.11.0" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/fly-io": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/fly-io/-/fly-io-1.0.0-rc.12.tgz", - "integrity": "sha512-ACgR8Kcx/OP8gAF+PnXYneGGZNUqyiQHrcTB3/lRcv5gQdacXhTAvreV/d0t7jclRo5BBdNxnd5TuuHpr9vDLA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/fly-io/-/fly-io-1.0.0-rc.15.tgz", + "integrity": "sha512-lQ1DuktDE+69snRw/ZIZ1Tlc6fVx9gNlCIHX2CA8p8DVtpIg389vligmfFDAtmyQVnix/0ZJydo5OFnk1Ip53A==", + "license": "Apache-2.0", + "dependencies": { + "@distilled.cloud/core": "1.0.0-rc.15" + }, + "peerDependencies": { + "effect": "^4.0.0" + } + }, + "node_modules/@distilled.cloud/gcp": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/gcp/-/gcp-1.0.0-rc.15.tgz", + "integrity": "sha512-NTxSKZWmFa+psZZh/aTOIKVlayU2SIlgOKEqA2R80Ys3fcI0JVXjfkCPLfMURfV19YDjJ6jbWQ4cV+xqSC/x1w==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/hetzner": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/hetzner/-/hetzner-1.0.0-rc.12.tgz", - "integrity": "sha512-G6QzOXTNply63I90IjOtNofs0nHkEd1we/fgXiOjobLZt4+sKjjNBVTAHYHGMDeidbtxw270oF9JakgT11Pzww==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/hetzner/-/hetzner-1.0.0-rc.15.tgz", + "integrity": "sha512-lUPogzlQeMGp29thsiF7bDBsRfnN5PzTqiujdDc/ZjWDBpq3teudkzbdMMsL12RB5qvB9xb3INwi9JWqv1lQcQ==", + "license": "Apache-2.0", + "dependencies": { + "@distilled.cloud/core": "1.0.0-rc.15" + }, + "peerDependencies": { + "effect": "^4.0.0" + } + }, + "node_modules/@distilled.cloud/iceberg": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/iceberg/-/iceberg-1.0.0-rc.15.tgz", + "integrity": "sha512-qNLBVGyuIP/OdgvRht41u/QPOEvJ2kUntC7++bL6YGPbB6TPbDDPd72xeP/gXogIgLsQj8RP1gETjJCQREtl2Q==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" + } + }, + "node_modules/@distilled.cloud/infisical": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/infisical/-/infisical-1.0.0-rc.15.tgz", + "integrity": "sha512-nDN7k8cxXkKkgFL42u3YSNySpjzGWhy2QgRzLbweSVxmhNNjIpxAsW/ct8HUf4Y9CM9Pg8KtQItsMyf8C+NgGg==", + "license": "Apache-2.0", + "dependencies": { + "@distilled.cloud/core": "1.0.0-rc.15" + }, + "peerDependencies": { + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/neon": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/neon/-/neon-1.0.0-rc.12.tgz", - "integrity": "sha512-jiypsVQCcKa9qNgG9HULXNgXj1E4FFO96Hk9p2JX6CEHca7ePldJSJEOf7roogQGC7lrnldBDCiZrxQLOapD3Q==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/neon/-/neon-1.0.0-rc.15.tgz", + "integrity": "sha512-lRZMqLuYNALiMEjJRISKyeiF/7SVW8F4JeTfqk/QAl0MY5SxJvKpkgrCoKNj2kVI06IH1D8iTt3sxf5syXRLEQ==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/planetscale": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/planetscale/-/planetscale-1.0.0-rc.12.tgz", - "integrity": "sha512-YCTB7soZtqzzgcCAUtrNHjAIsq92XS8cpjnbn3VU+sCQ0LnfnFGeMGJxQ/7qLHtx7Hv4+Ms8XNOs2Q5P81Skig==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/planetscale/-/planetscale-1.0.0-rc.15.tgz", + "integrity": "sha512-7ZghpGNi63Wfp6tU17Oex7aRjZ5O+ARFfl071S3JaeC7ube13Ji6LS/H10MG+DMpCq/vFVDtVB2ZNtevUcAI3w==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/prisma": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/prisma/-/prisma-1.0.0-rc.12.tgz", - "integrity": "sha512-Z1KyUrhxkUt0KdxmNUd29Dtl9NizKv7HFHUuTcA1j6Ar5apUMvlR0f4lKqpliXW8Om3oVS3Wgvm+gDsxul2Ojg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/prisma/-/prisma-1.0.0-rc.15.tgz", + "integrity": "sha512-we2sbP5XHuq2XXi1NOp7ierK8apy3wZOrzIw2bi8CidQYn8OWCUIi9IgUBX6V3kCT1iRq0Y1FwiW6c9jJwtGsw==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/railway": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/railway/-/railway-1.0.0-rc.12.tgz", - "integrity": "sha512-KOXSpl7r9qTiOYabJ6wUmM418exzv6YE5BVztaqKSGMuNGP/lAqhZm709/p97Zg6vHpxhJLIgmCq00z7hRM3sQ==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/railway/-/railway-1.0.0-rc.15.tgz", + "integrity": "sha512-Z/ebbgxozAuj9vog8Ar05BoFrQwKuKKGsocsLpMlQr26VT+x9GL71P3uAhpF+Fhg7FFS3NjYB9R6MAIxPP62tA==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@distilled.cloud/stripe": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@distilled.cloud/stripe/-/stripe-1.0.0-rc.12.tgz", - "integrity": "sha512-4OuZF362yU66G1jDFDgRFFv8ZNRPZ6lw8Wrd5R/isRNHpGgvggLNlKg1QXWEQaTcRzn3RBXczqQCVwQV9IRZDQ==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/stripe/-/stripe-1.0.0-rc.15.tgz", + "integrity": "sha512-sYIPEg1MKZrj6aSy4o08anO5dVpyFJHzgEk0WfiMm5BT0oEFtldUuPe/FjEOoY+mFvZ+o+26lJ5voEnWLJSCUA==", + "license": "Apache-2.0", + "dependencies": { + "@distilled.cloud/core": "1.0.0-rc.15" + }, + "peerDependencies": { + "effect": "^4.0.0" + } + }, + "node_modules/@distilled.cloud/turso": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/turso/-/turso-1.0.0-rc.15.tgz", + "integrity": "sha512-hvudbvmdatgi198Exlid3zgF/WGBD3UDa0A2F4QPD3IYR08Y+O5ZFcovPcV841JDIREJN0hKmgsrmpoFczxbDA==", + "license": "Apache-2.0", + "dependencies": { + "@distilled.cloud/core": "1.0.0-rc.15" + }, + "peerDependencies": { + "effect": "^4.0.0" + } + }, + "node_modules/@distilled.cloud/zerossl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@distilled.cloud/zerossl/-/zerossl-1.0.0-rc.15.tgz", + "integrity": "sha512-lu69h33Ib18kATq0W1c1zw6CH3YkUvKWGj1QUi8lbuINXPXHb2/88ATzehDQr5GM/qxtO+azgglhs/NVOf4UVw==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/core": "1.0.0-rc.12" + "@distilled.cloud/core": "1.0.0-rc.15" }, "peerDependencies": { - "effect": ">=4.0.0-rc.115 || >=4.0.0" + "effect": "^4.0.0" } }, "node_modules/@effect/platform-bun": { - "version": "4.0.0-rc.115", - "resolved": "https://registry.npmjs.org/@effect/platform-bun/-/platform-bun-4.0.0-rc.115.tgz", - "integrity": "sha512-ilTFcs66ghInP8WHCJYa4bx/yzohYM9dRR//qjmVBBpSKu1hrCTiRWpEcc1N6zrnOEPk2kyVaKg5ykSMgiBMqQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@effect/platform-bun/-/platform-bun-4.0.3.tgz", + "integrity": "sha512-Pl2TXjRTRRvGxtGfYWW8L+B1qkZo8mEx7Itvo2lUd15OQPZGDE111S6JDm9EgGfDK3BUz3sbyNeQD687GQm2iQ==", "license": "MIT", "dependencies": { - "@effect/platform-node-shared": "^4.0.0-rc.115" + "@effect/platform-node-shared": "^4.0.3" }, "peerDependencies": { - "effect": "^4.0.0-rc.115" + "effect": "^4.0.3" } }, "node_modules/@effect/platform-node": { - "version": "4.0.0-rc.115", - "resolved": "https://registry.npmjs.org/@effect/platform-node/-/platform-node-4.0.0-rc.115.tgz", - "integrity": "sha512-mW03Wjq2FiTL5oi9yOkaKpCyUNBaduKgTk7qMHNELvC21Jq2qOAfdsfH+p+s2zmpB9w2RSa+ApODv4WjO9wQfw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@effect/platform-node/-/platform-node-4.0.3.tgz", + "integrity": "sha512-ctvREpC1EzRVsOm0JyIzYHajAQScFaec6Gl+M7TEIpEqdmmx6Ju11Um92EA/CE2KBfY5Fp1QWlqTkK6lOL7tqg==", "license": "MIT", "dependencies": { - "@effect/platform-node-shared": "^4.0.0-rc.115", - "undici": "^8.10.2" + "@effect/platform-node-shared": "^4.0.3", + "undici": "^8.11.2" }, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "effect": "^4.0.0-rc.115", + "effect": "^4.0.3", "redis": ">=5.0.0 <7.0.0" } }, "node_modules/@effect/platform-node-shared": { - "version": "4.0.0-rc.115", - "resolved": "https://registry.npmjs.org/@effect/platform-node-shared/-/platform-node-shared-4.0.0-rc.115.tgz", - "integrity": "sha512-rcBwhDIfb82akJoTv7Vwh8K5x9vC/XfuePS7JjOr+z+dORXzsmKqI1BUQF27ARCN9fTAbDh7I6YvBlWhLjnqNg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@effect/platform-node-shared/-/platform-node-shared-4.0.3.tgz", + "integrity": "sha512-5srkJfmWklWSgwZwlkMeTXobfNtxuwuTCvY9JWdPXRv/PBXAe5kKSptzRxT778oFFsaE9IMI8X61pvkifT9Eug==", "license": "MIT", "dependencies": { - "@types/ws": "^8.18.1", - "ws": "^8.21.3" + "@types/ws": "^8.18.2", + "ws": "^8.22.0" }, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "effect": "^4.0.0-rc.115" + "effect": "^4.0.3" } }, "node_modules/@effect/sql-d1": { - "version": "4.0.0-rc.115", - "resolved": "https://registry.npmjs.org/@effect/sql-d1/-/sql-d1-4.0.0-rc.115.tgz", - "integrity": "sha512-o2JkGjN02NBMxwDtJbwcC4teyMiOApqN085fdVMid4FUC6ixj/WyKyPpe51QGOMQQTTl2BkXccA/IwDIywGXlA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@effect/sql-d1/-/sql-d1-4.0.3.tgz", + "integrity": "sha512-PPItxp9aAmJTRIfZtyJMuQCB7d1ExFvIRkGWnfQuWZlPxjtYfQT+V2kS9AUqf0OC1/nZnAFwLlFKCDgumLmy6A==", "license": "MIT", "dependencies": { - "@cloudflare/workers-types": "^5.20260905.1" + "@cloudflare/workers-types": "^5.20261003.1" }, "peerDependencies": { - "effect": "^4.0.0-rc.115" + "effect": "^4.0.3" } }, "node_modules/@effect/sql-sqlite-do": { - "version": "4.0.0-rc.115", - "resolved": "https://registry.npmjs.org/@effect/sql-sqlite-do/-/sql-sqlite-do-4.0.0-rc.115.tgz", - "integrity": "sha512-7lH9h+w4DW2tI+dus4hEac5mSCy1wgiT+uw/VvjR8YhNDemd3zEFY03NkbD81ipfOpnu2QA1YwgM58ExyPlhug==", - "license": "MIT", - "peerDependencies": { - "effect": "^4.0.0-rc.115" - } - }, - "node_modules/@effect/vitest": { - "version": "4.0.0-rc.115", - "resolved": "https://registry.npmjs.org/@effect/vitest/-/vitest-4.0.0-rc.115.tgz", - "integrity": "sha512-DJR0hfdr3HNXAkzW8AzfvUz6VmrNFLrKk9tZGDURi33faMyxkc7ZewxELMMZwGpOTGN4mfqYVbX2CXgI+ZJBeQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@effect/sql-sqlite-do/-/sql-sqlite-do-4.0.3.tgz", + "integrity": "sha512-q3DeEnsmAbD2jQX9g3+U0iij7VFVgzPtSLoOwbB9oegeBkzzBXVDrblAvw2NgEfs0OctkaIkJhiYJROqSafQKA==", "license": "MIT", "peerDependencies": { - "effect": "^4.0.0-rc.115", - "vitest": ">=5.0.0 <6.0.0" + "effect": "^4.0.3" } }, "node_modules/@electric-sql/pglite": { @@ -1881,6 +1649,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1897,6 +1668,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1913,6 +1687,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1929,6 +1706,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1945,6 +1725,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1961,6 +1744,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1977,6 +1763,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1993,6 +1782,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2009,6 +1801,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2031,6 +1826,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2053,6 +1851,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2075,6 +1876,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2097,6 +1901,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2119,6 +1926,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2141,6 +1951,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2163,6 +1976,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2517,7 +2333,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@manypkg/tools/-/tools-2.1.2.tgz", "integrity": "sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "jju": "^1.4.0", @@ -2604,6 +2420,23 @@ "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==", "license": "MIT" }, + "node_modules/@neon/functions": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@neon/functions/-/functions-0.11.0.tgz", + "integrity": "sha512-xyIEk9aULR2nBTzyTgxljkM8aS+Ppe9oVw/t5mdqYOlY/mLFppHSZHq8hiWVDWy6Ca8oLd3loTtfVz+cZQQjsA==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "hono": "^4.7.8" + }, + "peerDependenciesMeta": { + "hono": { + "optional": true + } + } + }, "node_modules/@next/env": { "version": "16.4.2", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.4.2.tgz", @@ -2692,6 +2525,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2708,6 +2544,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2724,6 +2563,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2740,6 +2582,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2793,18 +2638,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nodable/entities": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.1.0.tgz", - "integrity": "sha512-LsS/DjHr+uDM647Gru/cA8+J3a3HfhttwCKLyuoyN7yXTFCxKBtSgMQBvrW9yNPa2/zDRUNuGPaH5QUFoa4arQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], - "license": "MIT" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3077,9 +2910,9 @@ } }, "node_modules/@prisma/cli-engine": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@prisma/cli-engine/-/cli-engine-0.7.0.tgz", - "integrity": "sha512-CL21NYQQEAR9IEApPfUzMhv/VtbsJs4efRSxlvzHoFT69vynr0Wuu5KNNbZgIlONOEfULqP0djFCD4VaA/HVPg==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@prisma/cli-engine/-/cli-engine-0.9.0.tgz", + "integrity": "sha512-M53Xx/KiPQ02iT8FXT2c9X+3fVgMVfWK/emf/tq+jRNFi7b0Ffwaeo1yz8XS4UKOza5DaKRwMs+K8U44z9corg==", "license": "Apache-2.0", "dependencies": { "@clack/prompts": "1.5.0", @@ -3098,81 +2931,92 @@ } }, "node_modules/@prisma/composer": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/@prisma/composer/-/composer-0.29.1.tgz", - "integrity": "sha512-g9iyBsYNHKJXEo2bCd7er46rlP015vHlSVV8Qwexo/X55zclv4IYWjGVctA0ugoeTllCy5/dBb6K22Z8Dpf+Mw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@prisma/composer/-/composer-0.35.0.tgz", + "integrity": "sha512-qQZY57p6JpnJxiuZ/EWxC2VBJIW3UqA7FCbuCRCh2coecojDAPhEQ4NaBac7JhpFJxWZpsaXmzGznF5sX53lSQ==", "license": "Apache-2.0", "dependencies": { - "@effect/sql-d1": "4.0.0-rc.115", - "@effect/sql-sqlite-do": "4.0.0-rc.115", - "@effect/vitest": "4.0.0-rc.115", + "@effect/sql-d1": "4.0.3", + "@effect/sql-sqlite-do": "4.0.3", + "@effect/vitest": "4.0.3", "@prisma/management-api-sdk": "^1.80.0", "@standard-schema/spec": "^1.1.0", - "alchemy": "2.0.0-beta.78", + "alchemy": "2.0.0-beta.83", "arktype": "^2.2.3", "cross-spawn": "^7.0.6", - "effect": "4.0.0-rc.115", + "effect": "4.0.3", "esbuild": "^0.28.1" }, "engines": { - "node": ">=22.18.0" + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" } }, "node_modules/@prisma/composer-cli": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/@prisma/composer-cli/-/composer-cli-0.29.1.tgz", - "integrity": "sha512-59e2vWCLF8Y3pTJhAHZofww8bmVWKdgr9u8ALk8nVMrSbNIDGhND/UccBa03KX6tQvCiBtbhEtpmxv5Db3kM+A==", - "dev": true, + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@prisma/composer-cli/-/composer-cli-0.35.0.tgz", + "integrity": "sha512-CjWQzfx5AjTYnAoLpF/zxlQgIUvDsnydb4kl79XiwoxeAqd7mLnedecFFHL7AQAoJRmohGkDft5WDPvMnuGR0Q==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/composer": "0.29.1", - "alchemy": "2.0.0-beta.78", + "@prisma/composer": "0.35.0", + "@prisma/management-api-sdk": "^1.80.0", + "alchemy": "2.0.0-beta.83", "cross-spawn": "^7.0.6", - "effect": "4.0.0-rc.115", + "effect": "4.0.3", "esbuild": "^0.28.1" }, "engines": { - "node": ">=22.18.0" + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" }, "peerDependencies": { - "@prisma/cli-engine": "0.7.0" + "@prisma/cli-engine": "0.9.0" } }, "node_modules/@prisma/composer-prisma-cloud": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/@prisma/composer-prisma-cloud/-/composer-prisma-cloud-0.29.1.tgz", - "integrity": "sha512-wp3PxvKxp0EwwSi/9Vr/M4V66x47uaMK8wQEb32E6SYXMRJvuNKhsakdvWQiA2r23KMQWH0qEFQcq6KiUPt0nw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@prisma/composer-prisma-cloud/-/composer-prisma-cloud-0.35.0.tgz", + "integrity": "sha512-TuvTJU8PNT6UYXcyiAkp4E0ofiHk12o5N+E0yktjld2TZiLZvNJFhVGBIQFZrmr8o6YaZVZyL3X8dAa3AsBd8A==", "license": "Apache-2.0", "dependencies": { - "@distilled.cloud/prisma": "1.0.0-rc.12", - "@effect/platform-bun": "4.0.0-rc.115", - "@effect/platform-node": "4.0.0-rc.115", - "@effect/platform-node-shared": "4.0.0-rc.115", - "@prisma/composer": "0.29.1", + "@distilled.cloud/prisma": "1.0.0-rc.15", + "@effect/platform-bun": "4.0.3", + "@effect/platform-node": "4.0.3", + "@effect/platform-node-shared": "4.0.3", + "@prisma/composer": "0.35.0", "@prisma/dev": "^0.25.2", "@prisma/management-api-sdk": "^1.80.0", - "@prisma/orm-toolchain": "8.0.0-rc.17", + "@prisma/orm-toolchain": "8.0.0-rc.20", "@standard-schema/spec": "^1.1.0", - "alchemy": "2.0.0-beta.78", + "alchemy": "2.0.0-beta.83", "arktype": "^2.2.3", - "effect": "4.0.0-rc.115", + "effect": "4.0.3", "jose": "^6.1.3", "pathe": "^2.0.3", "pg": "8.22.0", "tsdown": "^0.22.7" }, "engines": { - "node": ">=22.18.0" + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" }, "peerDependencies": { - "@prisma/orm-postgres": "8.0.0-rc.17" + "@prisma/orm-postgres": "8.0.0-rc.20" + } + }, + "node_modules/@prisma/composer/node_modules/@effect/vitest": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@effect/vitest/-/vitest-4.0.3.tgz", + "integrity": "sha512-jsIkpssf0CFuOeycW2sQZkWx0qzlsRv+JIkzd9qQpoFDcHC9JZFSdIjEBAhsdp20pDy4Y/b2vT/ew7wXZxvEig==", + "license": "MIT", + "peerDependencies": { + "effect": "^4.0.3", + "vitest": ">=5.0.0 <6.0.0" } }, "node_modules/@prisma/compute-sdk": { "version": "0.43.0", "resolved": "https://registry.npmjs.org/@prisma/compute-sdk/-/compute-sdk-0.43.0.tgz", "integrity": "sha512-q5Tf5hpDFZmcS4eNeH7IU8OnavToP9h4NsrDXjW7uIZf/pjaQIWItQJi9DZHPkH8n/SvT/oHQrmzPsJ9NPW/hg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@vercel/nft": "^1.10.2", @@ -3248,13 +3092,13 @@ } }, "node_modules/@prisma/orm-family-sql": { - "version": "8.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@prisma/orm-family-sql/-/orm-family-sql-8.0.0-rc.17.tgz", - "integrity": "sha512-KbdapnNyIX44hwgxvBV9Zl8qjIbYAm6VgM7uW7u6fDlx1QP41L7s5INxaADdoceqgWTo/KUqVKLpMq7RcGByzg==", + "version": "8.0.0-rc.20", + "resolved": "https://registry.npmjs.org/@prisma/orm-family-sql/-/orm-family-sql-8.0.0-rc.20.tgz", + "integrity": "sha512-DkfzI4FZnIrbsbk0yWrlSZZnAj73MenOxgxsSzADy4ThBTAtg1lZ3RbiBFDTMgBHiZUTELdnVjsIf2YloHXh9g==", "license": "Apache-2.0", "dependencies": { - "@prisma/orm-framework": "8.0.0-rc.17", - "@prisma/orm-toolchain": "8.0.0-rc.17", + "@prisma/orm-framework": "8.0.0-rc.20", + "@prisma/orm-toolchain": "8.0.0-rc.20", "@standard-schema/spec": "^1.1.0", "arktype": "^2.2.2", "pathe": "^2.0.3", @@ -3272,9 +3116,9 @@ } }, "node_modules/@prisma/orm-framework": { - "version": "8.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@prisma/orm-framework/-/orm-framework-8.0.0-rc.17.tgz", - "integrity": "sha512-KfircGILFXGoBPPg6I4caNF/tpVe+2UF6kMO+PF/KfOwMsFh11mR5nEijiSqy+5l7oVWGsT0GNz2inysbg+UWg==", + "version": "8.0.0-rc.20", + "resolved": "https://registry.npmjs.org/@prisma/orm-framework/-/orm-framework-8.0.0-rc.20.tgz", + "integrity": "sha512-yxeW/KdIRLj9lmxLr2PhvaDPTRpO9B6blGyW39W3SALPaFJ5riimvixbgQco7jyfTotqwTRIStCSUzJdsaDnCg==", "license": "Apache-2.0", "dependencies": { "@standard-schema/spec": "^1.1.0", @@ -3293,15 +3137,15 @@ } }, "node_modules/@prisma/orm-postgres": { - "version": "8.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@prisma/orm-postgres/-/orm-postgres-8.0.0-rc.17.tgz", - "integrity": "sha512-1jwZH/APF9222PFufvvcBoQXcfEXjmXjqJ1uEU7PMUaxHbL1FwwDcFxHmjujSt9mFOopGSvr5b5BkIjNWbUinw==", + "version": "8.0.0-rc.20", + "resolved": "https://registry.npmjs.org/@prisma/orm-postgres/-/orm-postgres-8.0.0-rc.20.tgz", + "integrity": "sha512-axn65mmR6RQDWCn8ScgEdKZi8ib1dZdPm0WH2VmwgtAw9jTijgsX+7qX4C+NPPHZ58DenIbrPza+1I1bFCF+iw==", "license": "Apache-2.0", "dependencies": { - "@prisma/orm-family-sql": "8.0.0-rc.17", - "@prisma/orm-framework": "8.0.0-rc.17", - "@prisma/orm-target-postgres": "8.0.0-rc.17", - "@prisma/orm-toolchain": "8.0.0-rc.17", + "@prisma/orm-family-sql": "8.0.0-rc.20", + "@prisma/orm-framework": "8.0.0-rc.20", + "@prisma/orm-target-postgres": "8.0.0-rc.20", + "@prisma/orm-toolchain": "8.0.0-rc.20", "@types/node": "26.1.2", "@types/pg": "8.20.4", "arktype": "^2.2.2", @@ -3335,14 +3179,14 @@ "license": "MIT" }, "node_modules/@prisma/orm-target-postgres": { - "version": "8.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@prisma/orm-target-postgres/-/orm-target-postgres-8.0.0-rc.17.tgz", - "integrity": "sha512-SAE42aFV8ulzNmkJFKk16zVa3CxjTroGllBTkGBgnC2zEiNnBebIXHo7DmSWC70iMM6qFSDL0g2pmwAMwDIGEg==", + "version": "8.0.0-rc.20", + "resolved": "https://registry.npmjs.org/@prisma/orm-target-postgres/-/orm-target-postgres-8.0.0-rc.20.tgz", + "integrity": "sha512-7ixyhfbn6SCEtC8jmVN/jSsIXBlp/gGvIDOkTLXxjM9XXWa334HYDy/l4J3BuDGLJHle5f4MXceEdVlJTCXj3w==", "license": "Apache-2.0", "dependencies": { - "@prisma/orm-family-sql": "8.0.0-rc.17", - "@prisma/orm-framework": "8.0.0-rc.17", - "@prisma/orm-toolchain": "8.0.0-rc.17", + "@prisma/orm-family-sql": "8.0.0-rc.20", + "@prisma/orm-framework": "8.0.0-rc.20", + "@prisma/orm-toolchain": "8.0.0-rc.20", "@standard-schema/spec": "^1.1.0", "@types/node": "26.1.2", "@types/pg": "8.20.4", @@ -3378,12 +3222,12 @@ "license": "MIT" }, "node_modules/@prisma/orm-toolchain": { - "version": "8.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@prisma/orm-toolchain/-/orm-toolchain-8.0.0-rc.17.tgz", - "integrity": "sha512-iG1knDO9Hg1/b5NOj74T7Bx5d2imJVq+qSrKDD94nwjOyvIZ5ygKSzVq2hb0KoAjae8PONgznSOBRMA8Kh+p4Q==", + "version": "8.0.0-rc.20", + "resolved": "https://registry.npmjs.org/@prisma/orm-toolchain/-/orm-toolchain-8.0.0-rc.20.tgz", + "integrity": "sha512-MQnUgGmzOYDE4QJiyY8p612cRLFjQmV8fl7HL+rCxSK02Sm4HhTRIG6UFIKWcvfh5hAeR9rkJiOS/WLApZOTHA==", "license": "Apache-2.0", "dependencies": { - "@prisma/orm-framework": "8.0.0-rc.17", + "@prisma/orm-framework": "8.0.0-rc.20", "@vercel/detect-agent": "^1.2.4", "arktype": "^2.2.2", "c12": "^3.3.4", @@ -3404,7 +3248,7 @@ "wrap-ansi": "^10.0.0" }, "peerDependencies": { - "@prisma/cli-engine": "0.7.0", + "@prisma/cli-engine": "0.9.0", "typescript": ">=5.9", "vite": "^7.0.0 || ^8.0.0" }, @@ -4191,6 +4035,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4207,6 +4054,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4223,6 +4073,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4239,6 +4092,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4255,6 +4111,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4271,6 +4130,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4357,138 +4219,31 @@ } } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sec-ant/readable-stream": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sindresorhus/merge-streams": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@smithy/core": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.37.0.tgz", - "integrity": "sha512-97imTcCyiPd8k0e9+ts/Sp0W65Dn9Ti357J7M09Ov4tI8Q1JDJVFSoAJtjEVKqfHEr3Pdo2jfolqKCaaiszH0g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz", - "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.33.2", - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz", - "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.18.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.13.0.tgz", - "integrity": "sha512-/GGcPoNJ2nzGytdAanpbx7mpp9Coa8LP+/Rpmbktr8YeLuIXs9B0SEz+X3AeonHdyzOIFJdKbN7jOZ+Tj+pMGw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.37.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.7.2.tgz", - "integrity": "sha512-XsIDj5gVG4YRxGS4n4TiBDAogPWRHXKZyor6JW/sEuaa/7BvKADe9j45jI2dPYCnYbKkLBmbZ4qN9ns0sH+kMQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.33.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.4.tgz", - "integrity": "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.35.0", - "@smithy/types": "^4.19.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.19.0.tgz", - "integrity": "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.6.2.tgz", - "integrity": "sha512-wTQX1hPfElIqY6AzM/s6c4UgpMxCL4MwJ5u6340ksLPq78lur6Y+keheIDk5gMX4G3KFh4MERcDt6xhRq+ie1Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.33.2", - "tslib": "^2.6.2" - }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@standard-schema/spec": { @@ -4970,6 +4725,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4984,6 +4742,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4998,6 +4759,9 @@ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5012,6 +4776,9 @@ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5026,6 +4793,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5040,6 +4810,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5054,6 +4827,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5068,6 +4844,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5082,6 +4861,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5096,6 +4878,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5342,6 +5127,9 @@ "arm" ], "deprecated": "yuku-codegen is pure JavaScript since 0.14", + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5356,6 +5144,9 @@ "arm" ], "deprecated": "yuku-codegen is pure JavaScript since 0.14", + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5370,6 +5161,9 @@ "arm64" ], "deprecated": "yuku-codegen is pure JavaScript since 0.14", + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5384,6 +5178,9 @@ "arm64" ], "deprecated": "yuku-codegen is pure JavaScript since 0.14", + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5398,6 +5195,9 @@ "x64" ], "deprecated": "yuku-codegen is pure JavaScript since 0.14", + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5412,6 +5212,9 @@ "x64" ], "deprecated": "yuku-codegen is pure JavaScript since 0.14", + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5510,6 +5313,9 @@ "arm" ], "deprecated": "yuku-parser runs on yuku-core since 0.14", + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5524,6 +5330,9 @@ "arm" ], "deprecated": "yuku-parser runs on yuku-core since 0.14", + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5538,6 +5347,9 @@ "arm64" ], "deprecated": "yuku-parser runs on yuku-core since 0.14", + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5552,6 +5364,9 @@ "arm64" ], "deprecated": "yuku-parser runs on yuku-core since 0.14", + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5566,6 +5381,9 @@ "x64" ], "deprecated": "yuku-parser runs on yuku-core since 0.14", + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5580,6 +5398,9 @@ "x64" ], "deprecated": "yuku-parser runs on yuku-core since 0.14", + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5690,36 +5511,41 @@ } }, "node_modules/alchemy": { - "version": "2.0.0-beta.78", - "resolved": "https://registry.npmjs.org/alchemy/-/alchemy-2.0.0-beta.78.tgz", - "integrity": "sha512-Dv3IstS7W5AFPgU4T2PKe/SRrtqfKnMHYEKaWWCjVneE8VTl6Zz7M/oG/jz1YSmzdPaq3YinQlRQK3ZFzhTmkw==", + "version": "2.0.0-beta.83", + "resolved": "https://registry.npmjs.org/alchemy/-/alchemy-2.0.0-beta.83.tgz", + "integrity": "sha512-t0N4FwLU1JxLnKZGdsBNdqiycWDM5HEYmmIguMImplE07Vh+FPpMR8ypk3K3TY5Tg0sFFppXFVwtwiDp6xlX9w==", "license": "Apache-2.0", "dependencies": { - "@alchemy.run/cloudflare-runtime": "2.0.0-beta.78", - "@alchemy.run/floci": "2.0.0-beta.78", - "@alchemy.run/node-utils": "2.0.0-beta.78", - "@alchemy.run/sigil": "0.0.0-alpha.11", - "@aws-sdk/credential-providers": "^3.0.0", - "@distilled.cloud/aws": "1.0.0-rc.12", - "@distilled.cloud/axiom": "1.0.0-rc.12", - "@distilled.cloud/cloudflare": "1.0.0-rc.12", - "@distilled.cloud/core": "1.0.0-rc.12", - "@distilled.cloud/fly-io": "1.0.0-rc.12", - "@distilled.cloud/hetzner": "1.0.0-rc.12", - "@distilled.cloud/neon": "1.0.0-rc.12", - "@distilled.cloud/planetscale": "1.0.0-rc.12", - "@distilled.cloud/prisma": "1.0.0-rc.12", - "@distilled.cloud/railway": "1.0.0-rc.12", - "@distilled.cloud/stripe": "1.0.0-rc.12", - "@effect/sql-d1": ">=4.0.0-rc.115 || >=4.0.0", - "@effect/sql-sqlite-do": ">=4.0.0-rc.115 || >=4.0.0", - "@effect/vitest": ">=4.0.0-rc.115 || >=4.0.0", + "@alchemy.run/cloudflare-runtime": "2.0.0-beta.83", + "@alchemy.run/floci": "2.0.0-beta.83", + "@alchemy.run/node-utils": "2.0.0-beta.83", + "@alchemy.run/sigil": "0.1.0-alpha.1", + "@distilled.cloud/acme": "1.0.0-rc.15", + "@distilled.cloud/aws": "1.0.0-rc.15", + "@distilled.cloud/axiom": "1.0.0-rc.15", + "@distilled.cloud/cloudflare": "1.0.0-rc.15", + "@distilled.cloud/core": "1.0.0-rc.15", + "@distilled.cloud/doppler": "1.0.0-rc.15", + "@distilled.cloud/fly-io": "1.0.0-rc.15", + "@distilled.cloud/gcp": "1.0.0-rc.15", + "@distilled.cloud/hetzner": "1.0.0-rc.15", + "@distilled.cloud/iceberg": "1.0.0-rc.15", + "@distilled.cloud/infisical": "1.0.0-rc.15", + "@distilled.cloud/neon": "1.0.0-rc.15", + "@distilled.cloud/planetscale": "1.0.0-rc.15", + "@distilled.cloud/prisma": "1.0.0-rc.15", + "@distilled.cloud/railway": "1.0.0-rc.15", + "@distilled.cloud/stripe": "1.0.0-rc.15", + "@distilled.cloud/turso": "1.0.0-rc.15", + "@distilled.cloud/zerossl": "1.0.0-rc.15", + "@effect/sql-d1": "^4.0.0", + "@effect/sql-sqlite-do": "^4.0.0", "@libsql/client": "^0.17.0", + "@neon/functions": "0.11.0", "@octokit/rest": "^22.0.1", "@octokit/webhooks": "^14.2.0", "@prisma/dev": "^0.20.0", "@types/aws-lambda": "^8.10.152", - "aws4fetch": "^1.0.20", "capnweb": "^0.12.0", "fflate": "^0.8.3", "libsodium-wrappers": "^0.8.3", @@ -5727,26 +5553,29 @@ "picomatch": "^4.0.4", "rolldown": "1.2.5", "tinyglobby": "^0.2.17", - "undici": "^7.16.0", "yaml": "^2.9.0" }, "bin": { "alchemy": "bin/cli.js" }, "peerDependencies": { - "@alchemy.run/frontend-frameworks": "2.0.0-beta.78", + "@alchemy.run/frontend-frameworks": "2.0.0-beta.83", "@aws/durable-execution-sdk-js": "^2.1.0", - "@effect/platform-bun": ">=4.0.0-rc.115 || >=4.0.0", - "@effect/platform-node": ">=4.0.0-rc.115 || >=4.0.0", - "@effect/sql-mysql2": ">=4.0.0-rc.115 || >=4.0.0", - "@effect/sql-pg": ">=4.0.0-rc.115 || >=4.0.0", + "@effect/platform-bun": "^4.0.0", + "@effect/platform-node": "^4.0.0", + "@effect/sql-libsql": "^4.0.0", + "@effect/sql-mysql2": "^4.0.0", + "@effect/sql-pg": "^4.0.0", + "@effect/vitest": "^4.0.0", + "@prisma/orm-postgres": "^8.0.0-rc.11", "@vercel/nft": "^1.10.2", "drizzle-kit": "1.0.0-rc.5-ab785fc", "drizzle-orm": "1.0.0-rc.5-ab785fc", - "effect": ">=4.0.0-rc.115 || >=4.0.0", + "effect": "^4.0.0", "mongodb": "^6.10.0", "mysql2": "^3.24.2", "pg": "^8.22.0", + "prisma": "^8.0.0-rc.15", "vite": "^8.0.7", "ws": "^8.21.0" }, @@ -5763,12 +5592,21 @@ "@effect/platform-node": { "optional": true }, + "@effect/sql-libsql": { + "optional": true + }, "@effect/sql-mysql2": { "optional": true }, "@effect/sql-pg": { "optional": true }, + "@effect/vitest": { + "optional": true + }, + "@prisma/orm-postgres": { + "optional": true + }, "@vercel/nft": { "optional": true }, @@ -5787,6 +5625,9 @@ "pg": { "optional": true }, + "prisma": { + "optional": true + }, "vite": { "optional": true }, @@ -5847,15 +5688,6 @@ "zeptomatch": "2.1.0" } }, - "node_modules/alchemy/node_modules/undici": { - "version": "7.30.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", - "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, "node_modules/alchemy/node_modules/valibot": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", @@ -5903,18 +5735,6 @@ "node": ">=14" } }, - "node_modules/anynum": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", - "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/aria-hidden": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", @@ -6166,12 +5986,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws4fetch": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz", - "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==", - "license": "MIT" - }, "node_modules/axe-core": { "version": "4.14.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.14.0.tgz", @@ -6196,7 +6010,7 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peerDependencies": { "react-native-b4a": "*" @@ -6221,7 +6035,7 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peerDependencies": { "bare-abort-controller": "*" @@ -6236,7 +6050,7 @@ "version": "4.8.4", "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.4.tgz", "integrity": "sha512-MPC2eyrpbk3A873sK4y6rk+Iv4o2enxTjHZ8zH3HO0mjrpm5XjnguY2XmfidIsDV6vnjpbMBKUAJpV0ISD2SsQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "bare-events": "^2.5.4", @@ -6261,14 +6075,14 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/bare-stream": { "version": "2.13.4", "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "b4a": "^1.8.1", @@ -6296,7 +6110,7 @@ "version": "2.5.5", "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.5.tgz", "integrity": "sha512-iM9qJCruvpt2rPaLsrumVDru9pogI7PHziHIa9kTNRFt8C8HuSa+kI9kFezK+NvHEFnnz71rKGZ1EAilq/3V/Q==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "bare-path": "^3.0.0" @@ -6336,12 +6150,6 @@ "file-uri-to-path": "1.0.0" } }, - "node_modules/bowser": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.15.0.tgz", - "integrity": "sha512-xic6KSZglg/I4ymp45fdM4GWfYpBK79Fxd/TOvJktlaPgQqqDxvmGwOPZl2V0dyeS3Lj5c180bLhXF43iyAjZQ==", - "license": "MIT" - }, "node_modules/brace-expansion": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", @@ -6406,7 +6214,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" @@ -6898,7 +6706,7 @@ "version": "5.5.1", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "bundle-name": "^4.1.0", @@ -6915,7 +6723,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -6946,7 +6754,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=12" @@ -7061,9 +6869,9 @@ } }, "node_modules/effect": { - "version": "4.0.0-rc.115", - "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-rc.115.tgz", - "integrity": "sha512-ogYulZ5ffeOzrJqQrG0XOkDO5lKn2s9KNHhoxJy/6wKUkF0i12dlVXWA9TgsgQ+zV/JzizyG0+XMPepZEQX6vw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.3.tgz", + "integrity": "sha512-LoWn/Uwwx0JjHSDmBAAahx8sB+klzM6vsZovnbYowBNw6B4QScJLRLBByO61plvNiycx7HPN8FnsWUCu0eXW5w==", "license": "MIT" }, "node_modules/electron-to-chromium": { @@ -7848,7 +7656,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "bare-events": "^2.7.0" @@ -7858,7 +7666,7 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", @@ -7891,9 +7699,9 @@ } }, "node_modules/exsolve": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.3.tgz", - "integrity": "sha512-kuzL9zherLQoqDeQaS55AiiIg1kb4KYXaSusTtgZTPlvKP2gGCI82LNmBNh5RjDVUXIcmgx+/Mx7vPwRzBbhJA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.2.0.tgz", + "integrity": "sha512-h1pQPR9XoeSMdntRNCjyTznaume4UpfXrULBnlkZ2CUM/G5eSa+pBByGldO5uR8dxbPC+OuJXsPPNf+JZ81ADg==", "license": "MIT" }, "node_modules/fast-decode-uri-component": { @@ -7912,7 +7720,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -8008,45 +7816,6 @@ "fast-string-width": "^3.0.2" } }, - "node_modules/fast-xml-builder": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz", - "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "path-expression-matcher": "^1.6.2", - "xml-naming": "^0.3.0" - } - }, - "node_modules/fast-xml-parser": { - "version": "5.11.2", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.2.tgz", - "integrity": "sha512-R9iDuNrQYeQut46cn2r2wHKn4HYzVDvDm5J1wW+koZewykv0yuO2HChTYeZtrULyHIDM9cj9TUXloCsueCQUog==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "@nodable/entities": "^3.0.1", - "fast-xml-builder": "^1.2.0", - "is-unsafe": "^2.0.0", - "path-expression-matcher": "^1.6.2", - "strnum": "^2.4.2", - "xml-naming": "^0.3.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/fastq": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", @@ -8084,7 +7853,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-unicode-supported": "^2.0.0" @@ -8365,7 +8134,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@sec-ant/readable-stream": "^0.4.1", @@ -8512,15 +8281,6 @@ "integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==", "license": "MIT" }, - "node_modules/graphql": { - "version": "16.11.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", - "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -8681,7 +8441,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=18.18.0" @@ -8896,7 +8656,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "is-docker": "cli.js" @@ -8987,7 +8747,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=20" @@ -9000,7 +8760,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-docker": "^3.0.0" @@ -9072,7 +8832,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=12" @@ -9133,7 +8893,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -9197,7 +8957,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -9206,18 +8966,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-unsafe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.2.tgz", - "integrity": "sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -9268,7 +9016,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-inside-container": "^1.0.0" @@ -9324,7 +9072,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/jose": { @@ -9649,6 +9397,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -9670,6 +9421,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -9691,6 +9445,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -9712,6 +9469,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -9909,6 +9669,21 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/mime": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.2.tgz", + "integrity": "sha512-ikRzZ2RwbUVDyL9SsU0f2Phcmav4yq5r8faH5ZZKHtOZrMBLnrQOrj0uvqZ5R1uQnj+RO7Wcklw/lAf81mP9LQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/minimatch": { "version": "10.2.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", @@ -10151,7 +9926,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "path-key": "^4.0.0", @@ -10168,7 +9943,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=12" @@ -10323,7 +10098,7 @@ "version": "11.0.4", "resolved": "https://registry.npmjs.org/open/-/open-11.0.4.tgz", "integrity": "sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "default-browser": "^5.5.1", @@ -10434,7 +10209,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -10453,21 +10228,6 @@ "node": ">=8" } }, - "node_modules/path-expression-matcher": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", - "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -10657,9 +10417,9 @@ } }, "node_modules/pkg-types/node_modules/confbox": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", - "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.2.tgz", + "integrity": "sha512-ZUAKhKlkCOrIGKc8cj7zek9s9z1mveHif/25nUshI2BzCRzF7KH7KHa+Lnh2ubq+e1tIlaFuP763mzW4qVoehg==", "license": "MIT" }, "node_modules/pluralize": { @@ -10752,7 +10512,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=20" @@ -10790,7 +10550,7 @@ "version": "9.3.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.1.tgz", "integrity": "sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "parse-ms": "^4.0.0" @@ -10803,18 +10563,18 @@ } }, "node_modules/prisma": { - "version": "8.0.0-rc.22", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-8.0.0-rc.22.tgz", - "integrity": "sha512-PaOZPZq2b/UDUgFkC2IlsAKG+ulv2+ojJg2lHquYPZY1OElQeD8D0PEoekCWusaRor2AhjS/uiOc+AGZuAskAg==", - "dev": true, + "version": "8.0.0-rc.28", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-8.0.0-rc.28.tgz", + "integrity": "sha512-sIZdArUGOCivaWZCaSPorWqlB6bDZojuuAWWLSRF/tBGqHHffoDRYznANBSawu0gD/BpjT+jiZDn8K54X4O+Tw==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@manypkg/tools": "^2.1.2", - "@prisma/cli-engine": "0.7.0", - "@prisma/composer-cli": "0.29.1", + "@prisma/cli-engine": "0.9.0", + "@prisma/composer-cli": "0.35.0", "@prisma/compute-sdk": "0.43.0", "@prisma/management-api-sdk": "1.80.0", - "@prisma/orm-toolchain": "8.0.0-rc.17", + "@prisma/orm-toolchain": "8.0.0-rc.20", "@vercel/detect-agent": "^1.2.3", "better-result": "^2.9.2", "cross-spawn": "^7.0.6", @@ -10826,14 +10586,14 @@ "prisma": "dist/prisma.js" }, "engines": { - "node": ">=22.18.0" + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" } }, "node_modules/prisma/node_modules/@prisma/management-api-sdk": { "version": "1.80.0", "resolved": "https://registry.npmjs.org/@prisma/management-api-sdk/-/management-api-sdk-1.80.0.tgz", "integrity": "sha512-pQN6hBqR2pgaF3S5sMCe3iZIlThIOgUXoDcinB6/AxXO3qM6oWnirSUAJlhFQ/Hr5A2PmdxScOuCB1dJYHZ4KQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "openapi-fetch": "0.14.0" @@ -10979,21 +10739,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-reconciler": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.34.0.tgz", - "integrity": "sha512-eVibvB1fc9j5Qf1KRkiM3GzRPBdjg2P6G6QV2f3P6Eb3OGMxRBhdg2rrUwjbV5JrQaJj8nOa8uajmmJA5tJWjQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.28.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^19.3.0" - } - }, "node_modules/react-remove-scroll": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", @@ -11334,7 +11079,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -11743,7 +11488,7 @@ "version": "2.28.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "events-universal": "^1.0.0", @@ -11910,7 +11655,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -11919,21 +11664,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strnum": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.5.1.tgz", - "integrity": "sha512-UM4nTLWzQiDBseVxUGxN0wJzIk0UU89Qi7pKXz9qISo+lhviJikTuikBsK/oFRlpvYHvSdz0TVoLyQ+sEpYwxA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "anynum": "^1.0.1" - } - }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -12001,7 +11731,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.2.tgz", "integrity": "sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "b4a": "^1.9.0", @@ -12024,7 +11754,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "streamx": "^2.12.5" @@ -12056,7 +11786,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" @@ -12066,7 +11796,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/tinybench": { @@ -12471,7 +12201,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -12843,6 +12573,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -12860,6 +12593,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -12877,6 +12613,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -12894,6 +12633,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -12911,6 +12653,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -12928,6 +12673,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -13350,9 +13098,9 @@ } }, "node_modules/workerd": { - "version": "1.20260901.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260901.1.tgz", - "integrity": "sha512-Z5PVprrPRlV70+RzgbFpzoPpjCLGtErhsQ9Ahe96pe7hJGMsmexTU4+rhUPH+2Sguf7vRAxFFsqXpkIleRFSPA==", + "version": "1.20261002.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20261002.1.tgz", + "integrity": "sha512-HFTb9FGAEWB5AVnHP/czY/vy9tqtyV7rreb9MKntsAJi8aElBkyq7P0MLPQLgTtKOk6A+EivoS39gDH/lwzu3Q==", "hasInstallScript": true, "license": "Apache-2.0", "bin": { @@ -13362,11 +13110,11 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260901.1", - "@cloudflare/workerd-darwin-arm64": "1.20260901.1", - "@cloudflare/workerd-linux-64": "1.20260901.1", - "@cloudflare/workerd-linux-arm64": "1.20260901.1", - "@cloudflare/workerd-windows-64": "1.20260901.1" + "@cloudflare/workerd-darwin-64": "1.20261002.1", + "@cloudflare/workerd-darwin-arm64": "1.20261002.1", + "@cloudflare/workerd-linux-64": "1.20261002.1", + "@cloudflare/workerd-linux-arm64": "1.20261002.1", + "@cloudflare/workerd-windows-64": "1.20261002.1" } }, "node_modules/wrap-ansi": { @@ -13386,9 +13134,9 @@ } }, "node_modules/ws": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", - "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.1.tgz", + "integrity": "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -13410,7 +13158,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.1.tgz", "integrity": "sha512-c16PKLIRxBYpR3jSWsdS7SFp0COm5SOQdiFjY818dYFHJbMGuLBszpF07LBWmOS1uzrHa9VGdynsEgoeKy0oOQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-wsl": "^3.1.0", @@ -13423,21 +13171,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/xml-naming": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", - "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -13533,7 +13266,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz", "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" diff --git a/compute/room-booking-prisma-template/package.json b/compute/room-booking-prisma-template/package.json index e142aaf9010e..d29de57c83e3 100644 --- a/compute/room-booking-prisma-template/package.json +++ b/compute/room-booking-prisma-template/package.json @@ -28,11 +28,10 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@prisma/composer": "0.29.1", - "@prisma/composer-prisma-cloud": "0.29.1", - "@prisma/orm-postgres": "8.0.0-rc.17", - "alchemy": "2.0.0-beta.78", - "effect": "4.0.0-rc.115", + "@prisma/composer": "0.35.0", + "@prisma/composer-prisma-cloud": "0.35.0", + "@prisma/orm-postgres": "8.0.0-rc.20", + "alchemy": "2.0.0-beta.83", "next": "16.4.2", "react": "19.3.1", "react-dom": "19.3.1", @@ -41,18 +40,28 @@ "zod": "^4.6.7" }, "devDependencies": { + "@effect/platform-bun": "4.0.3", + "@effect/platform-node": "4.0.3", + "@effect/platform-node-shared": "4.0.3", "@prisma/dev": "0.25.2", "@types/node": "^26.6.5", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", "eslint": "^10.13.0", "eslint-config-next": "16.4.2", - "prisma": "8.0.0-rc.22", + "prisma": "8.0.0-rc.28", "tsx": "^4.23.15", "typescript": "^6.0.3", "vitest": "^5.0.5" }, "overrides": { + "effect": "4.0.3", + "@effect/platform-bun": "4.0.3", + "@effect/platform-node": "4.0.3", + "@effect/platform-node-shared": "4.0.3", + "@effect/sql-d1": "4.0.3", + "@effect/sql-sqlite-do": "4.0.3", + "@effect/vitest": "4.0.3", "@vitejs/devtools": "0.7.6" } } diff --git a/compute/room-booking-prisma-template/prisma.config.ts b/compute/room-booking-prisma-template/prisma.config.ts index 16f36c573d85..29ff4b340f70 100644 --- a/compute/room-booking-prisma-template/prisma.config.ts +++ b/compute/room-booking-prisma-template/prisma.config.ts @@ -10,7 +10,7 @@ import { prismaCloud, prismaState } from "@prisma/composer-prisma-cloud/control" // scripts need. Not .env: `next build` copies .env into the standalone output // that `prisma deploy` uploads, and a local database URL has no business there. try { - process.loadEnvFile(".env.local"); + process.loadEnvFile?.(".env.local"); // optional: Bun 1.3.13 (the Deploy Button workflow) has none and reads .env.local itself } catch (error) { // no .env.local: use the environment as it is (CI, Composer deploys) if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; diff --git a/compute/room-booking-prisma-template/src/app/about.tsx b/compute/room-booking-prisma-template/src/app/about.tsx index bec23da54dde..4815c9b24790 100644 --- a/compute/room-booking-prisma-template/src/app/about.tsx +++ b/compute/room-booking-prisma-template/src/app/about.tsx @@ -1,101 +1,73 @@ -// What this template is and how it works, below the booking board. Delete this -// file and its use in page.tsx when the app is yours. +// What this template is built with, in the cards below the timeline, and where +// its source lives. Delete the cards in page.tsx and the source link in +// board.tsx when the app is yours. export const SOURCE_URL = "https://github.com/prisma/prisma-examples/tree/latest/compute/room-booking-prisma-template"; -export const DEPLOY_URL = "https://console.prisma.io/apps/room-booking-prisma-template"; -const steps = [ - { - where: "In your browser", - what: "The timeline runs the same rules as the server, so a taken slot or a time that has passed turns red before you let go.", - code: "bookingRules()", - }, - { - where: "On the server", - what: "A Prisma ORM transaction locks the room, reads its bookings again and checks the same rules. Two requests for one room take turns, and the second one is told what is in the way.", - code: "Room.where({ id }).forNoKeyUpdate()", - }, +// The marks belong to their owners. Next.js is from Simple Icons 16.34.0 (CC0), +// drawn in the text color. Prisma is its current mark, from prisma.io/icon.svg, +// in its own colors. Resource Scheduler has no logo, so it gets three bookings +// on a timeline. +const nextLogo = ( + +); + +const prismaLogo = ( + +); + +const schedulerLogo = ( + +); + +export const stack = [ { - where: "In Postgres", - what: "An exclusion constraint refuses two bookings of one room that overlap, even from a write that skips the app.", - code: "EXCLUDE USING gist", + name: "Next.js 16", + kind: "Framework", + what: "App Router. The page reads the rooms and the day's bookings on the server; the timeline runs in the browser.", + href: "https://nextjs.org", + logo: nextLogo, }, -]; - -const stack = [ { name: "Prisma ORM 8", + kind: "ORM", what: "Typed queries and migrations from one schema contract. Every booking change runs in a transaction.", href: "https://www.prisma.io/orm", + logo: prismaLogo, }, { name: "Prisma Postgres", + kind: "Database", what: "Stores the rooms and bookings. Every deploy, preview and laptop gets a database of its own.", href: "https://www.prisma.io/postgres", + logo: prismaLogo, }, { name: "Prisma Compute", + kind: "Hosting", what: "Runs the Next.js server. module.ts declares the app and its database, and a deploy provisions both.", href: "https://www.prisma.io/compute", + logo: prismaLogo, }, { name: "Resource Scheduler", - what: "The timeline: drag to book, move and resize.", - href: "https://www.npmjs.com/package/resource-scheduler", + kind: "Timeline", + what: "The timeline: drag to book, move and resize, day and week views, utilization, free-slot search and calendar files.", + href: "https://resource-scheduler-docs.vercel.app", + logo: schedulerLogo, }, ]; - -export function About({ rooms, bookings, queryMs }: { rooms: number; bookings: number; queryMs: number }) { - return ( - <> -
-

Why a room can never be booked twice

-

- Every booking passes three checks on its way in. The last two hold when two people click the same slot at the - same moment. -

-
    - {steps.map((step) => ( -
  1. -

    {step.where}

    -

    {step.what}

    - {step.code} -
  2. - ))} -
-

The tests send 25 bookings for the same slot at once. Exactly one gets through.

-
- -
-
-

Built on Prisma, end to end

-

- This page read {rooms} rooms and {bookings} bookings from Prisma Postgres in {queryMs} ms. -

- -

- Setting it up with a coding agent? Point it at AGENTS.md: it covers setup, schema changes and - deploys. -

-
-
- {stack.map((item) => ( -
-
- {item.name} -
-
{item.what}
-
- ))} -
-
- - ); -} diff --git a/compute/room-booking-prisma-template/src/app/board.tsx b/compute/room-booking-prisma-template/src/app/board.tsx index 7d55d3609119..997a04cdacf4 100644 --- a/compute/room-booking-prisma-template/src/app/board.tsx +++ b/compute/room-booking-prisma-template/src/app/board.tsx @@ -1,18 +1,38 @@ "use client"; -import { useEffect, useMemo, useRef, useState } from "react"; +import Link from "next/link"; +import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"; +import type { ReactNode } from "react"; import { ResourceScheduler, ViewType } from "resource-scheduler"; -import type { Placement, ResourceInput, SchedulerEvent, VisibleRange } from "resource-scheduler"; +import type { + Placement, + ResourceInput, + ResourceSchedulerHandle, + SchedulerEvent, + VisibleRange, +} from "resource-scheduler"; +import { eventsToICS } from "resource-scheduler/exchange"; import "resource-scheduler/style.css"; import { ApiRequestError, api } from "@/lib/api-client"; import type { BookingDTO, RoomDTO } from "@/lib/api-types"; -import { CLOSE_HOUR, OFFICE_TIME_ZONE, OPEN_HOUR, SLOT_MINUTES, formatOfficeTime } from "@/lib/office"; +import { + CLOSE_HOUR, + OFFICE_TIME_ZONE, + OPEN_HOUR, + SLOT_MINUTES, + formatOfficeDay, + formatOfficeTime, + officeTime, +} from "@/lib/office"; import { bookingRules } from "@/lib/rules"; -import { BookingDialog, rememberBooker } from "./booking-dialog"; +import { SOURCE_URL } from "./about"; +import { BookingDialog, rememberBooker, rememberedBooker } from "./booking-dialog"; import type { BookingDetails, BookingDraft } from "./booking-dialog"; -import { RoomStatus } from "./room-status"; +import { FindTime } from "./find-time"; +import { RoomLabel } from "./room-status"; +import { ThemeToggle } from "./theme-toggle"; type Booking = { id: string; @@ -21,15 +41,19 @@ type Booking = { roomId: string; startsAt: Date; endsAt: Date; - /** Shown but not saved yet: drawn grey, and cannot be moved until the server answers. */ + /** Shown but not saved yet: drawn dashed, and cannot be moved until the server answers. */ pending: boolean; }; type Notice = { kind: "success" | "error"; text: string }; -const PENDING_COLOR = "var(--pending)"; -const FLOOR_COLORS = 4; // --floor-1 to --floor-4 in globals.css, reused when there are more floors const rules = bookingRules(); +const city = OFFICE_TIME_ZONE.split("/").pop()!.replaceAll("_", " "); +/** Tells React when another tab saves a booker name. */ +const subscribeToBooker = (notify: () => void) => { + window.addEventListener("storage", notify); + return () => window.removeEventListener("storage", notify); +}; const fromDTO = (b: BookingDTO): Booking => ({ ...b, @@ -46,32 +70,82 @@ const isConflict = (error: unknown) => error instanceof ApiRequestError && error /** A conflict, or a failure that may have reached the server: show what is saved now. */ const shouldRefresh = (error: unknown) => isConflict(error) || !(error instanceof ApiRequestError); +/** Line icons on a 24-pixel grid, drawn in the text color. */ +const icon = (paths: ReactNode) => ( + +); +const clockIcon = icon( + <> + + + , +); +const downloadIcon = icon(); +const printIcon = icon(); +const codeIcon = icon(); + +/** Saves an iCalendar file, which Google Calendar, Outlook and Apple Calendar open. */ +function saveCalendar(fileName: string, ics: string) { + const link = document.createElement("a"); + link.href = `data:text/calendar;charset=utf-8,${encodeURIComponent(ics)}`; + link.download = fileName; + link.click(); +} + /** - * The calendar. Every change is optimistic: it shows at once, is sent to the - * API, and is rolled back with the server's message if the server refuses. + * The app's window: the calendar, with "Find a time" beside it. Every change + * is optimistic: it shows at once, is sent to the API, and is rolled back with + * the server's message if the server refuses. */ export function Board({ rooms, initialBookings, initialDate, serverNow, + children, }: { rooms: RoomDTO[]; initialBookings: BookingDTO[]; initialDate: string; - /** The server's clock at render, so the first paint of the room plates matches on both sides. */ + /** The server's clock at render, so the first paint of the room statuses matches on both sides. */ serverNow: string; + /** Shown below the timeline. */ + children?: ReactNode; }) { const [bookings, setBookings] = useState(() => initialBookings.map(fromDTO)); const [start] = useState(() => new Date(initialDate)); const [now, setNow] = useState(() => new Date(serverNow)); + const [view, setView] = useState(ViewType.Day); + // What the grid shows: the day the page opened on, until the scheduler reports its range. + const [range, setRange] = useState(() => ({ + start: officeTime(0, 0, 0, start), + end: officeTime(1, 0, 0, start), + })); + // The range now, for a write that fails after the grid has moved on. + const rangeRef = useRef(range); const [draft, setDraft] = useState(null); const [notice, setNotice] = useState(null); const [loading, setLoading] = useState(false); - const visibleRange = useRef(null); + const scheduler = useRef(null); + const reported = useRef(false); const loadController = useRef(null); + // Bookings under the name this browser booked with last are drawn as yours. + // The server has no localStorage, so it renders none as yours. + const me = useSyncExternalStore(subscribeToBooker, rememberedBooker, () => ""); - // The room plates say "free" or "in use" on the minute. + // The room statuses say "free" or "in use" on the minute. useEffect(() => { const timer = setInterval(() => setNow(new Date()), 15_000); return () => clearInterval(timer); @@ -84,31 +158,34 @@ export function Board({ return () => clearTimeout(timer); }, [notice]); - const roomName = (id: string) => rooms.find((r) => r.id === id)?.name ?? "the room"; + // Open the day at the current time, or at opening time when the page opened on tomorrow. + useEffect(() => { + scheduler.current?.scrollToTime(start); + }, [start]); - // Every floor gets a color, like the signs in an office building, on its plates and its bookings. - const floorColor = useMemo(() => { - const floors = [...new Set(rooms.map((r) => r.floor))]; - return (floor: string) => `var(--floor-${(floors.indexOf(floor) % FLOOR_COLORS) + 1})`; - }, [rooms]); + const roomById = useMemo(() => new Map(rooms.map((r) => [r.id, r])), [rooms]); + const roomName = (id: string) => roomById.get(id)?.name ?? "the room"; const resources = useMemo( () => rooms.map((r) => ({ id: r.id, name: r.name, role: `${r.seats} seats`, group: r.floor })), [rooms], ); - const events = useMemo(() => { - const floorOf = new Map(rooms.map((r) => [r.id, r.floor])); - return bookings.map((b) => ({ - id: b.id, - title: b.title, - description: `Booked by ${b.booker}`, - resourceId: b.roomId, - startDate: b.startsAt, - endDate: b.endsAt, - color: b.pending ? PENDING_COLOR : floorColor(floorOf.get(b.roomId) ?? ""), - })); - }, [bookings, rooms, floorColor]); + const events = useMemo( + () => + bookings.map((b) => ({ + id: b.id, + title: b.title, + description: `Booked by ${b.booker}`, + resourceId: b.roomId, + startDate: b.startsAt, + endDate: b.endsAt, + })), + [bookings], + ); + + // A room's status is about now, so it shows only while today is on screen. + const todayOnScreen = range.start < officeTime(1, 0, 0, now) && range.end > officeTime(0, 0, 0, now); // The shared rules, plus one that only matters in the browser: a booking // that is still being saved cannot be moved yet. @@ -203,40 +280,200 @@ export function Board({ } } - /** The grid moved to another day: load it. The first day came with the page. */ - function onRangeChange(range: VisibleRange) { - const first = visibleRange.current === null; - visibleRange.current = range; - if (!first) void fetchRange(range); + /** The grid moved to another day or week: load it. The first day came with the page. */ + function onRangeChange(next: VisibleRange) { + rangeRef.current = next; + setRange(next); + if (reported.current) void fetchRange(next); + reported.current = true; } - /** Reloads the day on screen, after a conflict showed it is out of date. */ + /** Reloads the days on screen, after a failed write may have left them out of date. */ async function refresh() { - if (visibleRange.current) await fetchRange(visibleRange.current); + await fetchRange(rangeRef.current); + } + + /** Every saved booking on screen, as one calendar file: one still saving may yet be refused. */ + function exportShown() { + const shown = events.filter( + (e) => + !bookings.some((b) => b.id === e.id && b.pending) && e.startDate < range.end && e.endDate > range.start, + ); + saveCalendar("room-bookings.ics", eventsToICS(shown, { resources, name: "Meeting rooms" })); } return ( - <> - setDraft({ resourceId: room.id, ...slot, roomName: room.name })} - /> - -
-

Timeline

-

- Drag across free time to book. Drag a booking to move it, drag its edge to resize it, click it to cancel. - Times are {OFFICE_TIME_ZONE} time. -

- {loading && ( - - Loading… +
+ + + Rooms + + +
+
+

Book a meeting room

+

+ {view === ViewType.Week + ? "Drag a booking to another day to move it. Switch to Day to book a new time." + : "Drag across free time to book. Drag a booking to move it, or an edge to change its time."} +

+
+
+ {/* Intl output can differ by a space between the server's runtime and the browser. */} + + {clockIcon} + {formatOfficeDay(now)}, {formatOfficeTime(now)} in {city} - )} -
+ + + +
+ + + + +
+
+ setDraft({ ...slot, roomName: roomName(slot.resourceId) })} + onEventDrop={(event, _fromRoom, toRoom, startsAt, endsAt) => reschedule(event.id, toRoom, startsAt, endsAt)} + onEventResize={(event, roomId, startsAt, endsAt) => reschedule(event.id, roomId, startsAt, endsAt)} + onRangeChange={onRangeChange} + renderResourceHeader={(resource) => ( + + )} + // The scheduler's wrapper around this keeps the keyboard, focus ring, popover and resize handles. + // Times come from the booking: the event's dates are shifted into the scheduler's time zone. + renderTimeSlot={(event) => { + const booking = bookings.find((b) => b.id === event.id); + return ( +
+ {event.title} + {booking && ( + + {formatOfficeTime(booking.startsAt)} – {formatOfficeTime(booking.endsAt)} + + )} +
+ ); + }} + renderEventPopover={(event, resource, close) => { + const booking = bookings.find((b) => b.id === event.id); + return ( +
+
+

{event.title}

+ {booking && ( +

+ {resource.name}, {formatOfficeDay(booking.startsAt)}, {formatOfficeTime(booking.startsAt)} to{" "} + {formatOfficeTime(booking.endsAt)} +
+ Booked by {booking.booker} +

+ )} +
+ {booking?.pending ? ( +

Saving…

+ ) : ( +
+ + +
+ )} +
+ ); + }} + /> +
+ +
+
    +
  • Yours
  • +
  • Booked
  • +
  • Saving
  • +
+ {loading ? Loading… : Times are {OFFICE_TIME_ZONE} time.} +
+ + {children} +
{notice && ( @@ -249,60 +486,6 @@ export function Board({ )}
-
- setDraft({ ...slot, roomName: roomName(slot.resourceId) })} - onEventDrop={(event, _fromRoom, toRoom, startsAt, endsAt) => reschedule(event.id, toRoom, startsAt, endsAt)} - onEventResize={(event, roomId, startsAt, endsAt) => reschedule(event.id, roomId, startsAt, endsAt)} - onRangeChange={onRangeChange} - renderEventPopover={(event, resource, close) => { - const booking = bookings.find((b) => b.id === event.id); - return ( -
-

{event.title}

- {booking && ( -

- {resource.name}, {formatOfficeTime(booking.startsAt)} to {formatOfficeTime(booking.endsAt)} -
- Booked by {booking.booker} -

- )} - {booking?.pending ? ( -

Saving…

- ) : ( - - )} -
- ); - }} - /> -
- {draft && ( setDraft(null)} /> )} - +
); } diff --git a/compute/room-booking-prisma-template/src/app/booking-dialog.tsx b/compute/room-booking-prisma-template/src/app/booking-dialog.tsx index 2e7bf60ba2d7..c6e78dc8578d 100644 --- a/compute/room-booking-prisma-template/src/app/booking-dialog.tsx +++ b/compute/room-booking-prisma-template/src/app/booking-dialog.tsx @@ -19,7 +19,7 @@ export type BookingDraft = SlotSelection & { const BOOKER_KEY = "room-booking:booker"; -function rememberedBooker(): string { +export function rememberedBooker(): string { try { return localStorage.getItem(BOOKER_KEY) ?? ""; } catch { diff --git a/compute/room-booking-prisma-template/src/app/find-time.tsx b/compute/room-booking-prisma-template/src/app/find-time.tsx new file mode 100644 index 000000000000..2b21132e8587 --- /dev/null +++ b/compute/room-booking-prisma-template/src/app/find-time.tsx @@ -0,0 +1,130 @@ +"use client"; + +import { useId, useState } from "react"; + +import type { RoomDTO } from "@/lib/api-types"; +import { findFreeSlots } from "@/lib/free-slots"; +import type { FreeSlot } from "@/lib/free-slots"; +import { formatOfficeDay, formatOfficeTime } from "@/lib/office"; + +type Span = { roomId: string; startsAt: Date; endsAt: Date }; + +const LENGTHS = [30, 60, 90, 120]; // minutes +const PEOPLE = [1, 4, 8, 12]; + +/** + * Suggests the first free time of each room that fits, on the days on screen, + * found with the scheduler's `findAvailableSlots`. Picking one opens the + * booking form, like selecting the same time on the timeline. + */ +export function FindTime({ + rooms, + bookings, + range, + now, + loading, + onPick, +}: { + rooms: RoomDTO[]; + bookings: Span[]; + range: { start: Date; end: Date }; + now: Date; + /** The days on screen are still loading: their bookings are not in `bookings` yet. */ + loading: boolean; + onPick: (room: RoomDTO, slot: FreeSlot) => void; +}) { + const [minutes, setMinutes] = useState(60); + const [people, setPeople] = useState(1); + const today = formatOfficeDay(now); + + // The suggestions that share a time, under that time. + const groups: { time: string; slots: FreeSlot[] }[] = []; + const slots = loading ? [] : findFreeSlots(rooms, bookings, { from: range.start, to: range.end, minutes, seats: people, now }); + for (const slot of slots) { + const day = formatOfficeDay(slot.start); + const time = `${day === today ? "" : `${day}, `}${slot.start <= now ? "Now" : formatOfficeTime(slot.start)} – ${formatOfficeTime(slot.end)}`; + const last = groups.at(-1); + if (last?.time === time) last.slots.push(slot); + else groups.push({ time, slots: [slot] }); + } + + return ( +
+

Find a time

+ (m < 60 ? `${m} min` : `${m / 60} h`)} + /> + (n === 1 ? "Any" : `${n}+`)} /> + + {groups.length > 0 ? ( +
+ {groups.map(({ time, slots }) => ( +
+ {/* Intl output can differ by a space between the server's runtime and the browser. */} +

+ {time} +

+
    + {slots.map((slot) => { + const room = rooms.find((r) => r.id === slot.roomId)!; + return ( +
  • + +
  • + ); + })} +
+
+ ))} +
+ ) : loading ? ( +

Loading the days on screen…

+ ) : ( +

No room is free for that long on the days on screen.

+ )} +
+ ); +} + +/** A row of radio buttons drawn as a segmented control. */ +function Choice({ + legend, + options, + value, + onChange, + label, +}: { + legend: string; + options: number[]; + value: number; + onChange: (value: number) => void; + label: (value: number) => string; +}) { + const name = useId(); + return ( +
+ {legend} +
+ {options.map((option) => ( + + ))} +
+
+ ); +} diff --git a/compute/room-booking-prisma-template/src/app/globals.css b/compute/room-booking-prisma-template/src/app/globals.css index 7a5edacf3b5a..7b3df1705027 100644 --- a/compute/room-booking-prisma-template/src/app/globals.css +++ b/compute/room-booking-prisma-template/src/app/globals.css @@ -1,79 +1,113 @@ /* Page styles. The scheduler brings its own scoped stylesheet (resource-scheduler/style.css) and is themed below with its --rs-* variables. */ +/* Frosted glass over fields of color, in light and dark. The tokens build on + each other: the scene behind the window, the glass, ink and lines, the one + violet, room states, then depth and shape. Components use only these, and + the scheduler's --rs-* variables map onto them, so a new look is a change to + this block. Every color is light-dark(light, dark): the OS setting picks one, + unless the theme button set data-theme="light" or "dark" on . */ :root { color-scheme: light dark; - --paper: #f2f4f7; - --surface: #ffffff; - --ink: #172033; - --muted: #5a6476; - --line: #dde2ea; - --line-strong: #c6cdd8; - --action: #172033; - --action-fg: #ffffff; - --link: #4338ca; - --free: #11804f; - --busy: #c2410c; - --danger: #c0262d; - --pending: #94a3b8; - --mark: #172033; - /* One color per floor, for its plates and bookings. */ - --floor-1: #0b8783; - --floor-2: #5146d8; - --floor-3: #b86200; - --floor-4: #b4237f; - --radius: 10px; - --shadow: 0 1px 2px rgb(16 24 40 / 0.05), 0 12px 32px -16px rgb(16 24 40 / 0.18); -} - -@media (prefers-color-scheme: dark) { - :root { - --paper: #0d121b; - --surface: #151c28; - --ink: #e6eaf1; - --muted: #9aa5b6; - --line: #253042; - --line-strong: #344157; - --action: #e6eaf1; - --action-fg: #0d121b; - --link: #a9a3ff; - --free: #3ccf8e; - --busy: #fb8c4c; - --danger: #ff7a7f; - --pending: #64748b; - --mark: #222c3d; - --floor-1: #2cc4bd; - --floor-2: #8f86ff; - --floor-3: #f0a63a; - --floor-4: #ee6bbd; - --shadow: 0 1px 2px rgb(0 0 0 / 0.3), 0 12px 32px -16px rgb(0 0 0 / 0.6); - } -} -/* The scheduler, in the page's colors. :root[data-theme] outranks the - package's own :root and [data-theme=system] rules in both modes. */ + /* The scene: a pale or deep page, and shapes of color for the glass to blur. */ + --page: light-dark(#e3eaf7, #090b27); + --orb-blue: light-dark(#8db2f5, #2a54c6); + --orb-indigo: light-dark(#6b7bf0, #3a3bcb); + --orb-violet: light-dark(#c2acf5, #6a2caf); + --orb-coral: light-dark(#f4a088, #df5e39); + --orb-mist: light-dark(rgb(255 255 255 / 0.8), rgb(176 184 208 / 0.55)); + + /* Glass, from the window down: cards on it, controls raised off them, and + what floats above everything (dialogs, popovers, toasts, and the room + column the timeline scrolls under). */ + --glass: light-dark(rgb(255 255 255 / 0.5), rgb(19 21 40 / 0.68)); + --glass-card: light-dark(rgb(255 255 255 / 0.48), rgb(255 255 255 / 0.035)); + --glass-raised: light-dark(rgb(255 255 255 / 0.88), rgb(255 255 255 / 0.1)); + --glass-float: light-dark(rgb(247 248 255 / 0.82), rgb(27 29 52 / 0.84)); + --glass-edge: light-dark(rgb(255 255 255 / 0.8), rgb(255 255 255 / 0.09)); + --glass-shine: inset 0 1px 0 light-dark(rgb(255 255 255 / 0.9), rgb(255 255 255 / 0.07)); + --blur: blur(28px) saturate(170%); + + /* Ink and lines */ + --fg: light-dark(#191a2e, #eff0fb); + --muted: light-dark(#4e536d, #a5aac5); + --faint: light-dark(#8a8ea6, #6f7493); + --line: light-dark(rgb(26 30 72 / 0.1), rgb(255 255 255 / 0.08)); + --band: light-dark(rgb(255 255 255 / 0.4), rgb(255 255 255 / 0.03)); + --track: light-dark(rgb(26 30 72 / 0.07), rgb(255 255 255 / 0.07)); + --hover: light-dark(rgb(255 255 255 / 0.6), rgb(255 255 255 / 0.06)); + + /* The one violet: a fill (a gradient to --accent-to), a tint, and text. */ + --accent: light-dark(#7c3aed, #a78bfa); + --accent-to: light-dark(#a855f7, #c9a7fc); + --accent-fill: linear-gradient(135deg, var(--accent), var(--accent-to)); + --accent-ink: light-dark(#6d28d9, #c4b5fd); + --on-accent: light-dark(#ffffff, #1a1033); + --tint: light-dark(rgb(124 58 237 / 0.12), rgb(167 139 250 / 0.17)); + --tint-hover: light-dark(rgb(124 58 237 / 0.19), rgb(167 139 250 / 0.26)); + + /* A room's state, only ever as a dot beside the words, and the one red. */ + --free: light-dark(#16a34a, #4ade80); + --busy: light-dark(#d97706, #fbbf24); + --danger: light-dark(#dc2626, #f87171); + + /* Depth and shape */ + --shadow-xs: 0 1px 2px light-dark(rgb(40 48 120 / 0.1), rgb(0 0 0 / 0.35)); + --shadow-card: 0 12px 32px -20px light-dark(rgb(40 48 120 / 0.4), rgb(0 0 0 / 0.7)); + --shadow: 0 32px 80px -32px light-dark(rgb(40 48 120 / 0.5), rgb(0 0 0 / 0.8)); + --radius-sm: 8px; + --radius: 12px; + --radius-lg: 16px; + --radius-xl: 22px; + --frame: clamp(0.5rem, 2.5vw, 2rem); + --pad: clamp(1rem, 2.5vw, 1.75rem); + /* DM Sans, loaded in layout.tsx */ + --font: var(--font-sans), system-ui, sans-serif; +} + +:root[data-theme="light"] { + color-scheme: light; +} + +:root[data-theme="dark"] { + color-scheme: dark; +} + +/* The scheduler, in glass: its surfaces are see-through, so the card under it + shows. :root[data-theme] outranks the package's own :root and + [data-theme=system] rules in both modes. */ :root[data-theme] { - --rs-background: var(--surface); - --rs-foreground: var(--ink); - --rs-card: var(--surface); - --rs-card-foreground: var(--ink); - --rs-popover: var(--surface); - --rs-popover-foreground: var(--ink); - --rs-primary: var(--link); - --rs-secondary: var(--paper); - --rs-secondary-foreground: var(--ink); - --rs-muted: var(--paper); + --rs-background: var(--glass-float); + --rs-foreground: var(--fg); + --rs-card: var(--glass-float); + --rs-card-foreground: var(--fg); + --rs-popover: var(--glass-float); + --rs-popover-foreground: var(--fg); + --rs-primary: var(--accent); + --rs-primary-foreground: var(--on-accent); + --rs-primary-text-mix: 100%; + --rs-secondary: var(--track); + --rs-secondary-foreground: var(--fg); + --rs-muted: var(--band); --rs-muted-foreground: var(--muted); - --rs-accent: color-mix(in srgb, var(--link) 12%, var(--surface)); - --rs-accent-foreground: var(--link); + --rs-accent: var(--hover); + --rs-accent-foreground: var(--fg); --rs-destructive: var(--danger); + --rs-destructive-foreground: #ffffff; + --rs-warning: var(--busy); --rs-border: var(--line); - --rs-input: var(--line-strong); - --rs-ring: var(--link); - --rs-surface: var(--surface); - --rs-surface-muted: color-mix(in srgb, var(--paper) 55%, var(--surface)); - --rs-surface-hover: color-mix(in srgb, var(--link) 6%, var(--surface)); - --rs-today: var(--surface); + --rs-input: var(--line); + --rs-ring: var(--accent); + --rs-surface: transparent; + --rs-surface-muted: var(--track); + --rs-surface-hover: var(--tint); + --rs-today: light-dark(rgb(124 58 237 / 0.05), rgb(167 139 250 / 0.06)); + --rs-unavailable: var(--line); + --rs-tooltip: var(--fg); + --rs-shadow-xs: var(--shadow-xs); + --rs-shadow-sm: var(--shadow-xs); + --rs-shadow-md: var(--shadow); --rs-radius: var(--radius); } @@ -81,37 +115,69 @@ box-sizing: border-box; } +html { + background: var(--page); +} + body { + min-height: 100dvh; margin: 0; - background: var(--paper); - color: var(--ink); - font-family: var(--font-text), system-ui, sans-serif; - font-size: 1rem; + color: var(--fg); + font-family: var(--font); + font-size: 0.9375rem; line-height: 1.5; -webkit-font-smoothing: antialiased; } +/* The scene: soft fields of color and a few crisp shapes, fixed, so the window + and its cards blur them differently as the page scrolls over them. */ +body::before, +body::after { + content: ""; + position: fixed; + z-index: -1; + pointer-events: none; +} + +body::before { + inset: 0; + background: + radial-gradient(circle 8.5rem at 6% 22%, var(--orb-blue) 97%, transparent), + radial-gradient(circle 7rem at 2% 94%, var(--orb-coral) 97%, transparent), + radial-gradient(circle 2.25rem at 94% 6%, var(--orb-mist) 94%, transparent), + radial-gradient(48% 42% at 86% 80%, color-mix(in srgb, var(--orb-coral) 60%, transparent), transparent), + radial-gradient(44% 40% at 62% 104%, var(--orb-violet), transparent), + radial-gradient(40% 46% at 100% 30%, color-mix(in srgb, var(--orb-violet) 70%, transparent), transparent), + radial-gradient(46% 50% at 0% 0%, color-mix(in srgb, var(--orb-blue) 60%, transparent), transparent); +} + +body::after { + top: -7rem; + left: 28%; + width: 44%; + height: 22rem; + border-radius: 10rem; + background: linear-gradient(120deg, var(--orb-indigo) 30%, var(--orb-violet)); +} + h1, -h2, -h3 { - font-family: var(--font-display), var(--font-text), sans-serif; - font-weight: 700; - line-height: 1.1; +h2 { margin: 0; + line-height: 1.25; } a { - color: var(--link); - text-underline-offset: 0.2em; + color: inherit; + text-decoration-color: var(--line); + text-underline-offset: 3px; } -code { - font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; - font-size: 0.8125rem; +a:hover { + text-decoration-color: currentColor; } :focus-visible { - outline: 2px solid var(--link); + outline: 2px solid var(--accent); outline-offset: 2px; } @@ -128,230 +194,572 @@ code { white-space: nowrap; } -.shell { - max-width: 90rem; - margin: 0 auto; - padding: 0 clamp(1rem, 3vw, 2.5rem) 2.5rem; +/* The window: on wide screens the sidebar beside the header and timeline, on + narrow ones the brand, the header, "Find a time", then the timeline. */ + +.app { + position: relative; + isolation: isolate; + display: grid; + grid-template-areas: "brand" "head" "side" "main"; + width: min(100% - 2 * var(--frame), 100rem); + min-height: calc(100dvh - 2 * var(--frame)); + margin: var(--frame) auto; + border: 1px solid var(--glass-edge); + border-radius: var(--radius-xl); + box-shadow: var(--shadow); } -/* Top bar */ +/* The glass is a layer of its own: a backdrop-filter on .app itself would make + it the containing block of the fixed toasts and pin them to the window. */ +.app::before { + content: ""; + position: absolute; + inset: 0; + z-index: -1; + border-radius: inherit; + background: var(--glass); + box-shadow: var(--glass-shine); + backdrop-filter: var(--blur); +} + +@media (min-width: 64rem) { + .app { + grid-template-columns: 16.5rem minmax(0, 1fr); + grid-template-rows: auto 1fr; + grid-template-areas: "brand head" "side main"; + } -.topbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 1rem; - padding: 1rem 0; - border-bottom: 1px solid var(--line); + .brand, + .sidebar { + border-right: 1px solid var(--line); + } } .brand { - display: inline-flex; + grid-area: brand; + display: flex; align-items: center; gap: 0.625rem; - color: var(--ink); - font-family: var(--font-display), sans-serif; - font-size: 1.375rem; - font-weight: 700; + padding: 1rem var(--pad) 0; + font-size: 1.125rem; + font-weight: 600; + letter-spacing: -0.01em; text-decoration: none; } .brand-mark { + flex: none; width: 1.75rem; height: 1.75rem; } -.topbar-nav { +.head { + grid-area: head; display: flex; + flex-wrap: wrap; align-items: center; + justify-content: space-between; + gap: 0.75rem 1.5rem; + padding: 1rem var(--pad); + border-bottom: 1px solid var(--line); +} + +.head h1 { + font-size: 1.25rem; + font-weight: 600; + letter-spacing: -0.015em; +} + +.head p { + max-width: 40rem; + margin: 0.125rem 0 0; + color: var(--muted); + font-size: 0.8125rem; +} + +.head-tools { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; +} + +/* The office clock, drawn like a search field. */ +.clock { + display: inline-flex; + align-items: center; + gap: 0.5rem; + min-height: 2.25rem; + padding: 0 0.875rem; + border: 1px solid var(--glass-edge); + border-radius: var(--radius-sm); + background: var(--glass-card); + color: var(--muted); + font-size: 0.8125rem; + font-variant-numeric: tabular-nums; + white-space: nowrap; +} + +.sidebar { + grid-area: side; + display: flex; + flex-direction: column; gap: 1.5rem; - font-size: 0.9375rem; + min-width: 0; + padding: 1.25rem var(--pad); + border-bottom: 1px solid var(--line); } -.topbar-link { +@media (min-width: 64rem) { + .brand { + padding: 0 1.25rem; + } + + .sidebar { + padding: 1.25rem; + border-bottom: 0; + } +} + +/* Links at the foot of the sidebar, drawn like its rows. */ +.side-link { + display: flex; + align-items: center; + gap: 0.625rem; + margin-top: auto; + padding: 0.5rem 0.625rem; + border-radius: var(--radius-sm); color: var(--muted); - font-weight: 500; + font-size: 0.875rem; text-decoration: none; + transition: background-color 150ms, color 150ms; } -.topbar-link:hover { - color: var(--ink); +.side-link:hover { + background: var(--hover); + color: var(--fg); } -/* Intro */ +.main { + grid-area: main; + min-width: 0; + padding: 1.5rem var(--pad) 2rem; +} -.intro { - margin: clamp(1.75rem, 4vw, 3rem) 0 1.75rem; +/* Section names, small and spaced, like the sidebar's in a mail app. */ +.panel h2, +.stack-section h2 { + color: var(--muted); + font-size: 0.6875rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; } -.intro h1 { - font-size: clamp(2.25rem, 1.6rem + 2.4vw, 3.5rem); - letter-spacing: -0.015em; +/* Find a time */ + +.choice { + min-width: 0; + margin: 1rem 0 0; + padding: 0; + border: 0; } -.intro p { - max-width: 42rem; - margin: 0.625rem 0 0; +.choice legend { + margin-bottom: 0.375rem; + padding: 0; color: var(--muted); - font-size: 1.125rem; + font-size: 0.8125rem; } -/* Right now: one plate per room */ +/* Radio buttons, drawn as a segmented control. */ +.choice-options { + display: grid; + grid-auto-columns: 1fr; + grid-auto-flow: column; + gap: 2px; + padding: 3px; + border-radius: var(--radius-sm); + background: var(--track); +} -.now { - margin-bottom: 2.5rem; +.choice label { + position: relative; + padding: 0.3125rem 0; + border-radius: 6px; + color: var(--muted); + font-size: 0.8125rem; + font-weight: 500; + font-variant-numeric: tabular-nums; + text-align: center; + white-space: nowrap; + cursor: pointer; + transition: color 150ms, background-color 150ms, box-shadow 150ms; } -.now-head { - display: flex; - flex-wrap: wrap; - align-items: baseline; - gap: 0.25rem 1rem; - margin-bottom: 0.875rem; +.choice label:hover { + color: var(--fg); } -.now-head h2, -.board-head h2 { - font-size: 1.375rem; +.choice label:has(:checked) { + background: var(--glass-raised); + color: var(--fg); + box-shadow: var(--shadow-xs); } -.clock { +.choice label:has(:focus-visible) { + outline: 2px solid var(--accent); + outline-offset: 1px; +} + +.choice input { + position: absolute; + inset: 0; margin: 0; + opacity: 0; + cursor: pointer; +} + +.picks { + display: grid; + gap: 1rem; + margin-top: 1.5rem; +} + +.pick-time { + margin: 0 0 0.25rem; color: var(--muted); + font-size: 0.75rem; + font-weight: 600; font-variant-numeric: tabular-nums; } -.plates { +.pick-rooms { display: grid; - grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); - gap: 0.75rem; - margin: 0; + gap: 2px; + margin: 0 -0.625rem; padding: 0; list-style: none; } -.plate { - position: relative; - display: flex; - flex-direction: column; - gap: 0.125rem; - min-width: 0; - padding: 0.875rem 1rem 1rem 1.25rem; - border: 1px solid var(--line); - border-radius: var(--radius); - background: var(--surface); +/* A whole room is its button, lifted like a chosen row on hover; "Book" takes + the place of the seats. */ +.pick { + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + width: 100%; + min-height: 2.25rem; + padding: 0 0.625rem; + border: 1px solid transparent; + border-radius: var(--radius-sm); + background: none; + color: inherit; + font: inherit; + font-size: 0.875rem; + font-weight: 500; + text-align: start; + cursor: pointer; + transition: background-color 150ms, border-color 150ms, box-shadow 150ms; +} + +.pick:is(:hover, :focus-visible) { + border-color: var(--glass-edge); + background: var(--hover); + box-shadow: var(--shadow-xs); +} + +.pick-seats, +.pick-go { + grid-area: 1 / 2; + justify-self: end; + font-size: 0.75rem; + transition: opacity 150ms; +} + +/* A count, like the badges beside a sidebar's rows. */ +.pick-seats { + padding: 0 0.4375rem; + border: 1px solid var(--glass-edge); + border-radius: 6px; + background: var(--glass-card); + color: var(--muted); + font-variant-numeric: tabular-nums; + line-height: 1.375rem; +} + +.pick-go { + color: var(--accent-ink); + font-weight: 600; + opacity: 0; +} + +.pick:is(:hover, :focus-visible) .pick-seats { + opacity: 0; +} + +.pick:is(:hover, :focus-visible) .pick-go { + opacity: 1; +} + +.picks-empty { + margin: 1.25rem 0 0; + color: var(--muted); + font-size: 0.875rem; +} + +/* Timeline */ + +/* The scheduler grows to fit its rooms and scrolls sideways through the day. + With many rooms, give the frame a fixed height instead: the scheduler then + scrolls inside it and only draws the rows on screen. */ +.board-frame { + border: 1px solid var(--glass-edge); + border-radius: var(--radius-lg); + background: var(--glass-card); + box-shadow: var(--shadow-card); overflow: hidden; } -/* The floor's color down the left edge, like a wayfinding sign. */ -.plate::before { - content: ""; - position: absolute; - inset: 0 auto 0 0; - width: 5px; - background: var(--floor); +/* Three of the scheduler's parts need a surface of their own: the date + buttons, the chosen view, and the room column, which frosts the bookings + that scroll under it. */ +.rs-root .rs-nav { + border-color: var(--glass-edge); + background: var(--glass-raised); +} + +.rs-toolbar [aria-pressed="true"] { + background: var(--glass-raised); +} + +.rs-root .rs-resources { + background: var(--glass-float); + backdrop-filter: blur(16px); } -.plate-head { +.rs-scroll { + scrollbar-color: light-dark(rgb(26 30 72 / 0.25), rgb(255 255 255 / 0.2)) transparent; + scrollbar-width: thin; +} + +/* A booking's details: a frosted card over the timeline, grown from the booking. */ +.rs-root[data-slot="popover-content"] { + border-color: var(--glass-edge); + border-radius: var(--radius); + box-shadow: var(--shadow), var(--glass-shine); + backdrop-filter: var(--blur); +} + +.rs-root[data-slot="popover-content"][data-state="open"] { + animation: pop 160ms ease-out; +} + +/* Every other slot ends on the half hour (the day opens on the hour). "of .slot" + skips the bookings layer that some rows start with. */ +.slot:nth-child(odd of .slot) { + border-inline-end-color: color-mix(in srgb, var(--line) 45%, transparent); +} + +/* A room in the resource column, drawn by renderResourceHeader. */ +.room { + display: grid; + gap: 0.125rem; + width: 100%; + min-width: 0; +} + +.room-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; } -.plate-name { - font-family: var(--font-display), sans-serif; - font-size: 1.625rem; - font-weight: 700; - line-height: 1.1; +.room-name { + overflow: hidden; + font-weight: 600; + text-overflow: ellipsis; + white-space: nowrap; } -.plate-seats, -.plate-floor { +.room-seats { + flex: none; color: var(--muted); - font-size: 0.8125rem; + font-size: 0.75rem; + font-variant-numeric: tabular-nums; +} + +.room-status { + overflow: hidden; + color: var(--muted); + font-size: 0.75rem; + font-variant-numeric: tabular-nums; + text-overflow: ellipsis; white-space: nowrap; } -.plate-floor { - color: var(--floor); - font-weight: 600; +.room-status::before { + content: ""; + display: inline-block; + width: 0.375rem; + height: 0.375rem; + margin: 0 0.375rem 0.0625rem 0; + border-radius: 50%; + background: var(--faint); + vertical-align: middle; +} + +.room-status[data-state="free"]::before { + background: var(--free); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--free) 20%, transparent); } -.plate-status { +.room-status[data-state="busy"]::before { + background: var(--busy); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--busy) 20%, transparent); +} + +/* A booking on the timeline, drawn by renderTimeSlot: a tint of the violet, or + the violet itself when it is yours. */ +.rs-root [data-rs-event] { + border-radius: var(--radius-sm); +} + +.booking { + position: absolute; + inset: 0 1px; display: flex; - align-items: center; - gap: 0.5rem; - margin: 0.75rem 0 0; - font-weight: 600; - font-size: 0.9375rem; + flex-direction: column; + justify-content: center; + gap: 1px; + padding: 0 0.5rem 0 0.625rem; + border-radius: var(--radius-sm); + background: var(--tint); + box-shadow: inset 2px 0 0 var(--accent); + color: var(--fg); + font-size: 0.8125rem; + line-height: 1.25; + container-type: inline-size; + transition: background-color 150ms, filter 150ms; } -.plate-status::before { - content: ""; - flex: none; - width: 0.5rem; - height: 0.5rem; - border-radius: 50%; - background: currentColor; +[data-rs-event]:hover .booking { + background: var(--tint-hover); } -.plate[data-state="free"] .plate-status { - color: var(--free); +.booking[data-mine] { + background: var(--accent-fill); + box-shadow: 0 6px 16px -8px var(--accent); + color: var(--on-accent); } -.plate[data-state="busy"] .plate-status { - color: var(--busy); +[data-rs-event]:hover .booking[data-mine] { + background: var(--accent-fill); + filter: brightness(1.08); } -.plate[data-state="closed"] .plate-status { - color: var(--muted); +.booking[data-pending] { + border: 1px dashed var(--accent); + background: var(--band); + box-shadow: none; + color: var(--fg); } -.plate-title { - margin: 0.125rem 0 0 1rem; +.booking-title, +.booking-time { overflow: hidden; - color: var(--muted); - font-size: 0.875rem; text-overflow: ellipsis; white-space: nowrap; } -.plate-book { - align-self: flex-start; - margin-top: 0.75rem; +.booking-title { + font-weight: 600; } -/* Timeline */ +.booking-time { + font-size: 0.75rem; + font-variant-numeric: tabular-nums; + opacity: 0.8; +} -.board-head { +/* Too narrow for the times: the title gets the room. */ +@container (max-width: 6rem) { + .booking-time { + display: none; + } +} + +.board-foot { display: flex; flex-wrap: wrap; - align-items: baseline; - gap: 0.25rem 1rem; - margin-bottom: 0.875rem; + align-items: center; + justify-content: space-between; + gap: 0.5rem 1.5rem; + margin-top: 0.875rem; + color: var(--muted); + font-size: 0.8125rem; } -.board-head p { - flex: 1 1 24rem; +/* The key: one swatch per kind of booking, drawn like the bookings. */ +.legend { + display: flex; + flex-wrap: wrap; + gap: 0.25rem 1rem; margin: 0; - font-size: 0.9375rem; + padding: 0; + list-style: none; } -.loading { - color: var(--muted); +.legend li { + display: inline-flex; + align-items: center; + gap: 0.375rem; +} + +.legend li::before { + content: ""; + width: 0.875rem; + height: 0.625rem; + border-radius: 3px; + background: var(--tint); + box-shadow: inset 2px 0 0 var(--accent); +} + +.legend .mine::before { + background: var(--accent-fill); + box-shadow: none; +} + +.legend .pending::before { + border: 1px dashed var(--accent); + background: none; + box-shadow: none; +} + +/* A booking's details, opened from the timeline */ + +.details { + display: grid; + gap: 0.875rem; font-size: 0.875rem; } -/* The scheduler fills its parent, so the parent needs a height. */ -.board-frame { - height: min(35rem, 78vh); - min-height: 26rem; - border: 1px solid var(--line); - border-radius: calc(var(--radius) + 2px); - background: var(--surface); - box-shadow: var(--shadow); - overflow: hidden; +.details p { + margin: 0; +} + +.details .details-title { + margin-bottom: 0.25rem; + font-size: 1rem; + font-weight: 600; +} + +.details-actions { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; } /* Messages after a change */ @@ -371,14 +779,14 @@ code { align-items: center; gap: 0.75rem; max-width: 36rem; - padding: 0.625rem 0.625rem 0.625rem 1rem; - border: 1px solid var(--line-strong); + padding: 0.5rem 0.5rem 0.5rem 1rem; + border: 1px solid var(--glass-edge); border-radius: var(--radius); - background: var(--surface); - box-shadow: var(--shadow); - font-size: 0.9375rem; + background: var(--glass-float); + box-shadow: var(--shadow), var(--glass-shine); + backdrop-filter: var(--blur); pointer-events: auto; - animation: toast-in 180ms ease-out; + animation: rise 220ms cubic-bezier(0.2, 0.8, 0.2, 1); } .toast::before { @@ -408,92 +816,144 @@ code { } .toast-close:hover { - background: var(--paper); - color: var(--ink); + background: var(--hover); + color: var(--fg); } -@keyframes toast-in { +@keyframes rise { + from { + opacity: 0; + transform: translateY(0.75rem); + } +} + +@keyframes pop { + from { + opacity: 0; + transform: translateY(0.25rem) scale(0.97); + } +} + +@keyframes fade { from { opacity: 0; - transform: translateY(0.5rem); } } @media (prefers-reduced-motion: reduce) { - .toast { + .toast, + .dialog[open], + .dialog[open]::backdrop, + .rs-root[data-slot="popover-content"][data-state="open"] { animation: none; } } -/* Buttons */ +/* Buttons: glass raised off the window, and the violet for the main action. */ .button { display: inline-flex; align-items: center; justify-content: center; - min-height: 2.375rem; - padding: 0.5rem 1rem; - border: 1px solid var(--line-strong); - border-radius: 8px; - background: var(--surface); - color: var(--ink); + gap: 0.5rem; + min-height: 2.25rem; + padding: 0 0.875rem; + border: 1px solid var(--glass-edge); + border-radius: var(--radius-sm); + background: var(--glass-raised); + box-shadow: var(--shadow-xs); + color: var(--fg); font: inherit; - font-size: 0.9375rem; - font-weight: 600; + font-size: 0.875rem; + font-weight: 500; text-decoration: none; white-space: nowrap; cursor: pointer; - transition: background-color 120ms, border-color 120ms; + transition: background-color 150ms, border-color 150ms, color 150ms, filter 150ms; } .button:hover { - background: color-mix(in srgb, var(--ink) 5%, var(--surface)); + background: light-dark(#ffffff, rgb(255 255 255 / 0.16)); +} + +.button svg { + flex: none; + width: 1rem; + height: 1rem; } .button.primary { - border-color: var(--action); - background: var(--action); - color: var(--action-fg); + border-color: transparent; + background: var(--accent-fill); + box-shadow: 0 8px 20px -10px var(--accent); + color: var(--on-accent); } .button.primary:hover { - background: color-mix(in srgb, var(--action) 86%, var(--paper)); + filter: brightness(1.08); } .button.danger { - border-color: color-mix(in srgb, var(--danger) 45%, transparent); color: var(--danger); } +.button.danger:hover { + border-color: color-mix(in srgb, var(--danger) 35%, transparent); + background: color-mix(in srgb, var(--danger) 10%, transparent); +} + +.button.icon { + width: 2.25rem; + padding: 0; + color: var(--muted); +} + +.button.icon:hover { + color: var(--fg); +} + .button.small { min-height: 2rem; - padding: 0.25rem 0.75rem; - font-size: 0.875rem; + padding: 0 0.75rem; + font-size: 0.8125rem; } -/* Booking form */ +/* Booking form: a frosted card over a dimmed, blurred page. */ .dialog { - width: min(28rem, calc(100vw - 2rem)); + width: min(26rem, calc(100vw - 2rem)); padding: 1.5rem; - border: 1px solid var(--line); - border-radius: 14px; - background: var(--surface); - color: var(--ink); - box-shadow: 0 24px 64px -16px rgb(10 15 30 / 0.4); + border: 1px solid var(--glass-edge); + border-radius: var(--radius-lg); + background: var(--glass-float); + box-shadow: var(--shadow), var(--glass-shine); + color: var(--fg); + backdrop-filter: var(--blur); +} + +.dialog[open] { + animation: pop 220ms cubic-bezier(0.2, 0.8, 0.2, 1); } .dialog::backdrop { - background: rgb(13 18 27 / 0.5); + background: light-dark(rgb(40 46 110 / 0.18), rgb(3 4 18 / 0.55)); + backdrop-filter: blur(4px); +} + +.dialog[open]::backdrop { + animation: fade 220ms ease-out; } .dialog h2 { - font-size: 1.75rem; + font-size: 1.25rem; + font-weight: 600; + letter-spacing: -0.015em; } .dialog-when { margin: 0.375rem 0 1.25rem; - font-size: 0.9375rem; + color: var(--muted); + font-variant-numeric: tabular-nums; } .field { @@ -504,22 +964,24 @@ code { display: block; margin-bottom: 0.375rem; font-size: 0.875rem; - font-weight: 600; + font-weight: 500; } .field input { width: 100%; - padding: 0.625rem 0.75rem; - border: 1px solid var(--line-strong); - border-radius: 8px; - background: var(--paper); - color: var(--ink); + padding: 0.5rem 0.75rem; + border: 1px solid var(--line); + border-radius: var(--radius-sm); + background: var(--glass-raised); + color: var(--fg); font: inherit; + transition: border-color 150ms, box-shadow 150ms; } .field input:focus-visible { - outline-offset: 0; - border-color: var(--link); + outline: 0; + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--tint); } .field input[aria-invalid="true"] { @@ -539,196 +1001,151 @@ code { margin-top: 1.5rem; } -/* A booking's details, opened from the timeline */ - -.details { - display: grid; - gap: 0.625rem; - font-size: 0.875rem; -} +/* Built with: one card per part, the whole card its link. */ -.details p { - margin: 0; +.stack-section { + margin-top: 2.5rem; } -.details-title { - font-family: var(--font-display), sans-serif; - font-size: 1.125rem; - font-weight: 700; -} - -/* How it works */ - -.how { - margin-top: clamp(3rem, 7vw, 5rem); - padding-top: clamp(2rem, 4vw, 3rem); - border-top: 1px solid var(--line); -} - -.how h2, -.stack h2 { - max-width: 40rem; - font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem); - letter-spacing: -0.01em; -} - -.lede { - max-width: 40rem; - margin: 0.75rem 0 2rem; - color: var(--muted); - font-size: 1.0625rem; -} - -/* The three checks, in the order a booking meets them. */ -.steps { +/* Narrow enough for all five in a row from a 1280-pixel screen up. */ +.stack { display: grid; - grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); - gap: 1rem; - margin: 0; + grid-template-columns: repeat(auto-fit, minmax(min(100%, 10.5rem), 1fr)); + gap: 0.875rem; + margin: 0.875rem 0 0; padding: 0; list-style: none; - counter-reset: step; } -.steps li { +.card { + position: relative; display: flex; flex-direction: column; - gap: 0.5rem; - padding: 1.25rem; - border: 1px solid var(--line); - border-radius: var(--radius); - background: var(--surface); - counter-increment: step; -} - -.steps h3 { - display: flex; - align-items: center; - gap: 0.625rem; - font-size: 1.25rem; -} - -.steps h3::before { - content: counter(step); - display: grid; - place-items: center; - width: 1.75rem; - height: 1.75rem; - border-radius: 50%; - background: var(--ink); - color: var(--paper); - font-size: 0.9375rem; -} - -.steps p { - margin: 0; - color: var(--muted); + border: 1px solid var(--glass-edge); + border-radius: var(--radius-lg); + background: var(--glass-card); + box-shadow: var(--shadow-card); + overflow: hidden; + transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 200ms; } -.steps code { - align-self: flex-start; - margin-top: auto; - padding: 0.25rem 0.5rem; - border-radius: 6px; - background: var(--paper); - color: var(--ink); +.card:hover { + box-shadow: var(--shadow-card), 0 0 0 1px var(--glass-edge); + transform: translateY(-2px); } -.proof { - margin: 1.25rem 0 0; - font-weight: 600; +@media (prefers-reduced-motion: reduce) { + .card:hover { + transform: none; + } } -/* Built on Prisma */ - -.stack { +.card-top { display: grid; - grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); - gap: 2rem clamp(2rem, 5vw, 5rem); - margin-top: clamp(3rem, 7vw, 5rem); - padding-top: clamp(2rem, 4vw, 3rem); - border-top: 1px solid var(--line); + gap: 0.875rem; + padding: 1rem 1rem 0.875rem; + background: var(--band); } -.live { - margin: 0.875rem 0 0; - color: var(--muted); - font-size: 1.0625rem; +.card-title { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; } -.live strong { - color: var(--ink); - font-variant-numeric: tabular-nums; +.card-title a { + font-size: 0.9375rem; + font-weight: 600; + text-decoration: none; } -.actions { - display: flex; - flex-wrap: wrap; - gap: 0.625rem; - margin-top: 1.5rem; +.card-title a::after { + content: ""; + position: absolute; + inset: 0; } -.agent-note { - max-width: 30rem; - margin: 1.25rem 0 0; +.tag { + flex: none; + padding: 0 0.5rem; + border: 1px solid var(--glass-edge); + border-radius: 6px; + background: var(--glass-raised); color: var(--muted); - font-size: 0.9375rem; + font-size: 0.6875rem; + line-height: 1.375rem; } -.stack-list { +.card p { margin: 0; + padding: 0.875rem 1rem 1rem; + border-top: 1px solid var(--line); + color: var(--muted); + font-size: 0.8125rem; } -.stack-list div { +/* Each logo on a small tile, the same size whatever its shape. */ +.stack-logo { display: grid; - grid-template-columns: 11rem 1fr; - gap: 0.25rem 1.5rem; - padding: 1rem 0; - border-bottom: 1px solid var(--line); -} - -.stack-list div:first-child { - padding-top: 0.25rem; + place-items: center; + width: 2.25rem; + height: 2.25rem; + border: 1px solid var(--glass-edge); + border-radius: 10px; + background: var(--glass-raised); + box-shadow: var(--shadow-xs); } -.stack-list dt { - font-family: var(--font-display), sans-serif; - font-size: 1.125rem; - font-weight: 700; +.stack-logo svg { + width: 1.125rem; + height: 1.125rem; } -.stack-list dt a { - color: var(--ink); - text-decoration: none; -} +/* Print the timeline alone, in light, without the glass; the scheduler's own + print styles lay it out. */ +@media print { + :root[data-theme] { + color-scheme: light; + } -.stack-list dt a:hover { - color: var(--link); -} + html { + background: none; + } -.stack-list dd { - margin: 0; - color: var(--muted); -} + body::before, + body::after, + .app::before, + .brand, + .head-tools, + .sidebar, + .board-foot, + .stack-section, + .toast-region { + display: none; + } -.footer { - margin-top: clamp(3rem, 6vw, 4.5rem); - padding-top: 1.5rem; - border-top: 1px solid var(--line); - color: var(--muted); - font-size: 0.875rem; -} + .app { + display: block; + width: auto; + min-height: 0; + margin: 0; + border: 0; + box-shadow: none; + } -.footer p { - margin: 0; -} + .head { + padding: 0 0 1rem; + border: 0; + } -@media (max-width: 40rem) { - .topbar-link { - display: none; + .main { + padding: 0; } - .stack-list div { - grid-template-columns: 1fr; + .board-frame { + border: 0; + background: none; + box-shadow: none; } } diff --git a/compute/room-booking-prisma-template/src/app/icon.svg b/compute/room-booking-prisma-template/src/app/icon.svg index 4c6f33ad683c..bb9cdfb59ae2 100644 --- a/compute/room-booking-prisma-template/src/app/icon.svg +++ b/compute/room-booking-prisma-template/src/app/icon.svg @@ -1 +1 @@ - + diff --git a/compute/room-booking-prisma-template/src/app/layout.tsx b/compute/room-booking-prisma-template/src/app/layout.tsx index 341585afbb5a..18b2500ed547 100644 --- a/compute/room-booking-prisma-template/src/app/layout.tsx +++ b/compute/room-booking-prisma-template/src/app/layout.tsx @@ -1,11 +1,12 @@ import type { Metadata, Viewport } from "next"; -import { Barlow, Barlow_Semi_Condensed } from "next/font/google"; +import { DM_Sans } from "next/font/google"; import "./globals.css"; -// Barlow is drawn after road and wayfinding signs, which is what a room board is. -const text = Barlow({ subsets: ["latin"], weight: ["400", "500", "600"], variable: "--font-text" }); -const display = Barlow_Semi_Condensed({ subsets: ["latin"], weight: ["600", "700"], variable: "--font-display" }); +// One calm, geometric family for everything; tabular figures line the times up. +const sans = DM_Sans({ subsets: ["latin"], variable: "--font-sans" }); + +const themeScript = `try{var t=localStorage.getItem("theme");if(t==="light"||t==="dark")document.documentElement.dataset.theme=t}catch(e){}`; export const metadata: Metadata = { title: "Rooms: meeting room booking on Prisma", @@ -15,15 +16,20 @@ export const metadata: Metadata = { export const viewport: Viewport = { themeColor: [ - { media: "(prefers-color-scheme: light)", color: "#f2f4f7" }, - { media: "(prefers-color-scheme: dark)", color: "#0d121b" }, + { media: "(prefers-color-scheme: light)", color: "#e3eaf7" }, + { media: "(prefers-color-scheme: dark)", color: "#090b27" }, ], }; export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { return ( - // data-theme="system": the scheduler follows the OS light or dark setting, like the page. - + // data-theme="system": the page and the scheduler follow the OS light or dark + // setting, until the theme button picks one. The script applies that choice + // before the first paint, so React is told the attribute may differ. + + +