chore(studio): move the TanStack Start deploy onto Nitro (#50030)

Moves the Studio TanStack Start build off the hand-rolled Vercel setup
(an `api/server.js` function shim, rewrites in `vercel.ts`, a custom
`?dpl=` skew-protection Vite plugin, and `scripts/serve.js` for
self-hosted) and onto Nitro, which TanStack Start documents as its
deployment path. Documents are served from the static SPA shell on the
CDN; only `/api/*` and `/_serverFn/*` invoke the function.

**Removed:**
- `api/server.js`, `scripts/serve.js`, `scripts/smoke-server.mjs`
- The `skewProtectionDpl` Vite plugin, `renderBuiltUrl`, and the
`vite:preloadError` reload backstop in `router.tsx` (TanStack Router
already reloads once on a failed lazy import)
- Rewrites, `functions`, `outputDirectory`, and `cleanUrls` from
`vercel.ts` (redirects and headers stay)
- `magic-string` and `@jridgewell/remapping` devDependencies, the
`preview` script

**Added:**
- `nitro` plugin in `vite.config.ts`. Preset is auto-detected:
`.vercel/output` on Vercel, a self-contained node server in `.output`
everywhere else. `vercel.immutableStaticFiles` puts hashed chunks under
`/_vercel/immutable/` so tabs opened before a redeploy keep loading
their chunks; `functions.maxDuration: 300` carries over the old function
timeout
- `scripts/vercel-spa-routes.ts`: Nitro module that rewrites the
generated Build Output routes (documents -> `_shell.html`, allow-list ->
`__server`, missing chunk -> 404, base-path prefixes), with a unit test
- `server.ts`: TanStack Start server entry that initializes Sentry
before the route tree loads and wraps the handler with
`wrapFetchWithSentry`

**Changed:**
- `start:tanstack` runs `.output/server/index.mjs` directly with Node's
`--env-file-if-exists` for the `.env` cascade. Node doesn't expand
`$VAR` references, so `scripts/generateLocalEnv.js` now writes literal
values into `.env.test`
- Dockerfile's TanStack stage copies `.output` instead of running `pnpm
deploy`; the `server.js` shim loads `.env` and imports the Nitro server
- `NEXT_PUBLIC_BASE_PATH` (the platform's `/dashboard`) only sets the
router basepath; Vite's `base` stays at the root so chunks can use the
immutable store. The routes module emits prefixed rules for
`/dashboard/api/*` and `/dashboard/_serverFn/*` and rewrites `public/`
files requested under the prefix back to the root
- Self-hosted security headers come from a Nitro `routeRules` entry; on
Vercel they stay in `vercel.ts`
- `tslib` is inlined for the build only: Nitro's dev runner has no
interop for its CJS wrapper
- Monaco's worker chunks follow the client assets dir so they land in
the immutable store too

Verified on the `studio-staging` preview (`STUDIO_FRAMEWORK=tanstack` is
scoped to this branch there): documents come back as the static shell,
`/dashboard/api/*` hits the function, `public/` files resolve under the
prefix, a missing immutable chunk 404s. Across two deployments of this
branch, the older deployment's chunks still load from the immutable
store and requests carrying its `__vdpl` cookie are answered by that
deployment. Self-hosted path covered by the TanStack E2E job and the
Docker build job.

## To test

- On the `studio-staging` preview: `/dashboard/project/<ref>` should
show `content-disposition: inline; filename="_shell.html"` and a
single-region `x-vercel-id`; `/dashboard/api/get-utc-time` a two-region
id
- Sign in and click through a few pages, including one that opens Monaco
(SQL editor) so the worker chunks load
- After the next deploy, a tab left open on the previous one should
still navigate (lazy chunks) and call the API without errors
- Self-hosted: `STUDIO_FRAMEWORK=tanstack pnpm --filter studio build &&
pnpm --filter studio start`, then check `/api/platform/profile` and that
responses carry the security headers


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Production TanStack deployments now run on Nitro’s self-contained
server output.
* Vercel routing serves static pages first while directing API and
server-function requests appropriately.
* Server-function requests can include deployment identification for
consistent handling.
* Local environment generation now writes resolved configuration values.

* **Bug Fixes**
  * Improved handling of missing static assets and SPA fallback routing.
* Server-side error monitoring now captures request errors in the new
runtime.

* **Refactor**
* Replaced the legacy production server and smoke-test workflow with
Nitro-based startup.
  * Removed automatic reload handling for stale client assets.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
This commit is contained in:
Alaister YoungandAlaister Young authored and GitHub committed 2026-09-15 21:46:45 +10:00
1 parent 0db2667819
commit 3bac7165bd
23 files changed
+750 -846

No files matched your search

+2
View File
@@ -1,3 +1,5 @@
# Default for the TanStack server (`pnpm start`); shell env and later env files override it.
PORT=8082
STUDIO_PG_META_URL=http://localhost:8000/pg
POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password
+13 -35
View File
@@ -71,42 +71,20 @@ RUN mkdir -p /srv && \
cp -a apps/studio/.next/static /srv/apps/studio/.next/static && \
cp -a apps/studio/public /srv/apps/studio/public
# Compile TanStack Start (Vite)
# Compile TanStack Start (Vite + Nitro)
FROM dev AS build-tanstack
# build:tanstack = vite build --mode production, then a smoke test that
# boots the server bundle so module-scope crashes fail the image build.
RUN NODE_OPTIONS=--max-old-space-size=4096 pnpm --filter studio run build:tanstack
# Assemble the runtime tree at /srv. Unlike Next's standalone output, the
# Vite SSR bundle externalizes studio's dependencies and resolves them from
# node_modules at request time, so the tree is a prod-only `pnpm deploy` of
# studio (node_modules + manifest) plus the built dist/ and the runtime
# scripts. `scripts/serve.js` is the HTTP server (the same entry
# start:tanstack uses); the server.js shim gives the production stage a
# single CMD that works for both frameworks. `.env` is kept because
# serve.js loads it as the base of the runtime env cascade (container env
# vars always win over file values).
#
# --ignore-scripts: pnpm 11 hard-errors (ERR_PNPM_IGNORED_BUILDS) on
# dependency build scripts without an allowBuilds entry, and deploy turns
# the workspace packages into file: deps whose `only-allow pnpm` preinstall
# guards trip it. No lifecycle script is needed here anyway: the tree is
# fully prebuilt, and nothing in studio's prod graph is approved to build
# (allowBuilds only permits node-pty and supabase, both dev-only).
RUN pnpm --filter studio deploy --prod --legacy --ignore-scripts /srv/apps/studio && \
cd /srv/apps/studio && \
find . -mindepth 1 -maxdepth 1 \
! -name node_modules ! -name package.json ! -name scripts \
! -name instrument.server.mjs ! -name .env \
-exec rm -rf {} + && \
cp -a /app/apps/studio/dist ./dist && \
printf "import('./scripts/serve.js')\n" > server.js
# Boot the pruned tree exactly the way the container will run it, so a
# dependency that's runtime-imported but missing from `dependencies`
# (present only in devDependencies) fails the build here instead of
# 500ing the deployed container.
RUN cd /srv/apps/studio && node scripts/smoke-server.mjs
# Assemble the runtime tree at /srv. Nitro's node-server output is
# self-contained (`.output/server` bundles the app with its traced
# dependencies, `.output/public` holds the client assets), so no node_modules
# install is needed. The server.js shim loads `.env` (container env vars win)
# and gives the production stage one CMD for both frameworks; package.json is
# copied so the shim runs as ESM.
RUN mkdir -p /srv/apps/studio && \
cp -a apps/studio/.output /srv/apps/studio/.output && \
cp apps/studio/package.json apps/studio/.env /srv/apps/studio/ && \
printf "process.loadEnvFile(new URL('.env', import.meta.url))\nawait import('./.output/server/index.mjs')\n" > /srv/apps/studio/server.js
# Alias whichever framework build was selected so the production stage can
# COPY from a single stage name. BuildKit only builds the selected branch.
@@ -115,8 +93,8 @@ FROM build-${STUDIO_FRAMEWORK} AS build
# Copy only compiled code and dependencies
FROM base AS production
COPY --from=build /srv ./
# serve.js (TanStack) defaults to port 8082; pin both servers to the port
# the healthcheck and compose files expect. Next's server.js reads PORT too.
# Both servers read PORT (the TanStack `start` script defaults it to 8082);
# pin it to the port the healthcheck and compose files expect.
ENV PORT=3000
EXPOSE 3000
ENTRYPOINT ["docker-entrypoint.sh"]
-50
View File
@@ -1,50 +0,0 @@
// STUDIO_FRAMEWORK gates whether this function actually serves the TanStack
// SSR handler. Vercel auto-detects every file under /api as a Function
// regardless of the framework preset (vercel.com/docs/functions), so we
// can't keep this file from being deployed in the Next.js prod build — we
// just make it inert when the env var is unset.
const isTanstack = process.env.STUDIO_FRAMEWORK === 'tanstack'
// Computed path keeps `dist/server/server.js` out of Vercel's function
// bundler's static analysis. In the Next.js prod deploy the `dist/` tree
// doesn't exist, but Vercel still bundles this file because it lives under
// `api/`. With the .join() the bundler treats the import as runtime-only
// and the missing dist/ isn't a build error. In TanStack mode the SSR
// bundle is shipped into the function via the `functions['api/server.js']
// .includeFiles` config in vercel.ts.
const tanstackEntry = ['..', 'dist', 'server', 'server.js'].join('/')
// Initialize server-side Sentry BEFORE the handler module is imported, so its
// instrumentation is in place when route modules evaluate. Gated to TanStack
// (the Next deploy uses instrumentation.ts / sentry.server.config.ts instead).
// Vercel functions can't use a `--import` startup flag, so we import the
// instrument module here at boot. Vercel provides env vars via process.env.
// A Sentry boot failure must never take the API down — mirror scripts/serve.js
// and fall back to the identity wrapper if init or the SDK import throws.
let wrapFetchWithSentry = (fetchHandler) => fetchHandler
if (isTanstack) {
try {
await import('../instrument.server.mjs')
} catch (err) {
console.warn('[api/server] Sentry server init skipped:', err?.message ?? err)
}
;({ wrapFetchWithSentry } = await import('@sentry/tanstackstart-react').catch(() => ({
wrapFetchWithSentry: (fetchHandler) => fetchHandler,
})))
}
const rawHandler = isTanstack
? (await import(tanstackEntry)).default
: { fetch: () => new Response('Not Found', { status: 404 }) }
// Wrap the fetch handler so request-scoped errors (including those swallowed
// into a 500 downstream) are captured with request context.
const handler = isTanstack
? { ...rawHandler, fetch: wrapFetchWithSentry(rawHandler.fetch.bind(rawHandler)) }
: rawHandler
// Vercel's Web API handler convention: export an object with `fetch(request)`.
// TanStack's server build is already shaped that way — default-export it
// verbatim and Vercel hands us a real Web Request.
// eslint-disable-next-line no-restricted-exports
export default handler
@@ -5,12 +5,10 @@ import { BASE_PATH } from '@/lib/constants'
import type { ResponseError, UseCustomQueryOptions } from '@/types'
export async function getDeploymentCommit() {
// Deliberately unpinned: API fetches never carry the `?dpl=` skew-protection
// pin — only built asset URLs do (TanStack, see skewProtectionDpl in
// vite.config.ts) — so Vercel's edge routes this to the LATEST deployment
// and the check can detect a newer version while the session's assets stay
// pinned. We keep the basePath URL so it still routes to studio in
// production (root `/api/*` there is the marketing site).
// Deliberately unpinned: TanStack only adds x-deployment-id to server-function
// calls (start.ts), and Nitro's session cookie is disabled. This API request
// sees the latest deployment while older assets stay in the immutable store.
// Keep the basePath so www routes this request to Studio in production.
const response = await fetchHandler(`${BASE_PATH}/api/get-deployment-commit`)
return (await response.json()) as { commitSha: string; commitTime: string }
}
@@ -27,9 +27,8 @@ const DeployCheckToast = ({ id }: { id: string | number }) => {
<Button
variant="primary"
onClick={() => {
// Document requests carry no skew-protection pin (only built
// asset URLs do — see skewProtectionDpl in vite.config.ts), so a
// plain reload lands on the latest deployment.
// Vercel never pins document navigations to a deployment, so a
// plain reload lands on the latest one.
router.reload()
}}
>
+5 -10
View File
@@ -1,14 +1,9 @@
// Server-side Sentry init for the TanStack Start runtime.
// Server-side Sentry init for the TanStack Start runtime. Mirrors
// sentry.server.config.ts (Next) with the `@sentry/tanstackstart-react` SDK.
// server.ts imports it first so it runs before the route tree evaluates.
//
// Mirrors sentry.server.config.ts (the Next.js server init) but uses the
// unified `@sentry/tanstackstart-react` SDK. Loaded:
// - self-hosted / e2e (scripts/serve.js): dynamically imported AFTER the
// .env files are read into process.env, so the DSN is available.
// - Vercel (api/server.js): imported at module top (gated to TanStack),
// Vercel injects env vars into process.env for us.
//
// Reads process.env at call time (unlike NEXT_PUBLIC_* which the client bundle
// inlines at build time), so it must run after env loading on self-hosted.
// Reads process.env at call time (NEXT_PUBLIC_* are inlined at build time
// instead), so env files must be loaded before the server boots self-hosted.
import * as Sentry from '@sentry/tanstackstart-react'
+3 -6
View File
@@ -9,14 +9,12 @@
"dev": "node scripts/dispatch.js dev",
"build": "node scripts/dispatch.js build",
"start": "node scripts/dispatch.js start",
"preview": "vite preview --mode ${MODE:-production} --port 8082",
"dev:next": "next dev -p ${STUDIO_PORT:-8082}",
"build:next": "next build && if [ \"$SKIP_ASSET_UPLOAD\" != \"1\" ]; then ./../../scripts/upload-static-assets.sh; fi",
"start:next": "next start -p 8082",
"dev:tanstack": "NODE_OPTIONS=--max-old-space-size=8192 vite dev --port ${STUDIO_PORT:-8082}",
"build:tanstack": "vite build --mode ${MODE:-production} && pnpm smoke:tanstack",
"smoke:tanstack": "node scripts/smoke-server.mjs",
"start:tanstack": "node scripts/serve.js",
"build:tanstack": "vite build --mode ${MODE:-production}",
"start:tanstack": "node --env-file-if-exists=.env --env-file-if-exists=.env.local --env-file-if-exists=.env.${MODE:-production} --env-file-if-exists=.env.${MODE:-production}.local .output/server/index.mjs",
"lint": "eslint .",
"lint:ratchet": "tsx scripts/ratchet-eslint-rules.ts --rules-file scripts/ratchet-rules.json",
"clean": "rimraf .next .turbo tsconfig.tsbuildinfo",
@@ -168,7 +166,6 @@
"@faker-js/faker": "^9.9.0",
"@graphql-codegen/cli": "5.0.5",
"@graphql-typed-document-node/core": "^3.2.0",
"@jridgewell/remapping": "^2.3.5",
"@tailwindcss/postcss": "^4.2.4",
"@tailwindcss/vite": "4.2.4",
"@tanstack/devtools-vite": "^0.6.0",
@@ -205,9 +202,9 @@
"graphql-ws": "5.14.1",
"import-in-the-middle": "^2.0.0",
"jsdom-testing-mocks": "^1.13.1",
"magic-string": "^0.30.21",
"msw": "^2.3.0",
"next-router-mock": "^0.9.13",
"nitro": "3.0.260903-beta",
"node-mocks-http": "^1.17.2",
"postcss": "catalog:",
"raw-loader": "^4.0.2",
-33
View File
@@ -11,37 +11,6 @@ export interface RouterContext {
queryClient: QueryClient
}
// Skew protection: every asset URL in the built bundle carries a
// `?dpl=<deployment-id>` pin (see skewProtectionDpl in vite.config.ts), so a
// long-lived dashboard session keeps loading lazily-imported chunks from the
// deployment it was built by instead of 404ing after a redeploy. Backstop: if
// a lazily-loaded chunk still 404s — most likely the pinned deployment aged
// out of Skew Protection's Maximum Age, so Vercel refuses the `?dpl=` request
// — Vite emits `vite:preloadError`. Reload so we land on the latest
// deployment, whose asset URLs pin to itself. A short time-window guard
// prevents a reload loop if the latest deployment is itself broken.
function registerChunkErrorBackstop() {
if (typeof window === 'undefined') return
window.addEventListener('vite:preloadError', (event) => {
const KEY = 'studio:chunk-error-reload-at'
let last = 0
try {
last = Number(sessionStorage.getItem(KEY) || 0)
} catch {
// sessionStorage unavailable — fall through and attempt a reload anyway.
}
// Reloaded very recently → likely a loop; let Vite surface the error.
if (Date.now() - last < 10_000) return
event.preventDefault()
try {
sessionStorage.setItem(KEY, String(Date.now()))
} catch {
// ignore — worst case we lose loop protection for this reload.
}
window.location.reload()
})
}
function getContext(): RouterContext {
return {
queryClient: getQueryClient(),
@@ -49,8 +18,6 @@ function getContext(): RouterContext {
}
export function getRouter() {
registerChunkErrorBackstop()
const context = getContext()
const router = createRouter({
@@ -0,0 +1,86 @@
import { describe, expect, it } from 'vitest'
import { buildSpaRoutes } from '../vercel-spa-routes'
const IMMUTABLE = '/_vercel/immutable/initial/nitro'
const cacheRule = {
headers: { 'cache-control': 'public, max-age=31536000, immutable' },
src: `${IMMUTABLE}/(.*)`,
}
const skewCookieRule = {
src: '/.*',
has: [{ type: 'header', key: 'Sec-Fetch-Dest', value: 'document' }],
headers: { 'Set-Cookie': '__vdpl=dpl_123; Path=/; SameSite=Lax; Secure; HttpOnly' },
continue: true,
}
const filesystem = { handle: 'filesystem' }
const functionCatchAll = { src: '/(.*)', dest: '/__server' }
describe('buildSpaRoutes', () => {
it('keeps the rules ahead of the filesystem handler and replaces the function catch-all', () => {
const routes = buildSpaRoutes(
[cacheRule, skewCookieRule, filesystem, functionCatchAll],
'/__server',
IMMUTABLE
)
expect(routes).toEqual([
cacheRule,
skewCookieRule,
filesystem,
{ src: '/_serverFn/(.*)', dest: '/__server' },
{ src: '/api/(.*)', dest: '/__server' },
{ src: `${IMMUTABLE}/(.*)`, status: 404 },
{ src: '/(.*)', dest: '/_shell.html' },
])
})
it('adds prefixed rules and a public-file rewrite under a base path', () => {
const routes = buildSpaRoutes(
[cacheRule, filesystem, functionCatchAll],
'/__server',
IMMUTABLE,
'/dashboard'
)
expect(routes.slice(2)).toEqual([
{ src: '/dashboard/_serverFn/(.*)', dest: '/__server' },
{ src: '/dashboard/api/(.*)', dest: '/__server' },
{ src: '/_serverFn/(.*)', dest: '/__server' },
{ src: '/api/(.*)', dest: '/__server' },
{ src: `${IMMUTABLE}/(.*)`, status: 404 },
{ src: '/dashboard/(.*\\.\\w+)', dest: '/$1' },
{ src: '/(.*)', dest: '/_shell.html' },
])
})
it('fails loudly when Nitro output changes shape', () => {
expect(() => buildSpaRoutes([functionCatchAll], '/__server', '/assets')).toThrow(
/no \{ handle: "filesystem" \}/
)
expect(() => buildSpaRoutes([filesystem], '/__server', '/assets')).toThrow(
/catch-all .* was not found/
)
})
it.each([
[`/dashboard${IMMUTABLE}/old-chunk.js`, `${IMMUTABLE}/old-chunk.js`],
[`/dashboard${IMMUTABLE}/editor.worker.js`, `${IMMUTABLE}/editor.worker.js`],
[`/dashboard${IMMUTABLE}/styles.css`, `${IMMUTABLE}/styles.css`],
['/dashboard/img/logo.svg', '/img/logo.svg'],
['/dashboard/api/export.json', '/__server'],
['/dashboard/_serverFn/function-id', '/__server'],
['/dashboard/project/example/editor', '/_shell.html'],
])('routes %s to %s after the filesystem misses', (pathname, destination) => {
const routes = buildSpaRoutes(
[filesystem, functionCatchAll],
'/__server',
IMMUTABLE,
'/dashboard'
)
const rule = routes.find(({ src }) => src && new RegExp(`^${src}$`).test(pathname))
expect(rule?.dest).toBeDefined()
expect(pathname.replace(new RegExp(`^${rule!.src}$`), rule!.dest!)).toBe(destination)
})
})
+2 -2
View File
@@ -3,7 +3,7 @@
// tanstack-flavoured variant based on STUDIO_FRAMEWORK. We parse the env files
// (via the shared scripts/lib/env.js parser) and pull out only
// STUDIO_FRAMEWORK — we deliberately don't load the whole file into the
// child's process.env, because scripts/serve.js / vite do their own .env
// child's process.env, because vite and the start script do their own .env
// loading and would otherwise refuse to override the dispatcher-set values,
// including NEXT_PUBLIC_IS_PLATFORM which the e2e `.env.test` needs to flip to
// `false`.
@@ -29,7 +29,7 @@ if (!target) {
const studioRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
// Shell env wins, then `.env.local`, then `.env` — the same precedence
// scripts/serve.js and vite use, so STUDIO_FRAMEWORK set in either file is
// vite and the start script use, so STUDIO_FRAMEWORK set in either file is
// picked up (not just `.env.local`).
const fileEnv = readEnvFiles(studioRoot, ['.env', '.env.local'])
const studioFramework = process.env.STUDIO_FRAMEWORK ?? fileEnv.STUDIO_FRAMEWORK
+7 -9
View File
@@ -1,12 +1,10 @@
// Shared .env parsing for the studio build/serve scripts (dispatch.js,
// serve.js). We parse the small subset of the dotenv format we actually rely
// on — `KEY=value`, an optional `export` prefix, and surrounding single/double
// quotes — rather than taking on the `dotenv` dependency for a couple of build
// scripts. Crucially, nothing here touches `process.env`: callers decide what
// to do with the parsed values, which is what lets dispatch.js read a single
// key without leaking the whole file into the child process.
//
// dispatch.js and serve.js must agree on this format, so it lives here once.
// .env parsing for scripts/dispatch.js. We parse the small subset of the
// dotenv format we actually rely on — `KEY=value`, an optional `export`
// prefix, and surrounding single/double quotes — rather than taking on the
// `dotenv` dependency for one build script. Crucially, nothing here touches
// `process.env`: callers decide what to do with the parsed values, which is
// what lets dispatch.js read a single key without leaking the whole file into
// the child process.
import { readFileSync } from 'node:fs'
import path from 'node:path'
-214
View File
@@ -1,214 +0,0 @@
#!/usr/bin/env node
// Standalone Node HTTP server that hosts the production studio build.
//
// We export the fetch-handler shape from `dist/server/server.js` because
// Vercel consumes it directly (see `apps/studio/api/server.js`). For
// self-hosted / e2e, we need an HTTP listener of our own — this is that
// listener.
//
// Responsibilities:
// - Load env files in vite preview's order so non-NEXT_PUBLIC_* values
// (POSTGRES_PASSWORD, PG_META_CRYPTO_KEY, etc.) are in process.env
// at request time. NEXT_PUBLIC_* are already inlined into the bundle
// at build time and don't need to be re-loaded.
// - Serve static client assets from `dist/client/` directly with the
// right MIME types and cache headers.
// - Forward everything else to the TanStack Start handler exported
// from `dist/server/server.js`.
import { createReadStream } from 'node:fs'
import { stat } from 'node:fs/promises'
import { createServer } from 'node:http'
import path from 'node:path'
import { Readable } from 'node:stream'
import { fileURLToPath } from 'node:url'
import { readEnvFiles } from './lib/env.js'
const studioRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const clientDir = path.join(studioRoot, 'dist/client')
const mode = process.env.MODE || 'production'
const envFiles = ['.env', '.env.local', `.env.${mode}`, `.env.${mode}.local`]
const parsed = readEnvFiles(studioRoot, envFiles)
// Don't clobber values the shell already provides — match `vite preview`.
for (const [k, v] of Object.entries(parsed)) {
if (process.env[k] !== undefined) continue
process.env[k] = v.replace(
/\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?/g,
(_, name) => process.env[name] ?? parsed[name] ?? ''
)
}
// Initialize server-side Sentry now that .env values are in process.env (the
// instrument module reads process.env.NEXT_PUBLIC_SENTRY_DSN at call time).
// Imported dynamically here rather than via a `--import` flag so it runs after
// the env-loading block above. Non-fatal if the SDK isn't present.
try {
await import(path.join(studioRoot, 'instrument.server.mjs'))
} catch (err) {
console.warn('[serve] Sentry server init skipped:', err?.message ?? err)
}
const { wrapFetchWithSentry } = await import('@sentry/tanstackstart-react').catch(() => ({
wrapFetchWithSentry: (fetchHandler) => fetchHandler,
}))
const { default: rawHandler } = await import(path.join(studioRoot, 'dist/server/server.js'))
// Wrap so request-scoped errors (incl. ones swallowed into a 500) are captured.
const handler = {
...rawHandler,
fetch: wrapFetchWithSentry(rawHandler.fetch.bind(rawHandler)),
}
const mimeByExt = new Map([
['.js', 'application/javascript; charset=utf-8'],
['.mjs', 'application/javascript; charset=utf-8'],
['.css', 'text/css; charset=utf-8'],
['.html', 'text/html; charset=utf-8'],
['.json', 'application/json; charset=utf-8'],
['.map', 'application/json; charset=utf-8'],
['.png', 'image/png'],
['.jpg', 'image/jpeg'],
['.jpeg', 'image/jpeg'],
['.gif', 'image/gif'],
['.svg', 'image/svg+xml'],
['.ico', 'image/x-icon'],
['.woff', 'font/woff'],
['.woff2', 'font/woff2'],
['.txt', 'text/plain; charset=utf-8'],
['.webmanifest', 'application/manifest+json'],
])
// Vite emits hashed filenames (e.g. `index-DB4J79t9.js`) for everything
// it bundles. Those are content-addressed so we serve them immutable.
const HASHED_RE = /-[A-Za-z0-9_-]{6,}\.[a-z0-9]+$/
async function serveStatic(req, res) {
let pathname
try {
pathname = new URL(req.url, 'http://localhost').pathname
} catch {
return false
}
if (pathname === '/' || pathname.endsWith('/')) return false
if (pathname.includes('..') || pathname.includes('\\')) return false
const filePath = path.join(clientDir, pathname)
if (!filePath.startsWith(clientDir + path.sep)) return false
let st
try {
st = await stat(filePath)
} catch {
return false
}
if (!st.isFile()) return false
res.statusCode = 200
res.setHeader(
'content-type',
mimeByExt.get(path.extname(filePath).toLowerCase()) ?? 'application/octet-stream'
)
res.setHeader('content-length', String(st.size))
res.setHeader(
'cache-control',
HASHED_RE.test(pathname) ? 'public, max-age=31536000, immutable' : 'no-cache'
)
await new Promise((resolve, reject) => {
const stream = createReadStream(filePath)
stream.on('error', reject)
stream.on('end', resolve)
stream.pipe(res)
})
return true
}
function toWebRequest(req) {
const protocol = req.socket.encrypted ? 'https' : 'http'
const url = `${protocol}://${req.headers.host ?? 'localhost'}${req.url}`
const headers = new Headers()
for (const [k, v] of Object.entries(req.headers)) {
if (k.startsWith(':')) continue
if (Array.isArray(v)) for (const vv of v) headers.append(k, vv)
else if (v !== undefined) headers.set(k, v)
}
const init = { method: req.method, headers }
// Only attach a body for methods that can carry one AND that actually
// have body bytes coming. Wrapping `req` in `Readable.toWeb(req)` for
// requests where Node has nothing to deliver leaves undici's
// `extractBody` looking at an already-consumed stream and throwing
// `TypeError: Response body object should not be disturbed or locked`
// at the `new Request(...)` call below.
const contentLength = Number(req.headers['content-length'] ?? '0')
const hasBody =
req.method !== 'GET' &&
req.method !== 'HEAD' &&
(contentLength > 0 || req.headers['transfer-encoding'] === 'chunked')
if (hasBody) {
init.body = Readable.toWeb(req)
init.duplex = 'half'
}
return new Request(url, init)
}
async function pipeWebResponse(response, res) {
res.statusCode = response.status
// The Headers iterator collapses duplicate keys, and for `set-cookie` it joins
// every cookie into one comma-separated value — which corrupts auth/session
// cookies. Pull the cookies out separately via getSetCookie() and set them as
// an array so each one becomes its own header.
const setCookies =
typeof response.headers.getSetCookie === 'function' ? response.headers.getSetCookie() : []
for (const [k, v] of response.headers) {
if (k.toLowerCase() === 'set-cookie') continue
res.setHeader(k, v)
}
if (setCookies.length > 0) res.setHeader('set-cookie', setCookies)
if (!response.body) {
res.end()
return
}
// Pipe via Readable.fromWeb so the underlying stream gets proper backpressure
// and gets released cleanly. `for await (chunk of response.body)` works in
// simple cases but can leave the body in a "disturbed / locked" state when
// the handler internally peeks at it — surfacing as
// `TypeError: Response body object should not be disturbed or locked` on a
// subsequent request.
await new Promise((resolve, reject) => {
const readable = Readable.fromWeb(response.body)
readable.on('error', reject)
res.on('error', reject)
res.on('close', resolve)
res.on('finish', resolve)
readable.pipe(res)
})
}
// Security headers for the self-hosted server. Mirrors the non-platform branch
// of next.config.ts `headers()` (self-hosted is always IS_PLATFORM=false, so the
// CSP is just `frame-ancestors 'none'` and there's no HSTS). The platform CSP is
// applied at the edge via vercel.ts instead; see security-headers.ts. Set before
// any response is written so both the static and handler paths inherit them.
const SECURITY_HEADERS = [
['X-Frame-Options', 'DENY'],
['X-Content-Type-Options', 'nosniff'],
['Content-Security-Policy', "frame-ancestors 'none';"],
['Referrer-Policy', 'strict-origin-when-cross-origin'],
]
const port = Number(process.env.PORT || 8082)
createServer(async (req, res) => {
try {
for (const [key, value] of SECURITY_HEADERS) res.setHeader(key, value)
if (await serveStatic(req, res)) return
const response = await handler.fetch(toWebRequest(req))
await pipeWebResponse(response, res)
} catch (err) {
console.error('[serve] request failed:', err)
if (!res.headersSent) {
res.statusCode = 500
res.setHeader('content-type', 'text/plain; charset=utf-8')
}
res.end('Internal Server Error')
}
}).listen(port, () => {
console.log(`Studio listening on http://localhost:${port} (mode=${mode})`)
})
-73
View File
@@ -1,73 +0,0 @@
#!/usr/bin/env node
// Post-build smoke test: boot the built TanStack server handler and make a
// real request with a platform-like environment, so module-scope boot
// crashes fail the BUILD instead of the deployed function at runtime.
//
// Why this exists: the TanStack server entry eagerly imports the entire route
// tree (`loadEntries`), so every route module is evaluated the first time the
// single function handler serves a request. A bad top-level side effect in any
// one route — e.g. `createClient(process.env.SUPABASE_URL!, ...)` at module
// scope, where SUPABASE_URL is unset on platform — throws during that import
// and 500s every route, including trivial ones like /api/get-utc-time. Those
// failures only showed up at runtime on Vercel; this catches them at build.
//
// What this catches: anything that throws while the route tree is imported
// (the most common class — missing env vars read at module scope).
// What this does NOT catch: assets missing from the *Vercel function bundle*
// (e.g. libpg-query.wasm). Local node_modules still has those, so booting the
// plain `dist/server` build won't surface them — that needs booting the
// `vercel build` output. Pass that function's entry as argv[1] to reuse this
// script in a deploy-gating CI step.
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const studioRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const serverEntry = process.argv[2]
? path.resolve(process.argv[2])
: path.join(studioRoot, 'dist/server/server.js')
// Simulate the platform function runtime: these are only set on self-hosted,
// so removing them surfaces any route that needs them at module-load time.
for (const key of ['SUPABASE_URL', 'SUPABASE_SERVICE_KEY', 'SUPABASE_SERVICE_ROLE_KEY']) {
delete process.env[key]
}
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ''
// A request to any route forces `loadEntries` to import the full route tree,
// so a single cheap, dependency-free endpoint is enough to exercise the boot.
const ROUTES = ['/api/get-utc-time']
console.log(`[smoke] booting ${path.relative(studioRoot, serverEntry)}`)
const { default: handler } = await import(serverEntry)
let failed = false
for (const route of ROUTES) {
const url = `http://localhost${basePath}${route}`
try {
const res = await handler.fetch(new Request(url))
if (res.status >= 500) {
failed = true
const body = await res.text().catch(() => '')
console.error(`[smoke] ✗ ${route} → ${res.status}\n${body.slice(0, 800)}`)
} else {
console.log(`[smoke] ✓ ${route} → ${res.status}`)
}
} catch (err) {
failed = true
console.error(`[smoke] ✗ ${route} threw while booting the server:\n`, err)
}
}
if (failed) {
console.error(
'\n[smoke] FAILED — the server bundle does not boot cleanly. This usually means a\n' +
'route module has a top-level side effect (e.g. createClient at module scope with a\n' +
'missing env var). Make it lazy so it only runs inside the handler.'
)
process.exit(1)
}
console.log('\n[smoke] passed — server boots and routes respond without a 5xx.')
process.exit(0)
+97
View File
@@ -0,0 +1,97 @@
import { readFile, writeFile } from 'node:fs/promises'
import { basename, resolve } from 'node:path'
import type { NitroModule } from 'nitro/types'
// Nitro module that makes the Vercel Build Output a static-first SPA: documents
// are served from the prerendered shell on the CDN and only server functions
// and API routes invoke the function. Nitro's own config sends every
// non-static path to the function.
//
// A module rather than config because `nitro({ hooks: { compiled } })`
// REPLACES the vercel preset's `compiled` hook (config.json is never written),
// and `vercel.config.routes` is defu-merged: user routes land above Nitro's
// `handle: filesystem` while its function catch-all is still appended last.
//
// Runs before TanStack Start writes `_shell.html`.
/** Must match `spa.prerender.outputPath` in tanstackStart() (default `/_shell`). */
export const SHELL_PATH = '/_shell.html'
/** Must match `serverFns.base` in tanstackStart() (default `/_serverFn`). */
export const SERVER_FN_BASE = '/_serverFn'
/** Directory under `routes/` that holds the server routes. */
export const API_PREFIX = '/api'
type Route = Record<string, unknown> & {
src?: string
dest?: string
handle?: string
}
/**
* After `handle: filesystem`: only `/_serverFn/*` and `/api/*` reach the
* function, a missing hashed chunk 404s instead of becoming the HTML shell,
* and everything else is the shell.
*
* @param assetsPrefix `/assets`, or `/_vercel/immutable/<salt>/nitro` with
* `vercel.immutableStaticFiles`.
* @param basePath `NEXT_PUBLIC_BASE_PATH` (e.g. `/dashboard`). Pages, API
* routes, server functions and browser asset URLs live under it while the
* static files and immutable store stay at the root. Prefixed rules come
* first and asset requests under the prefix are rewritten to the root.
*/
export function buildSpaRoutes(
generated: Route[],
functionDest: string,
assetsPrefix: string,
basePath = ''
): Route[] {
const fsIndex = generated.findIndex((r) => r.handle === 'filesystem')
if (fsIndex === -1) {
throw new Error('[vercel-spa-routes] generated config has no { handle: "filesystem" }')
}
const before = generated.slice(0, fsIndex)
const after = generated.slice(fsIndex + 1)
const isFunctionCatchAll = (r: Route) => r.src === '/(.*)' && r.dest === functionDest
if (!after.some(isFunctionCatchAll)) {
throw new Error(
`[vercel-spa-routes] expected Nitro catch-all { src: "/(.*)", dest: "${functionDest}" } was not found`
)
}
const kept = after.filter((r) => !isFunctionCatchAll(r) && r.handle !== 'filesystem')
const prefixes = basePath ? [basePath, ''] : ['']
return [
...before,
{ handle: 'filesystem' },
...kept,
...prefixes.flatMap((prefix) => [
{ src: `${prefix}${SERVER_FN_BASE}/(.*)`, dest: functionDest },
{ src: `${prefix}${API_PREFIX}/(.*)`, dest: functionDest },
]),
{ src: `${assetsPrefix}/(.*)`, status: 404 },
...(basePath ? [{ src: `${basePath}/(.*\\.\\w+)`, dest: '/$1' }] : []),
{ src: '/(.*)', dest: SHELL_PATH },
]
}
export function vercelSpaRoutes({ basePath = '' }: { basePath?: string } = {}): NitroModule {
return {
name: 'vercel-spa-routes',
setup(nitro) {
if (nitro.options.preset !== 'vercel') return
nitro.hooks.hook('compiled', async () => {
const configPath = resolve(nitro.options.output.dir, 'config.json')
const functionDest = `/${basename(nitro.options.output.serverDir).replace(/\.func$/, '')}`
const assetsPrefix =
'/' + (nitro.options.buildAssetsDir || 'assets').replace(/^\/|\/$/g, '')
const config = JSON.parse(await readFile(configPath, 'utf8'))
config.routes = buildSpaRoutes(config.routes, functionDest, assetsPrefix, basePath)
await writeFile(configPath, JSON.stringify(config, null, 2))
nitro.logger.success(
`[vercel-spa-routes] documents -> ${SHELL_PATH}, ${basePath}${SERVER_FN_BASE}/* and ${basePath}${API_PREFIX}/* -> ${functionDest}, missing ${assetsPrefix}/* -> 404`
)
})
},
}
}
+4 -5
View File
@@ -1,10 +1,9 @@
import { getCSP } from './csp'
// Security response headers for the app. On the Next build these are applied via
// `next.config.ts` `headers()`; the TanStack build has no such hook and (on
// Vercel) serves a static shell with no server to attach them, so they're
// applied through `vercel.ts` (deploy) and `scripts/serve.js` (self-hosted)
// instead. Keep this in sync with the `/(.*?)` header block in next.config.ts.
// Security response headers. The Next build applies them via next.config.ts
// `headers()`; the TanStack build has no such hook, so they're applied through
// `vercel.ts` (Vercel) and Nitro `routeRules` in `vite.config.ts`
// (self-hosted). Keep in sync with the `/(.*?)` header block in next.config.ts.
//
// Env-gated exactly like next.config:
// - CSP: full `getCSP()` on platform, else just `frame-ancestors 'none'`.
+17
View File
@@ -0,0 +1,17 @@
// TanStack Start server entry; Nitro bundles it into the server for every
// target. Sentry's init must run before the route tree evaluates, so the
// instrument module is imported first; `wrapFetchWithSentry` then captures
// request-scoped errors, including ones swallowed into a 500 downstream.
import './instrument.server.mjs'
import { wrapFetchWithSentry } from '@sentry/tanstackstart-react'
import handler, { createServerEntry, type ServerEntry } from '@tanstack/react-start/server-entry'
const requestHandler: ServerEntry = wrapFetchWithSentry({
fetch(request: Request) {
return handler.fetch(request)
},
})
// eslint-disable-next-line no-restricted-exports
export default createServerEntry(requestHandler)
+14 -9
View File
@@ -8,14 +8,11 @@ import { BASE_PATH, IS_PLATFORM } from '@/lib/constants'
import { isHostedSupportedApiPath } from '@/lib/hosted-api-allowlist'
// Self-hosted-only API routes must 404 in platform (hosted) mode. Under the
// Next pages router this lives in middleware (proxy.ts), but TanStack Start
// has no middleware runtime, so the guard is migrated here as a global
// request middleware sharing the same allowlist (lib/hosted-api-allowlist.ts).
// On Vercel our `/api/*` (and `/_serverFn/*`) requests are rewritten to the
// api/server.js function which runs the Start handler, so createStartHandler
// runs this server-side for every API request — even though pages are served
// as a static SPA shell. The guard therefore covers all API routes from a
// single place.
// Next pages router this lives in middleware (proxy.ts); TanStack Start has no
// middleware runtime, so it's a global request middleware sharing the same
// allowlist (lib/hosted-api-allowlist.ts). On Vercel every `/api/*` request
// still reaches the function (pages are a static shell), so this covers all
// API routes from one place.
const platformApiGuard = createMiddleware({ type: 'request' }).server(({ request, next }) => {
const { pathname } = new URL(request.url)
@@ -34,6 +31,14 @@ const platformApiGuard = createMiddleware({ type: 'request' }).server(({ request
return next()
})
// Vercel's standard request pin keeps server functions compatible with the
// current tab. Documents and the deployment check stay unpinned, so a reload
// gets the latest version. Vite only defines the ID when protection is enabled.
const deploymentMiddleware = createMiddleware({ type: 'function' }).client(({ next }) => {
const deploymentId = process.env.NEXT_PUBLIC_VERCEL_DEPLOYMENT_ID
return next({ headers: deploymentId ? { 'x-deployment-id': deploymentId } : {} })
})
// Sentry's global middlewares go at the FRONT so they wrap the whole request /
// server-function lifecycle — including errors that downstream code swallows
// into a 500, which the manual `@sentry/nextjs` approach never sees. The SDK's
@@ -42,5 +47,5 @@ const platformApiGuard = createMiddleware({ type: 'request' }).server(({ request
// explicitly so the instrumentation is visible in source.
export const startInstance = createStart(() => ({
requestMiddleware: [sentryGlobalRequestMiddleware, platformApiGuard],
functionMiddleware: [sentryGlobalFunctionMiddleware],
functionMiddleware: [sentryGlobalFunctionMiddleware, deploymentMiddleware],
}))
+18 -6
View File
@@ -33,6 +33,11 @@
"SUPABASE_URL",
"VERCEL",
"VERCEL_ENV",
// Project-level Vercel settings Nitro's vercel preset reads at build
// time; they change the emitted routes and asset paths.
"VERCEL_SKEW_PROTECTION_ENABLED",
"VERCEL_IMMUTABLE_STATIC_FILES_ENABLED",
"VERCEL_HASH_SALT",
"MAINTENANCE_MODE",
// These envs are used in the packages
"NEXT_PUBLIC_STORAGE_KEY",
@@ -106,12 +111,11 @@
// cache when it changes — without this, switching between test and
// production builds reuses a stale cached output.
"MODE",
// Read by scripts/serve.js (the Node host for `pnpm start`).
// Declared here so the studio-package turbo env lint rule passes
// even though turbo doesn't directly drive `start`.
// Read by the Nitro server behind `pnpm start`. Declared so the turbo
// env lint rule passes even though turbo doesn't drive `start`.
"PORT",
// Gates the TanStack vs Next path in api/server.js, vercel.ts,
// and scripts/dispatch.js (the dev/build/start dispatcher).
// Gates the TanStack vs Next path in vercel.ts and
// scripts/dispatch.js (the dev/build/start dispatcher).
"STUDIO_FRAMEWORK",
// Vite's built-in `import.meta.env.SSR` flag (used in ConnectStepsSection
// to gate Vite-only `import.meta.glob`). Not a real process env var
@@ -119,6 +123,12 @@
"SSR",
],
"passThroughEnv": [
// Vite bakes this into TanStack's server-function request header. Passed
// through rather than hashed so Next's same-commit redeploys keep
// hitting the turbo cache as before; a TanStack cache hit reuses the
// earlier deployment's id, which is the same code and falls back to
// latest if that deployment is gone.
"VERCEL_DEPLOYMENT_ID",
"CURRENT_CLI_VERSION",
"VERCEL_GIT_COMMIT_REF",
"VERCEL_GIT_COMMIT_SHA",
@@ -131,7 +141,9 @@
".next/**",
"!.next/cache/**",
"!.next/dev/**/*",
"dist/**",
// TanStack Start via Nitro: node server and Vercel Build Output.
".output/**",
".vercel/output/**",
],
},
},
+40 -117
View File
@@ -15,74 +15,39 @@ import { getSecurityHeaders } from './security-headers'
// Next.js preset untouched. Vercel reads `vercel.ts` regardless of the
// framework preset (per vercel.com/docs/project-configuration —
// `vercel.ts`'s `framework` field overrides the dashboard preset), so a
// no-op early return is the only way to keep the TanStack rewrites,
// `framework: null`, and `outputDirectory: 'dist/client'` below from
// clobbering the Next build. Set `STUDIO_FRAMEWORK=tanstack` on the
// TanStack Vercel project to opt in.
// no-op early return is the only way to keep `framework: null` and the
// headers below from clobbering the Next build. Set
// `STUDIO_FRAMEWORK=tanstack` on the TanStack Vercel project to opt in.
const isTanstack = process.env.STUDIO_FRAMEWORK === 'tanstack'
// Vite's `base` bakes the prefix into asset URLs but leaves the filesystem
// layout at `dist/client/...`. On Vercel we strip the prefix for file lookups
// and fall through to the SPA shell. When NEXT_PUBLIC_BASE_PATH is empty
// the prefixed rule set is skipped and only the root-level rules fire.
// Routing lives in Nitro's Build Output config (`.vercel/output/config.json`,
// shaped by scripts/vercel-spa-routes.ts). Vercel applies this file's
// `redirects`/`headers` ahead of it, which is also why no `rewrites` belong
// here: a catch-all rewrite at this layer swallows the API routes.
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ''
// Build the rewrites + headers for a given prefix ('' for root, or a base
// path like '/dashboard'). We run this once for each prefix and concatenate
// the results so we don't hand-duplicate every rule.
//
// Rewrite ordering: API + server-function passthrough first so extensioned
// API paths (/api/foo.json) don't get caught by the asset rule. Asset rule
// next — strips the basePath prefix so `/dashboard/assets/x.js` maps onto the
// `dist/client/assets/x.js` filesystem layout (a no-op identity when
// prefix=''). Shell rule LAST, and it deliberately matches only extensionless
// paths via a negative lookahead.
//
// Why the lookahead matters: a request WITH a file extension that doesn't
// resolve to a real file — e.g. a hashed chunk from an older deployment after
// a redeploy — must fall through to a 404, NOT the HTML shell. A catch-all
// `(.*)` shell swallows those misses and returns `text/html`, so the browser
// gets HTML for a `.js` request and throws "Failed to load module script …
// MIME type text/html" (and, because /assets/* is cached immutable, that HTML
// poisons the edge cache under the asset URL). A clean 404 instead lets skew
// protection's `?dpl=` routing (baked into asset URLs at build time — see
// skewProtectionDpl in vite.config.ts) serve the chunk from the deployment
// that still has it, or the client's `vite:preloadError` backstop recover.
function routesFor(prefix: string) {
return {
rewrites: [
routes.rewrite(`${prefix}/api/(.*)`, '/api/server'),
routes.rewrite(`${prefix}/_serverFn/(.*)`, '/api/server'),
routes.rewrite(`${prefix}/(.*\\.\\w+)`, '/$1'),
routes.rewrite(`${prefix}/((?!.*\\.\\w+$).*)`, '/_shell'),
],
headers: [
// Security headers for every response. The Next build sets these via
// next.config.ts `headers()`; the TanStack build serves a static shell
// with no server to attach them, so they live here. Matches next.config's
// `/(.*?)` block (CSP, X-Frame-Options, HSTS, etc.).
{ source: `${prefix}/(.*)`, headers: getSecurityHeaders() },
// Dynamic function responses must not be cached by any shared cache —
// handlers can still opt in with their own Cache-Control on the
// Response when a response IS safe to cache.
routes.cacheControl(`${prefix}/api/(.*)`, { private: true, noStore: true }),
routes.cacheControl(`${prefix}/_serverFn/(.*)`, { private: true, noStore: true }),
// Hashed bundles under /assets/* are content-addressed — safe to
// cache forever.
routes.cacheControl(`${prefix}/assets/(.*)`, {
public: true,
maxAge: '1year',
immutable: true,
}),
// Static images and favicons aren't content-hashed, so they can't be
// `immutable`, but they change rarely — mirror next.config's
// `cache-control` for these paths (img: max-age=2592000 = 30 days,
// favicon: max-age=86400 = 1 day). Prefixed like the other rules so
// `/dashboard/img/x.png` gets the header too when a basePath is set.
routes.cacheControl(`${prefix}/img/(.*)`, { public: true, maxAge: '30days' }),
routes.cacheControl(`${prefix}/favicon/(.*)`, { public: true, maxAge: '1day' }),
],
}
// Headers for a given prefix ('' for root, or a base path like '/dashboard').
// Run once per prefix and concatenated so no rule is hand-duplicated.
function headersFor(prefix: string) {
return [
// Security headers for every response. The Next build sets these via
// next.config.ts `headers()`; the TanStack build serves a static shell
// from the CDN, so they live at the edge. Matches next.config's `/(.*?)`
// block (CSP, X-Frame-Options, HSTS, etc.).
{ source: `${prefix}/(.*)`, headers: getSecurityHeaders() },
// Dynamic function responses must not be cached by any shared cache —
// handlers can still opt in with their own Cache-Control on the
// Response when a response IS safe to cache.
routes.cacheControl(`${prefix}/api/(.*)`, { private: true, noStore: true }),
routes.cacheControl(`${prefix}/_serverFn/(.*)`, { private: true, noStore: true }),
// Hashed chunks are covered by Nitro (`/_vercel/immutable/*`, immutable).
// Static images and favicons aren't content-hashed, so they can't be
// `immutable`, but they change rarely — mirror next.config's
// `cache-control` for these paths (img: max-age=2592000 = 30 days,
// favicon: max-age=86400 = 1 day).
routes.cacheControl(`${prefix}/img/(.*)`, { public: true, maxAge: '30days' }),
routes.cacheControl(`${prefix}/favicon/(.*)`, { public: true, maxAge: '1day' }),
]
}
// ---------------------------------------------------------------------------
@@ -118,69 +83,27 @@ function buildRedirects(): Redirect[] {
}
function buildTanstackConfig(): VercelConfig {
// When a base path is configured, emit both the prefixed and root rule
// sets (prefixed first so it wins for explicit /dashboard/* hits, root as
// a fallback for bare-domain traffic).
const ruleSets = (basePath ? [basePath, ''] : ['']).map(routesFor)
// Vercel's Flags Explorer probes `/.well-known/vercel/flags` and expects
// JSON. next.config.ts proxies it to supabase.com's endpoint and forces
// `content-type: application/json`; the TanStack build has no equivalent, so
// without these the path falls through to the extensionless catch-all shell
// rule and the browser gets HTML. This lives at the domain root (once, NOT
// inside routesFor) because next.config's rewrite sets `basePath: false`, so
// the path is never basePath-prefixed — well-known URLs are by convention at
// the root regardless of the app's basePath. The rewrite must be listed
// BEFORE the routesFor rewrites so it wins over the shell catch-all.
// `content-type: application/json`; mirror that here. It lives at the domain
// root (once, NOT per prefix) because next.config's rewrite sets
// `basePath: false` — well-known URLs are at the root regardless of the
// app's basePath.
const wellKnownFlags = '/.well-known/vercel/flags'
return {
// Nitro's Build Output directory is what gets deployed; no framework
// preset should route on top of it.
framework: null,
outputDirectory: 'dist/client',
cleanUrls: true,
redirects: buildRedirects(),
rewrites: [
routes.rewrite(wellKnownFlags, `https://supabase.com${wellKnownFlags}`),
...ruleSets.flatMap((r) => r.rewrites),
],
rewrites: [routes.rewrite(wellKnownFlags, `https://supabase.com${wellKnownFlags}`)],
headers: [
routes.header(wellKnownFlags, [{ key: 'content-type', value: 'application/json' }]),
...ruleSets.flatMap((r) => r.headers),
// When a base path is configured, emit both the prefixed and root rule
// sets (prefixed first so it wins for explicit /dashboard/* hits, root
// as a fallback for bare-domain traffic).
...(basePath ? [basePath, ''] : ['']).flatMap(headersFor),
],
// `api/server.js` imports the TanStack SSR bundle via a computed
// path so Vercel's function bundler doesn't try to statically
// resolve `dist/server/server.js` during the Next.js prod build
// (where `dist/` doesn't exist). `includeFiles` ships the SSR
// output into the function bundle for the TanStack build so the
// runtime import resolves.
functions: {
'api/server.js': {
// Every API + server-function request is rewritten onto this single
// function (see the `/api/*` and `/_serverFn/*` rewrites), so its
// timeout must cover the LONGEST per-route `maxDuration` the Next
// build declared — otherwise long AI streams and Stripe sync hit the
// platform default (~15s) and get killed. Next set these per route via
// `export const maxDuration`: generate-attachment-url (120),
// ai/code/complete (60), ai/onboarding/design (60), ai/sql/generate-v4
// (120), ai/feedback/rate (30), and integrations/stripe-sync (300).
// TanStack collapses them into one function, so we take the max: 300s.
maxDuration: 300,
// Ship the SSR output, plus libpg-query's wasm. libpg-query is
// externalized for SSR and loads its `.wasm` relative to its own
// dir (`__dirname`) at import time; Vercel's function bundler
// doesn't trace that node_modules asset, so co-ship it explicitly
// or the server crashes at boot with ENOENT on libpg-query.wasm.
//
// Target the real pnpm store path (repo-root `.pnpm`, two levels up
// from this Root Directory) rather than `node_modules/libpg-query`,
// which is a pnpm symlink — Vercel rejects packaging files reached
// through symlinked dirs ("invalid deployment package"). The real
// path also matches where Node resolves __dirname at runtime
// (/var/task/node_modules/.pnpm/libpg-query@.../wasm/...).
includeFiles:
'{dist/server/**,../../node_modules/.pnpm/libpg-query@*/node_modules/libpg-query/wasm/libpg-query.wasm}',
},
},
}
}
+76 -202
View File
@@ -4,15 +4,17 @@ import fs from 'node:fs'
import { createRequire } from 'node:module'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import remapping, { type SourceMapInput } from '@jridgewell/remapping'
import { sentryTanstackStart } from '@sentry/tanstackstart-react/vite'
import tailwindcss from '@tailwindcss/vite'
import { devtools } from '@tanstack/devtools-vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
import MagicString from 'magic-string'
import { nitro } from 'nitro/vite'
import { defineConfig, loadEnv, type Plugin } from 'vite'
import { vercelSpaRoutes } from './scripts/vercel-spa-routes'
import { getSecurityHeaders } from './security-headers'
const rootDir = path.dirname(fileURLToPath(import.meta.url))
const compatRoot = path.resolve(rootDir, 'compat/next')
@@ -269,9 +271,8 @@ function ssrStubGraphiql(): Plugin {
// back, ES module live-bindings can be undefined at the point the
// chunk that evaluates first tries to use them.
//
// Cycles are matched by chunk basename prefix (stripping the
// `assets/` directory and the `-<hash>.js` suffix), so the allowlist
// stays stable across builds even as Rolldown reassigns hashes.
// Cycles are matched by chunk basename prefix (directory and `-<hash>.js`
// suffix stripped), so the allowlist stays stable across builds.
const KNOWN_CHUNK_CYCLES: ReadonlyArray<ReadonlyArray<string>> = [
// `ui` ↔ `TreeView` chunk cycle. `cva` lives in the `ui` chunk
// (Rolldown pools it there because many ui files use it), TreeView
@@ -289,8 +290,8 @@ const KNOWN_CHUNK_CYCLES: ReadonlyArray<ReadonlyArray<string>> = [
function chunkPrefix(name: string): string {
return name
.replace(/^assets\//, '')
.replace(/-[A-Za-z0-9_-]{6,10}\.js$/, '')
.replace(/^.*\//, '')
.replace(/-[A-Za-z0-9_-]{6,}\.js$/, '')
.replace(/\.js$/, '')
}
@@ -369,155 +370,17 @@ function assertNoChunkCycles(): Plugin {
}
}
// Skew protection (vercel.com/docs/skew-protection): bake `?dpl=<deployment
// id>` into every asset URL the client bundle can request, so every hashed
// chunk resolves against the deployment that referenced it. Vercel's edge
// routes any request carrying `?dpl=` to that exact deployment, so a
// long-lived dashboard session keeps loading its own deployment's hashed
// chunks after a redeploy, while document navigations (which carry no pin)
// always land on the latest deployment. If the pinned deployment ages out of
// Skew Protection's Maximum Age, Vercel 404s the chunk and the
// `vite:preloadError` backstop in router.tsx reloads onto the latest deploy.
// API / server-function fetches are deliberately unpinned — that is what
// lets use-check-latest-deploy detect newer deploys mid-session.
//
// Three mechanisms are needed for full coverage:
// 1. `experimental.renderBuiltUrl` (in the config below) — asset and
// public-file URLs referenced from JS/CSS/HTML, including the
// `__vite__mapDeps` preload lists for dynamic imports.
// 2. The `generateBundle` hook here — chunk-to-chunk `import`/`from`
// specifiers. Rolldown renders these as bare relative paths that
// `renderBuiltUrl` never sees (vitejs/vite#13834), and they're what the
// browser actually fetches; the preload list alone would just warm a
// cache entry under a different URL. Module identity is keyed by URL, so
// the rewrite must cover EVERY specifier or a chunk could load twice
// (pinned + unpinned) and break singleton module state.
// 3. The `buildApp` hook here — `_shell.html` is prerendered by TanStack's
// own post-order `buildApp` hook from the router manifest, outside
// Vite's asset pipeline, so its <script>/<link> URLs and the embedded
// route-preload manifest are patched on disk afterwards. Without this,
// the shell's unpinned modulepreload URLs and the pinned import URLs are
// different cache keys and the whole entry graph downloads twice.
function skewProtectionDpl(opts: { dplSearch: string; assetsUrlPrefix: string }): Plugin {
const { dplSearch, assetsUrlPrefix } = opts
// Vite bundles `?worker` modules (Monaco's workers via graphiql) with the ROOT
// `build.assetsDir`, while Nitro only rewrites the client environment's (to
// `_vercel/immutable/<salt>/nitro` on Vercel). Keep the workers in the same
// directory so they land in the immutable store too.
function workersFollowClientAssetsDir(): Plugin {
return {
name: 'studio-skew-protection-dpl',
name: 'studio-workers-follow-client-assets-dir',
apply: 'build',
// TanStack's `tanstack-start-core:post-build` plugin is `enforce: 'post'`;
// matching it keeps THIS plugin sorted after it (same enforce group,
// registration order wins), which the buildApp hook below relies on.
enforce: 'post',
transform: {
handler(code, id) {
if (id !== '\0vite/preload-helper.js') return
// Vite's preload helper decides stylesheet-vs-modulepreload with
// `dep.endsWith(".css")`. The `?dpl=` suffix makes that false for
// every CSS dep, so lazy chunks' CSS would be injected as a script
// modulepreload — a console MIME error, and the stylesheet never
// applies. Make the check query-aware. Hard-fail if the helper's
// shape ever changes so a Vite upgrade can't silently regress this.
const cssCheck = 'dep.endsWith(".css")'
if (!code.includes(cssCheck)) {
this.error(
`studio-skew-protection-dpl: expected \`${cssCheck}\` in vite's preload helper — ` +
`vite changed its preload-helper shape; update this patch to match.`
)
}
return { code: code.replaceAll(cssCheck, '/\\.css(\\?|$)/.test(dep)'), map: null }
},
},
generateBundle: {
// `order: 'post'` so this runs AFTER `vite:build-import-analysis`'s
// normal-order generateBundle. That hook maps each dynamic-import
// specifier back to a bundle key — with no query stripping — to collect
// the chunk's CSS/preload deps; a `?dpl` suffix added any earlier makes
// the lookup miss and silently drops CSS preloading for lazy chunks.
order: 'post',
handler(_options, bundle) {
// Server chunks import each other via Node's filesystem resolution —
// only the browser-facing client build gets the query pin.
if (this.environment.name !== 'client') return
for (const chunk of Object.values(bundle)) {
if (chunk.type !== 'chunk') continue
const importees = new Set([...chunk.imports, ...chunk.dynamicImports])
if (importees.size === 0) continue
const chunkDir = path.posix.dirname(chunk.fileName)
let edits: MagicString | undefined
for (const importee of importees) {
const rel = path.posix.relative(chunkDir, importee)
const spec = rel.startsWith('.') ? rel : `./${rel}`
// Rolldown quotes static import specifiers with `"` and dynamic
// ones with backticks; cover `'` too for safety. Matching the
// exact quoted specifier of a known importee (hashed filename)
// can't collide with app string literals.
for (const quote of ['"', "'", '`']) {
const target = `${quote}${spec}${quote}`
for (
let at = chunk.code.indexOf(target);
at !== -1;
at = chunk.code.indexOf(target, at + target.length)
) {
edits ??= new MagicString(chunk.code)
edits.appendLeft(at + target.length - 1, dplSearch)
}
}
}
if (!edits) continue
chunk.code = edits.toString()
// Recombine the sourcemap so the columns Sentry maps stay accurate
// — every insertion shifts the rest of the minified line. Like
// vite:build-import-analysis's own generateBundle edits, updating
// `chunk.map` isn't enough: the `.js.map` file already exists in
// the bundle as an emitted asset by this point, so the combined map
// has to be written into that asset too.
if (chunk.map) {
const editMap = edits.generateMap({ source: chunk.fileName, hires: 'boundary' })
const original = chunk.map as unknown as SourceMapInput & {
file?: string
debugId?: string
}
const combined = {
...remapping([editMap as SourceMapInput, original], () => null),
// Preserve fields remapping drops but the toolchain relies on
// (`debugId` is how Sentry pairs the uploaded map to the chunk).
file: original.file,
...(original.debugId && { debugId: original.debugId }),
}
chunk.map = combined as unknown as typeof chunk.map
const mapAsset = bundle[`${chunk.fileName}.map`]
if (mapAsset && mapAsset.type === 'asset') {
mapAsset.source = JSON.stringify(combined)
}
}
}
},
},
buildApp: {
// TanStack's `tanstack-start-core:post-build` prerenders `_shell.html`
// in its own post-order buildApp hook. This hook must run after it, so
// this plugin must sort after that one: same hook order + same plugin
// `enforce` group (see above) + registered after `tanstackStart()` in
// the plugins array.
order: 'post',
async handler(builder) {
const clientEnv = builder.environments.client
if (!clientEnv) return
const outDir = path.resolve(builder.config.root, clientEnv.config.build.outDir)
const escapedPrefix = assetsUrlPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
// Quoted `/assets/...` URLs: <script src>/<link href> attributes and
// the serialized router manifest's preload lists. `[^"'`?]` keeps the
// match inside one URL and skips any that already carry a query.
const assetUrl = new RegExp(`(["'\`])(${escapedPrefix}[^"'\`?]+)\\1`, 'g')
for (const entry of fs.readdirSync(outDir, { recursive: true, withFileTypes: true })) {
if (!entry.isFile() || !entry.name.endsWith('.html')) continue
const filePath = path.join(entry.parentPath, entry.name)
const html = fs.readFileSync(filePath, 'utf-8')
const patched = html.replace(assetUrl, (_match, quote, url) => {
return `${quote}${url}${dplSearch}${quote}`
})
if (patched !== html) fs.writeFileSync(filePath, patched)
}
},
configResolved(config) {
const clientAssetsDir = config.environments.client?.build.assetsDir
if (clientAssetsDir) config.build.assetsDir = clientAssetsDir
},
}
}
@@ -602,6 +465,13 @@ export default defineConfig(({ command, mode }) => {
}
}
// Pin server-function calls to the build that created them, without a
// session cookie that also pins document reloads and the update check.
publicEnvDefines['process.env.NEXT_PUBLIC_VERCEL_DEPLOYMENT_ID'] =
JSON.stringify(
env.VERCEL_SKEW_PROTECTION_ENABLED === '1' ? env.VERCEL_DEPLOYMENT_ID : undefined
) ?? 'undefined'
// `MAINTENANCE_MODE` gates the "redirect everything to /maintenance" rule.
// It's deliberately unprefixed, and the other two consumers both read it at
// BUILD time: `next.config.ts` reads it in `redirects()`, which Next bakes
@@ -629,36 +499,19 @@ export default defineConfig(({ command, mode }) => {
publicEnvDefines[`process.env.${key}`] ??= 'undefined'
}
// Mirror Next's `basePath` via NEXT_PUBLIC_BASE_PATH. Unlike Next, TanStack
// Start has no single knob — the prefix has to be declared in three places
// (see BASE_PATH_REDIRECT_GUIDE.md):
// - Vite `base` — bakes the prefix into asset URLs in the
// built bundle.
// - tanstackStart router.basepath — must be passed explicitly. If
// omitted, the plugin's internal
// `deriveRouterBasepath` derives a value
// from `publicBase` and strips both
// leading and trailing slashes
// (`/dashboard` → `dashboard`), which then
// surfaces in `useRouter().basePath`
// consumers as relative URLs (e.g.
// `${BASE_PATH}/img/...` becomes
// `dashboard/img/...` and the browser
// resolves it against the current path).
// See planning.js:14 in
// @tanstack/start-plugin-core.
// - createRouter({ basepath }) — runtime navigation prefix; configured
// in router.tsx off the same env var
// (inlined via `define` above).
// Leaving the var empty keeps the app at `/` as today.
// Vite's public base keeps asset requests under www's `/dashboard` proxy.
// Nitro's baseURL stays `/`: its immutable manifest must use the reserved
// root path. vercel-spa-routes rewrites the browser's prefixed asset URLs
// to that root path, including assets retained from older deployments.
const basePath = env.NEXT_PUBLIC_BASE_PATH || undefined
// Skew protection — see the skewProtectionDpl comment above. Both are
// build-time system env vars on Vercel; unset on local/self-hosted builds,
// which disables the whole mechanism.
const skewDeploymentId =
env.VERCEL_SKEW_PROTECTION_ENABLED === '1' ? env.VERCEL_DEPLOYMENT_ID : undefined
const dplSearch = skewDeploymentId ? `?dpl=${encodeURIComponent(skewDeploymentId)}` : ''
// Self-hosted responses get next.config.ts's security headers via Nitro
// route rules. On Vercel they come from vercel.ts: a `/**` header route in
// the Build Output config would stop matching before Nitro's asset and
// skew-cookie rules.
const securityHeaders = Object.fromEntries(
getSecurityHeaders().map(({ key, value }) => [key, value])
)
// Substitutions that have to apply to *both* our app source (via Vite's
// `define`) and any pre-bundled dependencies (via esbuild's optimizeDeps).
@@ -713,19 +566,6 @@ export default defineConfig(({ command, mode }) => {
],
},
...(basePath && { base: basePath }),
// Skew protection part 1 (see skewProtectionDpl above): every asset /
// public-file URL rendered into the bundle — CSS url()s, images, worker
// URLs, `__vite__mapDeps` preload lists — gets the `?dpl=` pin. Returning
// a string opts out of Vite's base handling, so the base path is joined
// here. Left unset when the pin is off so Vite keeps its default
// base-relative URL rendering.
...(dplSearch && {
experimental: {
renderBuiltUrl(filename: string) {
return `${basePath ?? ''}/${filename}${dplSearch}`
},
},
}),
optimizeDeps: {
// graphiql's Vite worker setup (swapped in for the webpack one by the
// `graphiqlViteWorkers` plugin above) imports Monaco's workers with
@@ -867,7 +707,16 @@ export default defineConfig(({ command, mode }) => {
// above rewrites it to the `@sentry/react`-backed shim before SSR
// resolution ever sees the id, and `@sentry/react` ships real ESM
// ("import" condition → build/esm), so plain externalization works.
noExternal: ['lodash', /^next(\/|$)/, 'tslib', 'react-use', 'awesome-debounce-promise'],
// `tslib` is inlined for the BUILD only: Nitro's dev runner has no CJS
// interop for the `tslib.js` that its ESM wrapper default-imports, and
// every SSR request would 500. Left external in dev, Node loads it.
noExternal: [
'lodash',
/^next(\/|$)/,
...(command === 'build' ? ['tslib'] : []),
'react-use',
'awesome-debounce-promise',
],
},
plugins: [
nextCompat(),
@@ -877,8 +726,39 @@ export default defineConfig(({ command, mode }) => {
ssrLodashEs(),
umdAmdShortCircuit(),
assertNoChunkCycles(),
workersFollowClientAssetsDir(),
devtools(),
tailwindcss(),
// Nitro builds and hosts the server for every target: the Vercel
// function (`.vercel/output`, preset auto-detected from `VERCEL`) and
// the self-hosted node server (`.output`).
nitro({
// `server.ts` is TanStack Start's SSR entry, not a Nitro entry;
// without this Nitro's scan picks it up as both and warns.
serverEntry: false,
// Nitro bundles dependencies. libpg-query's emscripten glue reads
// `__dirname` and loads its `.wasm` from disk, so keep it external
// and fully copied (`*`).
traceDeps: ['libpg-query*'],
vercel: {
// Content-addressed chunks under `/_vercel/immutable/`, shared
// across deployments, so a tab opened before a redeploy keeps
// loading its lazy chunks.
immutableStaticFiles: true,
// Nitro uses a session-wide __vdpl cookie, which pins reloads too.
// Keep Skew Protection ENABLED in the Vercel dashboard: this flag
// only disables Nitro's cookie integration, not Vercel's routing.
// start.ts pins only server functions using Vercel's request header.
skewProtection: false,
// One function serves every API route, so the timeout must cover
// the longest one (integrations/stripe-sync).
functions: { maxDuration: 300 },
},
// Documents from the static shell; only /api/* and /_serverFn/*
// invoke the function.
modules: [vercelSpaRoutes({ basePath })],
...(!process.env.VERCEL && { routeRules: { '/**': { headers: securityHeaders } } }),
}),
tanstackStart({
srcDirectory: './',
spa: {
@@ -889,12 +769,6 @@ export default defineConfig(({ command, mode }) => {
...(basePath && { router: { basepath: basePath } }),
}),
viteReact(),
// Skew protection parts 2 + 3. Registered after tanstackStart() so the
// shell-patching buildApp hook runs after TanStack's prerender — see
// the skewProtectionDpl comment.
...(dplSearch
? [skewProtectionDpl({ dplSearch, assetsUrlPrefix: `${basePath ?? ''}/assets/` })]
: []),
// Sentry's TanStack Start plugin(s) MUST be last so source maps reflect
// every prior transform. `sentryTanstackStart` returns an ARRAY of
// plugins (route patterns, source-map upload, middleware auto-wrap), so
-7
View File
@@ -36,13 +36,6 @@
// vite.config.ts (`next/link` -> compat/next/link.tsx), which knip
// cannot resolve, so they need to be entries in their own right.
"compat/**/*.{ts,tsx}",
// Vercel serverless function. Vercel deploys every file under `api/`
// as a Function by convention; knip's vercel plugin only contributes
// `vercel.ts`, so this one is manual. Listing it also traces its
// runtime `import('../instrument.server.mjs')`, which is why
// instrument.server.mjs needs no entry of its own even though
// scripts/serve.js (its other importer) is ignored below.
"api/server.js",
],
"ignore": [
"public/**",
+352 -53
View File
@@ -734,13 +734,13 @@ importers:
version: 7.2.4(supports-color@8.1.1)
'@astrojs/mdx':
specifier: ^7.0.7
version: 7.0.8(@astrojs/markdown-satteri@0.3.8)(astro@7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.4(@electric-sql/pglite@0.4.5))(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(supports-color@8.1.1)
version: 7.0.8(@astrojs/markdown-satteri@0.3.8)(astro@7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.4.1)(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(supports-color@8.1.1)
'@astrojs/react':
specifier: ^6.0.4
version: 6.0.4(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
astro:
specifier: ^7.2.6
version: 7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.4(@electric-sql/pglite@0.4.5))(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
version: 7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.4.1)(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
lucide-react:
specifier: '*'
version: 0.436.0(react@19.2.6)
@@ -1079,7 +1079,7 @@ importers:
version: 1.167.1(@tanstack/query-core@5.101.2)(@tanstack/react-query@5.83.0(react@19.2.6))(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.8)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: 'catalog:'
version: 1.168.18(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
version: 1.168.18(crossws@0.4.12(srvx@1.0.4))(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/react-table':
specifier: 'catalog:'
version: 8.21.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -1330,9 +1330,6 @@ importers:
'@graphql-typed-document-node/core':
specifier: ^3.2.0
version: 3.2.0(graphql@16.11.0)
'@jridgewell/remapping':
specifier: ^2.3.5
version: 2.3.5
'@tailwindcss/postcss':
specifier: ^4.2.4
version: 4.2.4
@@ -1441,15 +1438,15 @@ importers:
jsdom-testing-mocks:
specifier: ^1.13.1
version: 1.13.1
magic-string:
specifier: ^0.30.21
version: 0.30.21
msw:
specifier: ^2.3.0
version: 2.11.3(@types/node@22.13.14)(typescript@6.0.2)
next-router-mock:
specifier: ^0.9.13
version: 0.9.13(next@16.3.5(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@22.13.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.77.4))(react@19.2.6)
nitro:
specifier: 3.0.260903-beta
version: 3.0.260903-beta
node-mocks-http:
specifier: ^1.17.2
version: 1.17.2(@types/node@22.13.14)
@@ -1645,7 +1642,7 @@ importers:
version: 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start':
specifier: 'catalog:'
version: 1.168.18(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
version: 1.168.18(crossws@0.4.12(srvx@1.0.4))(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@types/common-tags':
specifier: ^1.8.4
version: 1.8.4
@@ -2090,7 +2087,7 @@ importers:
version: 0.562.0(vue@3.5.35(typescript@6.0.2))
nuxt:
specifier: ^4.4.6
version: 4.5.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7(supports-color@8.1.1)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@8.1.1)))(@electric-sql/pglite@0.4.5)(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@22.13.14)(@vue/compiler-sfc@3.5.41)(aws4fetch@1.0.20)(cac@6.7.14)(commander@14.0.1)(db0@0.3.4(@electric-sql/pglite@0.4.5))(encoding@0.1.13)(esbuild@0.28.1)(eslint@9.37.0(jiti@2.7.0)(supports-color@8.1.1))(ioredis@5.10.1(supports-color@8.1.1))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.3)(rollup@4.60.3))(rollup@4.60.3)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))(yaml@2.9.0)
version: 4.5.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7(supports-color@8.1.1)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@8.1.1)))(@electric-sql/pglite@0.4.5)(@oxc-project/types@0.149.0)(@parcel/watcher@2.5.6)(@types/node@22.13.14)(@vue/compiler-sfc@3.5.41)(aws4fetch@1.0.20)(cac@6.7.14)(commander@14.0.1)(db0@0.3.4(@electric-sql/pglite@0.4.5))(encoding@0.1.13)(esbuild@0.28.1)(eslint@9.37.0(jiti@2.7.0)(supports-color@8.1.1))(ioredis@5.10.1(supports-color@8.1.1))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.8)(rollup@4.60.3))(rollup@4.60.3)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))(yaml@2.9.0)
vue:
specifier: ^3.5.35
version: 3.5.35(typescript@6.0.2)
@@ -5999,6 +5996,9 @@ packages:
'@oxc-project/types@0.143.0':
resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==}
'@oxc-project/types@0.149.0':
resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==}
'@oxc-resolver/binding-android-arm-eabi@11.24.2':
resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==}
cpu: [arm]
@@ -7109,36 +7109,72 @@ packages:
resolution: {integrity: sha512-C7c51Nn4yTxXFKvgh2txJFNweaVcfUPQxwEUFw4aWsCmfiBDJsTSwviIF8EcwjQ6k8bPyMWCl1vw4BdxE569Cg==}
engines: {node: '>= 10'}
'@rolldown/binding-android-arm-eabi@1.2.8':
resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [android]
'@rolldown/binding-android-arm64@1.2.3':
resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@rolldown/binding-android-arm64@1.2.8':
resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@rolldown/binding-darwin-arm64@1.2.3':
resolution: {integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-arm64@1.2.8':
resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.2.3':
resolution: {integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.2.8':
resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@rolldown/binding-freebsd-x64@1.2.3':
resolution: {integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@rolldown/binding-freebsd-x64@1.2.8':
resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@rolldown/binding-linux-arm-gnueabihf@1.2.3':
resolution: {integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm-gnueabihf@1.2.8':
resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.2.3':
resolution: {integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -7146,6 +7182,13 @@ packages:
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-gnu@1.2.8':
resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.2.3':
resolution: {integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -7153,6 +7196,13 @@ packages:
os: [linux]
libc: [musl]
'@rolldown/binding-linux-arm64-musl@1.2.8':
resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.2.3':
resolution: {integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -7160,6 +7210,13 @@ packages:
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-ppc64-gnu@1.2.8':
resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.2.3':
resolution: {integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -7167,6 +7224,13 @@ packages:
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.2.8':
resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.2.3':
resolution: {integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -7174,6 +7238,13 @@ packages:
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.2.8':
resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.2.3':
resolution: {integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -7181,24 +7252,49 @@ packages:
os: [linux]
libc: [musl]
'@rolldown/binding-linux-x64-musl@1.2.8':
resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.2.3':
resolution: {integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@rolldown/binding-openharmony-arm64@1.2.8':
resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@rolldown/binding-win32-arm64-msvc@1.2.3':
resolution: {integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-arm64-msvc@1.2.8':
resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.2.3':
resolution: {integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.2.8':
resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@rolldown/pluginutils@1.0.0-rc.3':
resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==}
@@ -10731,6 +10827,14 @@ packages:
crossws@0.3.5:
resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
crossws@0.4.12:
resolution: {integrity: sha512-aypfsr6t0uNvkqaZc6zvBfXzC6pLI0/sIulpkV6RwCVtZqG5ebBzv4weImKK0VNCj91Wl9F5j7p5WU4MNrybng==}
peerDependencies:
srvx: '>=0.11.5'
peerDependenciesMeta:
srvx:
optional: true
crypto-js@4.2.0:
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
deprecated: Active development of CryptoJS has been discontinued. This library is no longer maintained.
@@ -11048,6 +11152,9 @@ packages:
sqlite3:
optional: true
db0@0.4.1:
resolution: {integrity: sha512-6RBY/bSn42UrqATwsiULj2uYFyEykB3XeA/NLIVQeHNXlYV6D4Idxx3/aa6k5Y45Dwzx7sygeLotnqHWSeURYA==}
dc-browser@1.0.4:
resolution: {integrity: sha512-7oEtnzNlcE+hr4OvO3GR6Gndgw8BhW+wKOEwMqSleyY7N29jbAxzyW5BaJl7qBCw+6OIxfMWtY0T+6dxq8RWLw==}
@@ -11365,6 +11472,10 @@ packages:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
env-runner@0.2.1:
resolution: {integrity: sha512-2iDP2DfheAMMAKeXBggEuFmpSq+1Xs6wQoHba1g65pZFXlC3VHD1O6/tgVzS6GQLv+P2koPKZPKgKhXkigNBdg==}
hasBin: true
err-code@2.0.3:
resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
@@ -12531,6 +12642,19 @@ packages:
crossws:
optional: true
h3@2.0.1-rc.31:
resolution: {integrity: sha512-AG7qZzF99a0BSYoXT3evJgIhwSIUKow7R+hwkLRZS06WJ9nbSb7QXUDgs1ByBjp6svTvl++N/GKA7I9YPz9cQQ==}
engines: {node: '>=20.11.1'}
hasBin: true
peerDependencies:
crossws: ^0.4.12
ocache: '>=0.3.0'
peerDependenciesMeta:
crossws:
optional: true
ocache:
optional: true
hachure-fill@0.5.2:
resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
@@ -12744,6 +12868,9 @@ packages:
httpxy@0.5.1:
resolution: {integrity: sha512-JPhqYiixe1A1I+MXDewWDZqeudBGU8Q9jCHYN8ML+779RQzLjTi78HBvWz4jMxUD6h2/vUL12g4q/mFM0OUw1A==}
httpxy@0.5.5:
resolution: {integrity: sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==}
human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@@ -14641,9 +14768,17 @@ packages:
sass:
optional: true
nf3@0.3.24:
resolution: {integrity: sha512-HxLK4bo+5jNsEETZp4w3tJblHOA9MCBY14IN9nZJJV8JDxt9yNIYxuBuLMjTAR5GFa3HL61+8VQDUrXv3/C8fw==}
nice-try@1.0.5:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
nitro@3.0.260903-beta:
resolution: {integrity: sha512-54gANPi62O8rfMvepiJUVuIzEIinYfpeHbebywlLxvVTgIYXDwSvpaU9Id+0sJOBjBx0wC1/CVYXJkH7SY+l0g==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
nitropack@2.13.4:
resolution: {integrity: sha512-tX7bT6zxNeMwkc6hxHiZeUoTOjVrcjoh1Z3cmxOlodIqjl4HISgqfGOmkWSayky3Nv9Z5+KQH52F8nmXJY5AAA==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -14912,6 +15047,9 @@ packages:
resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==}
engines: {node: '>=12.20.0'}
ocache@0.3.0:
resolution: {integrity: sha512-RS/9P0zeBb0gDJmadGERLH8lZNXK7xbufTDhclkXGvFGTsj0G4M5/cLk+mizcERH29mLXNnocfB5wjcK70wGJg==}
ofetch@1.5.1:
resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==}
@@ -16354,6 +16492,11 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
rolldown@1.2.8:
resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
rollup-plugin-visualizer@7.0.1:
resolution: {integrity: sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg==}
engines: {node: '>=22'}
@@ -16378,6 +16521,9 @@ packages:
rou3@0.9.1:
resolution: {integrity: sha512-z/sSmzvtwMDDnxsPVhfWMuG6F6mbmhFDXoVqLmMfbpDD9qfV3GDmSQpf0+W296/ZDIpW2wcMmBfpVFzcnOi/nA==}
rou3@0.9.2:
resolution: {integrity: sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==}
roughjs@4.6.6:
resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==}
@@ -16836,6 +16982,11 @@ packages:
engines: {node: '>=20.16.0'}
hasBin: true
srvx@1.0.4:
resolution: {integrity: sha512-eZmYaxUfZSo7/8m8UdsHRJNmTLSCTPPom9d7a60vMmuVeZvwhbvflRR+00/CxTCjMOFa5+H8tgBeNDVZ+w7AAQ==}
engines: {node: '>=20.16.0'}
hasBin: true
sse.js@2.2.0:
resolution: {integrity: sha512-v1ciaikunC99FKS23rS973HYU0DnnvugtX13UggNvrgRUSungaGeLmKlm57t0dI2E1TcCqzc2Pj2njBDXFIpKQ==}
@@ -17765,6 +17916,9 @@ packages:
uploadthing:
optional: true
unstorage@2.0.0-alpha.10:
resolution: {integrity: sha512-6h1veZp8gnp4dGllf0tDijoXxDYymJu251IpKKkrSVH+QHL6tXFbwG85KM+CBHSgpkkgtPuIiZ9oLCLP5+1Evw==}
until-async@3.0.2:
resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==}
@@ -18867,13 +19021,13 @@ snapshots:
github-slugger: 2.0.0
satteri: 0.10.5
'@astrojs/mdx@7.0.8(@astrojs/markdown-satteri@0.3.8)(astro@7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.4(@electric-sql/pglite@0.4.5))(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(supports-color@8.1.1)':
'@astrojs/mdx@7.0.8(@astrojs/markdown-satteri@0.3.8)(astro@7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.4.1)(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(supports-color@8.1.1)':
dependencies:
'@astrojs/internal-helpers': 0.10.4
'@astrojs/markdown-remark': 7.2.4(supports-color@8.1.1)
'@mdx-js/mdx': 3.1.1(supports-color@8.1.1)
acorn: 8.18.0
astro: 7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.4(@electric-sql/pglite@0.4.5))(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
astro: 7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.4.1)(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
es-module-lexer: 2.3.2
estree-util-visit: 2.0.0
hast-util-to-html: 9.0.5
@@ -21961,11 +22115,11 @@ snapshots:
- rolldown
- unplugin
'@nuxt/nitro-server@4.5.2(c3f0ccbf4bde9bd4a229f96b3badde35)':
'@nuxt/nitro-server@4.5.2(4bec0acf20c572958b2bb91f944fc6a0)':
dependencies:
'@nuxt/devalue': 2.0.2
'@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)))
'@unhead/vue': 3.3.1(@oxc-project/types@0.143.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.2))(webpack@5.105.4(esbuild@0.28.1))
'@unhead/vue': 3.3.1(@oxc-project/types@0.149.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.2))(webpack@5.105.4(esbuild@0.28.1))
'@vue/shared': 3.5.41
consola: 3.4.2
defu: 6.1.7
@@ -21980,7 +22134,7 @@ snapshots:
mocked-exports: 0.1.1
nitropack: 2.13.4(@electric-sql/pglite@0.4.5)(aws4fetch@1.0.20)(encoding@0.1.13)(oxc-parser@0.143.0)(rolldown@1.2.3)(supports-color@8.1.1)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
nostics: 1.2.0
nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7(supports-color@8.1.1)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@8.1.1)))(@electric-sql/pglite@0.4.5)(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@22.13.14)(@vue/compiler-sfc@3.5.41)(aws4fetch@1.0.20)(cac@6.7.14)(commander@14.0.1)(db0@0.3.4(@electric-sql/pglite@0.4.5))(encoding@0.1.13)(esbuild@0.28.1)(eslint@9.37.0(jiti@2.7.0)(supports-color@8.1.1))(ioredis@5.10.1(supports-color@8.1.1))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.3)(rollup@4.60.3))(rollup@4.60.3)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))(yaml@2.9.0)
nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7(supports-color@8.1.1)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@8.1.1)))(@electric-sql/pglite@0.4.5)(@oxc-project/types@0.149.0)(@parcel/watcher@2.5.6)(@types/node@22.13.14)(@vue/compiler-sfc@3.5.41)(aws4fetch@1.0.20)(cac@6.7.14)(commander@14.0.1)(db0@0.3.4(@electric-sql/pglite@0.4.5))(encoding@0.1.13)(esbuild@0.28.1)(eslint@9.37.0(jiti@2.7.0)(supports-color@8.1.1))(ioredis@5.10.1(supports-color@8.1.1))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.8)(rollup@4.60.3))(rollup@4.60.3)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))(yaml@2.9.0)
nypm: 0.6.9
ohash: 2.0.11
pathe: 2.0.3
@@ -22060,7 +22214,7 @@ snapshots:
rc9: 3.0.1
std-env: 4.2.0
'@nuxt/vite-builder@4.5.2(b8937c6da1cc25d65b2c4fd0e82c4d2a)':
'@nuxt/vite-builder@4.5.2(7593670026f098f90d303525f2479dbe)':
dependencies:
'@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)))
'@vitejs/plugin-vue': 6.0.8(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.2))
@@ -22078,7 +22232,7 @@ snapshots:
knitwork: 1.3.0
mlly: 1.8.2
mocked-exports: 0.1.1
nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7(supports-color@8.1.1)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@8.1.1)))(@electric-sql/pglite@0.4.5)(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@22.13.14)(@vue/compiler-sfc@3.5.41)(aws4fetch@1.0.20)(cac@6.7.14)(commander@14.0.1)(db0@0.3.4(@electric-sql/pglite@0.4.5))(encoding@0.1.13)(esbuild@0.28.1)(eslint@9.37.0(jiti@2.7.0)(supports-color@8.1.1))(ioredis@5.10.1(supports-color@8.1.1))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.3)(rollup@4.60.3))(rollup@4.60.3)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))(yaml@2.9.0)
nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7(supports-color@8.1.1)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@8.1.1)))(@electric-sql/pglite@0.4.5)(@oxc-project/types@0.149.0)(@parcel/watcher@2.5.6)(@types/node@22.13.14)(@vue/compiler-sfc@3.5.41)(aws4fetch@1.0.20)(cac@6.7.14)(commander@14.0.1)(db0@0.3.4(@electric-sql/pglite@0.4.5))(encoding@0.1.13)(esbuild@0.28.1)(eslint@9.37.0(jiti@2.7.0)(supports-color@8.1.1))(ioredis@5.10.1(supports-color@8.1.1))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.8)(rollup@4.60.3))(rollup@4.60.3)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))(yaml@2.9.0)
nypm: 0.6.9
pathe: 2.0.3
pkg-types: 2.3.1
@@ -22096,7 +22250,7 @@ snapshots:
optionalDependencies:
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.7(supports-color@8.1.1))
rolldown: 1.2.3
rollup-plugin-visualizer: 7.0.1(rolldown@1.2.3)(rollup@4.60.3)
rollup-plugin-visualizer: 7.0.1(rolldown@1.2.8)(rollup@4.60.3)
transitivePeerDependencies:
- '@biomejs/biome'
- '@types/node'
@@ -22419,6 +22573,8 @@ snapshots:
'@oxc-project/types@0.143.0': {}
'@oxc-project/types@0.149.0': {}
'@oxc-resolver/binding-android-arm-eabi@11.24.2':
optional: true
@@ -23578,48 +23734,93 @@ snapshots:
'@resvg/resvg-wasm@2.4.0': {}
'@rolldown/binding-android-arm-eabi@1.2.8':
optional: true
'@rolldown/binding-android-arm64@1.2.3':
optional: true
'@rolldown/binding-android-arm64@1.2.8':
optional: true
'@rolldown/binding-darwin-arm64@1.2.3':
optional: true
'@rolldown/binding-darwin-arm64@1.2.8':
optional: true
'@rolldown/binding-darwin-x64@1.2.3':
optional: true
'@rolldown/binding-darwin-x64@1.2.8':
optional: true
'@rolldown/binding-freebsd-x64@1.2.3':
optional: true
'@rolldown/binding-freebsd-x64@1.2.8':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.2.3':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.2.8':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.2.3':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.2.8':
optional: true
'@rolldown/binding-linux-arm64-musl@1.2.3':
optional: true
'@rolldown/binding-linux-arm64-musl@1.2.8':
optional: true
'@rolldown/binding-linux-ppc64-gnu@1.2.3':
optional: true
'@rolldown/binding-linux-ppc64-gnu@1.2.8':
optional: true
'@rolldown/binding-linux-s390x-gnu@1.2.3':
optional: true
'@rolldown/binding-linux-s390x-gnu@1.2.8':
optional: true
'@rolldown/binding-linux-x64-gnu@1.2.3':
optional: true
'@rolldown/binding-linux-x64-gnu@1.2.8':
optional: true
'@rolldown/binding-linux-x64-musl@1.2.3':
optional: true
'@rolldown/binding-linux-x64-musl@1.2.8':
optional: true
'@rolldown/binding-openharmony-arm64@1.2.3':
optional: true
'@rolldown/binding-openharmony-arm64@1.2.8':
optional: true
'@rolldown/binding-win32-arm64-msvc@1.2.3':
optional: true
'@rolldown/binding-win32-arm64-msvc@1.2.8':
optional: true
'@rolldown/binding-win32-x64-msvc@1.2.3':
optional: true
'@rolldown/binding-win32-x64-msvc@1.2.8':
optional: true
'@rolldown/pluginutils@1.0.0-rc.3': {}
'@rolldown/pluginutils@1.0.0-rc.7': {}
@@ -24993,16 +25194,16 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
'@tanstack/react-start-rsc@0.1.17(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
'@tanstack/react-start-rsc@0.1.17(crossws@0.4.12(srvx@1.0.4))(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
dependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-server': 1.167.13(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-server': 1.167.13(crossws@0.4.12(srvx@1.0.4))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-core': 1.171.8
'@tanstack/router-utils': 1.162.1(supports-color@8.1.1)
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-fn-stubs': 1.162.0
'@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/start-server-core': 1.169.8
'@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.12(srvx@1.0.4))(esbuild@0.28.1)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/start-server-core': 1.169.8(crossws@0.4.12(srvx@1.0.4))
'@tanstack/start-storage-context': 1.167.10
pathe: 2.0.3
react: 19.2.6
@@ -25021,28 +25222,28 @@ snapshots:
- vite-plugin-solid
- webpack
'@tanstack/react-start-server@1.167.13(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
'@tanstack/react-start-server@1.167.13(crossws@0.4.12(srvx@1.0.4))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
'@tanstack/history': 1.162.0
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-core': 1.171.8
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-server-core': 1.169.8
'@tanstack/start-server-core': 1.169.8(crossws@0.4.12(srvx@1.0.4))
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
transitivePeerDependencies:
- crossws
'@tanstack/react-start@1.168.18(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
'@tanstack/react-start@1.168.18(crossws@0.4.12(srvx@1.0.4))(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
dependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-client': 1.168.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-rsc': 0.1.17(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/react-start-server': 1.167.13(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-start-rsc': 0.1.17(crossws@0.4.12(srvx@1.0.4))(esbuild@0.28.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/react-start-server': 1.167.13(crossws@0.4.12(srvx@1.0.4))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/router-utils': 1.162.1(supports-color@8.1.1)
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/start-server-core': 1.169.8
'@tanstack/start-plugin-core': 1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.12(srvx@1.0.4))(esbuild@0.28.1)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/start-server-core': 1.169.8(crossws@0.4.12(srvx@1.0.4))
pathe: 2.0.3
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
@@ -25116,7 +25317,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
'@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(esbuild@0.28.1)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
dependencies:
'@babel/core': 7.29.7(supports-color@8.1.1)
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.7(supports-color@8.1.1))
@@ -25129,7 +25330,7 @@ snapshots:
'@tanstack/router-utils': 1.162.1(supports-color@8.1.1)
'@tanstack/virtual-file-routes': 1.162.0
chokidar: 5.0.0
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.8)(rollup@4.60.3)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
zod: 4.4.3
optionalDependencies:
'@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -25173,7 +25374,7 @@ snapshots:
'@tanstack/start-fn-stubs@1.162.0': {}
'@tanstack/start-plugin-core@1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
'@tanstack/start-plugin-core@1.171.10(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(crossws@0.4.12(srvx@1.0.4))(esbuild@0.28.1)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/core': 7.29.7(supports-color@8.1.1)
@@ -25181,10 +25382,10 @@ snapshots:
'@rolldown/pluginutils': 1.0.1
'@tanstack/router-core': 1.171.8
'@tanstack/router-generator': 1.167.12(supports-color@8.1.1)
'@tanstack/router-plugin': 1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/router-plugin': 1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(esbuild@0.28.1)(rolldown@1.2.8)(rollup@4.60.3)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@tanstack/router-utils': 1.162.1(supports-color@8.1.1)
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-server-core': 1.169.8
'@tanstack/start-server-core': 1.169.8(crossws@0.4.12(srvx@1.0.4))
exsolve: 1.1.1
lightningcss: 1.33.0
pathe: 2.0.3
@@ -25213,14 +25414,14 @@ snapshots:
- vite-plugin-solid
- webpack
'@tanstack/start-server-core@1.169.8':
'@tanstack/start-server-core@1.169.8(crossws@0.4.12(srvx@1.0.4))':
dependencies:
'@tanstack/history': 1.162.0
'@tanstack/router-core': 1.171.8
'@tanstack/start-client-core': 1.170.6
'@tanstack/start-storage-context': 1.167.10
fetchdts: 0.1.7
h3-v2: h3@2.0.1-rc.20
h3-v2: h3@2.0.1-rc.20(crossws@0.4.12(srvx@1.0.4))
seroval: 1.6.2
transitivePeerDependencies:
- crossws
@@ -25944,10 +26145,10 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@unhead/bundler@3.3.1(@oxc-project/types@0.143.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(unhead@3.3.1(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)))(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
'@unhead/bundler@3.3.1(@oxc-project/types@0.149.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(unhead@3.3.1(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)))(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))':
dependencies:
magic-string: 1.1.0
oxc-walker: 1.1.1(@oxc-project/types@0.143.0)(oxc-parser@0.143.0)(rolldown@1.2.3)
oxc-walker: 1.1.1(@oxc-project/types@0.149.0)(oxc-parser@0.143.0)(rolldown@1.2.3)
unhead: 3.3.1(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
optionalDependencies:
@@ -25965,9 +26166,9 @@ snapshots:
- rollup
- unloader
'@unhead/vue@3.3.1(@oxc-project/types@0.143.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.2))(webpack@5.105.4(esbuild@0.28.1))':
'@unhead/vue@3.3.1(@oxc-project/types@0.149.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.2))(webpack@5.105.4(esbuild@0.28.1))':
dependencies:
'@unhead/bundler': 3.3.1(@oxc-project/types@0.143.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(unhead@3.3.1(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)))(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@unhead/bundler': 3.3.1(@oxc-project/types@0.149.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(unhead@3.3.1(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)))(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
hookable: 6.1.1
unhead: 3.3.1(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
@@ -26795,7 +26996,7 @@ snapshots:
astring@1.9.0: {}
astro@7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.4(@electric-sql/pglite@0.4.5))(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0):
astro@7.2.9(@astrojs/markdown-remark@7.2.4(supports-color@8.1.1))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.4.1)(ioredis@5.10.1(supports-color@8.1.1))(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0):
dependencies:
'@astrojs/compiler-rs': 0.4.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)
'@astrojs/internal-helpers': 0.10.4
@@ -26844,7 +27045,7 @@ snapshots:
tinyglobby: 0.2.17
ultrahtml: 1.7.0
unifont: 0.7.5
unstorage: 1.17.5(aws4fetch@1.0.20)(db0@0.3.4(@electric-sql/pglite@0.4.5))(ioredis@5.10.1(supports-color@8.1.1))
unstorage: 1.17.5(aws4fetch@1.0.20)(db0@0.4.1)(ioredis@5.10.1(supports-color@8.1.1))
vite: 8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
vitefu: 1.1.3(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
xxhash-wasm: 1.1.0
@@ -27746,6 +27947,10 @@ snapshots:
dependencies:
uncrypto: 0.1.3
crossws@0.4.12(srvx@1.0.4):
optionalDependencies:
srvx: 1.0.4
crypto-js@4.2.0: {}
css-background-parser@0.1.0: {}
@@ -28101,6 +28306,8 @@ snapshots:
optionalDependencies:
'@electric-sql/pglite': 0.4.5
db0@0.4.1: {}
dc-browser@1.0.4: {}
debounce-promise@3.1.2: {}
@@ -28362,6 +28569,13 @@ snapshots:
env-paths@2.2.1: {}
env-runner@0.2.1:
dependencies:
crossws: 0.4.12(srvx@1.0.4)
exsolve: 1.1.1
httpxy: 0.5.5
srvx: 1.0.4
err-code@2.0.3: {}
error-ex@1.3.2:
@@ -29824,10 +30038,20 @@ snapshots:
ufo: 1.6.4
uncrypto: 0.1.3
h3@2.0.1-rc.20:
h3@2.0.1-rc.20(crossws@0.4.12(srvx@1.0.4)):
dependencies:
rou3: 0.8.1
srvx: 0.11.22
optionalDependencies:
crossws: 0.4.12(srvx@1.0.4)
h3@2.0.1-rc.31(crossws@0.4.12(srvx@1.0.4))(ocache@0.3.0):
dependencies:
rou3: 0.9.2
srvx: 1.0.4
optionalDependencies:
crossws: 0.4.12(srvx@1.0.4)
ocache: 0.3.0
hachure-fill@0.5.2: {}
@@ -30162,6 +30386,8 @@ snapshots:
httpxy@0.5.1: {}
httpxy@0.5.5: {}
human-signals@2.1.0: {}
human-signals@5.0.0: {}
@@ -32606,8 +32832,26 @@ snapshots:
- '@types/node'
- babel-plugin-macros
nf3@0.3.24: {}
nice-try@1.0.5: {}
nitro@3.0.260903-beta:
dependencies:
consola: 3.4.2
crossws: 0.4.12(srvx@1.0.4)
db0: 0.4.1
env-runner: 0.2.1
h3: 2.0.1-rc.31(crossws@0.4.12(srvx@1.0.4))(ocache@0.3.0)
hookable: 6.1.1
nf3: 0.3.24
ocache: 0.3.0
rolldown: 1.2.8
rou3: 0.9.2
srvx: 1.0.4
unenv: 2.0.0-rc.24
unstorage: 2.0.0-alpha.10
nitropack@2.13.4(@electric-sql/pglite@0.4.5)(aws4fetch@1.0.20)(encoding@0.1.13)(oxc-parser@0.143.0)(rolldown@1.2.3)(supports-color@8.1.1)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.2
@@ -32887,17 +33131,17 @@ snapshots:
next: 15.5.25(@babel/core@7.29.7(supports-color@8.1.1))(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@22.13.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.77.4)
react-router: 7.18.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
nuxt@4.5.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7(supports-color@8.1.1)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@8.1.1)))(@electric-sql/pglite@0.4.5)(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@22.13.14)(@vue/compiler-sfc@3.5.41)(aws4fetch@1.0.20)(cac@6.7.14)(commander@14.0.1)(db0@0.3.4(@electric-sql/pglite@0.4.5))(encoding@0.1.13)(esbuild@0.28.1)(eslint@9.37.0(jiti@2.7.0)(supports-color@8.1.1))(ioredis@5.10.1(supports-color@8.1.1))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.3)(rollup@4.60.3))(rollup@4.60.3)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))(yaml@2.9.0):
nuxt@4.5.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7(supports-color@8.1.1)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@8.1.1)))(@electric-sql/pglite@0.4.5)(@oxc-project/types@0.149.0)(@parcel/watcher@2.5.6)(@types/node@22.13.14)(@vue/compiler-sfc@3.5.41)(aws4fetch@1.0.20)(cac@6.7.14)(commander@14.0.1)(db0@0.3.4(@electric-sql/pglite@0.4.5))(encoding@0.1.13)(esbuild@0.28.1)(eslint@9.37.0(jiti@2.7.0)(supports-color@8.1.1))(ioredis@5.10.1(supports-color@8.1.1))(lightningcss@1.33.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.8)(rollup@4.60.3))(rollup@4.60.3)(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))(yaml@2.9.0):
dependencies:
'@dxup/nuxt': 0.5.6(esbuild@0.28.1)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))
'@nuxt/cli': 3.37.0(@nuxt/schema@4.5.2)(cac@6.7.14)(commander@14.0.1)(magicast@0.5.4)(supports-color@8.1.1)
'@nuxt/devtools': 3.4.1(aws4fetch@1.0.20)(db0@0.3.4(@electric-sql/pglite@0.4.5))(ioredis@5.10.1(supports-color@8.1.1))(magic-string@1.1.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(supports-color@8.1.1)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)))(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.2))
'@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)))
'@nuxt/nitro-server': 4.5.2(c3f0ccbf4bde9bd4a229f96b3badde35)
'@nuxt/nitro-server': 4.5.2(4bec0acf20c572958b2bb91f944fc6a0)
'@nuxt/schema': 4.5.2
'@nuxt/telemetry': 2.8.0(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1))))
'@nuxt/vite-builder': 4.5.2(b8937c6da1cc25d65b2c4fd0e82c4d2a)
'@unhead/vue': 3.3.1(@oxc-project/types@0.143.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.2))(webpack@5.105.4(esbuild@0.28.1))
'@nuxt/vite-builder': 4.5.2(7593670026f098f90d303525f2479dbe)
'@unhead/vue': 3.3.1(@oxc-project/types@0.149.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.143.0)(rolldown@1.2.3)(rollup@4.60.3)(vite@7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.2))(webpack@5.105.4(esbuild@0.28.1))
'@vue/shared': 3.5.41
chokidar: 5.0.0
compatx: 0.2.0
@@ -32924,7 +33168,7 @@ snapshots:
ofetch: 1.5.1
ohash: 2.0.11
on-change: 6.0.2
oxc-walker: 1.1.1(@oxc-project/types@0.143.0)(oxc-parser@0.143.0)(rolldown@1.2.3)
oxc-walker: 1.1.1(@oxc-project/types@0.149.0)(oxc-parser@0.143.0)(rolldown@1.2.3)
pathe: 2.0.3
perfect-debounce: 2.1.0
picomatch: 4.0.5
@@ -33087,6 +33331,8 @@ snapshots:
obug@2.1.4: {}
ocache@0.3.0: {}
ofetch@1.5.1:
dependencies:
destr: 2.0.5
@@ -33302,9 +33548,9 @@ snapshots:
'@oxc-resolver/binding-win32-arm64-msvc': 11.24.2
'@oxc-resolver/binding-win32-x64-msvc': 11.24.2
oxc-walker@1.1.1(@oxc-project/types@0.143.0)(oxc-parser@0.143.0)(rolldown@1.2.3):
oxc-walker@1.1.1(@oxc-project/types@0.149.0)(oxc-parser@0.143.0)(rolldown@1.2.3):
optionalDependencies:
'@oxc-project/types': 0.143.0
'@oxc-project/types': 0.149.0
oxc-parser: 0.143.0
rolldown: 1.2.3
@@ -34776,6 +35022,27 @@ snapshots:
'@rolldown/binding-win32-arm64-msvc': 1.2.3
'@rolldown/binding-win32-x64-msvc': 1.2.3
rolldown@1.2.8:
dependencies:
'@oxc-project/types': 0.149.0
'@rolldown/pluginutils': 1.0.1
optionalDependencies:
'@rolldown/binding-android-arm-eabi': 1.2.8
'@rolldown/binding-android-arm64': 1.2.8
'@rolldown/binding-darwin-arm64': 1.2.8
'@rolldown/binding-darwin-x64': 1.2.8
'@rolldown/binding-freebsd-x64': 1.2.8
'@rolldown/binding-linux-arm-gnueabihf': 1.2.8
'@rolldown/binding-linux-arm64-gnu': 1.2.8
'@rolldown/binding-linux-arm64-musl': 1.2.8
'@rolldown/binding-linux-ppc64-gnu': 1.2.8
'@rolldown/binding-linux-s390x-gnu': 1.2.8
'@rolldown/binding-linux-x64-gnu': 1.2.8
'@rolldown/binding-linux-x64-musl': 1.2.8
'@rolldown/binding-openharmony-arm64': 1.2.8
'@rolldown/binding-win32-arm64-msvc': 1.2.8
'@rolldown/binding-win32-x64-msvc': 1.2.8
rollup-plugin-visualizer@7.0.1(rolldown@1.2.3)(rollup@4.60.3):
dependencies:
open: 11.0.0
@@ -34786,6 +35053,17 @@ snapshots:
rolldown: 1.2.3
rollup: 4.60.3
rollup-plugin-visualizer@7.0.1(rolldown@1.2.8)(rollup@4.60.3):
dependencies:
open: 11.0.0
picomatch: 4.0.7
source-map: 0.7.6
yargs: 18.0.0
optionalDependencies:
rolldown: 1.2.8
rollup: 4.60.3
optional: true
rollup@4.60.3:
dependencies:
'@types/estree': 1.0.8
@@ -34821,6 +35099,8 @@ snapshots:
rou3@0.9.1: {}
rou3@0.9.2: {}
roughjs@4.6.6:
dependencies:
hachure-fill: 0.5.2
@@ -35456,6 +35736,8 @@ snapshots:
srvx@0.11.22: {}
srvx@1.0.4: {}
sse.js@2.2.0: {}
ssri@10.0.6:
@@ -36414,14 +36696,14 @@ snapshots:
vite: 7.3.5(@types/node@22.13.14)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
webpack: 5.105.4(esbuild@0.28.1)
unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.3)(rollup@4.60.3)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)):
unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.8)(rollup@4.60.3)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)):
dependencies:
'@jridgewell/remapping': 2.3.5
picomatch: 4.0.5
webpack-virtual-modules: 0.6.2
optionalDependencies:
esbuild: 0.28.1
rolldown: 1.2.3
rolldown: 1.2.8
rollup: 4.60.3
vite: 8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
webpack: 5.105.4(esbuild@0.28.1)
@@ -36446,6 +36728,23 @@ snapshots:
db0: 0.3.4(@electric-sql/pglite@0.4.5)
ioredis: 5.10.1(supports-color@8.1.1)
unstorage@1.17.5(aws4fetch@1.0.20)(db0@0.4.1)(ioredis@5.10.1(supports-color@8.1.1)):
dependencies:
anymatch: 3.1.3
chokidar: 5.0.0
destr: 2.0.5
h3: 1.15.11
lru-cache: 11.3.6
node-fetch-native: 1.6.7
ofetch: 1.5.1
ufo: 1.6.4
optionalDependencies:
aws4fetch: 1.0.20
db0: 0.4.1
ioredis: 5.10.1(supports-color@8.1.1)
unstorage@2.0.0-alpha.10: {}
until-async@3.0.2: {}
untun@0.1.3:
+8 -6
View File
@@ -20,17 +20,19 @@ const defaultEnv = {
POSTGRES_HOST: 'db',
POSTGRES_DB: 'postgres',
POSTGRES_PORT: '5432',
SUPABASE_ANON_KEY: '$ANON_KEY',
SUPABASE_SERVICE_KEY: '$SERVICE_ROLE_KEY',
SUPABASE_URL: '$API_URL',
STUDIO_PG_META_URL: '$API_URL/pg',
SUPABASE_PUBLIC_URL: '$API_URL',
// Literal values, not `$API_URL` references: the TanStack server is started
// with Node's --env-file, which does not expand variables.
SUPABASE_ANON_KEY: generatedEnv.ANON_KEY,
SUPABASE_SERVICE_KEY: generatedEnv.SERVICE_ROLE_KEY,
SUPABASE_URL: generatedEnv.API_URL,
STUDIO_PG_META_URL: `${generatedEnv.API_URL}/pg`,
SUPABASE_PUBLIC_URL: generatedEnv.API_URL,
SENTRY_IGNORE_API_RESOLUTION_ERROR: '1',
LOGFLARE_URL: 'http://127.0.0.1:54327',
LOGFLARE_PRIVATE_ACCESS_TOKEN: 'api-key',
LOGFLARE_API_KEY: 'api-key',
NEXT_PUBLIC_SITE_URL: 'http://localhost:8082',
NEXT_PUBLIC_GOTRUE_URL: '$SUPABASE_PUBLIC_URL/auth/v1',
NEXT_PUBLIC_GOTRUE_URL: `${generatedEnv.API_URL}/auth/v1`,
NEXT_PUBLIC_HCAPTCHA_SITE_KEY: '10000000-ffff-ffff-ffff-000000000001',
NEXT_PUBLIC_NODE_ENV: 'test',
SNIPPETS_MANAGEMENT_FOLDER: '../../supabase/snippets',