diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72dd644..3b0e3bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,8 @@ jobs: - run: pnpm typecheck + - run: pnpm typecheck:examples + - run: pnpm lint - name: Check every published import is declared (JSR resolvability) diff --git a/README.md b/README.md index a56b5d1..b037a3c 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,8 @@ Adapters wrap `withSupabase` for a specific framework's middleware contract. The See the per-adapter docs above for setup, per-route auth, CORS, error handling, and other patterns. +To run `@supabase/middleware` entries inside Hono, H3, Elysia, NestJS, or TanStack Start without an adapter, copy the bridge for your framework from [`examples/frameworks/`](examples/frameworks/). The [Frameworks guide](https://supabase.com/docs/reference/server/frameworks) explains the bridges and how to move off the adapters. + ### Elysia ```ts diff --git a/examples/frameworks/README.md b/examples/frameworks/README.md new file mode 100644 index 0000000..5a25f96 --- /dev/null +++ b/examples/frameworks/README.md @@ -0,0 +1,22 @@ +# Framework bridges + +Each folder holds two files for one framework: + +- `supabase-middleware.ts` (`supabase.guard.ts` for NestJS) is the bridge. It runs a `@supabase/middleware` entry array inside the framework's own middleware slot. Copy it into your project as is, comments included. +- `app.ts` is a minimal app that uses the bridge with `withRequiredClaims` and `withSupabaseClient`. + +| Framework | Bridge | Usage | +| -------------- | -------------------------------------------------------------------------------- | ------------------------------------------------ | +| Hono | [`hono/supabase-middleware.ts`](hono/supabase-middleware.ts) | [`hono/app.ts`](hono/app.ts) | +| H3 / Nuxt | [`h3/supabase-middleware.ts`](h3/supabase-middleware.ts) | [`h3/app.ts`](h3/app.ts) | +| Elysia | [`elysia/supabase-middleware.ts`](elysia/supabase-middleware.ts) | [`elysia/app.ts`](elysia/app.ts) | +| NestJS | [`nestjs/supabase.guard.ts`](nestjs/supabase.guard.ts) | [`nestjs/app.ts`](nestjs/app.ts) | +| TanStack Start | [`tanstack-start/supabase-middleware.ts`](tanstack-start/supabase-middleware.ts) | [`tanstack-start/app.ts`](tanstack-start/app.ts) | + +The Elysia bridge is two functions that work only as a pair. `wrapElysia` runs the entries around the app, and `supabaseCtx` hands their values to the routes. An app served without `wrapElysia` throws on every route. + +The NestJS guard file works without a decorator transform, but the controller in `nestjs/app.ts` does not: Nest is built on decorators, so running it needs swc, ts-node, or a build step. Node's built-in type stripping rejects the `@Controller()` line. + +The guide that explains the bridges, the auth trap, and how to move off the framework adapters is on supabase.com: [Frameworks](https://supabase.com/docs/reference/server/frameworks). + +These files typecheck in CI through `pnpm typecheck:examples`. They are not part of the published package. `examples/package.json` is a private workspace package that holds the dependencies only the examples need, such as `@tanstack/react-start`; the root package does not list them. diff --git a/examples/frameworks/elysia/app.ts b/examples/frameworks/elysia/app.ts new file mode 100644 index 0000000..a5b92e5 --- /dev/null +++ b/examples/frameworks/elysia/app.ts @@ -0,0 +1,27 @@ +import { Elysia } from 'elysia' +import { withRequiredClaims } from '@supabase/server/middleware/required-claims' +import { withSupabaseClient } from '@supabase/server/middleware/client' + +import { supabaseCtx, wrapElysia } from './supabase-middleware.js' + +// `withRequiredClaims` answers 401 to any request without a valid user JWT, +// so the routes below only run for signed-in callers. `withClaims` would let +// anonymous requests through with `jwtClaims: null`. +const entries = [withRequiredClaims(), withSupabaseClient()] as const + +const app = new Elysia() + .use(supabaseCtx()) + .get('/todos', async (c) => { + const { data, error } = await c.supabase.from('todos').select() + if (error) throw error + return data + }) + .get('/me', (c) => ({ id: c.jwtClaims.sub })) + +// `wrapElysia` runs the entries around the whole app, and `supabaseCtx` above +// hands their contributions to the routes. The two work only as a pair: +// serving `app` directly, with `app.listen()` or `export default app`, skips +// the entries and every route throws. +export default { + fetch: wrapElysia(entries, (req) => app.handle(req)), +} diff --git a/examples/frameworks/elysia/supabase-middleware.ts b/examples/frameworks/elysia/supabase-middleware.ts new file mode 100644 index 0000000..cd6f587 --- /dev/null +++ b/examples/frameworks/elysia/supabase-middleware.ts @@ -0,0 +1,84 @@ +import { Elysia } from 'elysia' +import { pipeline } from '@supabase/middleware' +import type { + AnyEntry, + Contributions, + FetchHandler, + ValidateEntries, +} from '@supabase/middleware' + +/** + * The `handle` parameter type when the entries compose, or the engine's error + * string when they do not. Placing the check on the parameter surfaces the + * `middleware-conflict` or `middleware-prereq` message at the `wrapElysia` + * call, the same way `pipeline` reports it on its handler argument. + */ +type Handle = [ + ValidateEntries, +] extends [true] + ? (req: Request) => Response | Promise + : ValidateEntries + +/** Contributions for an in-flight request, keyed by the Request Elysia sees. */ +const HANDOFF = new WeakMap>() + +/** + * Elysia plugin that exposes the entries' contributions on the route context. + * + * `supabaseCtx` and `wrapElysia` are one unit. The wrapper runs the entries + * and stores their contributions for the request; the plugin reads them. When + * the wrapper did not run, the entries did not run either, so the plugin + * throws instead of handing the route an empty context. An app served with + * `app.listen()` or `export default app` therefore fails closed on every + * route. + * + * `Entries` is type-only. Pass the type of the same tuple `wrapElysia` + * receives, so route handlers see the keys that tuple contributes. + */ +export function supabaseCtx() { + return new Elysia() + .resolve((c) => { + const contributions = HANDOFF.get(c.request) + if (!contributions) { + throw new Error( + 'supabaseCtx() ran without wrapElysia(). The entries did not run, ' + + 'so this request is not gated. Serve the app through ' + + '`wrapElysia(entries, (req) => app.handle(req))`.', + ) + } + return contributions as Contributions + }) + .as('scoped') +} + +/** + * Wraps the whole app so the entries see the real outgoing Response. + * + * Elysia's lifecycle hooks run in the request phase only: a `.resolve()` hook + * has no `next()` and never sees the response. Composing around `app.handle` + * keeps the response phase for entries such as `withCors`. Because the + * entries wrap the whole app, they apply app-wide; scope per route with + * Elysia's own `.group()` and a separately wrapped sub-app. + * + * Pair it with `supabaseCtx`, which reads what this wrapper stores. + */ +export function wrapElysia( + entries: Entries, + handle: Handle, +): FetchHandler { + const next = handle as (req: Request) => Response | Promise + return pipeline(entries as readonly AnyEntry[], async (req, ctx) => { + const contributions: Record = {} + for (const [key, value] of Object.entries(ctx)) { + contributions[key] = value + } + HANDOFF.set(req, contributions) + try { + return await next(req) + } finally { + // The WeakMap already permits collection. The delete keeps the entry + // from outliving the request when the Request object is retained. + HANDOFF.delete(req) + } + }) +} diff --git a/examples/frameworks/h3/app.ts b/examples/frameworks/h3/app.ts new file mode 100644 index 0000000..b505af3 --- /dev/null +++ b/examples/frameworks/h3/app.ts @@ -0,0 +1,28 @@ +import { H3 } from 'h3' +import type { Contributions } from '@supabase/middleware' +import { withRequiredClaims } from '@supabase/server/middleware/required-claims' +import { withSupabaseClient } from '@supabase/server/middleware/client' + +import { toH3 } from './supabase-middleware.js' + +// `withRequiredClaims` answers 401 to any request without a valid user JWT, +// so the routes below only run for signed-in callers. `withClaims` would let +// anonymous requests through with `jwtClaims: null`. +const entries = [withRequiredClaims(), withSupabaseClient()] as const + +const app = new H3() +app.use(toH3(entries)) + +app.get('/todos', async (event) => { + const { supabase } = event.context as Contributions + const { data, error } = await supabase.from('todos').select() + if (error) throw error + return data +}) + +app.get('/me', (event) => { + const { jwtClaims } = event.context as Contributions + return { id: jwtClaims.sub } +}) + +export default { fetch: app.fetch } diff --git a/examples/frameworks/h3/supabase-middleware.ts b/examples/frameworks/h3/supabase-middleware.ts new file mode 100644 index 0000000..a636c77 --- /dev/null +++ b/examples/frameworks/h3/supabase-middleware.ts @@ -0,0 +1,67 @@ +import { defineMiddleware, toResponse } from 'h3' +import type { H3Event, Middleware } from 'h3' +import { bufferRequest, pipeline, seedContext } from '@supabase/middleware' +import type { AnyEntry, ValidateEntries } from '@supabase/middleware' + +/** + * The H3 middleware type when the entries compose, or the engine's error + * string when they do not. The string surfaces the `middleware-conflict` or + * `middleware-prereq` message at the `app.use` call. + */ +type Bridge = [ + ValidateEntries, +] extends [true] + ? Middleware + : ValidateEntries + +/** Per-request handoff from the H3 middleware to the pipeline's terminal. */ +const HANDOFF = Symbol('toH3.handoff') +interface Handoff { + event: H3Event + next: () => unknown +} + +/** + * Runs an entry array inside H3's middleware slot. + * + * The pipeline folds once, when `toH3` is called, so entries keep their state + * across requests. Each request travels through the fold with its H3 event + * under a symbol key, which the engine's context spreads preserve. + * + * The terminal copies every contributed key onto `event.context`, runs the + * rest of the H3 chain, and returns the downstream response back up through + * the entries. `event.context` is not generic, so read contributions through + * `Contributions` at the call site. + */ +export function toH3( + entries: Entries, +): Bridge { + const run = pipeline(entries as readonly AnyEntry[], async (_req, ctx) => { + const { event, next } = (ctx as Record)[HANDOFF] + for (const [key, value] of Object.entries(ctx)) { + event.context[key] = value + } + // H3 handlers may return plain values. Normalizing here means the + // response phase always receives a real Response. + return toResponse(await next(), event) + }) + + return defineMiddleware((event, next) => { + // The engine buffers a request body only when it seeds the context + // itself. This bridge seeds, so it buffers too. `event.req` is readonly in + // H3's types and a plain property at runtime; the cast puts the proxy on + // the event so an entry and the route read the same cached body. + if (event.req.body) { + ;(event as { req: H3Event['req'] }).req = bufferRequest( + event.req, + ) as H3Event['req'] + } + // On Cloudflare Workers the bindings live on the request's runtime info, + // which is how `getEnv` inside the entries reads `SUPABASE_URL` there. On + // Node the value is undefined and `getEnv` falls back to `process.env`. + return run(event.req, { + ...seedContext(event.req.runtime?.cloudflare?.env), + [HANDOFF]: { event, next } satisfies Handoff, + }) + }) as never +} diff --git a/examples/frameworks/hono/app.ts b/examples/frameworks/hono/app.ts new file mode 100644 index 0000000..ddc82c3 --- /dev/null +++ b/examples/frameworks/hono/app.ts @@ -0,0 +1,19 @@ +import { Hono } from 'hono' +import { withRequiredClaims } from '@supabase/server/middleware/required-claims' +import { withSupabaseClient } from '@supabase/server/middleware/client' + +import { toHono } from './supabase-middleware.js' + +// `withRequiredClaims` answers 401 to any request without a valid user JWT, +// so the routes below only run for signed-in callers. `withClaims` would let +// anonymous requests through with `jwtClaims: null`. +const app = new Hono() + .use('*', toHono([withRequiredClaims(), withSupabaseClient()])) + .get('/todos', async (c) => { + const { data, error } = await c.var.supabase.from('todos').select() + if (error) return c.json({ error: error.message }, 500) + return c.json(data) + }) + .get('/me', (c) => c.json({ id: c.var.jwtClaims.sub })) + +export default { fetch: app.fetch } diff --git a/examples/frameworks/hono/supabase-middleware.ts b/examples/frameworks/hono/supabase-middleware.ts new file mode 100644 index 0000000..4e1ba10 --- /dev/null +++ b/examples/frameworks/hono/supabase-middleware.ts @@ -0,0 +1,81 @@ +import type { Context, MiddlewareHandler, Next } from 'hono' +import { createMiddleware } from 'hono/factory' +import { bufferRequest, pipeline, seedContext } from '@supabase/middleware' +import type { + AnyEntry, + Contributions, + ValidateEntries, +} from '@supabase/middleware' + +/** + * The Hono middleware type when the entries compose, or the engine's error + * string when they do not. The string surfaces the `middleware-conflict` or + * `middleware-prereq` message at the `app.use` call. + */ +type Bridge = [ + ValidateEntries, +] extends [true] + ? MiddlewareHandler<{ Variables: Contributions }> + : ValidateEntries + +/** Per-request handoff from the Hono middleware to the pipeline's terminal. */ +const HANDOFF = Symbol('toHono.handoff') +interface Handoff { + c: Context + next: Next +} + +/** + * Runs an entry array inside Hono's middleware slot. + * + * The pipeline folds once, when `toHono` is called, so entries keep their + * state across requests. Each request travels through the fold with its Hono + * context under a symbol key, which the engine's context spreads preserve. + * + * The terminal publishes every contributed key onto `c.var`, runs the rest of + * the Hono chain, and returns Hono's real response back up through the + * entries. That return is what lets response-phase entries such as `withCors` + * stamp headers on the way out. + * + * Register the result with `.use()` before the routes it gates. Hono applies + * middleware only to routes registered after it. + * + * Hono carries the contributed keys through the return value of a chained + * call, so `app.use(toHono(a))` on one line and `app.get(...)` on the next + * typecheck the middleware but leave `c.var` untyped. A second array for other + * routes goes in a sub-app mounted with `app.route()`, each sub-app chaining + * its own `.use()` into its routes. + */ +export function toHono( + entries: Entries, +): Bridge { + const run = pipeline(entries as readonly AnyEntry[], async (_req, ctx) => { + const { c, next } = (ctx as Record)[HANDOFF] + for (const [key, value] of Object.entries(ctx)) { + c.set(key as never, value as never) + } + await next() + return c.res + }) + + return createMiddleware(async (c, next) => { + // The engine buffers a request body only when it seeds the context + // itself. This bridge seeds, so it buffers too, and puts the proxy on + // Hono's request so an entry and the route read the same cached body. + if (c.req.raw.body) c.req.raw = bufferRequest(c.req.raw) + // `c.env` holds the platform bindings on Cloudflare Workers, which is how + // `getEnv` inside the entries reads `SUPABASE_URL` there. On Node it holds + // the raw request pair and `getEnv` falls back to `process.env`. + const res = await run(c.req.raw, { + ...seedContext(c.env), + [HANDOFF]: { c, next } satisfies Handoff, + }) + if (res !== c.res) { + // Hono's `res` setter copies the previous response's headers onto the + // new one, which reverts any header the response phase rewrote. + // Clearing first makes the assignment authoritative. + c.res = undefined as never + c.res = res + } + }) as never +} diff --git a/examples/frameworks/nestjs/app.ts b/examples/frameworks/nestjs/app.ts new file mode 100644 index 0000000..51a8e51 --- /dev/null +++ b/examples/frameworks/nestjs/app.ts @@ -0,0 +1,32 @@ +import { Controller, Get, Req, UseGuards } from '@nestjs/common' +import type { Contributions } from '@supabase/middleware' +import { withRequiredClaims } from '@supabase/server/middleware/required-claims' +import { withSupabaseClient } from '@supabase/server/middleware/client' + +import { toNestGuard } from './supabase.guard.js' + +// `withRequiredClaims` answers 401 to any request without a valid user JWT, +// so the handlers below only run for signed-in callers. `withClaims` would +// let anonymous requests through with `jwtClaims: null`. +const entries = [withRequiredClaims(), withSupabaseClient()] as const + +// One guard class for every route. `toNestGuard` folds the pipeline when it +// is called, so a single call keeps entry state shared across requests. +const SupabaseGuard = toNestGuard(entries) + +@Controller('todos') +export class TodosController { + @Get() + @UseGuards(SupabaseGuard) + async list(@Req() req: Contributions) { + const { data, error } = await req.supabase.from('todos').select() + if (error) throw error + return data + } + + @Get('me') + @UseGuards(SupabaseGuard) + me(@Req() req: Contributions) { + return { id: req.jwtClaims.sub } + } +} diff --git a/examples/frameworks/nestjs/supabase.guard.ts b/examples/frameworks/nestjs/supabase.guard.ts new file mode 100644 index 0000000..67416c2 --- /dev/null +++ b/examples/frameworks/nestjs/supabase.guard.ts @@ -0,0 +1,191 @@ +import { HttpException, Injectable } from '@nestjs/common' +import type { CanActivate, ExecutionContext, Type } from '@nestjs/common' +import { pipeline, seedContext } from '@supabase/middleware' +import type { AnyEntry, ValidateEntries } from '@supabase/middleware' + +/** + * The guard class when the entries compose, or the engine's error string when + * they do not. The string surfaces the `middleware-conflict` or + * `middleware-prereq` message at the `@UseGuards` call. + */ +type Bridge = [ + ValidateEntries, +] extends [true] + ? Type + : ValidateEntries + +interface NestRequestLike { + headers: Record + method?: string + url?: string +} + +/** The header writer Express and Fastify both expose on their response. */ +interface NestResponseLike { + header(name: string, value: string | string[]): unknown +} + +/** + * Methods the Fetch standard forbids on `Request`. Nest still routes them, so + * the Web request carries `GET` for these and the entries run. + */ +const FORBIDDEN_METHODS = new Set(['CONNECT', 'TRACE', 'TRACK']) + +/** + * Request properties Nest's parameter decorators read, from `@nestjs/core`'s + * `route-params-factory`: `@Body()`, `@Query()`, `@Param()`, `@Headers()`, + * `@Session()`, `@UploadedFile()`, `@UploadedFiles()`, `@HostParam()`, + * `@Ip()`, and `@RawBody()`. A contribution under one of these names would + * change what a controller receives, so the guard refuses it. + */ +const RESERVED_KEYS = new Set([ + 'body', + 'rawBody', + 'params', + 'hosts', + 'query', + 'headers', + 'session', + 'file', + 'files', + 'ip', +]) + +/** + * Headers that describe the body. Nest serializes the reply body itself, so + * these would not match it. + */ +const BODY_FRAMING_HEADERS = new Set([ + 'content-length', + 'content-encoding', + 'transfer-encoding', +]) + +/** + * Builds a Web `Request` from Nest's platform request. Headers and method + * carry across; the body does not. An entry that reads the body sees an empty + * one and runs as if that were the payload, so a signature check or a body + * audit placed in this array passes with nothing checked. Those belong in + * Nest middleware. + */ +function toWebRequest(req: NestRequestLike): Request { + const headers = new Headers() + for (const [name, value] of Object.entries(req.headers ?? {})) { + // HTTP/2 pseudo-headers (`:method`, `:path`) are invalid Web header names. + if (name.startsWith(':')) continue + if (Array.isArray(value)) headers.set(name, value.join(', ')) + else if (value != null) headers.set(name, String(value)) + } + // Middleware that branch on the method, such as CORS preflight detection, + // need it carried across. + const method = req.method?.toUpperCase() ?? 'GET' + return new Request(`http://nestjs.local${req.url ?? '/'}`, { + method: FORBIDDEN_METHODS.has(method) ? 'GET' : method, + headers, + }) +} + +/** Per-request capture from the pipeline's terminal back to the guard. */ +const CAPTURE = Symbol('toNestGuard.capture') +interface Capture { + ran: boolean + contributions: Record +} + +/** + * Runs an entry array as a Nest guard. + * + * A guard covers context and short-circuit. On a short-circuit the guard + * copies the entry's headers onto the platform response and throws an + * `HttpException` with the entry's status and body, so `WWW-Authenticate`, + * CORS, and `Set-Cookie` headers reach the client. The response phase is not + * available: Nest's interceptors receive the controller's return value, not a + * `Response`, so there is nothing for a generator entry's `yield` to act on. + * `withCors` in this array therefore stamps only short-circuits, and a + * preflight never reaches it: guards run after routing, and with no `OPTIONS` + * route the preflight 404s first. CORS on Nest is `app.enableCors()`. + * + * The pipeline folds once, when `toNestGuard` is called, so entries keep + * their state across requests. Call it once and reuse the class on every + * route. Each request travels through the fold with a capture box under a + * symbol key, which the engine's context spreads preserve. The terminal marks + * the box and records the contributions; the guard then copies them onto + * Nest's request object as flat keys. + * + * One guard per request. A contribution whose key already exists on the + * request, or names a property Nest's parameter decorators read, throws + * instead of overwriting. A second `toNestGuard` on the same route finds the + * first one's keys and throws too, so put every entry in one array. + */ +export function toNestGuard( + entries: Entries, +): Bridge { + const run = pipeline(entries as readonly AnyEntry[], async (_req, ctx) => { + const capture = (ctx as Record)[CAPTURE] + capture.ran = true + for (const [key, value] of Object.entries(ctx)) { + capture.contributions[key] = value + } + return new Response(null, { status: 204 }) + }) + + class EntriesGuard implements CanActivate { + async canActivate(ec: ExecutionContext): Promise { + if (ec.getType() !== 'http') { + throw new HttpException( + { + message: 'Supabase middleware only supports HTTP contexts.', + code: 'unsupported_context', + }, + 500, + ) + } + + const http = ec.switchToHttp() + const req = http.getRequest>() + const capture: Capture = { ran: false, contributions: {} } + const res = await run(toWebRequest(req), { + ...seedContext(), + [CAPTURE]: capture, + }) + + if (!capture.ran) { + // An entry short-circuited. `HttpException` carries status and body + // only, so the headers go onto the platform response first. + const platformRes = http.getResponse() + res.headers.forEach((value, name) => { + if (name === 'set-cookie' || BODY_FRAMING_HEADERS.has(name)) return + platformRes.header(name, value) + }) + const cookies = res.headers.getSetCookie() + if (cookies.length > 0) platformRes.header('set-cookie', cookies) + // Nest wraps a string body into `{ statusCode, message }`, so the + // parsed object is what keeps the `{ message, code }` payload intact. + const text = await res.text() + let body: string | Record = text + try { + body = JSON.parse(text) as Record + } catch { + // A non-JSON short-circuit body is passed through as text. + } + throw new HttpException(body, res.status) + } + + for (const key of Object.keys(capture.contributions)) { + if (RESERVED_KEYS.has(key) || key in req) { + throw new Error( + `Middleware contribution "${key}" collides with a property of ` + + "Nest's request. Give the entry a different key.", + ) + } + } + Object.assign(req, capture.contributions) + return true + } + } + + // Applied as a call rather than decorator syntax, so the file also works + // without a decorator transform. + Injectable()(EntriesGuard) + return EntriesGuard as never +} diff --git a/examples/frameworks/tanstack-start/app.ts b/examples/frameworks/tanstack-start/app.ts new file mode 100644 index 0000000..e6d23e3 --- /dev/null +++ b/examples/frameworks/tanstack-start/app.ts @@ -0,0 +1,22 @@ +import { createServerFn } from '@tanstack/react-start' +import { withRequiredClaims } from '@supabase/server/middleware/required-claims' +import { withSupabaseClient } from '@supabase/server/middleware/client' + +import { toTanStackStart } from './supabase-middleware.js' + +// `withRequiredClaims` answers 401 to any request without a valid user JWT, +// so the server functions below only run for signed-in callers. `withClaims` +// would let anonymous requests through with `jwtClaims: null`. +const supabase = toTanStackStart([withRequiredClaims(), withSupabaseClient()]) + +export const getTodos = createServerFn() + .middleware([supabase]) + .handler(async ({ context }) => { + const { data, error } = await context.supabase.from('todos').select() + if (error) throw error + return data + }) + +export const whoAmI = createServerFn() + .middleware([supabase]) + .handler(async ({ context }) => ({ id: context.jwtClaims.sub })) diff --git a/examples/frameworks/tanstack-start/supabase-middleware.ts b/examples/frameworks/tanstack-start/supabase-middleware.ts new file mode 100644 index 0000000..d580f41 --- /dev/null +++ b/examples/frameworks/tanstack-start/supabase-middleware.ts @@ -0,0 +1,123 @@ +import { createMiddleware } from '@tanstack/react-start' +import { pipeline, seedContext } from '@supabase/middleware' +import type { + AnyEntry, + Contributions, + ValidateEntries, +} from '@supabase/middleware' + +/** + * Resolves to `unknown` when the entries compose, so the parameter type stays + * `Entries`. Otherwise it is the engine's error string, and the intersection + * makes the call fail with the `middleware-conflict` or `middleware-prereq` + * message. + */ +type Validated = [ + ValidateEntries, +] extends [true] + ? unknown + : ValidateEntries + +/** Per-request handoff from the Start middleware to the pipeline's terminal. */ +const HANDOFF = Symbol('toTanStackStart.handoff') +interface Handoff { + next: (options: { context: Record }) => unknown + ran: boolean +} + +/** + * Makes the body of `request` readable more than once, in place. + * + * The first reader drains the stream into a cache and every later reader is + * served from it, so an entry and the route handler both see the body. The + * engine's `bufferRequest` returns a wrapped `Request` instead, which does + * not fit here: a request middleware's `next()` accepts `context` only, and + * Start hands the route handler the same `Request` the middleware saw, so + * the readers go onto that object. + */ +function bufferInPlace(request: Request): void { + if (!request.body) return + const readOnce = request.arrayBuffer.bind(request) + let buffer: Promise | undefined + const arrayBuffer = () => (buffer ??= readOnce()) + const text = async () => new TextDecoder().decode(await arrayBuffer()) + const readers = { + arrayBuffer, + text, + json: async () => JSON.parse(await text()) as unknown, + bytes: async () => new Uint8Array(await arrayBuffer()), + blob: async () => new Blob([await arrayBuffer()]), + formData: async () => + new Response(await arrayBuffer(), { + headers: request.headers, + }).formData(), + // Every reader is cached, so the request is its own clone. + clone: () => request, + } + for (const [name, value] of Object.entries(readers)) { + Object.defineProperty(request, name, { value, configurable: true }) + } +} + +/** + * Runs an entry array as a TanStack Start request middleware. + * + * The pipeline folds once, when `toTanStackStart` is called, so entries keep + * their state across requests. Each request travels through the fold with + * Start's `next` under a symbol key, which the engine's context spreads + * preserve. The terminal hands the contributions to Start as the server + * context and returns the downstream response back up through the entries. + * + * `.server>` is what types `context` downstream. The + * generic has no constraint, so leaving it off types the context as + * `undefined`. + * + * On a server function, Start's fetcher returns any JSON body as the call's + * value without checking the status, so a 401 from `withRequiredClaims` would + * resolve the caller's promise with `{ message, code }`. A short-circuit on a + * server function is therefore rethrown as an error carrying `status` and + * `code`. Server routes get the `Response` back unchanged. + */ +export function toTanStackStart( + entries: Entries & Validated, +) { + const run = pipeline(entries as readonly AnyEntry[], async (_req, ctx) => { + const handoff = (ctx as Record)[HANDOFF] + handoff.ran = true + const context: Record = {} + for (const [key, value] of Object.entries(ctx)) { + context[key] = value + } + const result = (await handoff.next({ context })) as { response: Response } + return result.response + }) + + return createMiddleware({ type: 'request' }).server>( + async ({ request, next, handlerType }) => { + // The engine buffers a request body only when it seeds the context + // itself. This bridge seeds, so it buffers too. + bufferInPlace(request) + const handoff: Handoff = { next, ran: false } + const response = await run(request, { + ...seedContext(), + [HANDOFF]: handoff, + }) + if (handoff.ran || handlerType !== 'serverFn') return response + + const body = (await response + .clone() + .json() + .catch(() => null)) as { message?: string; code?: string } | null + throw Object.assign( + new Error( + body?.message ?? `Request failed with status ${response.status}`, + ), + { + status: response.status, + statusCode: response.status, + ...(body?.code !== undefined && { code: body.code }), + }, + ) + }, + ) +} diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 0000000..01e3874 --- /dev/null +++ b/examples/package.json @@ -0,0 +1,9 @@ +{ + "name": "@supabase/server-examples", + "private": true, + "devDependencies": { + "@tanstack/react-start": "^1.168.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } +} diff --git a/examples/tsconfig.json b/examples/tsconfig.json new file mode 100644 index 0000000..e1689ad --- /dev/null +++ b/examples/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "paths": { + "@supabase/server": ["../src/index.ts"], + "@supabase/server/middleware/*": ["../src/middleware/*/index.ts"], + "@supabase/server/*": ["../src/*/index.ts"] + } + }, + "include": ["**/*.ts", "../src/env.d.ts"] +} diff --git a/package.json b/package.json index a8e6483..de19e2e 100644 --- a/package.json +++ b/package.json @@ -181,8 +181,8 @@ "docs": "typedoc", "format": "prettier --write .", "gen:env": "bash e2e/scripts/gen-env.sh", - "lint": "eslint src", - "lint:fix": "eslint src --fix", + "lint": "eslint src examples", + "lint:fix": "eslint src examples --fix", "prepare": "simple-git-hooks", "smoke": "node scripts/smoke-load.mjs", "smoke:pack": "node scripts/smoke-pack.mjs", @@ -191,6 +191,7 @@ "test:watch": "vitest --project unit --project nestjs", "typecheck": "tsc --noEmit && tsc --noEmit -p src/adapters/nestjs", "typecheck:e2e": "tsc --noEmit -p e2e", + "typecheck:examples": "tsc --noEmit -p examples", "vendor:e2e": "bash e2e/scripts/vendor-pack.sh" }, "simple-git-hooks": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e05be8..54e510d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,7 +62,7 @@ importers: version: 1.4.29(@sinclair/typebox@0.34.49)(exact-mirror@1.0.0)(file-type@21.3.4)(openapi-types@12.1.3)(typescript@5.9.3) eslint: specifier: ^10.0.2 - version: 10.0.2(jiti@2.6.1) + version: 10.0.2(jiti@2.7.0) h3: specifier: 2.0.1-rc.20 version: 2.0.1-rc.20 @@ -104,13 +104,25 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.56.1 - version: 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3) + version: 8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3) unplugin-swc: specifier: ^1.5.9 version: 1.5.9(@swc/core@1.15.33)(rollup@4.63.5) vitest: specifier: ^5.0.0 - version: 5.0.0(@types/node@26.0.1)(vite@7.3.2(@types/node@26.0.1)(jiti@2.6.1)(yaml@2.8.3)) + version: 5.0.0(@types/node@26.0.1)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) + + examples: + devDependencies: + '@tanstack/react-start': + specifier: ^1.168.0 + version: 1.168.56(esbuild@0.28.2)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) + react: + specifier: ^19.0.0 + version: 19.3.0 + react-dom: + specifier: ^19.0.0 + version: 19.3.0(react@19.3.0) packages: @@ -130,10 +142,77 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@borewit/text-codec@0.2.2': resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} @@ -544,6 +623,22 @@ packages: engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'} hasBin: true + '@oozcitak/dom@2.0.2': + resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} + engines: {node: '>=20.0'} + + '@oozcitak/infra@2.0.2': + resolution: {integrity: sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==} + engines: {node: '>=20.0'} + + '@oozcitak/url@3.0.0': + resolution: {integrity: sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==} + engines: {node: '>=20.0'} + + '@oozcitak/util@10.0.0': + resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==} + engines: {node: '>=20.0'} + '@oxc-project/types@0.144.0': resolution: {integrity: sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==} @@ -956,6 +1051,139 @@ packages: '@swc/types@0.1.26': resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==} + '@tanstack/history@1.162.4': + resolution: {integrity: sha512-utTS5L2OkeYUzXGohL1Z8sefu1GLNOJcxe8Hd6iIdc/Xo1K1nDB2JEp4iSFhvYh33xKC9V91TxrS8qfrpoKobQ==} + engines: {node: '>=20.19'} + + '@tanstack/react-router@1.170.38': + resolution: {integrity: sha512-iHM9b0aDJbuvftmkiQXawzoqsdV68p2Re2safbVLCnxafe+iLKV++2i3hI/BMAP6uR92/Mjw94JKJJfD7pbwHQ==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-client@1.168.36': + resolution: {integrity: sha512-TRXZKag/Ng0TqVfcN4cbEDVJhEZYosHZ9/VAEhK3PK/YqznytK+bcoonE4jP5NCXMdH2YRTMsrWKrApEuWz0Cg==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-rsc@0.1.55': + resolution: {integrity: sha512-qfW89LMrewtMLHMCumMP3+3mf2gPezFE7FZ7CHPJis4paxt2A6Jl7eCpE6whYgc5DFiifUv9qdfbC22HIP0HAw==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rspack/core': '>=2.0.0-0' + '@vitejs/plugin-rsc': '>=0.5.30' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + react-server-dom-rspack: '>=0.0.2' + peerDependenciesMeta: + '@rspack/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + react-server-dom-rspack: + optional: true + + '@tanstack/react-start-server@1.167.43': + resolution: {integrity: sha512-/Fc1mOEBAyXmqxnihIkS1DrCcJX8ffrJ+xyc8nnFmO9YuCdrnZJskd48R1ZiOhM7u3vCPzDxUycIhKo8HlKn2g==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start@1.168.56': + resolution: {integrity: sha512-b/jVJS+yt7J0IKwXfVoA2mlhIi92sWfwCYrCVked0WERzgbYAad6xrOwNTljz9OKyeajofGrmMdKJgRzK1Lt2w==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + '@vitejs/plugin-rsc': '*' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + vite: + optional: true + + '@tanstack/react-store@0.11.1': + resolution: {integrity: sha512-HaIGKI3YLmjBYIvy5DFDY23oNaYZIsTZfngey07Uh5iLVJgM3bIGCnZeOFOqzjFld9JHWcaHJnasD/bKoGKwJQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.171.32': + resolution: {integrity: sha512-X86Jqk3vB2KJcUfS6oLi7B7yxbaa59QEhZRPPP1fRx3k+Jw/Fu1UYVBcRtdwK/OccQnrlQdVoKvNO3QXmyEBOw==} + engines: {node: '>=20.19'} + + '@tanstack/router-generator@1.167.38': + resolution: {integrity: sha512-lkqgFleDgfkW+QONVMKBs+ZGDUF0v9R9FkplS/GZvKDFpfnu+tZxmGlV2pryU1TDxyuSyjh56AtIla3NMrlScw==} + engines: {node: '>=20.19'} + + '@tanstack/router-plugin@1.168.40': + resolution: {integrity: sha512-ifiXjjR4uivxwRDhgYAcfyrcu+Mwx+vlG0QjjQ7N5C5sKmzpt5UtsL21TNVvZDSZl2Vae8DcL84Z02fOs8ZZMQ==} + engines: {node: '>=20.19'} + peerDependencies: + '@rsbuild/core': '>=1.0.2 || ^2.0.0' + '@tanstack/react-router': ^1.170.38 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' + vite-plugin-solid: ^2.11.10 || ^3.0.0-0 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.162.3': + resolution: {integrity: sha512-Icb0xGuG1+54IV0WMLRcc3ErTx2HeJWyPG661FkQ8UT6guoBq1FJjFRqlG/JS0xi4mFFkBhvwO7tbLa32f3yxA==} + engines: {node: '>=20.19'} + + '@tanstack/start-client-core@1.170.32': + resolution: {integrity: sha512-kawgeg3Ej/JvGeEN7txFdVN9kXOST9wl7yPojOGESiomwiubHKRdYUS6sBskW91j44H+HirQUppP4+Ha7kjb4A==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-fn-stubs@1.162.0': + resolution: {integrity: sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-plugin-core@1.171.46': + resolution: {integrity: sha512-d/cDPZNDdRl824mOamt6USO1ja5JAaIwkuT2FIBRkBdrKZgQ8GJWv5pqpkXMbKANc49V8bUBBOTjgiCdC7YsPw==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + vite: + optional: true + + '@tanstack/start-server-core@1.169.37': + resolution: {integrity: sha512-CjYMXg2XISMEJt9UVR4lbMRsnRyWPrdyuF8iuJ4gxTc1sVNRciwMVpMPVGQG2KuIma3ewlNxw8+Tpx2tnS3zRA==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-storage-context@1.167.34': + resolution: {integrity: sha512-cRPE0kjt1CnOIhepmNOvRFwCGhDAnCRohPD3HmzqJJThPkixLBRuLDvIYp4hRPtM1TJ9hiKbYl24IxAhjZO5TA==} + engines: {node: '>=22.12.0'} + + '@tanstack/store@0.11.1': + resolution: {integrity: sha512-mzTOBhypOuDJAy/D8n2MfUZ1HFkXnmSETviRyhqEC8LUE7/IZQExOTxMANj3KjTofYTkFNpBY67qaVrT41YccA==} + + '@tanstack/virtual-file-routes@1.162.0': + resolution: {integrity: sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==} + engines: {node: '>=20.19'} + '@tokenizer/inflate@0.4.1': resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} engines: {node: '>=18'} @@ -1299,10 +1527,18 @@ packages: avvio@9.2.0: resolution: {integrity: sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==} + babel-dead-code-elimination@1.0.12: + resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + baseline-browser-mapping@2.11.25: + resolution: {integrity: sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==} + engines: {node: '>=6.0.0'} + hasBin: true + body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} @@ -1311,6 +1547,11 @@ packages: resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} engines: {node: 18 || 20 || >=22} + browserslist@4.29.0: + resolution: {integrity: sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1338,6 +1579,9 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -1354,6 +1598,10 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -1423,6 +1671,12 @@ packages: engines: {node: '>=18'} hasBin: true + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -1494,9 +1748,17 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + dezalgo@1.0.4: resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -1517,6 +1779,9 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.5.433: + resolution: {integrity: sha512-5lCAbyZBjtmUt/RAGHRqrL2q0oEFRThDAsZHHDn9XHa89Qw7gMYOeSicBTy+AHfvo0r6vwsZvqNJTQIQy1BLzA==} + elysia@1.4.29: resolution: {integrity: sha512-GwMRGGwSdjfPt+w3LA0fqTuYJtS8uVRJicvoar98/HrO5qdFKDc9CwjIb6Kja+v39lkY+58hr2JvdR9jQzlUuA==} peerDependencies: @@ -1664,6 +1929,9 @@ packages: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} + exsolve@1.1.1: + resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==} + fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -1706,6 +1974,9 @@ packages: picomatch: optional: true + fetchdts@0.1.7: + resolution: {integrity: sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==} + fflate@0.8.3: resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} @@ -1760,6 +2031,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1909,6 +2184,10 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + isbot@5.2.2: + resolution: {integrity: sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==} + engines: {node: '>=18'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1920,6 +2199,10 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + jose@6.2.0: resolution: {integrity: sha512-xsfE1TcSCbUdo6U07tR0mvhg0flGxU8tPLbF03mirl2ukGQENhUg4ubGYQnhVH0b5stLlPM+WOqDkEl1R1y5sQ==} @@ -1930,6 +2213,11 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1948,6 +2236,11 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1958,6 +2251,80 @@ packages: light-my-request@6.6.0: resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -1998,9 +2365,15 @@ packages: resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} engines: {node: 20 || >=22} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.2.3: resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} @@ -2115,6 +2488,10 @@ packages: resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} engines: {node: '>=18'} + node-releases@2.0.56: + resolution: {integrity: sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==} + engines: {node: '>=18'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -2189,6 +2566,9 @@ packages: path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pg-cloudflare@1.4.0: resolution: {integrity: sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==} @@ -2277,6 +2657,11 @@ packages: engines: {node: '>=14'} hasBin: true + prettier@3.9.8: + resolution: {integrity: sha512-WRFq3Wn3WId7LLROfMLdH7xaFr2jR62wU8nLO6rQUOLOxNZUviyJQs1M0iIhLexSFy+L+w0ch66wtoO2jRjG0A==} + engines: {node: '>=14'} + hasBin: true + pretty-quick@4.2.2: resolution: {integrity: sha512-uAh96tBW1SsD34VhhDmWuEmqbpfYc/B3j++5MC/6b3Cb8Ow7NJsvKFhg0eoGu2xXX+o9RkahkTK6sUdd8E7g5w==} engines: {node: '>=14'} @@ -2320,10 +2705,23 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} + react-dom@19.3.0: + resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==} + peerDependencies: + react: ^19.3.0 + + react@19.3.0: + resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} + engines: {node: '>=0.10.0'} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -2417,12 +2815,14 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true semver@7.8.4: @@ -2434,6 +2834,16 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} + seroval-plugins@1.6.7: + resolution: {integrity: sha512-4Nk35ttD3DTDJW4hgw5StsVAPeU6qnDFnULAouw6tQ7oLTV/ICXrWpsXo2EE52eSP2joUMazbVf52mFEcADqRw==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.6.7: + resolution: {integrity: sha512-AeDcLh0yO2SFm9W71essgnSzLV9DI8ZH0x0knXn2DMnUZj728mpLbxjlbB6IqKCmqh8JA3cEqRyGoNkt584JcQ==} + engines: {node: '>=10'} + serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -2486,6 +2896,10 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -2500,6 +2914,11 @@ packages: engines: {node: '>=20.16.0'} hasBin: true + srvx@0.11.22: + resolution: {integrity: sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==} + engines: {node: '>=20.16.0'} + hasBin: true + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2676,6 +3095,9 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + uid@2.0.2: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} engines: {node: '>=8'} @@ -2710,9 +3132,56 @@ packages: resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} engines: {node: '>=18.12.0'} + unplugin@3.4.0: + resolution: {integrity: sha512-9skdIFlCsPdFV7wUfZxNsFInlW+7nJmGu2gkTu0OUhF56aXGsHab9x52/QhdJ4lC7ZDPWTxbiC4ANqVlsuaW3w==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rsbuild/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: ^0.28.1 + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rsbuild/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.7.0: + resolution: {integrity: sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -2768,6 +3237,14 @@ packages: yaml: optional: true + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + vitest@5.0.0: resolution: {integrity: sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==} engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} @@ -2833,6 +3310,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xmlbuilder2@4.0.3: + resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} + engines: {node: '>=20.0'} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -2841,6 +3322,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.8.3: resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} @@ -2875,6 +3359,9 @@ packages: yuku-parser@0.8.7: resolution: {integrity: sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ==} + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + snapshots: '@andrewbranch/untar.js@1.0.3': {} @@ -2906,8 +3393,108 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.29.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.9': + dependencies: + '@babel/types': 7.29.8 + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@borewit/text-codec@0.2.2': {} '@braidai/lang@1.1.2': {} @@ -3102,9 +3689,9 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.0.2(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.0.2(jiti@2.7.0))': dependencies: - eslint: 10.0.2(jiti@2.6.1) + eslint: 10.0.2(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -3279,6 +3866,23 @@ snapshots: dependencies: consola: 3.4.2 + '@oozcitak/dom@2.0.2': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/url': 3.0.0 + '@oozcitak/util': 10.0.0 + + '@oozcitak/infra@2.0.2': + dependencies: + '@oozcitak/util': 10.0.0 + + '@oozcitak/url@3.0.0': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + + '@oozcitak/util@10.0.0': {} + '@oxc-project/types@0.144.0': {} '@paralleldrive/cuid2@2.3.1': @@ -3542,6 +4146,222 @@ snapshots: dependencies: '@swc/counter': 0.1.3 + '@tanstack/history@1.162.4': {} + + '@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/history': 1.162.4 + '@tanstack/react-store': 0.11.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.32 + isbot: 5.2.2 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@tanstack/react-start-client@1.168.36(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.32 + '@tanstack/start-client-core': 1.170.32 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@tanstack/react-start-rsc@0.1.55(esbuild@0.28.2)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3))': + dependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.32 + '@tanstack/router-utils': 1.162.3 + '@tanstack/start-client-core': 1.170.32 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-plugin-core': 1.171.46(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) + '@tanstack/start-storage-context': 1.167.34 + pathe: 2.0.3 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rsbuild/core' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite + - vite-plugin-solid + - webpack + + '@tanstack/react-start-server@1.167.43(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.32 + '@tanstack/start-server-core': 1.169.37 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + transitivePeerDependencies: + - crossws + + '@tanstack/react-start@1.168.56(esbuild@0.28.2)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3))': + dependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-start-client': 1.168.36(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-start-rsc': 0.1.55(esbuild@0.28.2)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) + '@tanstack/react-start-server': 1.167.43(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-utils': 1.162.3 + '@tanstack/start-client-core': 1.170.32 + '@tanstack/start-plugin-core': 1.171.46(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) + '@tanstack/start-server-core': 1.169.37 + pathe: 2.0.3 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + vite: 7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - crossws + - esbuild + - react-server-dom-rspack + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/react-store@0.11.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/store': 0.11.1 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + use-sync-external-store: 1.7.0(react@19.3.0) + + '@tanstack/router-core@1.171.32': + dependencies: + '@tanstack/history': 1.162.4 + cookie-es: 3.1.1 + seroval: 1.6.7 + seroval-plugins: 1.6.7(seroval@1.6.7) + + '@tanstack/router-generator@1.167.38': + dependencies: + '@babel/types': 7.29.8 + '@tanstack/router-core': 1.171.32 + '@tanstack/router-utils': 1.162.3 + '@tanstack/virtual-file-routes': 1.162.0 + jiti: 2.7.0 + magic-string: 0.30.21 + prettier: 3.9.8 + zod: 4.6.5 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.168.40(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3))': + dependencies: + '@babel/core': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + '@tanstack/router-core': 1.171.32 + '@tanstack/router-generator': 1.167.38 + '@tanstack/router-utils': 1.162.3 + chokidar: 5.0.0 + unplugin: 3.4.0(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) + zod: 4.6.5 + optionalDependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + vite: 7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - supports-color + - unloader + + '@tanstack/router-utils@1.162.3': + dependencies: + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + ansis: 4.3.1 + babel-dead-code-elimination: 1.0.12 + diff: 8.0.4 + pathe: 2.0.3 + tinyglobby: 0.2.17 + transitivePeerDependencies: + - supports-color + + '@tanstack/start-client-core@1.170.32': + dependencies: + '@tanstack/router-core': 1.171.32 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-storage-context': 1.167.34 + seroval: 1.6.7 + + '@tanstack/start-fn-stubs@1.162.0': {} + + '@tanstack/start-plugin-core@1.171.46(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3))': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + '@tanstack/router-core': 1.171.32 + '@tanstack/router-generator': 1.167.38 + '@tanstack/router-plugin': 1.168.40(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) + '@tanstack/router-utils': 1.162.3 + '@tanstack/start-server-core': 1.169.37 + exsolve: 1.1.1 + lightningcss: 1.33.0 + pathe: 2.0.3 + picomatch: 4.0.7 + seroval: 1.6.7 + source-map: 0.7.6 + srvx: 0.11.22 + tinyglobby: 0.2.17 + ufo: 1.6.4 + vitefu: 1.1.3(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) + xmlbuilder2: 4.0.3 + zod: 4.6.5 + optionalDependencies: + vite: 7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - '@tanstack/react-router' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/start-server-core@1.169.37': + dependencies: + '@tanstack/history': 1.162.4 + '@tanstack/router-core': 1.171.32 + '@tanstack/start-client-core': 1.170.32 + '@tanstack/start-storage-context': 1.167.34 + fetchdts: 0.1.7 + h3-v2: h3@2.0.1-rc.20 + seroval: 1.6.7 + transitivePeerDependencies: + - crossws + + '@tanstack/start-storage-context@1.167.34': + dependencies: + '@tanstack/router-core': 1.171.32 + + '@tanstack/store@0.11.1': {} + + '@tanstack/virtual-file-routes@1.162.0': {} + '@tokenizer/inflate@0.4.1': dependencies: debug: 4.4.3 @@ -3602,15 +4422,15 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3))(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.56.1 - '@typescript-eslint/type-utils': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.1 - eslint: 10.0.2(jiti@2.6.1) + eslint: 10.0.2(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -3618,14 +4438,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.56.1 '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.56.1 debug: 4.4.3 - eslint: 10.0.2(jiti@2.6.1) + eslint: 10.0.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3648,13 +4468,13 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3) debug: 4.4.3 - eslint: 10.0.2(jiti@2.6.1) + eslint: 10.0.2(jiti@2.7.0) ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -3670,20 +4490,20 @@ snapshots: '@typescript-eslint/visitor-keys': 8.56.1 debug: 4.4.3 minimatch: 10.2.4 - semver: 7.7.4 + semver: 7.8.4 tinyglobby: 0.2.17 ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.56.1 '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - eslint: 10.0.2(jiti@2.6.1) + eslint: 10.0.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3693,14 +4513,14 @@ snapshots: '@typescript-eslint/types': 8.56.1 eslint-visitor-keys: 5.0.1 - '@vitest/mocker@5.0.0(vite@7.3.2(@types/node@26.0.1)(jiti@2.6.1)(yaml@2.8.3))': + '@vitest/mocker@5.0.0(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3))': dependencies: '@jridgewell/trace-mapping': 0.3.31 '@vitest/spy': 5.0.0 estree-walker: 3.0.3 magic-string: 1.2.3 optionalDependencies: - vite: 7.3.2(@types/node@26.0.1)(jiti@2.6.1)(yaml@2.8.3) + vite: 7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3) '@vitest/spy@5.0.0': {} @@ -3851,8 +4671,19 @@ snapshots: '@fastify/error': 4.2.0 fastq: 1.20.1 + babel-dead-code-elimination@1.0.12: + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + balanced-match@4.0.4: {} + baseline-browser-mapping@2.11.25: {} + body-parser@2.2.2: dependencies: bytes: 3.1.2 @@ -3871,6 +4702,14 @@ snapshots: dependencies: balanced-match: 4.0.4 + browserslist@4.29.0: + dependencies: + baseline-browser-mapping: 2.11.25 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.433 + node-releases: 2.0.56 + update-browserslist-db: 1.3.3(browserslist@4.29.0) + buffer-from@1.1.2: {} busboy@1.6.0: @@ -3893,6 +4732,8 @@ snapshots: callsites@3.1.0: {} + caniuse-lite@1.0.30001810: {} + chai@6.2.2: {} chalk@4.1.2: @@ -3904,6 +4745,10 @@ snapshots: char-regex@1.0.2: {} + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + cjs-module-lexer@1.4.3: {} cli-highlight@2.1.11: @@ -3977,6 +4822,10 @@ snapshots: dependencies: meow: 13.2.0 + convert-source-map@2.0.0: {} + + cookie-es@3.1.1: {} + cookie-signature@1.2.2: {} cookie@0.7.2: {} @@ -4028,11 +4877,15 @@ snapshots: dequal@2.0.3: {} + detect-libc@2.1.2: {} + dezalgo@1.0.4: dependencies: asap: 2.0.6 wrappy: 1.0.2 + diff@8.0.4: {} + dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -4047,6 +4900,8 @@ snapshots: ee-first@1.1.1: {} + electron-to-chromium@1.5.433: {} + elysia@1.4.29(@sinclair/typebox@0.34.49)(exact-mirror@1.0.0)(file-type@21.3.4)(openapi-types@12.1.3)(typescript@5.9.3): dependencies: '@sinclair/typebox': 0.34.49 @@ -4140,9 +4995,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.0.2(jiti@2.6.1): + eslint@10.0.2(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.2 '@eslint/config-helpers': 0.5.2 @@ -4173,7 +5028,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.6.1 + jiti: 2.7.0 transitivePeerDependencies: - supports-color @@ -4240,6 +5095,8 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.1.1: {} + fast-decode-uri-component@1.0.1: {} fast-deep-equal@3.1.3: {} @@ -4293,6 +5150,8 @@ snapshots: optionalDependencies: picomatch: 4.0.7 + fetchdts@0.1.7: {} + fflate@0.8.3: {} file-entry-cache@8.0.0: @@ -4360,6 +5219,8 @@ snapshots: function-bind@1.1.2: {} + gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} get-intrinsic@1.3.0: @@ -4478,12 +5339,16 @@ snapshots: is-promise@4.0.0: {} + isbot@5.2.2: {} + isexe@2.0.0: {} iterare@1.2.1: {} jiti@2.6.1: {} + jiti@2.7.0: {} + jose@6.2.0: {} js-tokens@4.0.0: {} @@ -4492,6 +5357,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} @@ -4506,6 +5373,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -4521,6 +5390,55 @@ snapshots: process-warning: 4.0.1 set-cookie-parser: 2.7.2 + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + lines-and-columns@1.2.4: {} linkify-it@5.0.0: @@ -4549,8 +5467,16 @@ snapshots: lru-cache@11.5.1: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lunr@2.3.9: {} + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + magic-string@1.2.3: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -4649,6 +5575,8 @@ snapshots: emojilib: 2.4.0 skin-tone: 2.0.0 + node-releases@2.0.56: {} + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -4713,6 +5641,8 @@ snapshots: path-to-regexp@8.4.2: {} + pathe@2.0.3: {} + pg-cloudflare@1.4.0: optional: true @@ -4796,6 +5726,8 @@ snapshots: prettier@3.8.1: {} + prettier@3.9.8: {} + pretty-quick@4.2.2(prettier@3.8.1): dependencies: '@pkgr/core': 0.2.9 @@ -4837,12 +5769,21 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 + react-dom@19.3.0(react@19.3.0): + dependencies: + react: 19.3.0 + scheduler: 0.28.0 + + react@19.3.0: {} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 + readdirp@5.1.1: {} + real-require@0.2.0: {} real-require@1.0.0: {} @@ -4957,9 +5898,11 @@ snapshots: safer-buffer@2.1.2: {} + scheduler@0.28.0: {} + secure-json-parse@4.1.0: {} - semver@7.7.4: {} + semver@6.3.1: {} semver@7.8.4: {} @@ -4979,6 +5922,12 @@ snapshots: transitivePeerDependencies: - supports-color + seroval-plugins@1.6.7(seroval@1.6.7): + dependencies: + seroval: 1.6.7 + + seroval@1.6.7: {} + serve-static@2.2.1: dependencies: encodeurl: 2.0.0 @@ -5040,12 +5989,16 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.7.6: {} + split2@4.2.0: {} srvx@0.11.15: {} srvx@0.11.18: {} + srvx@0.11.22: {} + stackback@0.0.2: {} statuses@2.0.2: {} @@ -5198,13 +6151,13 @@ snapshots: typescript: 5.9.3 yaml: 2.8.3 - typescript-eslint@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3))(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3) - eslint: 10.0.2(jiti@2.6.1) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.2(jiti@2.7.0))(typescript@5.9.3) + eslint: 10.0.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -5215,6 +6168,8 @@ snapshots: uc.micro@2.1.0: {} + ufo@1.6.4: {} + uid@2.0.2: dependencies: '@lukeed/csprng': 1.1.0 @@ -5250,10 +6205,31 @@ snapshots: picomatch: 4.0.7 webpack-virtual-modules: 0.6.2 + unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.4)(rollup@4.63.5)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.7 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + esbuild: 0.28.2 + rolldown: 1.2.4 + rollup: 4.63.5 + vite: 7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3) + + update-browserslist-db@1.3.3(browserslist@4.29.0): + dependencies: + browserslist: 4.29.0 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 + use-sync-external-store@1.7.0(react@19.3.0): + dependencies: + react: 19.3.0 + util-deprecate@1.0.2: {} validate-npm-package-name@5.0.1: {} @@ -5262,7 +6238,7 @@ snapshots: verkit@0.3.2: {} - vite@7.3.2(@types/node@26.0.1)(jiti@2.6.1)(yaml@2.8.3): + vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3): dependencies: esbuild: 0.28.2 fdir: 6.5.0(picomatch@4.0.7) @@ -5273,13 +6249,18 @@ snapshots: optionalDependencies: '@types/node': 26.0.1 fsevents: 2.3.3 - jiti: 2.6.1 + jiti: 2.7.0 + lightningcss: 1.33.0 yaml: 2.8.3 - vitest@5.0.0(@types/node@26.0.1)(vite@7.3.2(@types/node@26.0.1)(jiti@2.6.1)(yaml@2.8.3)): + vitefu@1.1.3(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)): + optionalDependencies: + vite: 7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3) + + vitest@5.0.0(@types/node@26.0.1)(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.0(vite@7.3.2(@types/node@26.0.1)(jiti@2.6.1)(yaml@2.8.3)) + '@vitest/mocker': 5.0.0(vite@7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3)) chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -5290,7 +6271,7 @@ snapshots: tinybench: 6.1.4 tinyexec: 1.3.0 tinyglobby: 0.2.17 - vite: 7.3.2(@types/node@26.0.1)(jiti@2.6.1)(yaml@2.8.3) + vite: 7.3.2(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.33.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.0.1 @@ -5318,10 +6299,19 @@ snapshots: wrappy@1.0.2: {} + xmlbuilder2@4.0.3: + dependencies: + '@oozcitak/dom': 2.0.2 + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + js-yaml: 4.1.1 + xtend@4.0.2: {} y18n@5.0.8: {} + yallist@3.1.1: {} + yaml@2.8.3: {} yargs-parser@20.2.9: {} @@ -5388,3 +6378,5 @@ snapshots: '@yuku-parser/binding-linux-x64-musl': 0.8.7 '@yuku-parser/binding-win32-arm64': 0.8.7 '@yuku-parser/binding-win32-x64': 0.8.7 + + zod@4.6.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5c65c3b..575b035 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,5 @@ +packages: + - examples minimumReleaseAge: 10080 minimumReleaseAgeExclude: - '@supabase/*' diff --git a/skills/supabase-server/SKILL.md b/skills/supabase-server/SKILL.md index 7d7a9f3..d4ee00c 100644 --- a/skills/supabase-server/SKILL.md +++ b/skills/supabase-server/SKILL.md @@ -432,18 +432,19 @@ The full documentation lives in the `docs/` directory of the `@supabase/server` - **If working inside the SDK repo:** `docs/` is at the project root. - **If the package is installed as a dependency:** look in `node_modules/@supabase/server/docs/`. -| Question | Doc file | -| ------------------------------------------------------------------------------------- | ------------------------------- | -| How do I create a basic endpoint? | `docs/getting-started.md` | -| What auth modes are available? Array syntax? Named keys? | `docs/auth-modes.md` | -| Which framework adapters exist? How do I contribute one? | `src/adapters/README.md` | -| How do I use this with Hono? | `docs/adapters/hono.md` | -| How do I use this with H3 / Nuxt? | `docs/adapters/h3.md` | -| How do I use low-level primitives for custom flows? | `docs/core-primitives.md` | -| How do environment variables work across runtimes? | `docs/environment-variables.md` | -| How do I handle errors? What codes exist? | `docs/error-handling.md` | -| How do I get typed database queries? | `docs/typescript-generics.md` | -| How do I use this with `@supabase/ssr` (Next.js, SvelteKit, Remix)? | `docs/ssr-frameworks.md` | -| How do I build an MCP server my users connect to (OAuth discovery, RLS-scoped tools)? | `docs/mcp.md` | -| What's the complete API surface? | `docs/api-reference.md` | -| What security decisions does this package make? | `docs/security.md` | +| Question | Doc file | +| ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- | +| How do I create a basic endpoint? | `docs/getting-started.md` | +| What auth modes are available? Array syntax? Named keys? | `docs/auth-modes.md` | +| Which framework adapters exist? How do I contribute one? | `src/adapters/README.md` | +| How do I use this with Hono? | `docs/adapters/hono.md` | +| How do I use this with H3 / Nuxt? | `docs/adapters/h3.md` | +| How do I run middleware entries inside Hono, H3, Elysia, NestJS, or TanStack Start without an adapter? | https://supabase.com/docs/reference/server/frameworks | +| How do I use low-level primitives for custom flows? | `docs/core-primitives.md` | +| How do environment variables work across runtimes? | `docs/environment-variables.md` | +| How do I handle errors? What codes exist? | `docs/error-handling.md` | +| How do I get typed database queries? | `docs/typescript-generics.md` | +| How do I use this with `@supabase/ssr` (Next.js, SvelteKit, Remix)? | `docs/ssr-frameworks.md` | +| How do I build an MCP server my users connect to (OAuth discovery, RLS-scoped tools)? | `docs/mcp.md` | +| What's the complete API surface? | `docs/api-reference.md` | +| What security decisions does this package make? | `docs/security.md` |