@ai-matrx/data 0.22.20 → 0.22.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,11 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.22.22
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/data/v0.22.21..npm/data/v0.22.22 -- apps/shared/data`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.22.21
10
+
11
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
12
+ `git diff npm/data/v0.22.20..npm/data/v0.22.21 -- apps/shared/data`).
13
+ No source changes intended and no consumer action required.
14
+
3
15
  ## 0.22.20
4
16
 
5
17
  Automatic changed-only republish (docs/metadata drift since the last tag — see
6
18
  `git diff npm/data/v0.22.19..npm/data/v0.22.20 -- apps/shared/data`).
7
19
  No source changes intended and no consumer action required.
8
20
 
21
+ ## 0.22.19
22
+
23
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
24
+ `git diff npm/data/v0.22.18..npm/data/v0.22.19 -- apps/shared/data`).
25
+ No source changes intended and no consumer action required.
26
+
9
27
  ## 0.22.18
10
28
 
11
29
  Automatic changed-only republish (docs/metadata drift since the last tag — see
package/dist/next.cjs CHANGED
@@ -397,7 +397,7 @@ function createNextSupabase(config) {
397
397
  continue;
398
398
  }
399
399
  console.warn(
400
- `[@ai-matrx/data/next] DROPPED a second Set-Cookie for "${name}" on host "${host ?? "?"}": Next's cookie store keeps one entry per name, so this write would have DELETED the first one. The lost header was: ${header}. A Route Handler here must pass appendSetCookie \u2014 an unremoved auth cookie at the other scope makes the next request resolve the PREVIOUS person.`
400
+ `[@ai-matrx/data/next] DROPPED a second Set-Cookie for "${name}" on host "${host ?? "?"}": Next's cookie store keeps one entry per name, so this write would have DELETED the first one. Cookie value omitted. A Route Handler here must pass appendSetCookie \u2014 an unremoved auth cookie at the other scope makes the next request resolve the PREVIOUS person.`
401
401
  );
402
402
  }
403
403
  for (const header of deferred) appendSetCookie?.(header);
@@ -444,7 +444,7 @@ function createNextSupabase(config) {
444
444
  continue;
445
445
  }
446
446
  console.warn(
447
- `[@ai-matrx/data/next] DROPPED a second Set-Cookie for "${name}" on host "${host ?? "?"}": this door passed no appendSetCookie, and the response jar keeps one entry per name, so this write would have DELETED the first one. The lost header was: ${header}. Pass appendSetCookie from this route \u2014 an unremoved auth cookie at the other scope makes the next request resolve the PREVIOUS person.`
447
+ `[@ai-matrx/data/next] DROPPED a second Set-Cookie for "${name}" on host "${host ?? "?"}": this door passed no appendSetCookie, and the response jar keeps one entry per name, so this write would have DELETED the first one. Cookie value omitted. Pass appendSetCookie from this route \u2014 an unremoved auth cookie at the other scope makes the next request resolve the PREVIOUS person.`
448
448
  );
449
449
  }
450
450
  for (const header of deferred) appendSetCookie?.(header);
package/dist/next.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/next.ts","../src/next/supabase.ts","../src/db/auth-cookie.ts"],"sourcesContent":["/**\n * `@ai-matrx/data/next` — the Next.js binding for the Supabase lane.\n *\n * One factory owns every Supabase client a Next app constructs (browser,\n * Server Component, Route Handler, proxy/middleware, OAuth callback), so the\n * domain-wide auth cookie, the SSR/browser split, the browser singleton, and\n * the whole middleware session dance are written once, here, instead of once\n * per app.\n *\n * This entry is deliberately UNSTAMPED (no `\"use client\"`): most of it runs on\n * the server, and stamping it would turn `serverClient`/`middlewareSession`\n * into client references inside an RSC. It also imports no `next/*` module —\n * Next's primitives arrive through structural seams.\n */\nexport {\n AMBIGUOUS_AUTH_COOKIE_HEADER,\n AUTH_COOKIE_RESPONSE_HEADERS,\n createNextSupabase,\n MissingSupabaseIdentityError,\n SPLIT_COOKIE_JAR_HEADER,\n toResponseCookie,\n type CookieToSet,\n type DatabaseLike,\n type MiddlewareResponse,\n type MiddlewareSession,\n type MiddlewareSessionArgs,\n type NextBrowserClient,\n type NextServerClient,\n type NextSupabase,\n type NextSupabaseConfig,\n type RequestCookieJar,\n type ResponseCookie,\n type ResponseCookieJar,\n type ServerCookieStore,\n} from \"./next/supabase\";\n\nexport {\n createAuthCookie,\n DEFAULT_AUTH_COOKIE_NAME,\n type AuthCookie,\n type AuthCookieOptions,\n type AuthCookieValue,\n type CreateAuthCookieArgs,\n} from \"./db/auth-cookie\";\n","/**\n * @ai-matrx/data/next — the Next.js Supabase binding.\n *\n * WHY THIS EXISTS (C22 / THE ALL-INCLUSIVE LAW): a Next.js app has FIVE\n * distinct places that must construct a Supabase client — the browser, a\n * Server Component, a Route Handler, the proxy/middleware, and an OAuth\n * callback — and every one of them must pass the SAME auth-cookie options or\n * it cannot see the session the others wrote. Before this module, matrx-frontend\n * carried that construction five times, each with its own env guard, its own\n * cookie adapter, and its own copy of the \"do not run code between\n * createServerClient and getUser()\" hazard. Every app on the platform would\n * have re-derived all five.\n *\n * The host now injects IDENTITY only — URL, publishable key, apex domain, the\n * cookie names — and the package owns every hard part:\n *\n * - env validation with a named, actionable error (never `process.env.X!`,\n * which fails as a cryptic `undefined.trim()` TypeError);\n * - the domain-wide cookie option construction on every single door;\n * - the SSR/browser split, including the Server-Component write that must be\n * swallowed because only the proxy may set cookies there;\n * - the browser singleton on a `globalThis` slot (module-level mutable state\n * is banned — dual ESM/CJS bundles would otherwise instantiate two clients\n * and split the session);\n * - the middleware session dance in ONE call: legacy-key migration, the\n * getUser() adjacency rule, session persistence under the new key, legacy\n * cookie clearing at the issuing scope, the no-store response headers, and\n * cookie carry-over onto redirects (dropping any one of these logs users\n * out at random, which is exactly how it was found).\n *\n * NO `next/*` IMPORT LIVES HERE. Next's own primitives (`cookies()`,\n * `NextResponse`) arrive through structural seams the host passes in, so this\n * module builds and tests outside Next and cannot drag the framework into a\n * non-Next consumer's graph.\n */\n\nimport {\n clearAuthCookiesAtScopes,\n createBrowserClient,\n createServerClient,\n DEFAULT_COOKIE_OPTIONS,\n} from \"@supabase/ssr\";\nimport {\n createAuthCookie,\n parseCookieHeader,\n type AuthCookie,\n type AuthCookieOptions,\n type AuthCookieValue,\n} from \"../db/auth-cookie\";\n\n/**\n * A `Database` type as generated by Supabase. Deliberately structural: the\n * package never imports a host's generated types, and a host that has not\n * generated any still gets a working (untyped-rows) client.\n */\nexport type DatabaseLike = Record<string, unknown>;\n\nexport type NextBrowserClient<DB extends DatabaseLike> = ReturnType<\n typeof createBrowserClient<DB>\n>;\nexport type NextServerClient<DB extends DatabaseLike> = ReturnType<\n typeof createServerClient<DB>\n>;\n\n/** A cookie to write, as `@supabase/ssr` hands it to `setAll`. */\nexport interface CookieToSet {\n name: string;\n value: string;\n options?: Record<string, unknown> | undefined;\n}\n\n/**\n * The subset of Next's `await cookies()` store this binding uses. Structural\n * on purpose — `ReadonlyRequestCookies` satisfies it by shape.\n */\nexport interface ServerCookieStore {\n getAll(): readonly AuthCookieValue[];\n set(name: string, value: string, options?: Record<string, unknown>): void;\n}\n\n/** The subset of `NextRequest[\"cookies\"]` the middleware binding uses. */\nexport interface RequestCookieJar {\n getAll(): readonly AuthCookieValue[];\n set(name: string, value: string): void;\n}\n\n/**\n * A cookie in Next's `ResponseCookies` shape: name, value, and every\n * attribute FLAT on the same object.\n *\n * 🚨 THIS SHAPE IS LOAD-BEARING, and modelling it as `never` cost the platform\n * a production outage (2026-09-01). `ResponseCookies.set` accepts either\n * `(name, value, attrs)` or ONE flat object — it does NOT accept `@supabase/ssr`'s\n * `{ name, value, options }`, and it does not throw on it either: it reads\n * `name`/`value`, ignores the unknown `options` key, and serializes\n * `sb-…-auth-v2.0=; Path=/`. Every attribute is lost. So:\n * · a token refresh wrote a HOST-ONLY session cookie shadowing the real\n * `.apex` one, and\n * · every chunk REMOVAL (`value: \"\"`, `maxAge: 0`) became a PERSISTENT EMPTY\n * host-only cookie instead of a deletion.\n * Two same-name cookies then reach the server in one `Cookie` header, the\n * empty one sorts first, `combineChunks`' `find()` takes it, and every server\n * read answers anonymous while the tab still shows a signed-in user.\n * `@supabase/ssr` cookies are converted with `toResponseCookie` at the single\n * boundary below; the `never` that hid this is gone.\n */\nexport interface ResponseCookie {\n name: string;\n value: string;\n domain?: string | undefined;\n path?: string | undefined;\n maxAge?: number | undefined;\n expires?: Date | number | undefined;\n httpOnly?: boolean | undefined;\n secure?: boolean | undefined;\n sameSite?: boolean | \"lax\" | \"strict\" | \"none\" | undefined;\n priority?: \"low\" | \"medium\" | \"high\" | undefined;\n partitioned?: boolean | undefined;\n}\n\n/**\n * Exactly the attributes Next's `ResponseCookies` serializes. Anything else on\n * an `@supabase/ssr` cookie is dropped by that class anyway — naming them is\n * what makes the drop visible instead of silent.\n */\nconst RESPONSE_COOKIE_ATTRIBUTES = [\n \"domain\",\n \"path\",\n \"maxAge\",\n \"expires\",\n \"httpOnly\",\n \"secure\",\n \"sameSite\",\n \"priority\",\n \"partitioned\",\n] as const satisfies readonly (keyof ResponseCookie)[];\n\n/**\n * The subset of `NextResponse[\"cookies\"]` the middleware binding uses.\n */\nexport interface ResponseCookieJar {\n getAll(): ResponseCookie[];\n set(cookie: ResponseCookie): void;\n}\n\n/** The subset of a `NextResponse` the middleware binding drives. */\nexport interface MiddlewareResponse {\n cookies: ResponseCookieJar;\n headers: {\n get(name: string): string | null;\n set(name: string, value: string): void;\n /**\n * Required for the split-jar heal. `ResponseCookies` keeps ONE entry per\n * cookie NAME, so the same name cannot be expired at two Domain scopes\n * through it — the second `set` replaces the first. The losing scope is\n * expired with raw appended `Set-Cookie` headers instead.\n */\n append(name: string, value: string): void;\n };\n}\n\nexport interface NextSupabaseConfig {\n /**\n * `process.env.NEXT_PUBLIC_SUPABASE_URL` — read STATICALLY at the call site.\n * Next only inlines `NEXT_PUBLIC_*` into client bundles for static member\n * accesses; a dynamic lookup inside this package would be `undefined` in\n * every browser bundle.\n */\n supabaseUrl: string | undefined;\n /** `process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`, read statically. */\n publishableKey: string | undefined;\n /** The apex domain one login must span, e.g. `\"aimatrx.com\"`. */\n apexDomain: string;\n /** Auth cookie name. Defaults to the package's `sb-matrx-auth`. */\n cookieName?: string;\n /** A superseded storage key to migrate from and then clear. */\n legacyCookieName?: string;\n /**\n * Host wrapper applied to the BROWSER client only — the one legitimate\n * host seam here (diagnostics/error capture). It must not change query\n * behavior; the package never inspects what it returns.\n *\n * `C extends object` because a Supabase client always IS one, and a real\n * capture wrapper is a `Proxy` that must constrain the same way. Leaving\n * `C` unconstrained made every honest host wrapper unassignable (found at\n * matrx-frontend adoption, 0.5.1).\n */\n wrapBrowserClient?: <C extends object>(client: C) => C;\n}\n\n/** Cache-defeating headers every auth-cookie-bearing response must carry. */\nexport const AUTH_COOKIE_RESPONSE_HEADERS: Readonly<Record<string, string>> =\n Object.freeze({\n \"Cache-Control\": \"private, no-cache, no-store, must-revalidate, max-age=0\",\n Expires: \"0\",\n Pragma: \"no-cache\",\n });\n\nconst CARRIED_HEADERS = [\"cache-control\", \"expires\", \"pragma\"] as const;\n\n/**\n * Stamped on any response whose auth cookie jar was found split and healed.\n * A shell that already rendered may be wearing an identity this pass could not\n * see, and must say so rather than paint a signed-in screen of anonymous data.\n */\nexport const SPLIT_COOKIE_JAR_HEADER = \"x-matrx-split-jar\";\n\n/**\n * Stamped when this request's auth cookie jar held one storage key BOTH\n * unchunked and chunked — see `AuthCookie.reconcile`. Distinct from\n * `SPLIT_COOKIE_JAR_HEADER` because the person's experience is different and\n * the screen must say the true thing:\n *\n * split jar → the tab still holds a session; the server just could not\n * read it. \"Sign in again\" restores the page.\n * ambiguous → we deliberately refused BOTH copies rather than risk\n * resolving the wrong person, so this person has just been\n * signed out BY US. That is an automatic intervention on\n * somebody's session and it must not happen quietly: the\n * browser's own read is anonymous too, so nothing else on the\n * page can notice, and without this header the app shows a\n * login screen with no explanation.\n */\nexport const AMBIGUOUS_AUTH_COOKIE_HEADER = \"x-matrx-auth-ambiguous\";\n\n/**\n * `@supabase/ssr`'s `{ name, value, options }` → Next's flat `ResponseCookie`.\n * The ONE conversion boundary; see {@link ResponseCookie} for what skipping it\n * cost.\n */\nexport function toResponseCookie(cookie: CookieToSet): ResponseCookie {\n const options = (cookie.options ?? {}) as Record<string, unknown>;\n // Built key by key, never spread: `options.name` would fight the real name,\n // and an unrecognized key would read as carried when Next drops it.\n const out: Record<string, unknown> = {\n name: cookie.name,\n value: cookie.value,\n };\n for (const attribute of RESPONSE_COOKIE_ATTRIBUTES) {\n const value = options[attribute];\n if (value !== undefined) out[attribute] = value;\n }\n return out as unknown as ResponseCookie;\n}\n\n/**\n * A raw `Set-Cookie` that EXPIRES `name` at the host-only scope — the scope a\n * response cannot reach through `ResponseCookies` once the same name is being\n * written at `.apex`. A browser ignores a `Set-Cookie` for a scope the current\n * host does not own, so this is a no-op on a healthy jar.\n */\nfunction hostOnlyExpiryHeader(name: string): string {\n return `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=Lax`;\n}\n\n/**\n * A `ResponseCookie` as a raw `Set-Cookie` header value, serialized the way\n * Next's `ResponseCookies` serializes it. Needed because a response can only\n * carry ONE entry per cookie NAME through `.cookies`, and `@supabase/ssr`\n * deliberately writes some names TWICE — see `applyCookies`.\n */\nexport function serializeSetCookie(cookie: ResponseCookie): string {\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n parts.push(`Path=${cookie.path ?? \"/\"}`);\n if (cookie.domain !== undefined) parts.push(`Domain=${cookie.domain}`);\n if (cookie.maxAge !== undefined) parts.push(`Max-Age=${cookie.maxAge}`);\n if (cookie.expires !== undefined) {\n const expires =\n cookie.expires instanceof Date\n ? cookie.expires\n : new Date(cookie.expires);\n parts.push(`Expires=${expires.toUTCString()}`);\n }\n if (cookie.httpOnly === true) parts.push(\"HttpOnly\");\n if (cookie.secure === true) parts.push(\"Secure\");\n if (typeof cookie.sameSite === \"string\") {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n return parts.join(\"; \");\n}\n\n/**\n * Thrown when identity is missing. Named and actionable — the alternative\n * (`process.env.X!`) surfaces as `Cannot read properties of undefined\n * (reading 'trim')` from inside Supabase, which taught nobody anything.\n */\nexport class MissingSupabaseIdentityError extends Error {\n readonly variable: string;\n constructor(variable: string) {\n super(\n `@ai-matrx/data/next: missing required Supabase identity \"${variable}\". ` +\n `Pass it as a STATIC member access (process.env.${variable}) to createNextSupabase().`,\n );\n this.name = \"MissingSupabaseIdentityError\";\n this.variable = variable;\n }\n}\n\nfunction requireIdentity(value: string | undefined, variable: string): string {\n const trimmed = value?.trim();\n if (!trimmed) throw new MissingSupabaseIdentityError(variable);\n return trimmed;\n}\n\n/**\n * THE AUTH RESOLVE BUDGET — the proxy is on the critical path of EVERY request\n * and nothing below it may hang forever.\n *\n * Next 16 runs `proxy.ts` in the Node.js runtime and that \"cannot be\n * configured\" (Next 16 upgrade guide), so on Vercel this pass is a Lambda with\n * a 15s task cap, concurrency of one per instance, and a real cold start. A\n * single `<Link>` prefetch storm — a chat sidebar is 20-40 links — spins up\n * that many instances at once, and an unbounded auth round trip in each one\n * turns a downstream blink into `504 MIDDLEWARE_INVOCATION_TIMEOUT` on the\n * page the person actually asked for. Measured on aimatrx.com, 2026-09-20: 19\n * such 504s in 48h, every one of them in a burst of simultaneous prefetches,\n * every one of them \"Task timed out after 15 seconds\".\n *\n * So the deadline is OURS, not the platform's: a budget short enough that a\n * stalled auth server costs one request its identity instead of costing the\n * whole page its response.\n */\nconst AUTH_RESOLVE_TIMEOUT_MS = 2_500;\n\n/**\n * The identity calls this budget covers, and ONLY these. Sign-in, sign-up,\n * OAuth code exchange and password reset also live under `/auth/v1/` and a\n * person is waiting on them; they keep their natural timeout.\n */\nconst IDENTITY_PATHS = [\n \"/auth/v1/user\",\n \"/auth/v1/token?grant_type=refresh_token\",\n \"/auth/v1/.well-known/jwks.json\",\n];\n\n/** Non-retryable by construction: not a 5xx, so auth-js stops retrying. */\nconst AUTH_BUDGET_EXHAUSTED_STATUS = 408;\nconst AUTH_BUDGET_EXHAUSTED_CODE = \"auth_budget_exhausted\";\n\n/**\n * ONE budget for the WHOLE identity resolve of a request, not one per socket.\n *\n * The 0.17.0 budget aborted each `/auth/v1/` socket at 2.5s. That was not\n * enough, and 2026-09-21 proved it: auth-js retries a token refresh with\n * exponential backoff for up to 30s (`AUTO_REFRESH_TICK_DURATION_MS`), and a\n * per-socket abort is exactly the retryable error that keeps that loop going —\n * five aborted attempts plus their pauses is ~15.5s, past Vercel's 15s task\n * cap, and the proxy answered `504` while believing it had a budget.\n *\n * This wrapper holds a deadline from the FIRST identity call. While the\n * deadline is open every identity call gets what is LEFT of it, aborting the\n * socket (a `Promise.race` would leave it hanging and holding the Lambda).\n * The moment an attempt aborts, or the deadline passes, the budget is\n * EXHAUSTED and every further identity call answers synchronously with a\n * synthetic `408 auth_budget_exhausted` — a non-retryable auth error — so\n * auth-js stops its loop at once instead of sleeping through it. The whole\n * resolve therefore costs at most the budget plus one 200ms backoff.\n *\n * `exhausted` is also the cookie gate: an exhausted budget must never write,\n * expire or heal a cookie, because auth-js clears the session on a\n * non-retryable refresh failure once the access token has truly expired, and\n * that write is a logout caused by a slow network.\n */\nexport interface AuthBudget {\n fetch: typeof fetch;\n readonly exhausted: boolean;\n}\n\n/**\n * 🚨 A REFRESH THAT WAS SENT IS NEVER ABANDONED AT THE BUDGET (2026-10-07).\n *\n * `grant_type=refresh_token` ROTATES the refresh token on the auth server the\n * moment it is processed. Aborting it at 2.5s did not stop the rotation — it only\n * threw away the new tokens, so no cookie was written and the browser kept a\n * refresh token the server had already spent. Past GoTrue's reuse window that\n * token is refused and the person is signed out (and a server-rendered read made\n * on the dropped session went out as `anon`: their own chats answered \"sign in\").\n * So a refresh, once on the wire, runs to completion and its answer is used,\n * bounded only by this ceiling — below Vercel's 15s proxy cap. A refresh that has\n * NOT been sent yet still obeys the budget (nothing rotated, nothing lost).\n */\nexport const AUTH_REFRESH_CEILING_MS = 8_000;\n\nconst REFRESH_GRANT_PATH = \"/auth/v1/token?grant_type=refresh_token\";\n\nexport function createAuthBudget(\n timeoutMs: number,\n options: { refreshCeilingMs?: number } = {},\n): AuthBudget {\n const refreshCeilingMs = Math.max(timeoutMs, options.refreshCeilingMs ?? AUTH_REFRESH_CEILING_MS);\n let deadline: number | null = null;\n let exhausted = false;\n\n const isIdentityCall = (url: string): boolean =>\n IDENTITY_PATHS.some((path) => url.includes(path));\n\n const exhaustedResponse = (): Response =>\n new Response(\n JSON.stringify({\n error_code: AUTH_BUDGET_EXHAUSTED_CODE,\n msg:\n `identity resolve budget of ${timeoutMs}ms is exhausted for this request; ` +\n `not retrying`,\n }),\n {\n status: AUTH_BUDGET_EXHAUSTED_STATUS,\n headers: { \"content-type\": \"application/json\" },\n },\n );\n\n const budgetedFetch: typeof fetch = async (input, init) => {\n const url =\n typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n if (!isIdentityCall(url) || init?.signal) {\n return fetch(input as RequestInfo, init);\n }\n const now = Date.now();\n if (deadline === null) deadline = now + timeoutMs;\n const remaining = deadline - now;\n if (exhausted || remaining <= 0) {\n exhausted = true;\n return exhaustedResponse();\n }\n // Once sent, a refresh is allowed the ceiling, not what is left of the budget.\n const allowed = url.includes(REFRESH_GRANT_PATH) ? refreshCeilingMs : remaining;\n try {\n return await fetch(input as RequestInfo, {\n ...init,\n signal: AbortSignal.timeout(allowed),\n });\n } catch (cause) {\n const name = String((cause as { name?: unknown })?.name ?? \"\");\n if (name === \"TimeoutError\" || name === \"AbortError\") exhausted = true;\n throw cause;\n }\n };\n\n return {\n fetch: budgetedFetch,\n get exhausted() {\n return exhausted;\n },\n };\n}\n\n/**\n * `true` for the errors that mean \"we could not REACH the auth authority\",\n * never for \"this person is not signed in\".\n *\n * The distinction is the whole point: a transport failure must not be read as\n * a logout, or a blink on the network signs people out (and, on a protected\n * route, bounces them to /login mid-session).\n *\n * Matched by NAME rather than by importing `isAuthRetryableFetchError`, so\n * this package keeps its single `@supabase/ssr` dependency and does not take\n * on `@supabase/auth-js` and its ESM/CJS dual-graph hazard.\n */\nfunction isAuthTransportFailure(error: unknown): boolean {\n if (!error || typeof error !== \"object\") return false;\n const name = String((error as { name?: unknown }).name ?? \"\");\n if (\n name === \"AuthRetryableFetchError\" ||\n name === \"TimeoutError\" ||\n name === \"AbortError\"\n ) {\n return true;\n }\n const status = (error as { status?: unknown }).status;\n if (status === 0 || status === 504 || status === 503) return true;\n // The synthetic answer this package hands auth-js once the budget is spent.\n const code = (error as { code?: unknown }).code;\n return status === AUTH_BUDGET_EXHAUSTED_STATUS || code === AUTH_BUDGET_EXHAUSTED_CODE;\n}\n\n/** What one identity resolve settled, and whether it could settle at all. */\ninterface ResolvedIdentity {\n user: ({ id: string } & Record<string, unknown>) | null;\n authUnavailable: boolean;\n}\n\n/**\n * THE ONE IDENTITY RESOLVE. `getClaims()`, never `getUser()`.\n *\n * `getUser()` sends a request to the Auth server for EVERY JWT — an auth-server\n * lookup on the critical path of every matched request. `getClaims()` verifies\n * the access token against the project's JWKS with WebCrypto, locally and\n * usually with no network call at all, and auth-js caches the key set in a\n * module-level `GLOBAL_JWKS` shared by every client in the process — so a warm\n * instance re-verifies with zero I/O and only a genuine token refresh or a\n * JWKS TTL expiry goes out to the network.\n *\n * This requires an ASYMMETRIC signing key on the project (ECC/RSA). With a\n * symmetric secret `getClaims()` silently degrades to a `getUser()`-shaped\n * request per call, which is correct but gives back none of the saving — so a\n * project that turns asymmetric keys off pays the old cost and nothing breaks.\n *\n * Verification is cryptographic, so the claims are as trusted as `getUser()`'s\n * answer; this is not the untrusted `getSession()` read.\n */\nasync function resolveIdentity(\n client: {\n auth: {\n getClaims: () => Promise<{\n data: { claims?: Record<string, unknown> } | null;\n error: unknown;\n }>;\n };\n },\n budget?: AuthBudget,\n): Promise<ResolvedIdentity> {\n const unreachable = (cause: unknown): ResolvedIdentity => {\n console.warn(\n `[@ai-matrx/data/next] AUTH UNREACHABLE within ${AUTH_RESOLVE_TIMEOUT_MS}ms — ` +\n `this request is being passed through WITHOUT an identity and WITHOUT ` +\n `touching a single auth cookie. Nobody is being signed out: the shell ` +\n `resolves auth again on the client, and the next request re-resolves it ` +\n `here. If this is not rare, the auth authority (or the network to it) is ` +\n `the thing to fix — raising this budget only trades it back for a 504. ` +\n `Cause: ${String(cause)}`,\n );\n return { user: null, authUnavailable: true };\n };\n\n try {\n const { data, error } = await client.auth.getClaims();\n if (error) {\n return isAuthTransportFailure(error) || budget?.exhausted\n ? unreachable(error)\n : { user: null, authUnavailable: false };\n }\n const claims = data?.claims;\n if (!claims || typeof claims.sub !== \"string\") {\n // No claims AND a spent budget is \"we could not tell\", not \"nobody\".\n return budget?.exhausted\n ? unreachable(\"identity budget exhausted before a token could be verified\")\n : { user: null, authUnavailable: false };\n }\n // The JWT carries the identity fields a proxy decides with. Kept as a\n // `{ id }`-shaped object so every existing `session.user` read — all of\n // which are truthiness or `.id` — is unchanged by the switch.\n return {\n user: { ...claims, id: claims.sub },\n authUnavailable: false,\n };\n } catch (cause) {\n return unreachable(cause);\n }\n}\n\nexport interface MiddlewareSessionArgs<Response> {\n /** `Host` header of the incoming request. */\n host: string | null | undefined;\n /** `request.cookies` */\n requestCookies: RequestCookieJar;\n /**\n * The raw `Cookie` header (`request.headers.get(\"cookie\")`). REQUIRED to\n * detect and heal a split jar — `request.cookies` has already collapsed the\n * duplicates. Without it this pass cannot see the shadow and cannot expire\n * it, which is exactly how the \"healed\" 0.7.x build still left poisoned\n * browsers poisoned.\n */\n cookieHeader?: string | null | undefined;\n /** `() => NextResponse.next({ request })` — a FRESH response each call. */\n createResponse: () => Response;\n /** `(url) => NextResponse.redirect(url)` */\n createRedirect: (url: URL) => Response;\n}\n\nexport interface MiddlewareSession<DB extends DatabaseLike, Response> {\n /**\n * The validated user, or `null`. Resolved via `getClaims()` — a local\n * cryptographic verification of the access token, not a `getSession()` read.\n *\n * `null` is ambiguous on its own: check `authUnavailable` before treating it\n * as \"signed out\". A transport failure produces `null` too, and acting on\n * that as a logout is how a network blink bounces a signed-in person.\n */\n user: ({ id: string } & Record<string, unknown>) | null;\n /** The client, in case the caller needs a further authenticated read. */\n client: NextServerClient<DB>;\n /** The response carrying every session cookie written during this pass. */\n response: Response;\n /**\n * A redirect that CARRIES the session cookies and cache headers. Building a\n * bare `NextResponse.redirect` here drops refreshed tokens on the floor and\n * logs the user out on the next request.\n */\n redirect(url: URL): Response;\n /**\n * `true` when this request arrived with the same auth cookie at two Domain\n * scopes. The response heals the jar, but the SHELL that already rendered\n * may be holding an identity this pass could not see — a host that renders\n * a signed-in shell must say so in words rather than paint a signed-in\n * screen full of anonymous data.\n */\n splitCookieJar: boolean;\n /**\n * `true` when a storage key arrived BOTH unchunked and chunked and this pass\n * therefore refused both copies rather than risk resolving the wrong person.\n * The person has been signed out by us and must be told so.\n */\n ambiguousAuthCookies: boolean;\n /**\n * `true` when the auth authority could not be REACHED inside the resolve\n * budget, so this pass has no opinion about who this is.\n *\n * The contract when it is set: `user` is `null` but the person is NOT signed\n * out, not one auth cookie was written, expired or healed, and the host must\n * neither bounce to login nor paint a signed-out screen as if it were\n * settled. Let the request through; the client resolves auth itself and the\n * next request re-resolves it here.\n */\n authUnavailable: boolean;\n}\n\nexport interface NextSupabase<DB extends DatabaseLike> {\n /** The auth-cookie primitive this binding is built on. */\n readonly authCookie: AuthCookie;\n readonly cookieName: string;\n optionsForHost(host: string | null | undefined): AuthCookieOptions;\n browserOptions(): AuthCookieOptions;\n\n /**\n * The browser client. Memoized on a `globalThis` slot keyed by identity, so\n * the ESM and CJS graphs of this package share ONE client and therefore one\n * auth state — a per-module singleton would silently split them.\n */\n browserClient(): NextBrowserClient<DB>;\n\n /**\n * A Server Component / Server Action client over `await cookies()`.\n * Cookie writes from a Server Component are swallowed by design: only the\n * proxy may set cookies there, and it refreshes the session anyway.\n */\n serverClient(args: {\n cookieStore: ServerCookieStore;\n host: string | null | undefined;\n /**\n * The raw `Cookie` request header (`(await headers()).get(\"cookie\")`).\n * REQUIRED to see a split jar: `cookies()` collapses two same-name cookies\n * to one before this code runs. Omitting it degrades this door to the\n * collapsed view, where the surviving copy is whichever the browser sent\n * last — a coin flip between the session and anonymous.\n */\n cookieHeader?: string | null | undefined;\n /**\n * See `routeClient`'s `appendSetCookie` — Next's `cookies()` store keys by\n * NAME exactly like `ResponseCookies`, so a two-scope removal collapses\n * here too. A Route Handler that signs somebody in MUST pass this (buffer\n * the headers and append them onto the response it returns, last).\n * A Server Component has no response to append to and its writes are\n * swallowed anyway, so it correctly passes nothing.\n */\n appendSetCookie?: (header: string) => void;\n }): NextServerClient<DB>;\n\n /**\n * A Route Handler client: reads the request's cookies, writes onto the\n * response you are about to return.\n */\n routeClient(args: {\n requestCookies: Pick<RequestCookieJar, \"getAll\">;\n setCookie: (cookie: CookieToSet) => void;\n host: string | null | undefined;\n /** See `serverClient` — same collapse, same requirement. */\n cookieHeader?: string | null | undefined;\n /**\n * `(header) => response.headers.append(\"Set-Cookie\", header)`.\n *\n * 🚨 REQUIRED ON ANY DOOR THAT SIGNS SOMEBODY IN. `@supabase/ssr` removes a\n * cookie at BOTH Domain scopes by emitting the SAME name twice, and every\n * jar a host can pass to `setCookie` — `NextResponse.cookies`, Next's\n * `cookies()` store — keeps ONE entry per NAME, so the second write\n * REPLACES the first and the `.apex` removal is silently lost. That is how\n * a stale UNCHUNKED auth cookie survives beside a fresh CHUNKED pair, and\n * `combineChunks` then serves the STALE one — the previous person's\n * session, to the person who just signed in (2026-09-08, R-O3).\n *\n * With this seam the duplicate goes out as a raw header instead. Without\n * it the drop is announced loudly rather than swallowed.\n *\n * THE CALLER'S OBLIGATION: `ResponseCookies.set` re-serializes the whole\n * `Set-Cookie` list from its own map and DISCARDS raw appends made since.\n * Nothing may touch `response.cookies` after a header appended here.\n */\n appendSetCookie?: (header: string) => void;\n }): NextServerClient<DB>;\n\n /**\n * THE middleware/proxy auth pass, entire. Runs legacy-key migration, builds\n * the client, resolves `getUser()` with nothing in between, persists a\n * migrated session under the current key, clears the superseded key at the\n * scope this host issued it on, and stamps no-store headers on any response\n * that carries cookies.\n */\n middlewareSession<Response extends MiddlewareResponse>(\n args: MiddlewareSessionArgs<Response>,\n ): Promise<MiddlewareSession<DB, Response>>;\n}\n\ninterface BrowserSlot {\n key: string;\n client: unknown;\n}\n\n/**\n * The browser-client singleton lives on `globalThis`, not in a module\n * variable: with dual ESM/CJS output each loader graph instantiates its own\n * copy of this module, and two Supabase browser clients means two auth\n * listeners racing on one cookie.\n */\nconst BROWSER_SLOT = Symbol.for(\"ai-matrx.data.next.browser-client\");\n\nfunction readBrowserSlot(): BrowserSlot | undefined {\n return (globalThis as Record<symbol, unknown>)[BROWSER_SLOT] as\n | BrowserSlot\n | undefined;\n}\n\nfunction writeBrowserSlot(slot: BrowserSlot): void {\n (globalThis as Record<symbol, unknown>)[BROWSER_SLOT] = slot;\n}\n\nexport function createNextSupabase<DB extends DatabaseLike = DatabaseLike>(\n config: NextSupabaseConfig,\n): NextSupabase<DB> {\n const url = requireIdentity(config.supabaseUrl, \"NEXT_PUBLIC_SUPABASE_URL\");\n const key = requireIdentity(\n config.publishableKey,\n \"NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY\",\n );\n const authCookie = createAuthCookie({\n apexDomain: config.apexDomain,\n ...(config.cookieName !== undefined\n ? { cookieName: config.cookieName }\n : {}),\n ...(config.legacyCookieName !== undefined\n ? { legacyCookieName: config.legacyCookieName }\n : {}),\n });\n const wrap = config.wrapBrowserClient;\n const slotKey = `${url}|${key}|${authCookie.cookieName}`;\n\n /**\n * `document.cookie` carries the SAME duplicates the server sees — and\n * `@supabase/ssr`'s default browser adapter resolves each chunk with\n * `find()`, so an empty shadow sorting first makes the BROWSER read\n * anonymous too. That is why the split-session banner never mounted in\n * production: the server said \"nobody\", and the client agreed, so nothing\n * looked inconsistent even though a real session was sitting right there.\n *\n * Reads are reconciled here (non-empty wins). Writes are NOT redirected:\n * they keep going through `document.cookie` with the canonical options, and\n * the shadow is expired by the proxy, which is the only side that can name a\n * cookie's scope.\n */\n const browserCookieAdapter = () => ({\n getAll: () =>\n authCookie.reconcile(\n typeof document === \"undefined\"\n ? []\n : parseCookieHeader(document.cookie),\n ).cookies,\n setAll: (cookiesToSet: readonly CookieToSet[]) => {\n if (typeof document === \"undefined\") return;\n for (const cookie of cookiesToSet) {\n const options = (cookie.options ?? {}) as Record<string, unknown>;\n const parts = [\n `${cookie.name}=${encodeURIComponent(cookie.value)}`,\n `Path=${typeof options.path === \"string\" ? options.path : \"/\"}`,\n ];\n if (typeof options.domain === \"string\") {\n parts.push(`Domain=${options.domain}`);\n }\n if (typeof options.maxAge === \"number\") {\n parts.push(`Max-Age=${options.maxAge}`);\n }\n if (typeof options.sameSite === \"string\") {\n parts.push(`SameSite=${options.sameSite}`);\n }\n if (options.secure === true || location.protocol === \"https:\") parts.push(\"Secure\");\n document.cookie = parts.join(\"; \");\n }\n },\n });\n\n const browserClient = (): NextBrowserClient<DB> => {\n const existing = readBrowserSlot();\n if (existing && existing.key === slotKey) {\n return existing.client as NextBrowserClient<DB>;\n }\n const raw = createBrowserClient<DB>(url, key, {\n cookieOptions: authCookie.browserOptions(),\n cookies: browserCookieAdapter(),\n });\n const client = wrap ? wrap(raw) : raw;\n writeBrowserSlot({ key: slotKey, client });\n return client;\n };\n\n /**\n * THE ONE READ SEAM. Every door reads the cookie jar through this — a\n * duplicated auth cookie name whose empty twin sorts first would otherwise\n * reassemble to \"\" and answer anonymous on that door alone (which is how\n * `manage` refused a valid Create while `www` worked).\n */\n const reconciledGetAll = (\n getAllRaw: () => readonly AuthCookieValue[],\n cookieHeader: string | null | undefined,\n ): (() => AuthCookieValue[]) => {\n // The raw header when we have it (duplicates intact), the collapsed jar\n // only as a fallback — see parseCookieHeader for why that matters.\n const parsed = parseCookieHeader(cookieHeader);\n if (parsed.length > 0) {\n const reconciled = authCookie.reconcile(parsed).cookies;\n return () => reconciled;\n }\n return () => authCookie.reconcile(getAllRaw()).cookies;\n };\n\n const serverClient: NextSupabase<DB>[\"serverClient\"] = ({\n cookieStore,\n host,\n cookieHeader,\n appendSetCookie,\n }) => {\n const written = new Set<string>();\n // A layout or page is a Lambda with the same task cap as the proxy, and a\n // `getClaims()` here can still trigger a token refresh. Same budget.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n return createServerClient<DB>(url, key, {\n cookieOptions: authCookie.optionsForHost(host),\n global: { fetch: budget.fetch },\n cookies: {\n getAll: reconciledGetAll(() => cookieStore.getAll(), cookieHeader),\n setAll: (cookiesToSet) => {\n try {\n // Two passes, never interleaved — see `routeClient`.\n const deferred: string[] = [];\n for (const cookie of cookiesToSet) {\n const { name, value, options } = cookie;\n if (!written.has(name)) {\n written.add(name);\n cookieStore.set(name, value, options as Record<string, unknown>);\n continue;\n }\n const header = serializeSetCookie(\n toResponseCookie(cookie as CookieToSet),\n );\n if (appendSetCookie) {\n deferred.push(header);\n continue;\n }\n console.warn(\n `[@ai-matrx/data/next] DROPPED a second Set-Cookie for \"${name}\" on host ` +\n `\"${host ?? \"?\"}\": Next's cookie store keeps one entry per name, so this ` +\n `write would have DELETED the first one. The lost header was: ${header}. ` +\n `A Route Handler here must pass appendSetCookie — an unremoved auth cookie ` +\n `at the other scope makes the next request resolve the PREVIOUS person.`,\n );\n }\n for (const header of deferred) appendSetCookie?.(header);\n } catch {\n // Called from a Server Component, where Next forbids cookie\n // writes. The proxy refreshes the session on every request, so\n // dropping this write is correct — not a swallowed failure.\n }\n },\n },\n });\n };\n\n const routeClient: NextSupabase<DB>[\"routeClient\"] = ({\n requestCookies,\n setCookie,\n host,\n cookieHeader,\n appendSetCookie,\n }) => {\n // See `appendSetCookie`: a name written twice cannot go through any\n // one-entry-per-NAME jar, and the second write is what deletes the first.\n const written = new Set<string>();\n // Same budget as the proxy: an API route is one Lambda, one identity resolve.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n return createServerClient<DB>(url, key, {\n cookieOptions: authCookie.optionsForHost(host),\n global: { fetch: budget.fetch },\n cookies: {\n getAll: reconciledGetAll(() => requestCookies.getAll(), cookieHeader),\n setAll: (cookiesToSet) => {\n if (budget.exhausted && cookiesToSet.length > 0) {\n // See middlewareSession.applyCookies — a spent budget writes nothing.\n console.warn(\n `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) in a ` +\n `route on host \"${host ?? \"?\"}\": identity budget exhausted; nobody is signed out.`,\n );\n return;\n }\n // 🚨 TWO PASSES, NEVER INTERLEAVED — the same rule the split-jar heal\n // learned the hard way. `ResponseCookies.set` re-serializes the whole\n // `Set-Cookie` list from its own map, DISCARDING every raw append made\n // since. Appending a duplicate in the middle of the batch and then\n // writing one more cookie eats it, which is a silently lost removal\n // all over again. Every `setCookie` first; every append after.\n const deferred: string[] = [];\n for (const cookie of cookiesToSet) {\n const { name } = cookie;\n if (!written.has(name)) {\n written.add(name);\n setCookie(cookie as CookieToSet);\n continue;\n }\n const header = serializeSetCookie(\n toResponseCookie(cookie as CookieToSet),\n );\n if (appendSetCookie) {\n deferred.push(header);\n continue;\n }\n // LOUD: a cookie write is being lost, and the loss is an identity\n // hazard, not a cosmetic one.\n console.warn(\n `[@ai-matrx/data/next] DROPPED a second Set-Cookie for \"${name}\" on host ` +\n `\"${host ?? \"?\"}\": this door passed no appendSetCookie, and the response jar ` +\n `keeps one entry per name, so this write would have DELETED the first one. ` +\n `The lost header was: ${header}. Pass appendSetCookie from this route — an ` +\n `unremoved auth cookie at the other scope makes the next request resolve the ` +\n `PREVIOUS person.`,\n );\n }\n for (const header of deferred) appendSetCookie?.(header);\n },\n },\n });\n };\n\n async function middlewareSession<Response extends MiddlewareResponse>(\n args: MiddlewareSessionArgs<Response>,\n ): Promise<MiddlewareSession<DB, Response>> {\n const { host, requestCookies, createResponse, createRedirect } = args;\n const hostCookieOptions = authCookie.optionsForHost(host);\n\n let response = createResponse();\n\n /**\n * THE BASELINE VIEW, taken from the RAW `Cookie` header so duplicates are\n * still there to be seen. `requestCookies.getAll()` has already collapsed\n * them (Next keys its jar by name), which is why reconciling over the jar\n * detected nothing in production.\n */\n const rawCookies = parseCookieHeader(args.cookieHeader);\n const baseline = authCookie.reconcile(\n rawCookies.length > 0 ? rawCookies : requestCookies.getAll(),\n );\n\n /**\n * Auth cookie names that arrived at TWO scopes. Only meaningful where this\n * host issues a Domain-scoped cookie; on localhost/preview the host-only\n * cookie IS the real one and must never be expired.\n */\n const shadowed = hostCookieOptions.domain\n ? new Set(baseline.shadowedNames)\n : new Set<string>();\n\n /**\n * An UNCHUNKED value and a CHUNKED pair for one storage key. Unlike a\n * two-scope shadow there is no copy here that could be the good one — the\n * reconciled view has already dropped the whole family — so it is expired\n * on EVERY host, host-only deployments included, and it is expired even\n * when `getUser()` resolved nobody: this is not a \"the auth server blinked\"\n * null, it is a jar the writer cannot produce.\n */\n for (const name of baseline.ambiguousNames) shadowed.add(name);\n if (baseline.ambiguousKeys.length > 0) {\n console.warn(\n `[@ai-matrx/data/next] AMBIGUOUS auth cookie jar on host \"${host ?? \"?\"}\": ` +\n `${baseline.ambiguousKeys.join(\", \")} arrived UNCHUNKED and CHUNKED at once. ` +\n `Serving either would risk resolving a DIFFERENT person's session, so this ` +\n `request is treated as signed out and every cookie in those families is being ` +\n `expired at both scopes. The person signs in once and the jar is clean.`,\n );\n }\n\n /** Writes made during this pass, layered over the baseline. */\n const overlay = new Map<string, string>();\n const readCookies = (): AuthCookieValue[] => {\n const merged = baseline.cookies.map((cookie) =>\n overlay.has(cookie.name)\n ? { name: cookie.name, value: overlay.get(cookie.name) as string }\n : cookie,\n );\n const seen = new Set(merged.map(({ name }) => name));\n for (const [name, value] of overlay) {\n if (!seen.has(name)) merged.push({ name, value });\n }\n return merged;\n };\n const noteWrite = (name: string, value: string): void => {\n overlay.set(name, value);\n requestCookies.set(name, value);\n };\n\n // Seed the current storage key from the superseded one BEFORE the client\n // reads cookies. Absent-only, so a live session is never overwritten.\n const migrated = authCookie.migrateLegacyCookies(readCookies());\n for (const { name, value } of migrated) noteWrite(name, value);\n\n /**\n * Names already written on this response through `.cookies`, and the\n * writes that could not go that way.\n *\n * 🚨 THE SECOND ONE-ENTRY-PER-NAME TRAP, and it is how the stale unchunked\n * auth cookie is MADE. `@supabase/ssr`'s `applyServerStorage` removes a\n * cookie at BOTH Domain scopes by emitting the SAME name twice —\n * `Domain=.apex; Max-Age=0` and then host-only `Max-Age=0`. Funnel both\n * through `ResponseCookies.set` and the second REPLACES the first, so the\n * `.apex` copy is never expired. Measured on the real class:\n *\n * set(\"sb-…-v2\", \"\", {domain: \".aimatrx.com\", maxAge: 0})\n * set(\"sb-…-v2\", \"\", {maxAge: 0})\n * set(\"sb-…-v2.0\", \"<new session>\", {domain: \".aimatrx.com\"})\n * -> [\"sb-…-v2=; Path=/; Max-Age=0; …\", host-only ONLY\n * \"sb-…-v2.0=<new session>; …; Domain=.aimatrx.com\"]\n *\n * So writing a CHUNKED session over an unchunked one leaves the old\n * unchunked cookie alive at `.apex` forever — and `combineChunks` returns\n * the unchunked value the moment it is non-empty, without ever reading the\n * chunks. Sign in as someone else and the app keeps resolving the previous\n * person. That is an identity leak, produced by a lost `Set-Cookie`.\n *\n * The heal already knew this class for its own two-scope expiry and used\n * raw appends. The ORDINARY write path did not, which is why the residue\n * kept being created. Every duplicate name now goes out as a raw appended\n * header too, flushed in the one final append pass below.\n */\n const writtenNames = new Set<string>();\n const duplicateWrites: string[] = [];\n\n // ONE budget for every identity call this pass makes — see createAuthBudget.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n\n const applyCookies = (\n cookiesToSet: readonly CookieToSet[],\n headers: Record<string, string> = {},\n ): void => {\n // 🚨 A SPENT BUDGET WRITES NOTHING. auth-js clears the session when a\n // refresh fails non-retryably on an already-expired access token, and\n // our synthetic 408 IS that non-retryable failure. Letting that write\n // through would sign the person out because the network was slow.\n if (budget.exhausted && cookiesToSet.length > 0) {\n console.warn(\n `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) on host ` +\n `\"${host ?? \"?\"}\": the identity budget was exhausted on this request, so nothing ` +\n `learned here is trusted enough to change the jar. Nobody is signed out; the next ` +\n `request re-resolves. Names: ${cookiesToSet.map((c) => c.name).join(\", \")}`,\n );\n return;\n }\n for (const { name, value } of cookiesToSet) noteWrite(name, value);\n const next = createResponse();\n for (const cookie of response.cookies.getAll()) {\n next.cookies.set(cookie);\n }\n for (const cookie of cookiesToSet) {\n const responseCookie = toResponseCookie(cookie);\n if (writtenNames.has(responseCookie.name)) {\n // Raw, so it cannot displace the entry already on the response.\n // Appends are flushed LAST (see the two-pass rule in applyHeal), so a\n // genuine same-scope re-write still lands after the earlier value.\n duplicateWrites.push(serializeSetCookie(responseCookie));\n continue;\n }\n writtenNames.add(responseCookie.name);\n next.cookies.set(responseCookie);\n }\n const responseHeaders =\n cookiesToSet.length > 0\n ? { ...AUTH_COOKIE_RESPONSE_HEADERS, ...headers }\n : headers;\n for (const [name, value] of Object.entries(responseHeaders)) {\n next.headers.set(name, value);\n }\n response = next;\n };\n\n const client = createServerClient<DB>(url, key, {\n cookieOptions: hostCookieOptions,\n // Identity calls out of THIS pass share one deadline; see createAuthBudget.\n global: { fetch: budget.fetch },\n cookies: {\n getAll: () => readCookies(),\n setAll: (cookiesToSet, headers) => {\n applyCookies(\n cookiesToSet as readonly CookieToSet[],\n (headers ?? {}) as Record<string, string>,\n );\n },\n },\n });\n\n // NOTHING may run between constructing the client and the identity\n // resolve. Any await in between has produced random logouts that are close\n // to undebuggable. The rule is structural here instead of a comment a host\n // has to remember — `resolveIdentity` is one adjacent await.\n const { user, authUnavailable } = await resolveIdentity(client, budget);\n\n // A valid session that arrived under the superseded key is persisted ONCE\n // under the current key. A token the old authority issued fails getUser(),\n // is never persisted, and gets cleared below.\n if (user && migrated.length > 0) {\n const { name: _name, ...cookieOptions } = hostCookieOptions;\n applyCookies(\n readCookies()\n .filter(\n ({ name, value }) =>\n authCookie.isCurrentCookie(name) && value.length > 0,\n )\n .map(({ name, value }) => ({\n name,\n value,\n options: { ...DEFAULT_COOKIE_OPTIONS, ...cookieOptions },\n })),\n );\n }\n\n // The superseded storage key must die at BOTH scopes, and the clear below\n // can only reach one of them (one entry per NAME on a response). Its\n // host-only twin goes out as a raw header with the shadow heal.\n if (authCookie.legacyCookieName && hostCookieOptions.domain) {\n const legacy = authCookie.legacyCookieName;\n for (const { name } of rawCookies) {\n if (name === legacy || name.startsWith(`${legacy}.`)) shadowed.add(name);\n }\n }\n\n // 🚨 NEVER EXPIRE THE SUPERSEDED KEY ON A RESOLVE WE COULD NOT MAKE.\n // The migration above persists legacy -> current ONLY when a user resolved.\n // So on an unreachable auth server the copy under the new name was never\n // written, and expiring the legacy one here would destroy the only cookie\n // holding the session — a logout caused by a network blink, which is the\n // exact class this whole pass exists to prevent.\n if (authCookie.legacyCookieName && !authUnavailable) {\n await clearAuthCookiesAtScopes({\n getAll: () => readCookies(),\n setAll: (cookiesToSet, headers) => {\n applyCookies(\n cookiesToSet as readonly CookieToSet[],\n (headers ?? {}) as Record<string, string>,\n );\n },\n storageKey: authCookie.legacyCookieName,\n scopes: hostCookieOptions.domain\n ? [{ domain: hostCookieOptions.domain }]\n : [{}],\n });\n }\n\n /**\n * THE HEAL — CONVERGE ON THE CANONICAL SCOPE, never merely delete.\n *\n * A shadowed name means the same cookie exists host-only AND at `.apex`,\n * and NOTHING in a `Cookie` header says which scope holds which value. So\n * blindly expiring host-only can destroy the live session — if the good\n * token happened to be the host-only copy, that is a logout we caused.\n *\n * Instead: REWRITE the reconciled (non-empty) value at the canonical\n * `.apex` scope through `cookies.set`, and expire the host-only scope with\n * a raw appended `Set-Cookie`. Whichever scope held the good value, the\n * good value ends up at the canonical scope and the shadow dies. Idempotent\n * and safe to repeat. When every copy is empty there is no session to\n * preserve, so both scopes are simply expired.\n *\n * Raw headers because `ResponseCookies` keeps one entry per NAME — the\n * canonical write and the other scope's expiry cannot both go through it.\n * Applied ONCE, last, on whatever response actually leaves; nothing may\n * read `.cookies` afterwards or these get re-parsed into the one-per-name\n * map and the heal is undone.\n */\n const healed = new WeakSet<object>();\n const applyHeal = (target: Response): Response => {\n if (healed.has(target)) return target;\n // An unresolved identity is not evidence about the jar. Converging or\n // expiring a scope here would be acting on an answer we never got.\n if (authUnavailable) return target;\n if (shadowed.size === 0 && duplicateWrites.length === 0) return target;\n healed.add(target);\n const current = new Map(readCookies().map((c) => [c.name, c.value]));\n\n // 🚨 TWO PASSES, NEVER INTERLEAVED. `ResponseCookies.set` re-parses the\n // whole `Set-Cookie` header list into its one-entry-per-NAME map and\n // rewrites it — so a raw append made BEFORE a later `.set` is eaten.\n // Interleaving them shipped to production and silently dropped every\n // host-only expiry: the canonical re-issue went out, the shadow lived on,\n // and the heal did nothing. Proven with `curl` against the deployed build.\n // Every `.cookies` write happens here, and nothing may touch `.cookies`\n // after the append pass below.\n const expiries: string[] = [];\n for (const name of shadowed) {\n const value = current.get(name) ?? \"\";\n if (value.length > 0 && !user) {\n // 🚨 NEVER FREEZE AN UNVALIDATED VALUE. The surviving copy is only\n // known-good because `getUser()` above resolved a session FROM the\n // reconciled view on THIS request. Without that, a structurally clean\n // but STALE token — a heal racing a token refresh — would be pinned\n // at the canonical scope, and both hosts would then reject a\n // clean-looking jar until re-login (found on production by the walk's\n // rapid poison/reload cycles). A null user can equally mean the auth\n // server was briefly unreachable, so expiring here would log people\n // out over a blip. Touch nothing; `splitCookieJar` still reports, and\n // the banner says it in words.\n continue;\n }\n if (value.length > 0) {\n // The survivor, re-issued at the ONE canonical scope.\n target.cookies.set(\n toResponseCookie({\n name,\n value,\n options: {\n ...DEFAULT_COOKIE_OPTIONS,\n domain: hostCookieOptions.domain,\n path: \"/\",\n // A Domain-scoped auth cookie only ever exists on https here,\n // and `Secure` is not part of cookie identity, so re-issuing\n // with it set can only tighten what is already there.\n secure: true,\n },\n }),\n );\n } else if (hostCookieOptions.domain) {\n expiries.push(\n `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Domain=${hostCookieOptions.domain}; Secure; SameSite=Lax`,\n );\n }\n expiries.push(hostOnlyExpiryHeader(name));\n }\n // The ordinary writes that could not go through `.cookies` because the\n // name was already taken there. Flushed with the expiries, in the same\n // single append pass, AFTER every `.cookies` write on this response —\n // touching `.cookies` from here on re-parses the header list and eats\n // every one of these.\n for (const header of duplicateWrites) {\n target.headers.append(\"Set-Cookie\", header);\n }\n for (const header of expiries) {\n target.headers.append(\"Set-Cookie\", header);\n }\n if (shadowed.size === 0) {\n // Nothing was healed — only a two-scope write was rescued. No split\n // stamp, no scream, and no cache headers beyond what the write already\n // carried.\n return target;\n }\n\n // Stamped by the PACKAGE, not the host, so it rides EVERY healed\n // response — including redirects, which a host that stamps it at the end\n // of its own pass never reaches.\n target.headers.set(SPLIT_COOKIE_JAR_HEADER, \"1\");\n if (baseline.ambiguousKeys.length > 0) {\n target.headers.set(AMBIGUOUS_AUTH_COOKIE_HEADER, \"1\");\n }\n\n // A response that rewrites cookies must never be cached.\n for (const [key, value] of Object.entries(AUTH_COOKIE_RESPONSE_HEADERS)) {\n target.headers.set(key, value);\n }\n // LOUD: this is an automatic intervention on a user's cookie jar.\n console.warn(\n `[@ai-matrx/data/next] split auth cookie jar healed on host \"${host ?? \"?\"}\": ` +\n `${[...shadowed].join(\", \")} arrived at two scopes; the host-only copies ` +\n `were expired so the ${hostCookieOptions.domain} set is the only one left.`,\n );\n return target;\n };\n\n return {\n user: user as MiddlewareSession<DB, Response>[\"user\"],\n client,\n get response() {\n return applyHeal(response);\n },\n redirect: (targetUrl: URL): Response => {\n const redirect = createRedirect(targetUrl);\n for (const cookie of response.cookies.getAll()) {\n redirect.cookies.set(cookie);\n }\n for (const name of CARRIED_HEADERS) {\n const value = response.headers.get(name);\n if (value) redirect.headers.set(name, value);\n }\n return applyHeal(redirect);\n },\n splitCookieJar: shadowed.size > 0,\n ambiguousAuthCookies: baseline.ambiguousKeys.length > 0,\n authUnavailable,\n };\n }\n\n return {\n authCookie,\n cookieName: authCookie.cookieName,\n optionsForHost: authCookie.optionsForHost,\n browserOptions: authCookie.browserOptions,\n browserClient,\n serverClient,\n routeClient,\n middlewareSession,\n };\n}\n","/**\n * Shared auth-cookie options for EVERY Supabase client an application\n * constructs (browser, server, middleware, OAuth callbacks). All construction\n * sites MUST pass these options — a client using the default cookie name\n * cannot see the session the others wrote.\n *\n * WHY (the 2026-07 Matrx deployment split): one login must work on every\n * subdomain of the platform's apex (e.g. app.example.com, manage.example.com),\n * so on those hosts the auth cookie is issued domain-wide\n * (`Domain=.example.com`) instead of Supabase's default host-only cookie.\n *\n * The cookie is also RENAMED (default `sb-<project-ref>-auth-token` →\n * `sb-matrx-auth`): re-issuing an existing host-only cookie as domain-wide\n * under the SAME name leaves two same-name cookies in the browser (different\n * Domain attributes = distinct cookies), and whichever the browser sends\n * first wins — auth flaps. A new name makes the transition deterministic:\n * every user re-logs-in exactly once and the old cookie expires unused.\n * The `sb-` prefix is kept on purpose — cleanup paths that scrub session\n * cookies delete by that prefix.\n *\n * The Domain attribute is ONLY set on real apex hosts. localhost and preview\n * hosts get a host-only cookie — a browser rejects any Set-Cookie whose\n * Domain does not cover the current host, so setting it unconditionally would\n * silently break auth everywhere else.\n *\n * The apex domain is a VALUE the host passes in (never read from an env var\n * here): `createAuthCookie({ apexDomain: \"aimatrx.com\" })`.\n *\n * THE RENAME MIGRATION (0.5.0 — moved in from matrx-frontend, where it was\n * 44 lines of host code every other consumer would have had to re-derive):\n * replacing the auth authority (a new Supabase project) means a NEW cookie\n * name, and a tab still running the OLD bundle keeps writing the OLD name for\n * days. `migrateLegacyCookies` seeds the new storage key from the old one\n * ONLY when the new key is absent — never overwriting a live session — and\n * `isCurrentCookie` recognizes both the base name and Supabase's numbered\n * chunk suffixes (`<name>.0`, `<name>.1`, …), which a naive `startsWith`\n * check gets wrong for any cookie that merely shares the prefix.\n */\n\n/** Structurally compatible with @supabase/ssr's CookieOptionsWithName. */\nexport interface AuthCookieOptions {\n name: string;\n /** Remote hosts require HTTPS; loopback development remains usable over HTTP. */\n secure: boolean;\n domain?: string;\n}\n\n/** A cookie as read from a request — name + value, nothing else needed. */\nexport interface AuthCookieValue {\n name: string;\n value: string;\n}\n\nexport const DEFAULT_AUTH_COOKIE_NAME = \"sb-matrx-auth\";\n\nexport interface CreateAuthCookieArgs {\n /** The apex domain one login must span (e.g. `\"aimatrx.com\"`). */\n apexDomain: string;\n /** Override only for a second, unrelated deployment — never per-app. */\n cookieName?: string;\n /**\n * The storage key a PREVIOUS auth authority used. When present, cookies\n * under this name are seeded onto `cookieName` (absent-only) by\n * `migrateLegacyCookies`, and the middleware binding clears them at the\n * scope this host issued them on.\n */\n legacyCookieName?: string;\n}\n\nexport interface AuthCookie {\n cookieName: string;\n /** The superseded storage key, when this deployment has one. */\n legacyCookieName: string | undefined;\n /**\n * Cookie options for the host serving the current request.\n * @param host `Host` header or `window.location.hostname` (port tolerated).\n */\n optionsForHost(host: string | null | undefined): AuthCookieOptions;\n /** Browser variant — resolves the host from `window` (SSR-safe). */\n browserOptions(): AuthCookieOptions;\n /**\n * Is this cookie name the CURRENT auth cookie — base name or one of\n * Supabase's numbered chunks (`<name>.0`)? A prefix test is wrong: it\n * matches `<name>.anything`, including unrelated cookies.\n */\n isCurrentCookie(name: string): boolean;\n /**\n * Cookies to seed under the current name, derived from the legacy name.\n * Returns `[]` when any current cookie already exists (never overwrite a\n * live session) or when no legacy name is configured.\n */\n migrateLegacyCookies(\n cookies: readonly AuthCookieValue[],\n ): AuthCookieValue[];\n /**\n * THE POISONED-JAR READ. A browser can hold the SAME auth cookie name twice\n * — once host-only, once at `.apex` — because different Domain attributes\n * make distinct cookies, and it sends both in one `Cookie` header. Every\n * reassembler downstream (`@supabase/ssr`'s `combineChunks`) resolves a\n * chunk with `find()`, i.e. FIRST WINS, and the browser decides the order.\n * An empty shadow that sorts first therefore reassembles to `\"\"` and every\n * server read answers anonymous while the tab is signed in.\n *\n * This collapses each auth-cookie NAME to one entry, preferring the first\n * NON-EMPTY value. Non-auth cookies pass through untouched, in order.\n *\n * A `Cookie` header carries no Domain, so which scope a value came from is\n * genuinely unknowable here — non-empty-wins is the strongest correct rule\n * available, and `@supabase/ssr` already refuses a chunk set that decodes\n * incoherently. `shadowedNames` names every auth cookie that arrived more\n * than once, so the caller can EXPIRE the losing scope instead of leaving\n * the user to clear cookies by hand.\n *\n * THE SECOND POISON, AND IT IS AN IDENTITY LEAK: one storage key arriving\n * BOTH unchunked (`<name>`) and chunked (`<name>.0`, `<name>.1`). These are\n * DIFFERENT cookie names, so the per-name collapse above cannot see them,\n * and `@supabase/ssr`'s `combineChunks` returns the UNCHUNKED value the\n * moment it is non-empty — it never even reads the chunks. A stale base\n * cookie therefore shadows a freshly written chunked pair, and every read\n * resolves the PREVIOUS person while the new session sits unread beside it.\n * That is a different user's session being served, not merely a logout.\n *\n * `applyServerStorage` never writes that shape — it removes every chunk-like\n * sibling of whatever it writes — so a jar holding both is RESIDUE from a\n * removal that did not land (see `middlewareSession`: a two-scope removal\n * collapses on `ResponseCookies`, which is how this residue is made).\n *\n * WHICH COPY IS NEWER IS UNKNOWABLE HERE, and both directions of residue are\n * producible, so preferring either shape is a coin flip on WHOSE SESSION TO\n * SERVE. This drops the whole family from the read instead: nobody is signed\n * in, every name in it is returned in `ambiguousNames` to be expired, and the\n * host's honest-screen banner says so with the one action that fixes it. A\n * wrong sign-out is recoverable in one click; a wrong identity is not.\n */\n reconcile(cookies: readonly AuthCookieValue[]): ReconciledAuthCookies;\n}\n\n/** The outcome of {@link AuthCookie.reconcile}. */\nexport interface ReconciledAuthCookies {\n /** One entry per name; auth names resolved to their non-empty value. */\n cookies: AuthCookieValue[];\n /** Auth cookie names that arrived more than once (i.e. at two scopes). */\n shadowedNames: string[];\n /**\n * Auth cookie names belonging to a storage key that arrived UNCHUNKED AND\n * CHUNKED at once — see {@link AuthCookie.reconcile}. Their values have been\n * dropped from `cookies`, and they must be expired on EVERY host, including\n * the ones that issue a host-only cookie: unlike a two-scope shadow there is\n * no copy here that could be the good one.\n */\n ambiguousNames: string[];\n /** The storage keys that arrived in both shapes. Named for the scream. */\n ambiguousKeys: string[];\n /** `true` when the jar is split and needs healing. */\n split: boolean;\n}\n\n/**\n * Parse a raw `Cookie` request header, **PRESERVING DUPLICATES AND ORDER**.\n *\n * 🚨 THIS IS THE ONLY WAY TO SEE A SPLIT JAR. Next's `RequestCookies` (and the\n * `cookies()` store built on it) parses the header into a Map keyed by NAME,\n * so two same-name cookies at two Domain scopes collapse to ONE before any\n * application code runs — and the survivor is whichever the browser happened\n * to send LAST. Measured against the real class:\n *\n * \"sb-…-v2.0=; sb-…-v2.0=base64-REAL\" -> [{ value: \"base64-REAL\" }] works\n * \"sb-…-v2.0=base64-REAL; sb-…-v2.0=\" -> [{ value: \"\" }] anonymous\n *\n * That coin flip IS the production break, and it is invisible from the\n * collapsed jar: reconciliation over `getAll()` is a no-op no matter how\n * carefully it is written. Every door must therefore reconcile over THIS.\n */\nexport function parseCookieHeader(\n header: string | null | undefined,\n): AuthCookieValue[] {\n if (!header) return [];\n const out: AuthCookieValue[] = [];\n for (const pair of header.split(\";\")) {\n const eq = pair.indexOf(\"=\");\n // A valueless segment is a name with an empty value — exactly the shadow\n // this exists to catch — so it must not be skipped.\n const name = (eq === -1 ? pair : pair.slice(0, eq)).trim();\n if (!name) continue;\n const raw = eq === -1 ? \"\" : pair.slice(eq + 1).trim();\n let value = raw;\n try {\n value = decodeURIComponent(raw);\n } catch {\n // A malformed escape is not a reason to lose the cookie; Next's parser\n // keeps the raw text too.\n }\n out.push({ name, value });\n }\n return out;\n}\n\n/**\n * Is `name` the storage key itself or one of its numbered chunks? The same\n * membership test `@supabase/ssr`'s `isChunkLike` uses, so a family is defined\n * here exactly as the writer defines it. A bare `startsWith` is wrong: it also\n * matches `<key>.anything`, i.e. unrelated cookies.\n */\nfunction belongsToStorageKey(name: string, storageKey: string): boolean {\n if (name === storageKey) return true;\n if (!name.startsWith(`${storageKey}.`)) return false;\n return /^(0|[1-9][0-9]*)$/.test(name.slice(storageKey.length + 1));\n}\n\n/** `<from>` → `<to>`, carrying Supabase's numbered chunk suffix across. */\nfunction renamedCookie(\n cookie: AuthCookieValue,\n from: string,\n to: string,\n): AuthCookieValue | null {\n if (cookie.name === from) return { ...cookie, name: to };\n const prefix = `${from}.`;\n if (!cookie.name.startsWith(prefix)) return null;\n const chunk = cookie.name.slice(prefix.length);\n if (!/^(0|[1-9][0-9]*)$/.test(chunk)) return null;\n return { ...cookie, name: `${to}.${chunk}` };\n}\n\nexport function createAuthCookie(args: CreateAuthCookieArgs): AuthCookie {\n const cookieName = args.cookieName ?? DEFAULT_AUTH_COOKIE_NAME;\n const legacyCookieName = args.legacyCookieName;\n const apex = args.apexDomain.toLowerCase();\n\n const optionsForHost = (\n host: string | null | undefined,\n ): AuthCookieOptions => {\n const normalized = (host ?? \"\").toLowerCase();\n const hostname = normalized.startsWith(\"[\")\n ? normalized.slice(1, normalized.indexOf(\"]\"))\n : normalized === \"::1\" ? normalized : normalized.split(\":\")[0] ?? \"\";\n const loopback = hostname === \"localhost\" || hostname.endsWith(\".localhost\") ||\n hostname === \"::1\" || /^127(?:\\.(?:0|[1-9]\\d{0,2})){3}$/.test(hostname) &&\n hostname.split(\".\").every((part) => Number(part) <= 255);\n const secure = !loopback;\n const onApex = hostname === apex || hostname.endsWith(`.${apex}`);\n return onApex\n ? { name: cookieName, domain: `.${apex}`, secure }\n : { name: cookieName, secure };\n };\n\n const isCurrentCookie = (name: string): boolean =>\n belongsToStorageKey(name, cookieName);\n\n const isAuthCookie = (name: string): boolean =>\n isCurrentCookie(name) ||\n (legacyCookieName !== undefined &&\n (name === legacyCookieName || name.startsWith(`${legacyCookieName}.`)));\n\n const reconcile = (\n cookies: readonly AuthCookieValue[],\n ): ReconciledAuthCookies => {\n const seen = new Map<string, number>();\n const out: AuthCookieValue[] = [];\n const shadowed = new Set<string>();\n\n for (const cookie of cookies) {\n const at = seen.get(cookie.name);\n if (at === undefined) {\n seen.set(cookie.name, out.length);\n out.push({ name: cookie.name, value: cookie.value });\n continue;\n }\n if (!isAuthCookie(cookie.name)) continue; // non-auth: first wins, as before\n shadowed.add(cookie.name);\n const held = out[at];\n // Non-empty wins. Later non-empty values do NOT displace an earlier\n // non-empty one — with no scope information, churning the choice would\n // make reassembly non-deterministic across requests.\n if (held !== undefined && held.value.length === 0) {\n out[at] = { name: cookie.name, value: cookie.value };\n }\n }\n\n // THE FAMILY PASS. A storage key may arrive unchunked AND chunked at once;\n // those are different NAMES, so the collapse above cannot have seen it.\n // `combineChunks` would take the unchunked value and never read the chunks,\n // which serves whichever person's session happens to be sitting in the base\n // cookie. Refuse the family instead of guessing — see `reconcile`'s docs.\n const ambiguous = new Set<string>();\n const ambiguousKeys: string[] = [];\n const byName = new Map(out.map((cookie) => [cookie.name, cookie] as const));\n const storageKeys = [cookieName, ...(legacyCookieName ? [legacyCookieName] : [])];\n for (const storageKey of storageKeys) {\n const unchunked = byName.get(storageKey);\n const firstChunk = byName.get(`${storageKey}.0`);\n if (!unchunked?.value || !firstChunk?.value) continue;\n ambiguousKeys.push(storageKey);\n for (const { name } of out) {\n if (belongsToStorageKey(name, storageKey)) ambiguous.add(name);\n }\n }\n\n const resolved =\n ambiguous.size === 0\n ? out\n : out.filter(({ name }) => !ambiguous.has(name));\n\n const shadowedNames = [...shadowed];\n const ambiguousNames = [...ambiguous];\n return {\n cookies: resolved,\n shadowedNames,\n ambiguousNames,\n ambiguousKeys,\n split: shadowedNames.length > 0 || ambiguousNames.length > 0,\n };\n };\n\n return {\n cookieName,\n legacyCookieName,\n optionsForHost,\n reconcile,\n browserOptions: () =>\n optionsForHost(\n typeof window !== \"undefined\" ? window.location.hostname : null,\n ),\n isCurrentCookie,\n migrateLegacyCookies: (cookies) => {\n if (!legacyCookieName) return [];\n if (cookies.some(({ name }) => isCurrentCookie(name))) return [];\n return cookies.flatMap((cookie) => {\n const renamed = renamedCookie(cookie, legacyCookieName, cookieName);\n return renamed ? [renamed] : [];\n });\n },\n };\n}\n"],"mappings":"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","names":["key"]}
1
+ {"version":3,"sources":["../src/next.ts","../src/next/supabase.ts","../src/db/auth-cookie.ts"],"sourcesContent":["/**\n * `@ai-matrx/data/next` — the Next.js binding for the Supabase lane.\n *\n * One factory owns every Supabase client a Next app constructs (browser,\n * Server Component, Route Handler, proxy/middleware, OAuth callback), so the\n * domain-wide auth cookie, the SSR/browser split, the browser singleton, and\n * the whole middleware session dance are written once, here, instead of once\n * per app.\n *\n * This entry is deliberately UNSTAMPED (no `\"use client\"`): most of it runs on\n * the server, and stamping it would turn `serverClient`/`middlewareSession`\n * into client references inside an RSC. It also imports no `next/*` module —\n * Next's primitives arrive through structural seams.\n */\nexport {\n AMBIGUOUS_AUTH_COOKIE_HEADER,\n AUTH_COOKIE_RESPONSE_HEADERS,\n createNextSupabase,\n MissingSupabaseIdentityError,\n SPLIT_COOKIE_JAR_HEADER,\n toResponseCookie,\n type CookieToSet,\n type DatabaseLike,\n type MiddlewareResponse,\n type MiddlewareSession,\n type MiddlewareSessionArgs,\n type NextBrowserClient,\n type NextServerClient,\n type NextSupabase,\n type NextSupabaseConfig,\n type RequestCookieJar,\n type ResponseCookie,\n type ResponseCookieJar,\n type ServerCookieStore,\n} from \"./next/supabase\";\n\nexport {\n createAuthCookie,\n DEFAULT_AUTH_COOKIE_NAME,\n type AuthCookie,\n type AuthCookieOptions,\n type AuthCookieValue,\n type CreateAuthCookieArgs,\n} from \"./db/auth-cookie\";\n","/**\n * @ai-matrx/data/next — the Next.js Supabase binding.\n *\n * WHY THIS EXISTS (C22 / THE ALL-INCLUSIVE LAW): a Next.js app has FIVE\n * distinct places that must construct a Supabase client — the browser, a\n * Server Component, a Route Handler, the proxy/middleware, and an OAuth\n * callback — and every one of them must pass the SAME auth-cookie options or\n * it cannot see the session the others wrote. Before this module, matrx-frontend\n * carried that construction five times, each with its own env guard, its own\n * cookie adapter, and its own copy of the \"do not run code between\n * createServerClient and getUser()\" hazard. Every app on the platform would\n * have re-derived all five.\n *\n * The host now injects IDENTITY only — URL, publishable key, apex domain, the\n * cookie names — and the package owns every hard part:\n *\n * - env validation with a named, actionable error (never `process.env.X!`,\n * which fails as a cryptic `undefined.trim()` TypeError);\n * - the domain-wide cookie option construction on every single door;\n * - the SSR/browser split, including the Server-Component write that must be\n * swallowed because only the proxy may set cookies there;\n * - the browser singleton on a `globalThis` slot (module-level mutable state\n * is banned — dual ESM/CJS bundles would otherwise instantiate two clients\n * and split the session);\n * - the middleware session dance in ONE call: legacy-key migration, the\n * getUser() adjacency rule, session persistence under the new key, legacy\n * cookie clearing at the issuing scope, the no-store response headers, and\n * cookie carry-over onto redirects (dropping any one of these logs users\n * out at random, which is exactly how it was found).\n *\n * NO `next/*` IMPORT LIVES HERE. Next's own primitives (`cookies()`,\n * `NextResponse`) arrive through structural seams the host passes in, so this\n * module builds and tests outside Next and cannot drag the framework into a\n * non-Next consumer's graph.\n */\n\nimport {\n clearAuthCookiesAtScopes,\n createBrowserClient,\n createServerClient,\n DEFAULT_COOKIE_OPTIONS,\n} from \"@supabase/ssr\";\nimport {\n createAuthCookie,\n parseCookieHeader,\n type AuthCookie,\n type AuthCookieOptions,\n type AuthCookieValue,\n} from \"../db/auth-cookie\";\n\n/**\n * A `Database` type as generated by Supabase. Deliberately structural: the\n * package never imports a host's generated types, and a host that has not\n * generated any still gets a working (untyped-rows) client.\n */\nexport type DatabaseLike = Record<string, unknown>;\n\nexport type NextBrowserClient<DB extends DatabaseLike> = ReturnType<\n typeof createBrowserClient<DB>\n>;\nexport type NextServerClient<DB extends DatabaseLike> = ReturnType<\n typeof createServerClient<DB>\n>;\n\n/** A cookie to write, as `@supabase/ssr` hands it to `setAll`. */\nexport interface CookieToSet {\n name: string;\n value: string;\n options?: Record<string, unknown> | undefined;\n}\n\n/**\n * The subset of Next's `await cookies()` store this binding uses. Structural\n * on purpose — `ReadonlyRequestCookies` satisfies it by shape.\n */\nexport interface ServerCookieStore {\n getAll(): readonly AuthCookieValue[];\n set(name: string, value: string, options?: Record<string, unknown>): void;\n}\n\n/** The subset of `NextRequest[\"cookies\"]` the middleware binding uses. */\nexport interface RequestCookieJar {\n getAll(): readonly AuthCookieValue[];\n set(name: string, value: string): void;\n}\n\n/**\n * A cookie in Next's `ResponseCookies` shape: name, value, and every\n * attribute FLAT on the same object.\n *\n * 🚨 THIS SHAPE IS LOAD-BEARING, and modelling it as `never` cost the platform\n * a production outage (2026-09-01). `ResponseCookies.set` accepts either\n * `(name, value, attrs)` or ONE flat object — it does NOT accept `@supabase/ssr`'s\n * `{ name, value, options }`, and it does not throw on it either: it reads\n * `name`/`value`, ignores the unknown `options` key, and serializes\n * `sb-…-auth-v2.0=; Path=/`. Every attribute is lost. So:\n * · a token refresh wrote a HOST-ONLY session cookie shadowing the real\n * `.apex` one, and\n * · every chunk REMOVAL (`value: \"\"`, `maxAge: 0`) became a PERSISTENT EMPTY\n * host-only cookie instead of a deletion.\n * Two same-name cookies then reach the server in one `Cookie` header, the\n * empty one sorts first, `combineChunks`' `find()` takes it, and every server\n * read answers anonymous while the tab still shows a signed-in user.\n * `@supabase/ssr` cookies are converted with `toResponseCookie` at the single\n * boundary below; the `never` that hid this is gone.\n */\nexport interface ResponseCookie {\n name: string;\n value: string;\n domain?: string | undefined;\n path?: string | undefined;\n maxAge?: number | undefined;\n expires?: Date | number | undefined;\n httpOnly?: boolean | undefined;\n secure?: boolean | undefined;\n sameSite?: boolean | \"lax\" | \"strict\" | \"none\" | undefined;\n priority?: \"low\" | \"medium\" | \"high\" | undefined;\n partitioned?: boolean | undefined;\n}\n\n/**\n * Exactly the attributes Next's `ResponseCookies` serializes. Anything else on\n * an `@supabase/ssr` cookie is dropped by that class anyway — naming them is\n * what makes the drop visible instead of silent.\n */\nconst RESPONSE_COOKIE_ATTRIBUTES = [\n \"domain\",\n \"path\",\n \"maxAge\",\n \"expires\",\n \"httpOnly\",\n \"secure\",\n \"sameSite\",\n \"priority\",\n \"partitioned\",\n] as const satisfies readonly (keyof ResponseCookie)[];\n\n/**\n * The subset of `NextResponse[\"cookies\"]` the middleware binding uses.\n */\nexport interface ResponseCookieJar {\n getAll(): ResponseCookie[];\n set(cookie: ResponseCookie): void;\n}\n\n/** The subset of a `NextResponse` the middleware binding drives. */\nexport interface MiddlewareResponse {\n cookies: ResponseCookieJar;\n headers: {\n get(name: string): string | null;\n set(name: string, value: string): void;\n /**\n * Required for the split-jar heal. `ResponseCookies` keeps ONE entry per\n * cookie NAME, so the same name cannot be expired at two Domain scopes\n * through it — the second `set` replaces the first. The losing scope is\n * expired with raw appended `Set-Cookie` headers instead.\n */\n append(name: string, value: string): void;\n };\n}\n\nexport interface NextSupabaseConfig {\n /**\n * `process.env.NEXT_PUBLIC_SUPABASE_URL` — read STATICALLY at the call site.\n * Next only inlines `NEXT_PUBLIC_*` into client bundles for static member\n * accesses; a dynamic lookup inside this package would be `undefined` in\n * every browser bundle.\n */\n supabaseUrl: string | undefined;\n /** `process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`, read statically. */\n publishableKey: string | undefined;\n /** The apex domain one login must span, e.g. `\"aimatrx.com\"`. */\n apexDomain: string;\n /** Auth cookie name. Defaults to the package's `sb-matrx-auth`. */\n cookieName?: string;\n /** A superseded storage key to migrate from and then clear. */\n legacyCookieName?: string;\n /**\n * Host wrapper applied to the BROWSER client only — the one legitimate\n * host seam here (diagnostics/error capture). It must not change query\n * behavior; the package never inspects what it returns.\n *\n * `C extends object` because a Supabase client always IS one, and a real\n * capture wrapper is a `Proxy` that must constrain the same way. Leaving\n * `C` unconstrained made every honest host wrapper unassignable (found at\n * matrx-frontend adoption, 0.5.1).\n */\n wrapBrowserClient?: <C extends object>(client: C) => C;\n}\n\n/** Cache-defeating headers every auth-cookie-bearing response must carry. */\nexport const AUTH_COOKIE_RESPONSE_HEADERS: Readonly<Record<string, string>> =\n Object.freeze({\n \"Cache-Control\": \"private, no-cache, no-store, must-revalidate, max-age=0\",\n Expires: \"0\",\n Pragma: \"no-cache\",\n });\n\nconst CARRIED_HEADERS = [\"cache-control\", \"expires\", \"pragma\"] as const;\n\n/**\n * Stamped on any response whose auth cookie jar was found split and healed.\n * A shell that already rendered may be wearing an identity this pass could not\n * see, and must say so rather than paint a signed-in screen of anonymous data.\n */\nexport const SPLIT_COOKIE_JAR_HEADER = \"x-matrx-split-jar\";\n\n/**\n * Stamped when this request's auth cookie jar held one storage key BOTH\n * unchunked and chunked — see `AuthCookie.reconcile`. Distinct from\n * `SPLIT_COOKIE_JAR_HEADER` because the person's experience is different and\n * the screen must say the true thing:\n *\n * split jar → the tab still holds a session; the server just could not\n * read it. \"Sign in again\" restores the page.\n * ambiguous → we deliberately refused BOTH copies rather than risk\n * resolving the wrong person, so this person has just been\n * signed out BY US. That is an automatic intervention on\n * somebody's session and it must not happen quietly: the\n * browser's own read is anonymous too, so nothing else on the\n * page can notice, and without this header the app shows a\n * login screen with no explanation.\n */\nexport const AMBIGUOUS_AUTH_COOKIE_HEADER = \"x-matrx-auth-ambiguous\";\n\n/**\n * `@supabase/ssr`'s `{ name, value, options }` → Next's flat `ResponseCookie`.\n * The ONE conversion boundary; see {@link ResponseCookie} for what skipping it\n * cost.\n */\nexport function toResponseCookie(cookie: CookieToSet): ResponseCookie {\n const options = (cookie.options ?? {}) as Record<string, unknown>;\n // Built key by key, never spread: `options.name` would fight the real name,\n // and an unrecognized key would read as carried when Next drops it.\n const out: Record<string, unknown> = {\n name: cookie.name,\n value: cookie.value,\n };\n for (const attribute of RESPONSE_COOKIE_ATTRIBUTES) {\n const value = options[attribute];\n if (value !== undefined) out[attribute] = value;\n }\n return out as unknown as ResponseCookie;\n}\n\n/**\n * A raw `Set-Cookie` that EXPIRES `name` at the host-only scope — the scope a\n * response cannot reach through `ResponseCookies` once the same name is being\n * written at `.apex`. A browser ignores a `Set-Cookie` for a scope the current\n * host does not own, so this is a no-op on a healthy jar.\n */\nfunction hostOnlyExpiryHeader(name: string): string {\n return `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=Lax`;\n}\n\n/**\n * A `ResponseCookie` as a raw `Set-Cookie` header value, serialized the way\n * Next's `ResponseCookies` serializes it. Needed because a response can only\n * carry ONE entry per cookie NAME through `.cookies`, and `@supabase/ssr`\n * deliberately writes some names TWICE — see `applyCookies`.\n */\nexport function serializeSetCookie(cookie: ResponseCookie): string {\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n parts.push(`Path=${cookie.path ?? \"/\"}`);\n if (cookie.domain !== undefined) parts.push(`Domain=${cookie.domain}`);\n if (cookie.maxAge !== undefined) parts.push(`Max-Age=${cookie.maxAge}`);\n if (cookie.expires !== undefined) {\n const expires =\n cookie.expires instanceof Date\n ? cookie.expires\n : new Date(cookie.expires);\n parts.push(`Expires=${expires.toUTCString()}`);\n }\n if (cookie.httpOnly === true) parts.push(\"HttpOnly\");\n if (cookie.secure === true) parts.push(\"Secure\");\n if (typeof cookie.sameSite === \"string\") {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n return parts.join(\"; \");\n}\n\n/**\n * Thrown when identity is missing. Named and actionable — the alternative\n * (`process.env.X!`) surfaces as `Cannot read properties of undefined\n * (reading 'trim')` from inside Supabase, which taught nobody anything.\n */\nexport class MissingSupabaseIdentityError extends Error {\n readonly variable: string;\n constructor(variable: string) {\n super(\n `@ai-matrx/data/next: missing required Supabase identity \"${variable}\". ` +\n `Pass it as a STATIC member access (process.env.${variable}) to createNextSupabase().`,\n );\n this.name = \"MissingSupabaseIdentityError\";\n this.variable = variable;\n }\n}\n\nfunction requireIdentity(value: string | undefined, variable: string): string {\n const trimmed = value?.trim();\n if (!trimmed) throw new MissingSupabaseIdentityError(variable);\n return trimmed;\n}\n\n/**\n * THE AUTH RESOLVE BUDGET — the proxy is on the critical path of EVERY request\n * and nothing below it may hang forever.\n *\n * Next 16 runs `proxy.ts` in the Node.js runtime and that \"cannot be\n * configured\" (Next 16 upgrade guide), so on Vercel this pass is a Lambda with\n * a 15s task cap, concurrency of one per instance, and a real cold start. A\n * single `<Link>` prefetch storm — a chat sidebar is 20-40 links — spins up\n * that many instances at once, and an unbounded auth round trip in each one\n * turns a downstream blink into `504 MIDDLEWARE_INVOCATION_TIMEOUT` on the\n * page the person actually asked for. Measured on aimatrx.com, 2026-09-20: 19\n * such 504s in 48h, every one of them in a burst of simultaneous prefetches,\n * every one of them \"Task timed out after 15 seconds\".\n *\n * So the deadline is OURS, not the platform's: a budget short enough that a\n * stalled auth server costs one request its identity instead of costing the\n * whole page its response.\n */\nconst AUTH_RESOLVE_TIMEOUT_MS = 2_500;\n\n/**\n * The identity calls this budget covers, and ONLY these. Sign-in, sign-up,\n * OAuth code exchange and password reset also live under `/auth/v1/` and a\n * person is waiting on them; they keep their natural timeout.\n */\nconst IDENTITY_PATHS = [\n \"/auth/v1/user\",\n \"/auth/v1/token?grant_type=refresh_token\",\n \"/auth/v1/.well-known/jwks.json\",\n];\n\n/** Non-retryable by construction: not a 5xx, so auth-js stops retrying. */\nconst AUTH_BUDGET_EXHAUSTED_STATUS = 408;\nconst AUTH_BUDGET_EXHAUSTED_CODE = \"auth_budget_exhausted\";\n\n/**\n * ONE budget for the WHOLE identity resolve of a request, not one per socket.\n *\n * The 0.17.0 budget aborted each `/auth/v1/` socket at 2.5s. That was not\n * enough, and 2026-09-21 proved it: auth-js retries a token refresh with\n * exponential backoff for up to 30s (`AUTO_REFRESH_TICK_DURATION_MS`), and a\n * per-socket abort is exactly the retryable error that keeps that loop going —\n * five aborted attempts plus their pauses is ~15.5s, past Vercel's 15s task\n * cap, and the proxy answered `504` while believing it had a budget.\n *\n * This wrapper holds a deadline from the FIRST identity call. While the\n * deadline is open every identity call gets what is LEFT of it, aborting the\n * socket (a `Promise.race` would leave it hanging and holding the Lambda).\n * The moment an attempt aborts, or the deadline passes, the budget is\n * EXHAUSTED and every further identity call answers synchronously with a\n * synthetic `408 auth_budget_exhausted` — a non-retryable auth error — so\n * auth-js stops its loop at once instead of sleeping through it. The whole\n * resolve therefore costs at most the budget plus one 200ms backoff.\n *\n * `exhausted` is also the cookie gate: an exhausted budget must never write,\n * expire or heal a cookie, because auth-js clears the session on a\n * non-retryable refresh failure once the access token has truly expired, and\n * that write is a logout caused by a slow network.\n */\nexport interface AuthBudget {\n fetch: typeof fetch;\n readonly exhausted: boolean;\n}\n\n/**\n * 🚨 A REFRESH THAT WAS SENT IS NEVER ABANDONED AT THE BUDGET (2026-10-07).\n *\n * `grant_type=refresh_token` ROTATES the refresh token on the auth server the\n * moment it is processed. Aborting it at 2.5s did not stop the rotation — it only\n * threw away the new tokens, so no cookie was written and the browser kept a\n * refresh token the server had already spent. Past GoTrue's reuse window that\n * token is refused and the person is signed out (and a server-rendered read made\n * on the dropped session went out as `anon`: their own chats answered \"sign in\").\n * So a refresh, once on the wire, runs to completion and its answer is used,\n * bounded only by this ceiling — below Vercel's 15s proxy cap. A refresh that has\n * NOT been sent yet still obeys the budget (nothing rotated, nothing lost).\n */\nexport const AUTH_REFRESH_CEILING_MS = 8_000;\n\nconst REFRESH_GRANT_PATH = \"/auth/v1/token?grant_type=refresh_token\";\n\nexport function createAuthBudget(\n timeoutMs: number,\n options: { refreshCeilingMs?: number } = {},\n): AuthBudget {\n const refreshCeilingMs = Math.max(timeoutMs, options.refreshCeilingMs ?? AUTH_REFRESH_CEILING_MS);\n let deadline: number | null = null;\n let exhausted = false;\n\n const isIdentityCall = (url: string): boolean =>\n IDENTITY_PATHS.some((path) => url.includes(path));\n\n const exhaustedResponse = (): Response =>\n new Response(\n JSON.stringify({\n error_code: AUTH_BUDGET_EXHAUSTED_CODE,\n msg:\n `identity resolve budget of ${timeoutMs}ms is exhausted for this request; ` +\n `not retrying`,\n }),\n {\n status: AUTH_BUDGET_EXHAUSTED_STATUS,\n headers: { \"content-type\": \"application/json\" },\n },\n );\n\n const budgetedFetch: typeof fetch = async (input, init) => {\n const url =\n typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n if (!isIdentityCall(url) || init?.signal) {\n return fetch(input as RequestInfo, init);\n }\n const now = Date.now();\n if (deadline === null) deadline = now + timeoutMs;\n const remaining = deadline - now;\n if (exhausted || remaining <= 0) {\n exhausted = true;\n return exhaustedResponse();\n }\n // Once sent, a refresh is allowed the ceiling, not what is left of the budget.\n const allowed = url.includes(REFRESH_GRANT_PATH) ? refreshCeilingMs : remaining;\n try {\n return await fetch(input as RequestInfo, {\n ...init,\n signal: AbortSignal.timeout(allowed),\n });\n } catch (cause) {\n const name = String((cause as { name?: unknown })?.name ?? \"\");\n if (name === \"TimeoutError\" || name === \"AbortError\") exhausted = true;\n throw cause;\n }\n };\n\n return {\n fetch: budgetedFetch,\n get exhausted() {\n return exhausted;\n },\n };\n}\n\n/**\n * `true` for the errors that mean \"we could not REACH the auth authority\",\n * never for \"this person is not signed in\".\n *\n * The distinction is the whole point: a transport failure must not be read as\n * a logout, or a blink on the network signs people out (and, on a protected\n * route, bounces them to /login mid-session).\n *\n * Matched by NAME rather than by importing `isAuthRetryableFetchError`, so\n * this package keeps its single `@supabase/ssr` dependency and does not take\n * on `@supabase/auth-js` and its ESM/CJS dual-graph hazard.\n */\nfunction isAuthTransportFailure(error: unknown): boolean {\n if (!error || typeof error !== \"object\") return false;\n const name = String((error as { name?: unknown }).name ?? \"\");\n if (\n name === \"AuthRetryableFetchError\" ||\n name === \"TimeoutError\" ||\n name === \"AbortError\"\n ) {\n return true;\n }\n const status = (error as { status?: unknown }).status;\n if (status === 0 || status === 504 || status === 503) return true;\n // The synthetic answer this package hands auth-js once the budget is spent.\n const code = (error as { code?: unknown }).code;\n return status === AUTH_BUDGET_EXHAUSTED_STATUS || code === AUTH_BUDGET_EXHAUSTED_CODE;\n}\n\n/** What one identity resolve settled, and whether it could settle at all. */\ninterface ResolvedIdentity {\n user: ({ id: string } & Record<string, unknown>) | null;\n authUnavailable: boolean;\n}\n\n/**\n * THE ONE IDENTITY RESOLVE. `getClaims()`, never `getUser()`.\n *\n * `getUser()` sends a request to the Auth server for EVERY JWT — an auth-server\n * lookup on the critical path of every matched request. `getClaims()` verifies\n * the access token against the project's JWKS with WebCrypto, locally and\n * usually with no network call at all, and auth-js caches the key set in a\n * module-level `GLOBAL_JWKS` shared by every client in the process — so a warm\n * instance re-verifies with zero I/O and only a genuine token refresh or a\n * JWKS TTL expiry goes out to the network.\n *\n * This requires an ASYMMETRIC signing key on the project (ECC/RSA). With a\n * symmetric secret `getClaims()` silently degrades to a `getUser()`-shaped\n * request per call, which is correct but gives back none of the saving — so a\n * project that turns asymmetric keys off pays the old cost and nothing breaks.\n *\n * Verification is cryptographic, so the claims are as trusted as `getUser()`'s\n * answer; this is not the untrusted `getSession()` read.\n */\nasync function resolveIdentity(\n client: {\n auth: {\n getClaims: () => Promise<{\n data: { claims?: Record<string, unknown> } | null;\n error: unknown;\n }>;\n };\n },\n budget?: AuthBudget,\n): Promise<ResolvedIdentity> {\n const unreachable = (cause: unknown): ResolvedIdentity => {\n console.warn(\n `[@ai-matrx/data/next] AUTH UNREACHABLE within ${AUTH_RESOLVE_TIMEOUT_MS}ms — ` +\n `this request is being passed through WITHOUT an identity and WITHOUT ` +\n `touching a single auth cookie. Nobody is being signed out: the shell ` +\n `resolves auth again on the client, and the next request re-resolves it ` +\n `here. If this is not rare, the auth authority (or the network to it) is ` +\n `the thing to fix — raising this budget only trades it back for a 504. ` +\n `Cause: ${String(cause)}`,\n );\n return { user: null, authUnavailable: true };\n };\n\n try {\n const { data, error } = await client.auth.getClaims();\n if (error) {\n return isAuthTransportFailure(error) || budget?.exhausted\n ? unreachable(error)\n : { user: null, authUnavailable: false };\n }\n const claims = data?.claims;\n if (!claims || typeof claims.sub !== \"string\") {\n // No claims AND a spent budget is \"we could not tell\", not \"nobody\".\n return budget?.exhausted\n ? unreachable(\"identity budget exhausted before a token could be verified\")\n : { user: null, authUnavailable: false };\n }\n // The JWT carries the identity fields a proxy decides with. Kept as a\n // `{ id }`-shaped object so every existing `session.user` read — all of\n // which are truthiness or `.id` — is unchanged by the switch.\n return {\n user: { ...claims, id: claims.sub },\n authUnavailable: false,\n };\n } catch (cause) {\n return unreachable(cause);\n }\n}\n\nexport interface MiddlewareSessionArgs<Response> {\n /** `Host` header of the incoming request. */\n host: string | null | undefined;\n /** `request.cookies` */\n requestCookies: RequestCookieJar;\n /**\n * The raw `Cookie` header (`request.headers.get(\"cookie\")`). REQUIRED to\n * detect and heal a split jar — `request.cookies` has already collapsed the\n * duplicates. Without it this pass cannot see the shadow and cannot expire\n * it, which is exactly how the \"healed\" 0.7.x build still left poisoned\n * browsers poisoned.\n */\n cookieHeader?: string | null | undefined;\n /** `() => NextResponse.next({ request })` — a FRESH response each call. */\n createResponse: () => Response;\n /** `(url) => NextResponse.redirect(url)` */\n createRedirect: (url: URL) => Response;\n}\n\nexport interface MiddlewareSession<DB extends DatabaseLike, Response> {\n /**\n * The validated user, or `null`. Resolved via `getClaims()` — a local\n * cryptographic verification of the access token, not a `getSession()` read.\n *\n * `null` is ambiguous on its own: check `authUnavailable` before treating it\n * as \"signed out\". A transport failure produces `null` too, and acting on\n * that as a logout is how a network blink bounces a signed-in person.\n */\n user: ({ id: string } & Record<string, unknown>) | null;\n /** The client, in case the caller needs a further authenticated read. */\n client: NextServerClient<DB>;\n /** The response carrying every session cookie written during this pass. */\n response: Response;\n /**\n * A redirect that CARRIES the session cookies and cache headers. Building a\n * bare `NextResponse.redirect` here drops refreshed tokens on the floor and\n * logs the user out on the next request.\n */\n redirect(url: URL): Response;\n /**\n * `true` when this request arrived with the same auth cookie at two Domain\n * scopes. The response heals the jar, but the SHELL that already rendered\n * may be holding an identity this pass could not see — a host that renders\n * a signed-in shell must say so in words rather than paint a signed-in\n * screen full of anonymous data.\n */\n splitCookieJar: boolean;\n /**\n * `true` when a storage key arrived BOTH unchunked and chunked and this pass\n * therefore refused both copies rather than risk resolving the wrong person.\n * The person has been signed out by us and must be told so.\n */\n ambiguousAuthCookies: boolean;\n /**\n * `true` when the auth authority could not be REACHED inside the resolve\n * budget, so this pass has no opinion about who this is.\n *\n * The contract when it is set: `user` is `null` but the person is NOT signed\n * out, not one auth cookie was written, expired or healed, and the host must\n * neither bounce to login nor paint a signed-out screen as if it were\n * settled. Let the request through; the client resolves auth itself and the\n * next request re-resolves it here.\n */\n authUnavailable: boolean;\n}\n\nexport interface NextSupabase<DB extends DatabaseLike> {\n /** The auth-cookie primitive this binding is built on. */\n readonly authCookie: AuthCookie;\n readonly cookieName: string;\n optionsForHost(host: string | null | undefined): AuthCookieOptions;\n browserOptions(): AuthCookieOptions;\n\n /**\n * The browser client. Memoized on a `globalThis` slot keyed by identity, so\n * the ESM and CJS graphs of this package share ONE client and therefore one\n * auth state — a per-module singleton would silently split them.\n */\n browserClient(): NextBrowserClient<DB>;\n\n /**\n * A Server Component / Server Action client over `await cookies()`.\n * Cookie writes from a Server Component are swallowed by design: only the\n * proxy may set cookies there, and it refreshes the session anyway.\n */\n serverClient(args: {\n cookieStore: ServerCookieStore;\n host: string | null | undefined;\n /**\n * The raw `Cookie` request header (`(await headers()).get(\"cookie\")`).\n * REQUIRED to see a split jar: `cookies()` collapses two same-name cookies\n * to one before this code runs. Omitting it degrades this door to the\n * collapsed view, where the surviving copy is whichever the browser sent\n * last — a coin flip between the session and anonymous.\n */\n cookieHeader?: string | null | undefined;\n /**\n * See `routeClient`'s `appendSetCookie` — Next's `cookies()` store keys by\n * NAME exactly like `ResponseCookies`, so a two-scope removal collapses\n * here too. A Route Handler that signs somebody in MUST pass this (buffer\n * the headers and append them onto the response it returns, last).\n * A Server Component has no response to append to and its writes are\n * swallowed anyway, so it correctly passes nothing.\n */\n appendSetCookie?: (header: string) => void;\n }): NextServerClient<DB>;\n\n /**\n * A Route Handler client: reads the request's cookies, writes onto the\n * response you are about to return.\n */\n routeClient(args: {\n requestCookies: Pick<RequestCookieJar, \"getAll\">;\n setCookie: (cookie: CookieToSet) => void;\n host: string | null | undefined;\n /** See `serverClient` — same collapse, same requirement. */\n cookieHeader?: string | null | undefined;\n /**\n * `(header) => response.headers.append(\"Set-Cookie\", header)`.\n *\n * 🚨 REQUIRED ON ANY DOOR THAT SIGNS SOMEBODY IN. `@supabase/ssr` removes a\n * cookie at BOTH Domain scopes by emitting the SAME name twice, and every\n * jar a host can pass to `setCookie` — `NextResponse.cookies`, Next's\n * `cookies()` store — keeps ONE entry per NAME, so the second write\n * REPLACES the first and the `.apex` removal is silently lost. That is how\n * a stale UNCHUNKED auth cookie survives beside a fresh CHUNKED pair, and\n * `combineChunks` then serves the STALE one — the previous person's\n * session, to the person who just signed in (2026-09-08, R-O3).\n *\n * With this seam the duplicate goes out as a raw header instead. Without\n * it the drop is announced loudly rather than swallowed.\n *\n * THE CALLER'S OBLIGATION: `ResponseCookies.set` re-serializes the whole\n * `Set-Cookie` list from its own map and DISCARDS raw appends made since.\n * Nothing may touch `response.cookies` after a header appended here.\n */\n appendSetCookie?: (header: string) => void;\n }): NextServerClient<DB>;\n\n /**\n * THE middleware/proxy auth pass, entire. Runs legacy-key migration, builds\n * the client, resolves `getUser()` with nothing in between, persists a\n * migrated session under the current key, clears the superseded key at the\n * scope this host issued it on, and stamps no-store headers on any response\n * that carries cookies.\n */\n middlewareSession<Response extends MiddlewareResponse>(\n args: MiddlewareSessionArgs<Response>,\n ): Promise<MiddlewareSession<DB, Response>>;\n}\n\ninterface BrowserSlot {\n key: string;\n client: unknown;\n}\n\n/**\n * The browser-client singleton lives on `globalThis`, not in a module\n * variable: with dual ESM/CJS output each loader graph instantiates its own\n * copy of this module, and two Supabase browser clients means two auth\n * listeners racing on one cookie.\n */\nconst BROWSER_SLOT = Symbol.for(\"ai-matrx.data.next.browser-client\");\n\nfunction readBrowserSlot(): BrowserSlot | undefined {\n return (globalThis as Record<symbol, unknown>)[BROWSER_SLOT] as\n | BrowserSlot\n | undefined;\n}\n\nfunction writeBrowserSlot(slot: BrowserSlot): void {\n (globalThis as Record<symbol, unknown>)[BROWSER_SLOT] = slot;\n}\n\nexport function createNextSupabase<DB extends DatabaseLike = DatabaseLike>(\n config: NextSupabaseConfig,\n): NextSupabase<DB> {\n const url = requireIdentity(config.supabaseUrl, \"NEXT_PUBLIC_SUPABASE_URL\");\n const key = requireIdentity(\n config.publishableKey,\n \"NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY\",\n );\n const authCookie = createAuthCookie({\n apexDomain: config.apexDomain,\n ...(config.cookieName !== undefined\n ? { cookieName: config.cookieName }\n : {}),\n ...(config.legacyCookieName !== undefined\n ? { legacyCookieName: config.legacyCookieName }\n : {}),\n });\n const wrap = config.wrapBrowserClient;\n const slotKey = `${url}|${key}|${authCookie.cookieName}`;\n\n /**\n * `document.cookie` carries the SAME duplicates the server sees — and\n * `@supabase/ssr`'s default browser adapter resolves each chunk with\n * `find()`, so an empty shadow sorting first makes the BROWSER read\n * anonymous too. That is why the split-session banner never mounted in\n * production: the server said \"nobody\", and the client agreed, so nothing\n * looked inconsistent even though a real session was sitting right there.\n *\n * Reads are reconciled here (non-empty wins). Writes are NOT redirected:\n * they keep going through `document.cookie` with the canonical options, and\n * the shadow is expired by the proxy, which is the only side that can name a\n * cookie's scope.\n */\n const browserCookieAdapter = () => ({\n getAll: () =>\n authCookie.reconcile(\n typeof document === \"undefined\"\n ? []\n : parseCookieHeader(document.cookie),\n ).cookies,\n setAll: (cookiesToSet: readonly CookieToSet[]) => {\n if (typeof document === \"undefined\") return;\n for (const cookie of cookiesToSet) {\n const options = (cookie.options ?? {}) as Record<string, unknown>;\n const parts = [\n `${cookie.name}=${encodeURIComponent(cookie.value)}`,\n `Path=${typeof options.path === \"string\" ? options.path : \"/\"}`,\n ];\n if (typeof options.domain === \"string\") {\n parts.push(`Domain=${options.domain}`);\n }\n if (typeof options.maxAge === \"number\") {\n parts.push(`Max-Age=${options.maxAge}`);\n }\n if (typeof options.sameSite === \"string\") {\n parts.push(`SameSite=${options.sameSite}`);\n }\n if (options.secure === true || location.protocol === \"https:\") parts.push(\"Secure\");\n document.cookie = parts.join(\"; \");\n }\n },\n });\n\n const browserClient = (): NextBrowserClient<DB> => {\n const existing = readBrowserSlot();\n if (existing && existing.key === slotKey) {\n return existing.client as NextBrowserClient<DB>;\n }\n const raw = createBrowserClient<DB>(url, key, {\n cookieOptions: authCookie.browserOptions(),\n cookies: browserCookieAdapter(),\n });\n const client = wrap ? wrap(raw) : raw;\n writeBrowserSlot({ key: slotKey, client });\n return client;\n };\n\n /**\n * THE ONE READ SEAM. Every door reads the cookie jar through this — a\n * duplicated auth cookie name whose empty twin sorts first would otherwise\n * reassemble to \"\" and answer anonymous on that door alone (which is how\n * `manage` refused a valid Create while `www` worked).\n */\n const reconciledGetAll = (\n getAllRaw: () => readonly AuthCookieValue[],\n cookieHeader: string | null | undefined,\n ): (() => AuthCookieValue[]) => {\n // The raw header when we have it (duplicates intact), the collapsed jar\n // only as a fallback — see parseCookieHeader for why that matters.\n const parsed = parseCookieHeader(cookieHeader);\n if (parsed.length > 0) {\n const reconciled = authCookie.reconcile(parsed).cookies;\n return () => reconciled;\n }\n return () => authCookie.reconcile(getAllRaw()).cookies;\n };\n\n const serverClient: NextSupabase<DB>[\"serverClient\"] = ({\n cookieStore,\n host,\n cookieHeader,\n appendSetCookie,\n }) => {\n const written = new Set<string>();\n // A layout or page is a Lambda with the same task cap as the proxy, and a\n // `getClaims()` here can still trigger a token refresh. Same budget.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n return createServerClient<DB>(url, key, {\n cookieOptions: authCookie.optionsForHost(host),\n global: { fetch: budget.fetch },\n cookies: {\n getAll: reconciledGetAll(() => cookieStore.getAll(), cookieHeader),\n setAll: (cookiesToSet) => {\n try {\n // Two passes, never interleaved — see `routeClient`.\n const deferred: string[] = [];\n for (const cookie of cookiesToSet) {\n const { name, value, options } = cookie;\n if (!written.has(name)) {\n written.add(name);\n cookieStore.set(name, value, options as Record<string, unknown>);\n continue;\n }\n const header = serializeSetCookie(\n toResponseCookie(cookie as CookieToSet),\n );\n if (appendSetCookie) {\n deferred.push(header);\n continue;\n }\n console.warn(\n `[@ai-matrx/data/next] DROPPED a second Set-Cookie for \"${name}\" on host ` +\n `\"${host ?? \"?\"}\": Next's cookie store keeps one entry per name, so this ` +\n `write would have DELETED the first one. Cookie value omitted. ` +\n `A Route Handler here must pass appendSetCookie — an unremoved auth cookie ` +\n `at the other scope makes the next request resolve the PREVIOUS person.`,\n );\n }\n for (const header of deferred) appendSetCookie?.(header);\n } catch {\n // Called from a Server Component, where Next forbids cookie\n // writes. The proxy refreshes the session on every request, so\n // dropping this write is correct — not a swallowed failure.\n }\n },\n },\n });\n };\n\n const routeClient: NextSupabase<DB>[\"routeClient\"] = ({\n requestCookies,\n setCookie,\n host,\n cookieHeader,\n appendSetCookie,\n }) => {\n // See `appendSetCookie`: a name written twice cannot go through any\n // one-entry-per-NAME jar, and the second write is what deletes the first.\n const written = new Set<string>();\n // Same budget as the proxy: an API route is one Lambda, one identity resolve.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n return createServerClient<DB>(url, key, {\n cookieOptions: authCookie.optionsForHost(host),\n global: { fetch: budget.fetch },\n cookies: {\n getAll: reconciledGetAll(() => requestCookies.getAll(), cookieHeader),\n setAll: (cookiesToSet) => {\n if (budget.exhausted && cookiesToSet.length > 0) {\n // See middlewareSession.applyCookies — a spent budget writes nothing.\n console.warn(\n `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) in a ` +\n `route on host \"${host ?? \"?\"}\": identity budget exhausted; nobody is signed out.`,\n );\n return;\n }\n // 🚨 TWO PASSES, NEVER INTERLEAVED — the same rule the split-jar heal\n // learned the hard way. `ResponseCookies.set` re-serializes the whole\n // `Set-Cookie` list from its own map, DISCARDING every raw append made\n // since. Appending a duplicate in the middle of the batch and then\n // writing one more cookie eats it, which is a silently lost removal\n // all over again. Every `setCookie` first; every append after.\n const deferred: string[] = [];\n for (const cookie of cookiesToSet) {\n const { name } = cookie;\n if (!written.has(name)) {\n written.add(name);\n setCookie(cookie as CookieToSet);\n continue;\n }\n const header = serializeSetCookie(\n toResponseCookie(cookie as CookieToSet),\n );\n if (appendSetCookie) {\n deferred.push(header);\n continue;\n }\n // LOUD: a cookie write is being lost, and the loss is an identity\n // hazard, not a cosmetic one.\n console.warn(\n `[@ai-matrx/data/next] DROPPED a second Set-Cookie for \"${name}\" on host ` +\n `\"${host ?? \"?\"}\": this door passed no appendSetCookie, and the response jar ` +\n `keeps one entry per name, so this write would have DELETED the first one. ` +\n `Cookie value omitted. Pass appendSetCookie from this route — an ` +\n `unremoved auth cookie at the other scope makes the next request resolve the ` +\n `PREVIOUS person.`,\n );\n }\n for (const header of deferred) appendSetCookie?.(header);\n },\n },\n });\n };\n\n async function middlewareSession<Response extends MiddlewareResponse>(\n args: MiddlewareSessionArgs<Response>,\n ): Promise<MiddlewareSession<DB, Response>> {\n const { host, requestCookies, createResponse, createRedirect } = args;\n const hostCookieOptions = authCookie.optionsForHost(host);\n\n let response = createResponse();\n\n /**\n * THE BASELINE VIEW, taken from the RAW `Cookie` header so duplicates are\n * still there to be seen. `requestCookies.getAll()` has already collapsed\n * them (Next keys its jar by name), which is why reconciling over the jar\n * detected nothing in production.\n */\n const rawCookies = parseCookieHeader(args.cookieHeader);\n const baseline = authCookie.reconcile(\n rawCookies.length > 0 ? rawCookies : requestCookies.getAll(),\n );\n\n /**\n * Auth cookie names that arrived at TWO scopes. Only meaningful where this\n * host issues a Domain-scoped cookie; on localhost/preview the host-only\n * cookie IS the real one and must never be expired.\n */\n const shadowed = hostCookieOptions.domain\n ? new Set(baseline.shadowedNames)\n : new Set<string>();\n\n /**\n * An UNCHUNKED value and a CHUNKED pair for one storage key. Unlike a\n * two-scope shadow there is no copy here that could be the good one — the\n * reconciled view has already dropped the whole family — so it is expired\n * on EVERY host, host-only deployments included, and it is expired even\n * when `getUser()` resolved nobody: this is not a \"the auth server blinked\"\n * null, it is a jar the writer cannot produce.\n */\n for (const name of baseline.ambiguousNames) shadowed.add(name);\n if (baseline.ambiguousKeys.length > 0) {\n console.warn(\n `[@ai-matrx/data/next] AMBIGUOUS auth cookie jar on host \"${host ?? \"?\"}\": ` +\n `${baseline.ambiguousKeys.join(\", \")} arrived UNCHUNKED and CHUNKED at once. ` +\n `Serving either would risk resolving a DIFFERENT person's session, so this ` +\n `request is treated as signed out and every cookie in those families is being ` +\n `expired at both scopes. The person signs in once and the jar is clean.`,\n );\n }\n\n /** Writes made during this pass, layered over the baseline. */\n const overlay = new Map<string, string>();\n const readCookies = (): AuthCookieValue[] => {\n const merged = baseline.cookies.map((cookie) =>\n overlay.has(cookie.name)\n ? { name: cookie.name, value: overlay.get(cookie.name) as string }\n : cookie,\n );\n const seen = new Set(merged.map(({ name }) => name));\n for (const [name, value] of overlay) {\n if (!seen.has(name)) merged.push({ name, value });\n }\n return merged;\n };\n const noteWrite = (name: string, value: string): void => {\n overlay.set(name, value);\n requestCookies.set(name, value);\n };\n\n // Seed the current storage key from the superseded one BEFORE the client\n // reads cookies. Absent-only, so a live session is never overwritten.\n const migrated = authCookie.migrateLegacyCookies(readCookies());\n for (const { name, value } of migrated) noteWrite(name, value);\n\n /**\n * Names already written on this response through `.cookies`, and the\n * writes that could not go that way.\n *\n * 🚨 THE SECOND ONE-ENTRY-PER-NAME TRAP, and it is how the stale unchunked\n * auth cookie is MADE. `@supabase/ssr`'s `applyServerStorage` removes a\n * cookie at BOTH Domain scopes by emitting the SAME name twice —\n * `Domain=.apex; Max-Age=0` and then host-only `Max-Age=0`. Funnel both\n * through `ResponseCookies.set` and the second REPLACES the first, so the\n * `.apex` copy is never expired. Measured on the real class:\n *\n * set(\"sb-…-v2\", \"\", {domain: \".aimatrx.com\", maxAge: 0})\n * set(\"sb-…-v2\", \"\", {maxAge: 0})\n * set(\"sb-…-v2.0\", \"<new session>\", {domain: \".aimatrx.com\"})\n * -> [\"sb-…-v2=; Path=/; Max-Age=0; …\", host-only ONLY\n * \"sb-…-v2.0=<new session>; …; Domain=.aimatrx.com\"]\n *\n * So writing a CHUNKED session over an unchunked one leaves the old\n * unchunked cookie alive at `.apex` forever — and `combineChunks` returns\n * the unchunked value the moment it is non-empty, without ever reading the\n * chunks. Sign in as someone else and the app keeps resolving the previous\n * person. That is an identity leak, produced by a lost `Set-Cookie`.\n *\n * The heal already knew this class for its own two-scope expiry and used\n * raw appends. The ORDINARY write path did not, which is why the residue\n * kept being created. Every duplicate name now goes out as a raw appended\n * header too, flushed in the one final append pass below.\n */\n const writtenNames = new Set<string>();\n const duplicateWrites: string[] = [];\n\n // ONE budget for every identity call this pass makes — see createAuthBudget.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n\n const applyCookies = (\n cookiesToSet: readonly CookieToSet[],\n headers: Record<string, string> = {},\n ): void => {\n // 🚨 A SPENT BUDGET WRITES NOTHING. auth-js clears the session when a\n // refresh fails non-retryably on an already-expired access token, and\n // our synthetic 408 IS that non-retryable failure. Letting that write\n // through would sign the person out because the network was slow.\n if (budget.exhausted && cookiesToSet.length > 0) {\n console.warn(\n `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) on host ` +\n `\"${host ?? \"?\"}\": the identity budget was exhausted on this request, so nothing ` +\n `learned here is trusted enough to change the jar. Nobody is signed out; the next ` +\n `request re-resolves. Names: ${cookiesToSet.map((c) => c.name).join(\", \")}`,\n );\n return;\n }\n for (const { name, value } of cookiesToSet) noteWrite(name, value);\n const next = createResponse();\n for (const cookie of response.cookies.getAll()) {\n next.cookies.set(cookie);\n }\n for (const cookie of cookiesToSet) {\n const responseCookie = toResponseCookie(cookie);\n if (writtenNames.has(responseCookie.name)) {\n // Raw, so it cannot displace the entry already on the response.\n // Appends are flushed LAST (see the two-pass rule in applyHeal), so a\n // genuine same-scope re-write still lands after the earlier value.\n duplicateWrites.push(serializeSetCookie(responseCookie));\n continue;\n }\n writtenNames.add(responseCookie.name);\n next.cookies.set(responseCookie);\n }\n const responseHeaders =\n cookiesToSet.length > 0\n ? { ...AUTH_COOKIE_RESPONSE_HEADERS, ...headers }\n : headers;\n for (const [name, value] of Object.entries(responseHeaders)) {\n next.headers.set(name, value);\n }\n response = next;\n };\n\n const client = createServerClient<DB>(url, key, {\n cookieOptions: hostCookieOptions,\n // Identity calls out of THIS pass share one deadline; see createAuthBudget.\n global: { fetch: budget.fetch },\n cookies: {\n getAll: () => readCookies(),\n setAll: (cookiesToSet, headers) => {\n applyCookies(\n cookiesToSet as readonly CookieToSet[],\n (headers ?? {}) as Record<string, string>,\n );\n },\n },\n });\n\n // NOTHING may run between constructing the client and the identity\n // resolve. Any await in between has produced random logouts that are close\n // to undebuggable. The rule is structural here instead of a comment a host\n // has to remember — `resolveIdentity` is one adjacent await.\n const { user, authUnavailable } = await resolveIdentity(client, budget);\n\n // A valid session that arrived under the superseded key is persisted ONCE\n // under the current key. A token the old authority issued fails getUser(),\n // is never persisted, and gets cleared below.\n if (user && migrated.length > 0) {\n const { name: _name, ...cookieOptions } = hostCookieOptions;\n applyCookies(\n readCookies()\n .filter(\n ({ name, value }) =>\n authCookie.isCurrentCookie(name) && value.length > 0,\n )\n .map(({ name, value }) => ({\n name,\n value,\n options: { ...DEFAULT_COOKIE_OPTIONS, ...cookieOptions },\n })),\n );\n }\n\n // The superseded storage key must die at BOTH scopes, and the clear below\n // can only reach one of them (one entry per NAME on a response). Its\n // host-only twin goes out as a raw header with the shadow heal.\n if (authCookie.legacyCookieName && hostCookieOptions.domain) {\n const legacy = authCookie.legacyCookieName;\n for (const { name } of rawCookies) {\n if (name === legacy || name.startsWith(`${legacy}.`)) shadowed.add(name);\n }\n }\n\n // 🚨 NEVER EXPIRE THE SUPERSEDED KEY ON A RESOLVE WE COULD NOT MAKE.\n // The migration above persists legacy -> current ONLY when a user resolved.\n // So on an unreachable auth server the copy under the new name was never\n // written, and expiring the legacy one here would destroy the only cookie\n // holding the session — a logout caused by a network blink, which is the\n // exact class this whole pass exists to prevent.\n if (authCookie.legacyCookieName && !authUnavailable) {\n await clearAuthCookiesAtScopes({\n getAll: () => readCookies(),\n setAll: (cookiesToSet, headers) => {\n applyCookies(\n cookiesToSet as readonly CookieToSet[],\n (headers ?? {}) as Record<string, string>,\n );\n },\n storageKey: authCookie.legacyCookieName,\n scopes: hostCookieOptions.domain\n ? [{ domain: hostCookieOptions.domain }]\n : [{}],\n });\n }\n\n /**\n * THE HEAL — CONVERGE ON THE CANONICAL SCOPE, never merely delete.\n *\n * A shadowed name means the same cookie exists host-only AND at `.apex`,\n * and NOTHING in a `Cookie` header says which scope holds which value. So\n * blindly expiring host-only can destroy the live session — if the good\n * token happened to be the host-only copy, that is a logout we caused.\n *\n * Instead: REWRITE the reconciled (non-empty) value at the canonical\n * `.apex` scope through `cookies.set`, and expire the host-only scope with\n * a raw appended `Set-Cookie`. Whichever scope held the good value, the\n * good value ends up at the canonical scope and the shadow dies. Idempotent\n * and safe to repeat. When every copy is empty there is no session to\n * preserve, so both scopes are simply expired.\n *\n * Raw headers because `ResponseCookies` keeps one entry per NAME — the\n * canonical write and the other scope's expiry cannot both go through it.\n * Applied ONCE, last, on whatever response actually leaves; nothing may\n * read `.cookies` afterwards or these get re-parsed into the one-per-name\n * map and the heal is undone.\n */\n const healed = new WeakSet<object>();\n const applyHeal = (target: Response): Response => {\n if (healed.has(target)) return target;\n // An unresolved identity is not evidence about the jar. Converging or\n // expiring a scope here would be acting on an answer we never got.\n if (authUnavailable) return target;\n if (shadowed.size === 0 && duplicateWrites.length === 0) return target;\n healed.add(target);\n const current = new Map(readCookies().map((c) => [c.name, c.value]));\n\n // 🚨 TWO PASSES, NEVER INTERLEAVED. `ResponseCookies.set` re-parses the\n // whole `Set-Cookie` header list into its one-entry-per-NAME map and\n // rewrites it — so a raw append made BEFORE a later `.set` is eaten.\n // Interleaving them shipped to production and silently dropped every\n // host-only expiry: the canonical re-issue went out, the shadow lived on,\n // and the heal did nothing. Proven with `curl` against the deployed build.\n // Every `.cookies` write happens here, and nothing may touch `.cookies`\n // after the append pass below.\n const expiries: string[] = [];\n for (const name of shadowed) {\n const value = current.get(name) ?? \"\";\n if (value.length > 0 && !user) {\n // 🚨 NEVER FREEZE AN UNVALIDATED VALUE. The surviving copy is only\n // known-good because `getUser()` above resolved a session FROM the\n // reconciled view on THIS request. Without that, a structurally clean\n // but STALE token — a heal racing a token refresh — would be pinned\n // at the canonical scope, and both hosts would then reject a\n // clean-looking jar until re-login (found on production by the walk's\n // rapid poison/reload cycles). A null user can equally mean the auth\n // server was briefly unreachable, so expiring here would log people\n // out over a blip. Touch nothing; `splitCookieJar` still reports, and\n // the banner says it in words.\n continue;\n }\n if (value.length > 0) {\n // The survivor, re-issued at the ONE canonical scope.\n target.cookies.set(\n toResponseCookie({\n name,\n value,\n options: {\n ...DEFAULT_COOKIE_OPTIONS,\n domain: hostCookieOptions.domain,\n path: \"/\",\n // A Domain-scoped auth cookie only ever exists on https here,\n // and `Secure` is not part of cookie identity, so re-issuing\n // with it set can only tighten what is already there.\n secure: true,\n },\n }),\n );\n } else if (hostCookieOptions.domain) {\n expiries.push(\n `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Domain=${hostCookieOptions.domain}; Secure; SameSite=Lax`,\n );\n }\n expiries.push(hostOnlyExpiryHeader(name));\n }\n // The ordinary writes that could not go through `.cookies` because the\n // name was already taken there. Flushed with the expiries, in the same\n // single append pass, AFTER every `.cookies` write on this response —\n // touching `.cookies` from here on re-parses the header list and eats\n // every one of these.\n for (const header of duplicateWrites) {\n target.headers.append(\"Set-Cookie\", header);\n }\n for (const header of expiries) {\n target.headers.append(\"Set-Cookie\", header);\n }\n if (shadowed.size === 0) {\n // Nothing was healed — only a two-scope write was rescued. No split\n // stamp, no scream, and no cache headers beyond what the write already\n // carried.\n return target;\n }\n\n // Stamped by the PACKAGE, not the host, so it rides EVERY healed\n // response — including redirects, which a host that stamps it at the end\n // of its own pass never reaches.\n target.headers.set(SPLIT_COOKIE_JAR_HEADER, \"1\");\n if (baseline.ambiguousKeys.length > 0) {\n target.headers.set(AMBIGUOUS_AUTH_COOKIE_HEADER, \"1\");\n }\n\n // A response that rewrites cookies must never be cached.\n for (const [key, value] of Object.entries(AUTH_COOKIE_RESPONSE_HEADERS)) {\n target.headers.set(key, value);\n }\n // LOUD: this is an automatic intervention on a user's cookie jar.\n console.warn(\n `[@ai-matrx/data/next] split auth cookie jar healed on host \"${host ?? \"?\"}\": ` +\n `${[...shadowed].join(\", \")} arrived at two scopes; the host-only copies ` +\n `were expired so the ${hostCookieOptions.domain} set is the only one left.`,\n );\n return target;\n };\n\n return {\n user: user as MiddlewareSession<DB, Response>[\"user\"],\n client,\n get response() {\n return applyHeal(response);\n },\n redirect: (targetUrl: URL): Response => {\n const redirect = createRedirect(targetUrl);\n for (const cookie of response.cookies.getAll()) {\n redirect.cookies.set(cookie);\n }\n for (const name of CARRIED_HEADERS) {\n const value = response.headers.get(name);\n if (value) redirect.headers.set(name, value);\n }\n return applyHeal(redirect);\n },\n splitCookieJar: shadowed.size > 0,\n ambiguousAuthCookies: baseline.ambiguousKeys.length > 0,\n authUnavailable,\n };\n }\n\n return {\n authCookie,\n cookieName: authCookie.cookieName,\n optionsForHost: authCookie.optionsForHost,\n browserOptions: authCookie.browserOptions,\n browserClient,\n serverClient,\n routeClient,\n middlewareSession,\n };\n}\n","/**\n * Shared auth-cookie options for EVERY Supabase client an application\n * constructs (browser, server, middleware, OAuth callbacks). All construction\n * sites MUST pass these options — a client using the default cookie name\n * cannot see the session the others wrote.\n *\n * WHY (the 2026-07 Matrx deployment split): one login must work on every\n * subdomain of the platform's apex (e.g. app.example.com, manage.example.com),\n * so on those hosts the auth cookie is issued domain-wide\n * (`Domain=.example.com`) instead of Supabase's default host-only cookie.\n *\n * The cookie is also RENAMED (default `sb-<project-ref>-auth-token` →\n * `sb-matrx-auth`): re-issuing an existing host-only cookie as domain-wide\n * under the SAME name leaves two same-name cookies in the browser (different\n * Domain attributes = distinct cookies), and whichever the browser sends\n * first wins — auth flaps. A new name makes the transition deterministic:\n * every user re-logs-in exactly once and the old cookie expires unused.\n * The `sb-` prefix is kept on purpose — cleanup paths that scrub session\n * cookies delete by that prefix.\n *\n * The Domain attribute is ONLY set on real apex hosts. localhost and preview\n * hosts get a host-only cookie — a browser rejects any Set-Cookie whose\n * Domain does not cover the current host, so setting it unconditionally would\n * silently break auth everywhere else.\n *\n * The apex domain is a VALUE the host passes in (never read from an env var\n * here): `createAuthCookie({ apexDomain: \"aimatrx.com\" })`.\n *\n * THE RENAME MIGRATION (0.5.0 — moved in from matrx-frontend, where it was\n * 44 lines of host code every other consumer would have had to re-derive):\n * replacing the auth authority (a new Supabase project) means a NEW cookie\n * name, and a tab still running the OLD bundle keeps writing the OLD name for\n * days. `migrateLegacyCookies` seeds the new storage key from the old one\n * ONLY when the new key is absent — never overwriting a live session — and\n * `isCurrentCookie` recognizes both the base name and Supabase's numbered\n * chunk suffixes (`<name>.0`, `<name>.1`, …), which a naive `startsWith`\n * check gets wrong for any cookie that merely shares the prefix.\n */\n\n/** Structurally compatible with @supabase/ssr's CookieOptionsWithName. */\nexport interface AuthCookieOptions {\n name: string;\n /** Remote hosts require HTTPS; loopback development remains usable over HTTP. */\n secure: boolean;\n domain?: string;\n}\n\n/** A cookie as read from a request — name + value, nothing else needed. */\nexport interface AuthCookieValue {\n name: string;\n value: string;\n}\n\nexport const DEFAULT_AUTH_COOKIE_NAME = \"sb-matrx-auth\";\n\nexport interface CreateAuthCookieArgs {\n /** The apex domain one login must span (e.g. `\"aimatrx.com\"`). */\n apexDomain: string;\n /** Override only for a second, unrelated deployment — never per-app. */\n cookieName?: string;\n /**\n * The storage key a PREVIOUS auth authority used. When present, cookies\n * under this name are seeded onto `cookieName` (absent-only) by\n * `migrateLegacyCookies`, and the middleware binding clears them at the\n * scope this host issued them on.\n */\n legacyCookieName?: string;\n}\n\nexport interface AuthCookie {\n cookieName: string;\n /** The superseded storage key, when this deployment has one. */\n legacyCookieName: string | undefined;\n /**\n * Cookie options for the host serving the current request.\n * @param host `Host` header or `window.location.hostname` (port tolerated).\n */\n optionsForHost(host: string | null | undefined): AuthCookieOptions;\n /** Browser variant — resolves the host from `window` (SSR-safe). */\n browserOptions(): AuthCookieOptions;\n /**\n * Is this cookie name the CURRENT auth cookie — base name or one of\n * Supabase's numbered chunks (`<name>.0`)? A prefix test is wrong: it\n * matches `<name>.anything`, including unrelated cookies.\n */\n isCurrentCookie(name: string): boolean;\n /**\n * Cookies to seed under the current name, derived from the legacy name.\n * Returns `[]` when any current cookie already exists (never overwrite a\n * live session) or when no legacy name is configured.\n */\n migrateLegacyCookies(\n cookies: readonly AuthCookieValue[],\n ): AuthCookieValue[];\n /**\n * THE POISONED-JAR READ. A browser can hold the SAME auth cookie name twice\n * — once host-only, once at `.apex` — because different Domain attributes\n * make distinct cookies, and it sends both in one `Cookie` header. Every\n * reassembler downstream (`@supabase/ssr`'s `combineChunks`) resolves a\n * chunk with `find()`, i.e. FIRST WINS, and the browser decides the order.\n * An empty shadow that sorts first therefore reassembles to `\"\"` and every\n * server read answers anonymous while the tab is signed in.\n *\n * This collapses each auth-cookie NAME to one entry, preferring the first\n * NON-EMPTY value. Non-auth cookies pass through untouched, in order.\n *\n * A `Cookie` header carries no Domain, so which scope a value came from is\n * genuinely unknowable here — non-empty-wins is the strongest correct rule\n * available, and `@supabase/ssr` already refuses a chunk set that decodes\n * incoherently. `shadowedNames` names every auth cookie that arrived more\n * than once, so the caller can EXPIRE the losing scope instead of leaving\n * the user to clear cookies by hand.\n *\n * THE SECOND POISON, AND IT IS AN IDENTITY LEAK: one storage key arriving\n * BOTH unchunked (`<name>`) and chunked (`<name>.0`, `<name>.1`). These are\n * DIFFERENT cookie names, so the per-name collapse above cannot see them,\n * and `@supabase/ssr`'s `combineChunks` returns the UNCHUNKED value the\n * moment it is non-empty — it never even reads the chunks. A stale base\n * cookie therefore shadows a freshly written chunked pair, and every read\n * resolves the PREVIOUS person while the new session sits unread beside it.\n * That is a different user's session being served, not merely a logout.\n *\n * `applyServerStorage` never writes that shape — it removes every chunk-like\n * sibling of whatever it writes — so a jar holding both is RESIDUE from a\n * removal that did not land (see `middlewareSession`: a two-scope removal\n * collapses on `ResponseCookies`, which is how this residue is made).\n *\n * WHICH COPY IS NEWER IS UNKNOWABLE HERE, and both directions of residue are\n * producible, so preferring either shape is a coin flip on WHOSE SESSION TO\n * SERVE. This drops the whole family from the read instead: nobody is signed\n * in, every name in it is returned in `ambiguousNames` to be expired, and the\n * host's honest-screen banner says so with the one action that fixes it. A\n * wrong sign-out is recoverable in one click; a wrong identity is not.\n */\n reconcile(cookies: readonly AuthCookieValue[]): ReconciledAuthCookies;\n}\n\n/** The outcome of {@link AuthCookie.reconcile}. */\nexport interface ReconciledAuthCookies {\n /** One entry per name; auth names resolved to their non-empty value. */\n cookies: AuthCookieValue[];\n /** Auth cookie names that arrived more than once (i.e. at two scopes). */\n shadowedNames: string[];\n /**\n * Auth cookie names belonging to a storage key that arrived UNCHUNKED AND\n * CHUNKED at once — see {@link AuthCookie.reconcile}. Their values have been\n * dropped from `cookies`, and they must be expired on EVERY host, including\n * the ones that issue a host-only cookie: unlike a two-scope shadow there is\n * no copy here that could be the good one.\n */\n ambiguousNames: string[];\n /** The storage keys that arrived in both shapes. Named for the scream. */\n ambiguousKeys: string[];\n /** `true` when the jar is split and needs healing. */\n split: boolean;\n}\n\n/**\n * Parse a raw `Cookie` request header, **PRESERVING DUPLICATES AND ORDER**.\n *\n * 🚨 THIS IS THE ONLY WAY TO SEE A SPLIT JAR. Next's `RequestCookies` (and the\n * `cookies()` store built on it) parses the header into a Map keyed by NAME,\n * so two same-name cookies at two Domain scopes collapse to ONE before any\n * application code runs — and the survivor is whichever the browser happened\n * to send LAST. Measured against the real class:\n *\n * \"sb-…-v2.0=; sb-…-v2.0=base64-REAL\" -> [{ value: \"base64-REAL\" }] works\n * \"sb-…-v2.0=base64-REAL; sb-…-v2.0=\" -> [{ value: \"\" }] anonymous\n *\n * That coin flip IS the production break, and it is invisible from the\n * collapsed jar: reconciliation over `getAll()` is a no-op no matter how\n * carefully it is written. Every door must therefore reconcile over THIS.\n */\nexport function parseCookieHeader(\n header: string | null | undefined,\n): AuthCookieValue[] {\n if (!header) return [];\n const out: AuthCookieValue[] = [];\n for (const pair of header.split(\";\")) {\n const eq = pair.indexOf(\"=\");\n // A valueless segment is a name with an empty value — exactly the shadow\n // this exists to catch — so it must not be skipped.\n const name = (eq === -1 ? pair : pair.slice(0, eq)).trim();\n if (!name) continue;\n const raw = eq === -1 ? \"\" : pair.slice(eq + 1).trim();\n let value = raw;\n try {\n value = decodeURIComponent(raw);\n } catch {\n // A malformed escape is not a reason to lose the cookie; Next's parser\n // keeps the raw text too.\n }\n out.push({ name, value });\n }\n return out;\n}\n\n/**\n * Is `name` the storage key itself or one of its numbered chunks? The same\n * membership test `@supabase/ssr`'s `isChunkLike` uses, so a family is defined\n * here exactly as the writer defines it. A bare `startsWith` is wrong: it also\n * matches `<key>.anything`, i.e. unrelated cookies.\n */\nfunction belongsToStorageKey(name: string, storageKey: string): boolean {\n if (name === storageKey) return true;\n if (!name.startsWith(`${storageKey}.`)) return false;\n return /^(0|[1-9][0-9]*)$/.test(name.slice(storageKey.length + 1));\n}\n\n/** `<from>` → `<to>`, carrying Supabase's numbered chunk suffix across. */\nfunction renamedCookie(\n cookie: AuthCookieValue,\n from: string,\n to: string,\n): AuthCookieValue | null {\n if (cookie.name === from) return { ...cookie, name: to };\n const prefix = `${from}.`;\n if (!cookie.name.startsWith(prefix)) return null;\n const chunk = cookie.name.slice(prefix.length);\n if (!/^(0|[1-9][0-9]*)$/.test(chunk)) return null;\n return { ...cookie, name: `${to}.${chunk}` };\n}\n\nexport function createAuthCookie(args: CreateAuthCookieArgs): AuthCookie {\n const cookieName = args.cookieName ?? DEFAULT_AUTH_COOKIE_NAME;\n const legacyCookieName = args.legacyCookieName;\n const apex = args.apexDomain.toLowerCase();\n\n const optionsForHost = (\n host: string | null | undefined,\n ): AuthCookieOptions => {\n const normalized = (host ?? \"\").toLowerCase();\n const hostname = normalized.startsWith(\"[\")\n ? normalized.slice(1, normalized.indexOf(\"]\"))\n : normalized === \"::1\" ? normalized : normalized.split(\":\")[0] ?? \"\";\n const loopback = hostname === \"localhost\" || hostname.endsWith(\".localhost\") ||\n hostname === \"::1\" || /^127(?:\\.(?:0|[1-9]\\d{0,2})){3}$/.test(hostname) &&\n hostname.split(\".\").every((part) => Number(part) <= 255);\n const secure = !loopback;\n const onApex = hostname === apex || hostname.endsWith(`.${apex}`);\n return onApex\n ? { name: cookieName, domain: `.${apex}`, secure }\n : { name: cookieName, secure };\n };\n\n const isCurrentCookie = (name: string): boolean =>\n belongsToStorageKey(name, cookieName);\n\n const isAuthCookie = (name: string): boolean =>\n isCurrentCookie(name) ||\n (legacyCookieName !== undefined &&\n (name === legacyCookieName || name.startsWith(`${legacyCookieName}.`)));\n\n const reconcile = (\n cookies: readonly AuthCookieValue[],\n ): ReconciledAuthCookies => {\n const seen = new Map<string, number>();\n const out: AuthCookieValue[] = [];\n const shadowed = new Set<string>();\n\n for (const cookie of cookies) {\n const at = seen.get(cookie.name);\n if (at === undefined) {\n seen.set(cookie.name, out.length);\n out.push({ name: cookie.name, value: cookie.value });\n continue;\n }\n if (!isAuthCookie(cookie.name)) continue; // non-auth: first wins, as before\n shadowed.add(cookie.name);\n const held = out[at];\n // Non-empty wins. Later non-empty values do NOT displace an earlier\n // non-empty one — with no scope information, churning the choice would\n // make reassembly non-deterministic across requests.\n if (held !== undefined && held.value.length === 0) {\n out[at] = { name: cookie.name, value: cookie.value };\n }\n }\n\n // THE FAMILY PASS. A storage key may arrive unchunked AND chunked at once;\n // those are different NAMES, so the collapse above cannot have seen it.\n // `combineChunks` would take the unchunked value and never read the chunks,\n // which serves whichever person's session happens to be sitting in the base\n // cookie. Refuse the family instead of guessing — see `reconcile`'s docs.\n const ambiguous = new Set<string>();\n const ambiguousKeys: string[] = [];\n const byName = new Map(out.map((cookie) => [cookie.name, cookie] as const));\n const storageKeys = [cookieName, ...(legacyCookieName ? [legacyCookieName] : [])];\n for (const storageKey of storageKeys) {\n const unchunked = byName.get(storageKey);\n const firstChunk = byName.get(`${storageKey}.0`);\n if (!unchunked?.value || !firstChunk?.value) continue;\n ambiguousKeys.push(storageKey);\n for (const { name } of out) {\n if (belongsToStorageKey(name, storageKey)) ambiguous.add(name);\n }\n }\n\n const resolved =\n ambiguous.size === 0\n ? out\n : out.filter(({ name }) => !ambiguous.has(name));\n\n const shadowedNames = [...shadowed];\n const ambiguousNames = [...ambiguous];\n return {\n cookies: resolved,\n shadowedNames,\n ambiguousNames,\n ambiguousKeys,\n split: shadowedNames.length > 0 || ambiguousNames.length > 0,\n };\n };\n\n return {\n cookieName,\n legacyCookieName,\n optionsForHost,\n reconcile,\n browserOptions: () =>\n optionsForHost(\n typeof window !== \"undefined\" ? window.location.hostname : null,\n ),\n isCurrentCookie,\n migrateLegacyCookies: (cookies) => {\n if (!legacyCookieName) return [];\n if (cookies.some(({ name }) => isCurrentCookie(name))) return [];\n return cookies.flatMap((cookie) => {\n const renamed = renamedCookie(cookie, legacyCookieName, cookieName);\n return renamed ? [renamed] : [];\n });\n },\n };\n}\n"],"mappings":"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","names":["key"]}
package/dist/next.js CHANGED
@@ -369,7 +369,7 @@ function createNextSupabase(config) {
369
369
  continue;
370
370
  }
371
371
  console.warn(
372
- `[@ai-matrx/data/next] DROPPED a second Set-Cookie for "${name}" on host "${host ?? "?"}": Next's cookie store keeps one entry per name, so this write would have DELETED the first one. The lost header was: ${header}. A Route Handler here must pass appendSetCookie \u2014 an unremoved auth cookie at the other scope makes the next request resolve the PREVIOUS person.`
372
+ `[@ai-matrx/data/next] DROPPED a second Set-Cookie for "${name}" on host "${host ?? "?"}": Next's cookie store keeps one entry per name, so this write would have DELETED the first one. Cookie value omitted. A Route Handler here must pass appendSetCookie \u2014 an unremoved auth cookie at the other scope makes the next request resolve the PREVIOUS person.`
373
373
  );
374
374
  }
375
375
  for (const header of deferred) appendSetCookie?.(header);
@@ -416,7 +416,7 @@ function createNextSupabase(config) {
416
416
  continue;
417
417
  }
418
418
  console.warn(
419
- `[@ai-matrx/data/next] DROPPED a second Set-Cookie for "${name}" on host "${host ?? "?"}": this door passed no appendSetCookie, and the response jar keeps one entry per name, so this write would have DELETED the first one. The lost header was: ${header}. Pass appendSetCookie from this route \u2014 an unremoved auth cookie at the other scope makes the next request resolve the PREVIOUS person.`
419
+ `[@ai-matrx/data/next] DROPPED a second Set-Cookie for "${name}" on host "${host ?? "?"}": this door passed no appendSetCookie, and the response jar keeps one entry per name, so this write would have DELETED the first one. Cookie value omitted. Pass appendSetCookie from this route \u2014 an unremoved auth cookie at the other scope makes the next request resolve the PREVIOUS person.`
420
420
  );
421
421
  }
422
422
  for (const header of deferred) appendSetCookie?.(header);
package/dist/next.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/next/supabase.ts","../src/db/auth-cookie.ts"],"sourcesContent":["/**\n * @ai-matrx/data/next — the Next.js Supabase binding.\n *\n * WHY THIS EXISTS (C22 / THE ALL-INCLUSIVE LAW): a Next.js app has FIVE\n * distinct places that must construct a Supabase client — the browser, a\n * Server Component, a Route Handler, the proxy/middleware, and an OAuth\n * callback — and every one of them must pass the SAME auth-cookie options or\n * it cannot see the session the others wrote. Before this module, matrx-frontend\n * carried that construction five times, each with its own env guard, its own\n * cookie adapter, and its own copy of the \"do not run code between\n * createServerClient and getUser()\" hazard. Every app on the platform would\n * have re-derived all five.\n *\n * The host now injects IDENTITY only — URL, publishable key, apex domain, the\n * cookie names — and the package owns every hard part:\n *\n * - env validation with a named, actionable error (never `process.env.X!`,\n * which fails as a cryptic `undefined.trim()` TypeError);\n * - the domain-wide cookie option construction on every single door;\n * - the SSR/browser split, including the Server-Component write that must be\n * swallowed because only the proxy may set cookies there;\n * - the browser singleton on a `globalThis` slot (module-level mutable state\n * is banned — dual ESM/CJS bundles would otherwise instantiate two clients\n * and split the session);\n * - the middleware session dance in ONE call: legacy-key migration, the\n * getUser() adjacency rule, session persistence under the new key, legacy\n * cookie clearing at the issuing scope, the no-store response headers, and\n * cookie carry-over onto redirects (dropping any one of these logs users\n * out at random, which is exactly how it was found).\n *\n * NO `next/*` IMPORT LIVES HERE. Next's own primitives (`cookies()`,\n * `NextResponse`) arrive through structural seams the host passes in, so this\n * module builds and tests outside Next and cannot drag the framework into a\n * non-Next consumer's graph.\n */\n\nimport {\n clearAuthCookiesAtScopes,\n createBrowserClient,\n createServerClient,\n DEFAULT_COOKIE_OPTIONS,\n} from \"@supabase/ssr\";\nimport {\n createAuthCookie,\n parseCookieHeader,\n type AuthCookie,\n type AuthCookieOptions,\n type AuthCookieValue,\n} from \"../db/auth-cookie\";\n\n/**\n * A `Database` type as generated by Supabase. Deliberately structural: the\n * package never imports a host's generated types, and a host that has not\n * generated any still gets a working (untyped-rows) client.\n */\nexport type DatabaseLike = Record<string, unknown>;\n\nexport type NextBrowserClient<DB extends DatabaseLike> = ReturnType<\n typeof createBrowserClient<DB>\n>;\nexport type NextServerClient<DB extends DatabaseLike> = ReturnType<\n typeof createServerClient<DB>\n>;\n\n/** A cookie to write, as `@supabase/ssr` hands it to `setAll`. */\nexport interface CookieToSet {\n name: string;\n value: string;\n options?: Record<string, unknown> | undefined;\n}\n\n/**\n * The subset of Next's `await cookies()` store this binding uses. Structural\n * on purpose — `ReadonlyRequestCookies` satisfies it by shape.\n */\nexport interface ServerCookieStore {\n getAll(): readonly AuthCookieValue[];\n set(name: string, value: string, options?: Record<string, unknown>): void;\n}\n\n/** The subset of `NextRequest[\"cookies\"]` the middleware binding uses. */\nexport interface RequestCookieJar {\n getAll(): readonly AuthCookieValue[];\n set(name: string, value: string): void;\n}\n\n/**\n * A cookie in Next's `ResponseCookies` shape: name, value, and every\n * attribute FLAT on the same object.\n *\n * 🚨 THIS SHAPE IS LOAD-BEARING, and modelling it as `never` cost the platform\n * a production outage (2026-09-01). `ResponseCookies.set` accepts either\n * `(name, value, attrs)` or ONE flat object — it does NOT accept `@supabase/ssr`'s\n * `{ name, value, options }`, and it does not throw on it either: it reads\n * `name`/`value`, ignores the unknown `options` key, and serializes\n * `sb-…-auth-v2.0=; Path=/`. Every attribute is lost. So:\n * · a token refresh wrote a HOST-ONLY session cookie shadowing the real\n * `.apex` one, and\n * · every chunk REMOVAL (`value: \"\"`, `maxAge: 0`) became a PERSISTENT EMPTY\n * host-only cookie instead of a deletion.\n * Two same-name cookies then reach the server in one `Cookie` header, the\n * empty one sorts first, `combineChunks`' `find()` takes it, and every server\n * read answers anonymous while the tab still shows a signed-in user.\n * `@supabase/ssr` cookies are converted with `toResponseCookie` at the single\n * boundary below; the `never` that hid this is gone.\n */\nexport interface ResponseCookie {\n name: string;\n value: string;\n domain?: string | undefined;\n path?: string | undefined;\n maxAge?: number | undefined;\n expires?: Date | number | undefined;\n httpOnly?: boolean | undefined;\n secure?: boolean | undefined;\n sameSite?: boolean | \"lax\" | \"strict\" | \"none\" | undefined;\n priority?: \"low\" | \"medium\" | \"high\" | undefined;\n partitioned?: boolean | undefined;\n}\n\n/**\n * Exactly the attributes Next's `ResponseCookies` serializes. Anything else on\n * an `@supabase/ssr` cookie is dropped by that class anyway — naming them is\n * what makes the drop visible instead of silent.\n */\nconst RESPONSE_COOKIE_ATTRIBUTES = [\n \"domain\",\n \"path\",\n \"maxAge\",\n \"expires\",\n \"httpOnly\",\n \"secure\",\n \"sameSite\",\n \"priority\",\n \"partitioned\",\n] as const satisfies readonly (keyof ResponseCookie)[];\n\n/**\n * The subset of `NextResponse[\"cookies\"]` the middleware binding uses.\n */\nexport interface ResponseCookieJar {\n getAll(): ResponseCookie[];\n set(cookie: ResponseCookie): void;\n}\n\n/** The subset of a `NextResponse` the middleware binding drives. */\nexport interface MiddlewareResponse {\n cookies: ResponseCookieJar;\n headers: {\n get(name: string): string | null;\n set(name: string, value: string): void;\n /**\n * Required for the split-jar heal. `ResponseCookies` keeps ONE entry per\n * cookie NAME, so the same name cannot be expired at two Domain scopes\n * through it — the second `set` replaces the first. The losing scope is\n * expired with raw appended `Set-Cookie` headers instead.\n */\n append(name: string, value: string): void;\n };\n}\n\nexport interface NextSupabaseConfig {\n /**\n * `process.env.NEXT_PUBLIC_SUPABASE_URL` — read STATICALLY at the call site.\n * Next only inlines `NEXT_PUBLIC_*` into client bundles for static member\n * accesses; a dynamic lookup inside this package would be `undefined` in\n * every browser bundle.\n */\n supabaseUrl: string | undefined;\n /** `process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`, read statically. */\n publishableKey: string | undefined;\n /** The apex domain one login must span, e.g. `\"aimatrx.com\"`. */\n apexDomain: string;\n /** Auth cookie name. Defaults to the package's `sb-matrx-auth`. */\n cookieName?: string;\n /** A superseded storage key to migrate from and then clear. */\n legacyCookieName?: string;\n /**\n * Host wrapper applied to the BROWSER client only — the one legitimate\n * host seam here (diagnostics/error capture). It must not change query\n * behavior; the package never inspects what it returns.\n *\n * `C extends object` because a Supabase client always IS one, and a real\n * capture wrapper is a `Proxy` that must constrain the same way. Leaving\n * `C` unconstrained made every honest host wrapper unassignable (found at\n * matrx-frontend adoption, 0.5.1).\n */\n wrapBrowserClient?: <C extends object>(client: C) => C;\n}\n\n/** Cache-defeating headers every auth-cookie-bearing response must carry. */\nexport const AUTH_COOKIE_RESPONSE_HEADERS: Readonly<Record<string, string>> =\n Object.freeze({\n \"Cache-Control\": \"private, no-cache, no-store, must-revalidate, max-age=0\",\n Expires: \"0\",\n Pragma: \"no-cache\",\n });\n\nconst CARRIED_HEADERS = [\"cache-control\", \"expires\", \"pragma\"] as const;\n\n/**\n * Stamped on any response whose auth cookie jar was found split and healed.\n * A shell that already rendered may be wearing an identity this pass could not\n * see, and must say so rather than paint a signed-in screen of anonymous data.\n */\nexport const SPLIT_COOKIE_JAR_HEADER = \"x-matrx-split-jar\";\n\n/**\n * Stamped when this request's auth cookie jar held one storage key BOTH\n * unchunked and chunked — see `AuthCookie.reconcile`. Distinct from\n * `SPLIT_COOKIE_JAR_HEADER` because the person's experience is different and\n * the screen must say the true thing:\n *\n * split jar → the tab still holds a session; the server just could not\n * read it. \"Sign in again\" restores the page.\n * ambiguous → we deliberately refused BOTH copies rather than risk\n * resolving the wrong person, so this person has just been\n * signed out BY US. That is an automatic intervention on\n * somebody's session and it must not happen quietly: the\n * browser's own read is anonymous too, so nothing else on the\n * page can notice, and without this header the app shows a\n * login screen with no explanation.\n */\nexport const AMBIGUOUS_AUTH_COOKIE_HEADER = \"x-matrx-auth-ambiguous\";\n\n/**\n * `@supabase/ssr`'s `{ name, value, options }` → Next's flat `ResponseCookie`.\n * The ONE conversion boundary; see {@link ResponseCookie} for what skipping it\n * cost.\n */\nexport function toResponseCookie(cookie: CookieToSet): ResponseCookie {\n const options = (cookie.options ?? {}) as Record<string, unknown>;\n // Built key by key, never spread: `options.name` would fight the real name,\n // and an unrecognized key would read as carried when Next drops it.\n const out: Record<string, unknown> = {\n name: cookie.name,\n value: cookie.value,\n };\n for (const attribute of RESPONSE_COOKIE_ATTRIBUTES) {\n const value = options[attribute];\n if (value !== undefined) out[attribute] = value;\n }\n return out as unknown as ResponseCookie;\n}\n\n/**\n * A raw `Set-Cookie` that EXPIRES `name` at the host-only scope — the scope a\n * response cannot reach through `ResponseCookies` once the same name is being\n * written at `.apex`. A browser ignores a `Set-Cookie` for a scope the current\n * host does not own, so this is a no-op on a healthy jar.\n */\nfunction hostOnlyExpiryHeader(name: string): string {\n return `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=Lax`;\n}\n\n/**\n * A `ResponseCookie` as a raw `Set-Cookie` header value, serialized the way\n * Next's `ResponseCookies` serializes it. Needed because a response can only\n * carry ONE entry per cookie NAME through `.cookies`, and `@supabase/ssr`\n * deliberately writes some names TWICE — see `applyCookies`.\n */\nexport function serializeSetCookie(cookie: ResponseCookie): string {\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n parts.push(`Path=${cookie.path ?? \"/\"}`);\n if (cookie.domain !== undefined) parts.push(`Domain=${cookie.domain}`);\n if (cookie.maxAge !== undefined) parts.push(`Max-Age=${cookie.maxAge}`);\n if (cookie.expires !== undefined) {\n const expires =\n cookie.expires instanceof Date\n ? cookie.expires\n : new Date(cookie.expires);\n parts.push(`Expires=${expires.toUTCString()}`);\n }\n if (cookie.httpOnly === true) parts.push(\"HttpOnly\");\n if (cookie.secure === true) parts.push(\"Secure\");\n if (typeof cookie.sameSite === \"string\") {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n return parts.join(\"; \");\n}\n\n/**\n * Thrown when identity is missing. Named and actionable — the alternative\n * (`process.env.X!`) surfaces as `Cannot read properties of undefined\n * (reading 'trim')` from inside Supabase, which taught nobody anything.\n */\nexport class MissingSupabaseIdentityError extends Error {\n readonly variable: string;\n constructor(variable: string) {\n super(\n `@ai-matrx/data/next: missing required Supabase identity \"${variable}\". ` +\n `Pass it as a STATIC member access (process.env.${variable}) to createNextSupabase().`,\n );\n this.name = \"MissingSupabaseIdentityError\";\n this.variable = variable;\n }\n}\n\nfunction requireIdentity(value: string | undefined, variable: string): string {\n const trimmed = value?.trim();\n if (!trimmed) throw new MissingSupabaseIdentityError(variable);\n return trimmed;\n}\n\n/**\n * THE AUTH RESOLVE BUDGET — the proxy is on the critical path of EVERY request\n * and nothing below it may hang forever.\n *\n * Next 16 runs `proxy.ts` in the Node.js runtime and that \"cannot be\n * configured\" (Next 16 upgrade guide), so on Vercel this pass is a Lambda with\n * a 15s task cap, concurrency of one per instance, and a real cold start. A\n * single `<Link>` prefetch storm — a chat sidebar is 20-40 links — spins up\n * that many instances at once, and an unbounded auth round trip in each one\n * turns a downstream blink into `504 MIDDLEWARE_INVOCATION_TIMEOUT` on the\n * page the person actually asked for. Measured on aimatrx.com, 2026-09-20: 19\n * such 504s in 48h, every one of them in a burst of simultaneous prefetches,\n * every one of them \"Task timed out after 15 seconds\".\n *\n * So the deadline is OURS, not the platform's: a budget short enough that a\n * stalled auth server costs one request its identity instead of costing the\n * whole page its response.\n */\nconst AUTH_RESOLVE_TIMEOUT_MS = 2_500;\n\n/**\n * The identity calls this budget covers, and ONLY these. Sign-in, sign-up,\n * OAuth code exchange and password reset also live under `/auth/v1/` and a\n * person is waiting on them; they keep their natural timeout.\n */\nconst IDENTITY_PATHS = [\n \"/auth/v1/user\",\n \"/auth/v1/token?grant_type=refresh_token\",\n \"/auth/v1/.well-known/jwks.json\",\n];\n\n/** Non-retryable by construction: not a 5xx, so auth-js stops retrying. */\nconst AUTH_BUDGET_EXHAUSTED_STATUS = 408;\nconst AUTH_BUDGET_EXHAUSTED_CODE = \"auth_budget_exhausted\";\n\n/**\n * ONE budget for the WHOLE identity resolve of a request, not one per socket.\n *\n * The 0.17.0 budget aborted each `/auth/v1/` socket at 2.5s. That was not\n * enough, and 2026-09-21 proved it: auth-js retries a token refresh with\n * exponential backoff for up to 30s (`AUTO_REFRESH_TICK_DURATION_MS`), and a\n * per-socket abort is exactly the retryable error that keeps that loop going —\n * five aborted attempts plus their pauses is ~15.5s, past Vercel's 15s task\n * cap, and the proxy answered `504` while believing it had a budget.\n *\n * This wrapper holds a deadline from the FIRST identity call. While the\n * deadline is open every identity call gets what is LEFT of it, aborting the\n * socket (a `Promise.race` would leave it hanging and holding the Lambda).\n * The moment an attempt aborts, or the deadline passes, the budget is\n * EXHAUSTED and every further identity call answers synchronously with a\n * synthetic `408 auth_budget_exhausted` — a non-retryable auth error — so\n * auth-js stops its loop at once instead of sleeping through it. The whole\n * resolve therefore costs at most the budget plus one 200ms backoff.\n *\n * `exhausted` is also the cookie gate: an exhausted budget must never write,\n * expire or heal a cookie, because auth-js clears the session on a\n * non-retryable refresh failure once the access token has truly expired, and\n * that write is a logout caused by a slow network.\n */\nexport interface AuthBudget {\n fetch: typeof fetch;\n readonly exhausted: boolean;\n}\n\n/**\n * 🚨 A REFRESH THAT WAS SENT IS NEVER ABANDONED AT THE BUDGET (2026-10-07).\n *\n * `grant_type=refresh_token` ROTATES the refresh token on the auth server the\n * moment it is processed. Aborting it at 2.5s did not stop the rotation — it only\n * threw away the new tokens, so no cookie was written and the browser kept a\n * refresh token the server had already spent. Past GoTrue's reuse window that\n * token is refused and the person is signed out (and a server-rendered read made\n * on the dropped session went out as `anon`: their own chats answered \"sign in\").\n * So a refresh, once on the wire, runs to completion and its answer is used,\n * bounded only by this ceiling — below Vercel's 15s proxy cap. A refresh that has\n * NOT been sent yet still obeys the budget (nothing rotated, nothing lost).\n */\nexport const AUTH_REFRESH_CEILING_MS = 8_000;\n\nconst REFRESH_GRANT_PATH = \"/auth/v1/token?grant_type=refresh_token\";\n\nexport function createAuthBudget(\n timeoutMs: number,\n options: { refreshCeilingMs?: number } = {},\n): AuthBudget {\n const refreshCeilingMs = Math.max(timeoutMs, options.refreshCeilingMs ?? AUTH_REFRESH_CEILING_MS);\n let deadline: number | null = null;\n let exhausted = false;\n\n const isIdentityCall = (url: string): boolean =>\n IDENTITY_PATHS.some((path) => url.includes(path));\n\n const exhaustedResponse = (): Response =>\n new Response(\n JSON.stringify({\n error_code: AUTH_BUDGET_EXHAUSTED_CODE,\n msg:\n `identity resolve budget of ${timeoutMs}ms is exhausted for this request; ` +\n `not retrying`,\n }),\n {\n status: AUTH_BUDGET_EXHAUSTED_STATUS,\n headers: { \"content-type\": \"application/json\" },\n },\n );\n\n const budgetedFetch: typeof fetch = async (input, init) => {\n const url =\n typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n if (!isIdentityCall(url) || init?.signal) {\n return fetch(input as RequestInfo, init);\n }\n const now = Date.now();\n if (deadline === null) deadline = now + timeoutMs;\n const remaining = deadline - now;\n if (exhausted || remaining <= 0) {\n exhausted = true;\n return exhaustedResponse();\n }\n // Once sent, a refresh is allowed the ceiling, not what is left of the budget.\n const allowed = url.includes(REFRESH_GRANT_PATH) ? refreshCeilingMs : remaining;\n try {\n return await fetch(input as RequestInfo, {\n ...init,\n signal: AbortSignal.timeout(allowed),\n });\n } catch (cause) {\n const name = String((cause as { name?: unknown })?.name ?? \"\");\n if (name === \"TimeoutError\" || name === \"AbortError\") exhausted = true;\n throw cause;\n }\n };\n\n return {\n fetch: budgetedFetch,\n get exhausted() {\n return exhausted;\n },\n };\n}\n\n/**\n * `true` for the errors that mean \"we could not REACH the auth authority\",\n * never for \"this person is not signed in\".\n *\n * The distinction is the whole point: a transport failure must not be read as\n * a logout, or a blink on the network signs people out (and, on a protected\n * route, bounces them to /login mid-session).\n *\n * Matched by NAME rather than by importing `isAuthRetryableFetchError`, so\n * this package keeps its single `@supabase/ssr` dependency and does not take\n * on `@supabase/auth-js` and its ESM/CJS dual-graph hazard.\n */\nfunction isAuthTransportFailure(error: unknown): boolean {\n if (!error || typeof error !== \"object\") return false;\n const name = String((error as { name?: unknown }).name ?? \"\");\n if (\n name === \"AuthRetryableFetchError\" ||\n name === \"TimeoutError\" ||\n name === \"AbortError\"\n ) {\n return true;\n }\n const status = (error as { status?: unknown }).status;\n if (status === 0 || status === 504 || status === 503) return true;\n // The synthetic answer this package hands auth-js once the budget is spent.\n const code = (error as { code?: unknown }).code;\n return status === AUTH_BUDGET_EXHAUSTED_STATUS || code === AUTH_BUDGET_EXHAUSTED_CODE;\n}\n\n/** What one identity resolve settled, and whether it could settle at all. */\ninterface ResolvedIdentity {\n user: ({ id: string } & Record<string, unknown>) | null;\n authUnavailable: boolean;\n}\n\n/**\n * THE ONE IDENTITY RESOLVE. `getClaims()`, never `getUser()`.\n *\n * `getUser()` sends a request to the Auth server for EVERY JWT — an auth-server\n * lookup on the critical path of every matched request. `getClaims()` verifies\n * the access token against the project's JWKS with WebCrypto, locally and\n * usually with no network call at all, and auth-js caches the key set in a\n * module-level `GLOBAL_JWKS` shared by every client in the process — so a warm\n * instance re-verifies with zero I/O and only a genuine token refresh or a\n * JWKS TTL expiry goes out to the network.\n *\n * This requires an ASYMMETRIC signing key on the project (ECC/RSA). With a\n * symmetric secret `getClaims()` silently degrades to a `getUser()`-shaped\n * request per call, which is correct but gives back none of the saving — so a\n * project that turns asymmetric keys off pays the old cost and nothing breaks.\n *\n * Verification is cryptographic, so the claims are as trusted as `getUser()`'s\n * answer; this is not the untrusted `getSession()` read.\n */\nasync function resolveIdentity(\n client: {\n auth: {\n getClaims: () => Promise<{\n data: { claims?: Record<string, unknown> } | null;\n error: unknown;\n }>;\n };\n },\n budget?: AuthBudget,\n): Promise<ResolvedIdentity> {\n const unreachable = (cause: unknown): ResolvedIdentity => {\n console.warn(\n `[@ai-matrx/data/next] AUTH UNREACHABLE within ${AUTH_RESOLVE_TIMEOUT_MS}ms — ` +\n `this request is being passed through WITHOUT an identity and WITHOUT ` +\n `touching a single auth cookie. Nobody is being signed out: the shell ` +\n `resolves auth again on the client, and the next request re-resolves it ` +\n `here. If this is not rare, the auth authority (or the network to it) is ` +\n `the thing to fix — raising this budget only trades it back for a 504. ` +\n `Cause: ${String(cause)}`,\n );\n return { user: null, authUnavailable: true };\n };\n\n try {\n const { data, error } = await client.auth.getClaims();\n if (error) {\n return isAuthTransportFailure(error) || budget?.exhausted\n ? unreachable(error)\n : { user: null, authUnavailable: false };\n }\n const claims = data?.claims;\n if (!claims || typeof claims.sub !== \"string\") {\n // No claims AND a spent budget is \"we could not tell\", not \"nobody\".\n return budget?.exhausted\n ? unreachable(\"identity budget exhausted before a token could be verified\")\n : { user: null, authUnavailable: false };\n }\n // The JWT carries the identity fields a proxy decides with. Kept as a\n // `{ id }`-shaped object so every existing `session.user` read — all of\n // which are truthiness or `.id` — is unchanged by the switch.\n return {\n user: { ...claims, id: claims.sub },\n authUnavailable: false,\n };\n } catch (cause) {\n return unreachable(cause);\n }\n}\n\nexport interface MiddlewareSessionArgs<Response> {\n /** `Host` header of the incoming request. */\n host: string | null | undefined;\n /** `request.cookies` */\n requestCookies: RequestCookieJar;\n /**\n * The raw `Cookie` header (`request.headers.get(\"cookie\")`). REQUIRED to\n * detect and heal a split jar — `request.cookies` has already collapsed the\n * duplicates. Without it this pass cannot see the shadow and cannot expire\n * it, which is exactly how the \"healed\" 0.7.x build still left poisoned\n * browsers poisoned.\n */\n cookieHeader?: string | null | undefined;\n /** `() => NextResponse.next({ request })` — a FRESH response each call. */\n createResponse: () => Response;\n /** `(url) => NextResponse.redirect(url)` */\n createRedirect: (url: URL) => Response;\n}\n\nexport interface MiddlewareSession<DB extends DatabaseLike, Response> {\n /**\n * The validated user, or `null`. Resolved via `getClaims()` — a local\n * cryptographic verification of the access token, not a `getSession()` read.\n *\n * `null` is ambiguous on its own: check `authUnavailable` before treating it\n * as \"signed out\". A transport failure produces `null` too, and acting on\n * that as a logout is how a network blink bounces a signed-in person.\n */\n user: ({ id: string } & Record<string, unknown>) | null;\n /** The client, in case the caller needs a further authenticated read. */\n client: NextServerClient<DB>;\n /** The response carrying every session cookie written during this pass. */\n response: Response;\n /**\n * A redirect that CARRIES the session cookies and cache headers. Building a\n * bare `NextResponse.redirect` here drops refreshed tokens on the floor and\n * logs the user out on the next request.\n */\n redirect(url: URL): Response;\n /**\n * `true` when this request arrived with the same auth cookie at two Domain\n * scopes. The response heals the jar, but the SHELL that already rendered\n * may be holding an identity this pass could not see — a host that renders\n * a signed-in shell must say so in words rather than paint a signed-in\n * screen full of anonymous data.\n */\n splitCookieJar: boolean;\n /**\n * `true` when a storage key arrived BOTH unchunked and chunked and this pass\n * therefore refused both copies rather than risk resolving the wrong person.\n * The person has been signed out by us and must be told so.\n */\n ambiguousAuthCookies: boolean;\n /**\n * `true` when the auth authority could not be REACHED inside the resolve\n * budget, so this pass has no opinion about who this is.\n *\n * The contract when it is set: `user` is `null` but the person is NOT signed\n * out, not one auth cookie was written, expired or healed, and the host must\n * neither bounce to login nor paint a signed-out screen as if it were\n * settled. Let the request through; the client resolves auth itself and the\n * next request re-resolves it here.\n */\n authUnavailable: boolean;\n}\n\nexport interface NextSupabase<DB extends DatabaseLike> {\n /** The auth-cookie primitive this binding is built on. */\n readonly authCookie: AuthCookie;\n readonly cookieName: string;\n optionsForHost(host: string | null | undefined): AuthCookieOptions;\n browserOptions(): AuthCookieOptions;\n\n /**\n * The browser client. Memoized on a `globalThis` slot keyed by identity, so\n * the ESM and CJS graphs of this package share ONE client and therefore one\n * auth state — a per-module singleton would silently split them.\n */\n browserClient(): NextBrowserClient<DB>;\n\n /**\n * A Server Component / Server Action client over `await cookies()`.\n * Cookie writes from a Server Component are swallowed by design: only the\n * proxy may set cookies there, and it refreshes the session anyway.\n */\n serverClient(args: {\n cookieStore: ServerCookieStore;\n host: string | null | undefined;\n /**\n * The raw `Cookie` request header (`(await headers()).get(\"cookie\")`).\n * REQUIRED to see a split jar: `cookies()` collapses two same-name cookies\n * to one before this code runs. Omitting it degrades this door to the\n * collapsed view, where the surviving copy is whichever the browser sent\n * last — a coin flip between the session and anonymous.\n */\n cookieHeader?: string | null | undefined;\n /**\n * See `routeClient`'s `appendSetCookie` — Next's `cookies()` store keys by\n * NAME exactly like `ResponseCookies`, so a two-scope removal collapses\n * here too. A Route Handler that signs somebody in MUST pass this (buffer\n * the headers and append them onto the response it returns, last).\n * A Server Component has no response to append to and its writes are\n * swallowed anyway, so it correctly passes nothing.\n */\n appendSetCookie?: (header: string) => void;\n }): NextServerClient<DB>;\n\n /**\n * A Route Handler client: reads the request's cookies, writes onto the\n * response you are about to return.\n */\n routeClient(args: {\n requestCookies: Pick<RequestCookieJar, \"getAll\">;\n setCookie: (cookie: CookieToSet) => void;\n host: string | null | undefined;\n /** See `serverClient` — same collapse, same requirement. */\n cookieHeader?: string | null | undefined;\n /**\n * `(header) => response.headers.append(\"Set-Cookie\", header)`.\n *\n * 🚨 REQUIRED ON ANY DOOR THAT SIGNS SOMEBODY IN. `@supabase/ssr` removes a\n * cookie at BOTH Domain scopes by emitting the SAME name twice, and every\n * jar a host can pass to `setCookie` — `NextResponse.cookies`, Next's\n * `cookies()` store — keeps ONE entry per NAME, so the second write\n * REPLACES the first and the `.apex` removal is silently lost. That is how\n * a stale UNCHUNKED auth cookie survives beside a fresh CHUNKED pair, and\n * `combineChunks` then serves the STALE one — the previous person's\n * session, to the person who just signed in (2026-09-08, R-O3).\n *\n * With this seam the duplicate goes out as a raw header instead. Without\n * it the drop is announced loudly rather than swallowed.\n *\n * THE CALLER'S OBLIGATION: `ResponseCookies.set` re-serializes the whole\n * `Set-Cookie` list from its own map and DISCARDS raw appends made since.\n * Nothing may touch `response.cookies` after a header appended here.\n */\n appendSetCookie?: (header: string) => void;\n }): NextServerClient<DB>;\n\n /**\n * THE middleware/proxy auth pass, entire. Runs legacy-key migration, builds\n * the client, resolves `getUser()` with nothing in between, persists a\n * migrated session under the current key, clears the superseded key at the\n * scope this host issued it on, and stamps no-store headers on any response\n * that carries cookies.\n */\n middlewareSession<Response extends MiddlewareResponse>(\n args: MiddlewareSessionArgs<Response>,\n ): Promise<MiddlewareSession<DB, Response>>;\n}\n\ninterface BrowserSlot {\n key: string;\n client: unknown;\n}\n\n/**\n * The browser-client singleton lives on `globalThis`, not in a module\n * variable: with dual ESM/CJS output each loader graph instantiates its own\n * copy of this module, and two Supabase browser clients means two auth\n * listeners racing on one cookie.\n */\nconst BROWSER_SLOT = Symbol.for(\"ai-matrx.data.next.browser-client\");\n\nfunction readBrowserSlot(): BrowserSlot | undefined {\n return (globalThis as Record<symbol, unknown>)[BROWSER_SLOT] as\n | BrowserSlot\n | undefined;\n}\n\nfunction writeBrowserSlot(slot: BrowserSlot): void {\n (globalThis as Record<symbol, unknown>)[BROWSER_SLOT] = slot;\n}\n\nexport function createNextSupabase<DB extends DatabaseLike = DatabaseLike>(\n config: NextSupabaseConfig,\n): NextSupabase<DB> {\n const url = requireIdentity(config.supabaseUrl, \"NEXT_PUBLIC_SUPABASE_URL\");\n const key = requireIdentity(\n config.publishableKey,\n \"NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY\",\n );\n const authCookie = createAuthCookie({\n apexDomain: config.apexDomain,\n ...(config.cookieName !== undefined\n ? { cookieName: config.cookieName }\n : {}),\n ...(config.legacyCookieName !== undefined\n ? { legacyCookieName: config.legacyCookieName }\n : {}),\n });\n const wrap = config.wrapBrowserClient;\n const slotKey = `${url}|${key}|${authCookie.cookieName}`;\n\n /**\n * `document.cookie` carries the SAME duplicates the server sees — and\n * `@supabase/ssr`'s default browser adapter resolves each chunk with\n * `find()`, so an empty shadow sorting first makes the BROWSER read\n * anonymous too. That is why the split-session banner never mounted in\n * production: the server said \"nobody\", and the client agreed, so nothing\n * looked inconsistent even though a real session was sitting right there.\n *\n * Reads are reconciled here (non-empty wins). Writes are NOT redirected:\n * they keep going through `document.cookie` with the canonical options, and\n * the shadow is expired by the proxy, which is the only side that can name a\n * cookie's scope.\n */\n const browserCookieAdapter = () => ({\n getAll: () =>\n authCookie.reconcile(\n typeof document === \"undefined\"\n ? []\n : parseCookieHeader(document.cookie),\n ).cookies,\n setAll: (cookiesToSet: readonly CookieToSet[]) => {\n if (typeof document === \"undefined\") return;\n for (const cookie of cookiesToSet) {\n const options = (cookie.options ?? {}) as Record<string, unknown>;\n const parts = [\n `${cookie.name}=${encodeURIComponent(cookie.value)}`,\n `Path=${typeof options.path === \"string\" ? options.path : \"/\"}`,\n ];\n if (typeof options.domain === \"string\") {\n parts.push(`Domain=${options.domain}`);\n }\n if (typeof options.maxAge === \"number\") {\n parts.push(`Max-Age=${options.maxAge}`);\n }\n if (typeof options.sameSite === \"string\") {\n parts.push(`SameSite=${options.sameSite}`);\n }\n if (options.secure === true || location.protocol === \"https:\") parts.push(\"Secure\");\n document.cookie = parts.join(\"; \");\n }\n },\n });\n\n const browserClient = (): NextBrowserClient<DB> => {\n const existing = readBrowserSlot();\n if (existing && existing.key === slotKey) {\n return existing.client as NextBrowserClient<DB>;\n }\n const raw = createBrowserClient<DB>(url, key, {\n cookieOptions: authCookie.browserOptions(),\n cookies: browserCookieAdapter(),\n });\n const client = wrap ? wrap(raw) : raw;\n writeBrowserSlot({ key: slotKey, client });\n return client;\n };\n\n /**\n * THE ONE READ SEAM. Every door reads the cookie jar through this — a\n * duplicated auth cookie name whose empty twin sorts first would otherwise\n * reassemble to \"\" and answer anonymous on that door alone (which is how\n * `manage` refused a valid Create while `www` worked).\n */\n const reconciledGetAll = (\n getAllRaw: () => readonly AuthCookieValue[],\n cookieHeader: string | null | undefined,\n ): (() => AuthCookieValue[]) => {\n // The raw header when we have it (duplicates intact), the collapsed jar\n // only as a fallback — see parseCookieHeader for why that matters.\n const parsed = parseCookieHeader(cookieHeader);\n if (parsed.length > 0) {\n const reconciled = authCookie.reconcile(parsed).cookies;\n return () => reconciled;\n }\n return () => authCookie.reconcile(getAllRaw()).cookies;\n };\n\n const serverClient: NextSupabase<DB>[\"serverClient\"] = ({\n cookieStore,\n host,\n cookieHeader,\n appendSetCookie,\n }) => {\n const written = new Set<string>();\n // A layout or page is a Lambda with the same task cap as the proxy, and a\n // `getClaims()` here can still trigger a token refresh. Same budget.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n return createServerClient<DB>(url, key, {\n cookieOptions: authCookie.optionsForHost(host),\n global: { fetch: budget.fetch },\n cookies: {\n getAll: reconciledGetAll(() => cookieStore.getAll(), cookieHeader),\n setAll: (cookiesToSet) => {\n try {\n // Two passes, never interleaved — see `routeClient`.\n const deferred: string[] = [];\n for (const cookie of cookiesToSet) {\n const { name, value, options } = cookie;\n if (!written.has(name)) {\n written.add(name);\n cookieStore.set(name, value, options as Record<string, unknown>);\n continue;\n }\n const header = serializeSetCookie(\n toResponseCookie(cookie as CookieToSet),\n );\n if (appendSetCookie) {\n deferred.push(header);\n continue;\n }\n console.warn(\n `[@ai-matrx/data/next] DROPPED a second Set-Cookie for \"${name}\" on host ` +\n `\"${host ?? \"?\"}\": Next's cookie store keeps one entry per name, so this ` +\n `write would have DELETED the first one. The lost header was: ${header}. ` +\n `A Route Handler here must pass appendSetCookie — an unremoved auth cookie ` +\n `at the other scope makes the next request resolve the PREVIOUS person.`,\n );\n }\n for (const header of deferred) appendSetCookie?.(header);\n } catch {\n // Called from a Server Component, where Next forbids cookie\n // writes. The proxy refreshes the session on every request, so\n // dropping this write is correct — not a swallowed failure.\n }\n },\n },\n });\n };\n\n const routeClient: NextSupabase<DB>[\"routeClient\"] = ({\n requestCookies,\n setCookie,\n host,\n cookieHeader,\n appendSetCookie,\n }) => {\n // See `appendSetCookie`: a name written twice cannot go through any\n // one-entry-per-NAME jar, and the second write is what deletes the first.\n const written = new Set<string>();\n // Same budget as the proxy: an API route is one Lambda, one identity resolve.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n return createServerClient<DB>(url, key, {\n cookieOptions: authCookie.optionsForHost(host),\n global: { fetch: budget.fetch },\n cookies: {\n getAll: reconciledGetAll(() => requestCookies.getAll(), cookieHeader),\n setAll: (cookiesToSet) => {\n if (budget.exhausted && cookiesToSet.length > 0) {\n // See middlewareSession.applyCookies — a spent budget writes nothing.\n console.warn(\n `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) in a ` +\n `route on host \"${host ?? \"?\"}\": identity budget exhausted; nobody is signed out.`,\n );\n return;\n }\n // 🚨 TWO PASSES, NEVER INTERLEAVED — the same rule the split-jar heal\n // learned the hard way. `ResponseCookies.set` re-serializes the whole\n // `Set-Cookie` list from its own map, DISCARDING every raw append made\n // since. Appending a duplicate in the middle of the batch and then\n // writing one more cookie eats it, which is a silently lost removal\n // all over again. Every `setCookie` first; every append after.\n const deferred: string[] = [];\n for (const cookie of cookiesToSet) {\n const { name } = cookie;\n if (!written.has(name)) {\n written.add(name);\n setCookie(cookie as CookieToSet);\n continue;\n }\n const header = serializeSetCookie(\n toResponseCookie(cookie as CookieToSet),\n );\n if (appendSetCookie) {\n deferred.push(header);\n continue;\n }\n // LOUD: a cookie write is being lost, and the loss is an identity\n // hazard, not a cosmetic one.\n console.warn(\n `[@ai-matrx/data/next] DROPPED a second Set-Cookie for \"${name}\" on host ` +\n `\"${host ?? \"?\"}\": this door passed no appendSetCookie, and the response jar ` +\n `keeps one entry per name, so this write would have DELETED the first one. ` +\n `The lost header was: ${header}. Pass appendSetCookie from this route — an ` +\n `unremoved auth cookie at the other scope makes the next request resolve the ` +\n `PREVIOUS person.`,\n );\n }\n for (const header of deferred) appendSetCookie?.(header);\n },\n },\n });\n };\n\n async function middlewareSession<Response extends MiddlewareResponse>(\n args: MiddlewareSessionArgs<Response>,\n ): Promise<MiddlewareSession<DB, Response>> {\n const { host, requestCookies, createResponse, createRedirect } = args;\n const hostCookieOptions = authCookie.optionsForHost(host);\n\n let response = createResponse();\n\n /**\n * THE BASELINE VIEW, taken from the RAW `Cookie` header so duplicates are\n * still there to be seen. `requestCookies.getAll()` has already collapsed\n * them (Next keys its jar by name), which is why reconciling over the jar\n * detected nothing in production.\n */\n const rawCookies = parseCookieHeader(args.cookieHeader);\n const baseline = authCookie.reconcile(\n rawCookies.length > 0 ? rawCookies : requestCookies.getAll(),\n );\n\n /**\n * Auth cookie names that arrived at TWO scopes. Only meaningful where this\n * host issues a Domain-scoped cookie; on localhost/preview the host-only\n * cookie IS the real one and must never be expired.\n */\n const shadowed = hostCookieOptions.domain\n ? new Set(baseline.shadowedNames)\n : new Set<string>();\n\n /**\n * An UNCHUNKED value and a CHUNKED pair for one storage key. Unlike a\n * two-scope shadow there is no copy here that could be the good one — the\n * reconciled view has already dropped the whole family — so it is expired\n * on EVERY host, host-only deployments included, and it is expired even\n * when `getUser()` resolved nobody: this is not a \"the auth server blinked\"\n * null, it is a jar the writer cannot produce.\n */\n for (const name of baseline.ambiguousNames) shadowed.add(name);\n if (baseline.ambiguousKeys.length > 0) {\n console.warn(\n `[@ai-matrx/data/next] AMBIGUOUS auth cookie jar on host \"${host ?? \"?\"}\": ` +\n `${baseline.ambiguousKeys.join(\", \")} arrived UNCHUNKED and CHUNKED at once. ` +\n `Serving either would risk resolving a DIFFERENT person's session, so this ` +\n `request is treated as signed out and every cookie in those families is being ` +\n `expired at both scopes. The person signs in once and the jar is clean.`,\n );\n }\n\n /** Writes made during this pass, layered over the baseline. */\n const overlay = new Map<string, string>();\n const readCookies = (): AuthCookieValue[] => {\n const merged = baseline.cookies.map((cookie) =>\n overlay.has(cookie.name)\n ? { name: cookie.name, value: overlay.get(cookie.name) as string }\n : cookie,\n );\n const seen = new Set(merged.map(({ name }) => name));\n for (const [name, value] of overlay) {\n if (!seen.has(name)) merged.push({ name, value });\n }\n return merged;\n };\n const noteWrite = (name: string, value: string): void => {\n overlay.set(name, value);\n requestCookies.set(name, value);\n };\n\n // Seed the current storage key from the superseded one BEFORE the client\n // reads cookies. Absent-only, so a live session is never overwritten.\n const migrated = authCookie.migrateLegacyCookies(readCookies());\n for (const { name, value } of migrated) noteWrite(name, value);\n\n /**\n * Names already written on this response through `.cookies`, and the\n * writes that could not go that way.\n *\n * 🚨 THE SECOND ONE-ENTRY-PER-NAME TRAP, and it is how the stale unchunked\n * auth cookie is MADE. `@supabase/ssr`'s `applyServerStorage` removes a\n * cookie at BOTH Domain scopes by emitting the SAME name twice —\n * `Domain=.apex; Max-Age=0` and then host-only `Max-Age=0`. Funnel both\n * through `ResponseCookies.set` and the second REPLACES the first, so the\n * `.apex` copy is never expired. Measured on the real class:\n *\n * set(\"sb-…-v2\", \"\", {domain: \".aimatrx.com\", maxAge: 0})\n * set(\"sb-…-v2\", \"\", {maxAge: 0})\n * set(\"sb-…-v2.0\", \"<new session>\", {domain: \".aimatrx.com\"})\n * -> [\"sb-…-v2=; Path=/; Max-Age=0; …\", host-only ONLY\n * \"sb-…-v2.0=<new session>; …; Domain=.aimatrx.com\"]\n *\n * So writing a CHUNKED session over an unchunked one leaves the old\n * unchunked cookie alive at `.apex` forever — and `combineChunks` returns\n * the unchunked value the moment it is non-empty, without ever reading the\n * chunks. Sign in as someone else and the app keeps resolving the previous\n * person. That is an identity leak, produced by a lost `Set-Cookie`.\n *\n * The heal already knew this class for its own two-scope expiry and used\n * raw appends. The ORDINARY write path did not, which is why the residue\n * kept being created. Every duplicate name now goes out as a raw appended\n * header too, flushed in the one final append pass below.\n */\n const writtenNames = new Set<string>();\n const duplicateWrites: string[] = [];\n\n // ONE budget for every identity call this pass makes — see createAuthBudget.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n\n const applyCookies = (\n cookiesToSet: readonly CookieToSet[],\n headers: Record<string, string> = {},\n ): void => {\n // 🚨 A SPENT BUDGET WRITES NOTHING. auth-js clears the session when a\n // refresh fails non-retryably on an already-expired access token, and\n // our synthetic 408 IS that non-retryable failure. Letting that write\n // through would sign the person out because the network was slow.\n if (budget.exhausted && cookiesToSet.length > 0) {\n console.warn(\n `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) on host ` +\n `\"${host ?? \"?\"}\": the identity budget was exhausted on this request, so nothing ` +\n `learned here is trusted enough to change the jar. Nobody is signed out; the next ` +\n `request re-resolves. Names: ${cookiesToSet.map((c) => c.name).join(\", \")}`,\n );\n return;\n }\n for (const { name, value } of cookiesToSet) noteWrite(name, value);\n const next = createResponse();\n for (const cookie of response.cookies.getAll()) {\n next.cookies.set(cookie);\n }\n for (const cookie of cookiesToSet) {\n const responseCookie = toResponseCookie(cookie);\n if (writtenNames.has(responseCookie.name)) {\n // Raw, so it cannot displace the entry already on the response.\n // Appends are flushed LAST (see the two-pass rule in applyHeal), so a\n // genuine same-scope re-write still lands after the earlier value.\n duplicateWrites.push(serializeSetCookie(responseCookie));\n continue;\n }\n writtenNames.add(responseCookie.name);\n next.cookies.set(responseCookie);\n }\n const responseHeaders =\n cookiesToSet.length > 0\n ? { ...AUTH_COOKIE_RESPONSE_HEADERS, ...headers }\n : headers;\n for (const [name, value] of Object.entries(responseHeaders)) {\n next.headers.set(name, value);\n }\n response = next;\n };\n\n const client = createServerClient<DB>(url, key, {\n cookieOptions: hostCookieOptions,\n // Identity calls out of THIS pass share one deadline; see createAuthBudget.\n global: { fetch: budget.fetch },\n cookies: {\n getAll: () => readCookies(),\n setAll: (cookiesToSet, headers) => {\n applyCookies(\n cookiesToSet as readonly CookieToSet[],\n (headers ?? {}) as Record<string, string>,\n );\n },\n },\n });\n\n // NOTHING may run between constructing the client and the identity\n // resolve. Any await in between has produced random logouts that are close\n // to undebuggable. The rule is structural here instead of a comment a host\n // has to remember — `resolveIdentity` is one adjacent await.\n const { user, authUnavailable } = await resolveIdentity(client, budget);\n\n // A valid session that arrived under the superseded key is persisted ONCE\n // under the current key. A token the old authority issued fails getUser(),\n // is never persisted, and gets cleared below.\n if (user && migrated.length > 0) {\n const { name: _name, ...cookieOptions } = hostCookieOptions;\n applyCookies(\n readCookies()\n .filter(\n ({ name, value }) =>\n authCookie.isCurrentCookie(name) && value.length > 0,\n )\n .map(({ name, value }) => ({\n name,\n value,\n options: { ...DEFAULT_COOKIE_OPTIONS, ...cookieOptions },\n })),\n );\n }\n\n // The superseded storage key must die at BOTH scopes, and the clear below\n // can only reach one of them (one entry per NAME on a response). Its\n // host-only twin goes out as a raw header with the shadow heal.\n if (authCookie.legacyCookieName && hostCookieOptions.domain) {\n const legacy = authCookie.legacyCookieName;\n for (const { name } of rawCookies) {\n if (name === legacy || name.startsWith(`${legacy}.`)) shadowed.add(name);\n }\n }\n\n // 🚨 NEVER EXPIRE THE SUPERSEDED KEY ON A RESOLVE WE COULD NOT MAKE.\n // The migration above persists legacy -> current ONLY when a user resolved.\n // So on an unreachable auth server the copy under the new name was never\n // written, and expiring the legacy one here would destroy the only cookie\n // holding the session — a logout caused by a network blink, which is the\n // exact class this whole pass exists to prevent.\n if (authCookie.legacyCookieName && !authUnavailable) {\n await clearAuthCookiesAtScopes({\n getAll: () => readCookies(),\n setAll: (cookiesToSet, headers) => {\n applyCookies(\n cookiesToSet as readonly CookieToSet[],\n (headers ?? {}) as Record<string, string>,\n );\n },\n storageKey: authCookie.legacyCookieName,\n scopes: hostCookieOptions.domain\n ? [{ domain: hostCookieOptions.domain }]\n : [{}],\n });\n }\n\n /**\n * THE HEAL — CONVERGE ON THE CANONICAL SCOPE, never merely delete.\n *\n * A shadowed name means the same cookie exists host-only AND at `.apex`,\n * and NOTHING in a `Cookie` header says which scope holds which value. So\n * blindly expiring host-only can destroy the live session — if the good\n * token happened to be the host-only copy, that is a logout we caused.\n *\n * Instead: REWRITE the reconciled (non-empty) value at the canonical\n * `.apex` scope through `cookies.set`, and expire the host-only scope with\n * a raw appended `Set-Cookie`. Whichever scope held the good value, the\n * good value ends up at the canonical scope and the shadow dies. Idempotent\n * and safe to repeat. When every copy is empty there is no session to\n * preserve, so both scopes are simply expired.\n *\n * Raw headers because `ResponseCookies` keeps one entry per NAME — the\n * canonical write and the other scope's expiry cannot both go through it.\n * Applied ONCE, last, on whatever response actually leaves; nothing may\n * read `.cookies` afterwards or these get re-parsed into the one-per-name\n * map and the heal is undone.\n */\n const healed = new WeakSet<object>();\n const applyHeal = (target: Response): Response => {\n if (healed.has(target)) return target;\n // An unresolved identity is not evidence about the jar. Converging or\n // expiring a scope here would be acting on an answer we never got.\n if (authUnavailable) return target;\n if (shadowed.size === 0 && duplicateWrites.length === 0) return target;\n healed.add(target);\n const current = new Map(readCookies().map((c) => [c.name, c.value]));\n\n // 🚨 TWO PASSES, NEVER INTERLEAVED. `ResponseCookies.set` re-parses the\n // whole `Set-Cookie` header list into its one-entry-per-NAME map and\n // rewrites it — so a raw append made BEFORE a later `.set` is eaten.\n // Interleaving them shipped to production and silently dropped every\n // host-only expiry: the canonical re-issue went out, the shadow lived on,\n // and the heal did nothing. Proven with `curl` against the deployed build.\n // Every `.cookies` write happens here, and nothing may touch `.cookies`\n // after the append pass below.\n const expiries: string[] = [];\n for (const name of shadowed) {\n const value = current.get(name) ?? \"\";\n if (value.length > 0 && !user) {\n // 🚨 NEVER FREEZE AN UNVALIDATED VALUE. The surviving copy is only\n // known-good because `getUser()` above resolved a session FROM the\n // reconciled view on THIS request. Without that, a structurally clean\n // but STALE token — a heal racing a token refresh — would be pinned\n // at the canonical scope, and both hosts would then reject a\n // clean-looking jar until re-login (found on production by the walk's\n // rapid poison/reload cycles). A null user can equally mean the auth\n // server was briefly unreachable, so expiring here would log people\n // out over a blip. Touch nothing; `splitCookieJar` still reports, and\n // the banner says it in words.\n continue;\n }\n if (value.length > 0) {\n // The survivor, re-issued at the ONE canonical scope.\n target.cookies.set(\n toResponseCookie({\n name,\n value,\n options: {\n ...DEFAULT_COOKIE_OPTIONS,\n domain: hostCookieOptions.domain,\n path: \"/\",\n // A Domain-scoped auth cookie only ever exists on https here,\n // and `Secure` is not part of cookie identity, so re-issuing\n // with it set can only tighten what is already there.\n secure: true,\n },\n }),\n );\n } else if (hostCookieOptions.domain) {\n expiries.push(\n `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Domain=${hostCookieOptions.domain}; Secure; SameSite=Lax`,\n );\n }\n expiries.push(hostOnlyExpiryHeader(name));\n }\n // The ordinary writes that could not go through `.cookies` because the\n // name was already taken there. Flushed with the expiries, in the same\n // single append pass, AFTER every `.cookies` write on this response —\n // touching `.cookies` from here on re-parses the header list and eats\n // every one of these.\n for (const header of duplicateWrites) {\n target.headers.append(\"Set-Cookie\", header);\n }\n for (const header of expiries) {\n target.headers.append(\"Set-Cookie\", header);\n }\n if (shadowed.size === 0) {\n // Nothing was healed — only a two-scope write was rescued. No split\n // stamp, no scream, and no cache headers beyond what the write already\n // carried.\n return target;\n }\n\n // Stamped by the PACKAGE, not the host, so it rides EVERY healed\n // response — including redirects, which a host that stamps it at the end\n // of its own pass never reaches.\n target.headers.set(SPLIT_COOKIE_JAR_HEADER, \"1\");\n if (baseline.ambiguousKeys.length > 0) {\n target.headers.set(AMBIGUOUS_AUTH_COOKIE_HEADER, \"1\");\n }\n\n // A response that rewrites cookies must never be cached.\n for (const [key, value] of Object.entries(AUTH_COOKIE_RESPONSE_HEADERS)) {\n target.headers.set(key, value);\n }\n // LOUD: this is an automatic intervention on a user's cookie jar.\n console.warn(\n `[@ai-matrx/data/next] split auth cookie jar healed on host \"${host ?? \"?\"}\": ` +\n `${[...shadowed].join(\", \")} arrived at two scopes; the host-only copies ` +\n `were expired so the ${hostCookieOptions.domain} set is the only one left.`,\n );\n return target;\n };\n\n return {\n user: user as MiddlewareSession<DB, Response>[\"user\"],\n client,\n get response() {\n return applyHeal(response);\n },\n redirect: (targetUrl: URL): Response => {\n const redirect = createRedirect(targetUrl);\n for (const cookie of response.cookies.getAll()) {\n redirect.cookies.set(cookie);\n }\n for (const name of CARRIED_HEADERS) {\n const value = response.headers.get(name);\n if (value) redirect.headers.set(name, value);\n }\n return applyHeal(redirect);\n },\n splitCookieJar: shadowed.size > 0,\n ambiguousAuthCookies: baseline.ambiguousKeys.length > 0,\n authUnavailable,\n };\n }\n\n return {\n authCookie,\n cookieName: authCookie.cookieName,\n optionsForHost: authCookie.optionsForHost,\n browserOptions: authCookie.browserOptions,\n browserClient,\n serverClient,\n routeClient,\n middlewareSession,\n };\n}\n","/**\n * Shared auth-cookie options for EVERY Supabase client an application\n * constructs (browser, server, middleware, OAuth callbacks). All construction\n * sites MUST pass these options — a client using the default cookie name\n * cannot see the session the others wrote.\n *\n * WHY (the 2026-07 Matrx deployment split): one login must work on every\n * subdomain of the platform's apex (e.g. app.example.com, manage.example.com),\n * so on those hosts the auth cookie is issued domain-wide\n * (`Domain=.example.com`) instead of Supabase's default host-only cookie.\n *\n * The cookie is also RENAMED (default `sb-<project-ref>-auth-token` →\n * `sb-matrx-auth`): re-issuing an existing host-only cookie as domain-wide\n * under the SAME name leaves two same-name cookies in the browser (different\n * Domain attributes = distinct cookies), and whichever the browser sends\n * first wins — auth flaps. A new name makes the transition deterministic:\n * every user re-logs-in exactly once and the old cookie expires unused.\n * The `sb-` prefix is kept on purpose — cleanup paths that scrub session\n * cookies delete by that prefix.\n *\n * The Domain attribute is ONLY set on real apex hosts. localhost and preview\n * hosts get a host-only cookie — a browser rejects any Set-Cookie whose\n * Domain does not cover the current host, so setting it unconditionally would\n * silently break auth everywhere else.\n *\n * The apex domain is a VALUE the host passes in (never read from an env var\n * here): `createAuthCookie({ apexDomain: \"aimatrx.com\" })`.\n *\n * THE RENAME MIGRATION (0.5.0 — moved in from matrx-frontend, where it was\n * 44 lines of host code every other consumer would have had to re-derive):\n * replacing the auth authority (a new Supabase project) means a NEW cookie\n * name, and a tab still running the OLD bundle keeps writing the OLD name for\n * days. `migrateLegacyCookies` seeds the new storage key from the old one\n * ONLY when the new key is absent — never overwriting a live session — and\n * `isCurrentCookie` recognizes both the base name and Supabase's numbered\n * chunk suffixes (`<name>.0`, `<name>.1`, …), which a naive `startsWith`\n * check gets wrong for any cookie that merely shares the prefix.\n */\n\n/** Structurally compatible with @supabase/ssr's CookieOptionsWithName. */\nexport interface AuthCookieOptions {\n name: string;\n /** Remote hosts require HTTPS; loopback development remains usable over HTTP. */\n secure: boolean;\n domain?: string;\n}\n\n/** A cookie as read from a request — name + value, nothing else needed. */\nexport interface AuthCookieValue {\n name: string;\n value: string;\n}\n\nexport const DEFAULT_AUTH_COOKIE_NAME = \"sb-matrx-auth\";\n\nexport interface CreateAuthCookieArgs {\n /** The apex domain one login must span (e.g. `\"aimatrx.com\"`). */\n apexDomain: string;\n /** Override only for a second, unrelated deployment — never per-app. */\n cookieName?: string;\n /**\n * The storage key a PREVIOUS auth authority used. When present, cookies\n * under this name are seeded onto `cookieName` (absent-only) by\n * `migrateLegacyCookies`, and the middleware binding clears them at the\n * scope this host issued them on.\n */\n legacyCookieName?: string;\n}\n\nexport interface AuthCookie {\n cookieName: string;\n /** The superseded storage key, when this deployment has one. */\n legacyCookieName: string | undefined;\n /**\n * Cookie options for the host serving the current request.\n * @param host `Host` header or `window.location.hostname` (port tolerated).\n */\n optionsForHost(host: string | null | undefined): AuthCookieOptions;\n /** Browser variant — resolves the host from `window` (SSR-safe). */\n browserOptions(): AuthCookieOptions;\n /**\n * Is this cookie name the CURRENT auth cookie — base name or one of\n * Supabase's numbered chunks (`<name>.0`)? A prefix test is wrong: it\n * matches `<name>.anything`, including unrelated cookies.\n */\n isCurrentCookie(name: string): boolean;\n /**\n * Cookies to seed under the current name, derived from the legacy name.\n * Returns `[]` when any current cookie already exists (never overwrite a\n * live session) or when no legacy name is configured.\n */\n migrateLegacyCookies(\n cookies: readonly AuthCookieValue[],\n ): AuthCookieValue[];\n /**\n * THE POISONED-JAR READ. A browser can hold the SAME auth cookie name twice\n * — once host-only, once at `.apex` — because different Domain attributes\n * make distinct cookies, and it sends both in one `Cookie` header. Every\n * reassembler downstream (`@supabase/ssr`'s `combineChunks`) resolves a\n * chunk with `find()`, i.e. FIRST WINS, and the browser decides the order.\n * An empty shadow that sorts first therefore reassembles to `\"\"` and every\n * server read answers anonymous while the tab is signed in.\n *\n * This collapses each auth-cookie NAME to one entry, preferring the first\n * NON-EMPTY value. Non-auth cookies pass through untouched, in order.\n *\n * A `Cookie` header carries no Domain, so which scope a value came from is\n * genuinely unknowable here — non-empty-wins is the strongest correct rule\n * available, and `@supabase/ssr` already refuses a chunk set that decodes\n * incoherently. `shadowedNames` names every auth cookie that arrived more\n * than once, so the caller can EXPIRE the losing scope instead of leaving\n * the user to clear cookies by hand.\n *\n * THE SECOND POISON, AND IT IS AN IDENTITY LEAK: one storage key arriving\n * BOTH unchunked (`<name>`) and chunked (`<name>.0`, `<name>.1`). These are\n * DIFFERENT cookie names, so the per-name collapse above cannot see them,\n * and `@supabase/ssr`'s `combineChunks` returns the UNCHUNKED value the\n * moment it is non-empty — it never even reads the chunks. A stale base\n * cookie therefore shadows a freshly written chunked pair, and every read\n * resolves the PREVIOUS person while the new session sits unread beside it.\n * That is a different user's session being served, not merely a logout.\n *\n * `applyServerStorage` never writes that shape — it removes every chunk-like\n * sibling of whatever it writes — so a jar holding both is RESIDUE from a\n * removal that did not land (see `middlewareSession`: a two-scope removal\n * collapses on `ResponseCookies`, which is how this residue is made).\n *\n * WHICH COPY IS NEWER IS UNKNOWABLE HERE, and both directions of residue are\n * producible, so preferring either shape is a coin flip on WHOSE SESSION TO\n * SERVE. This drops the whole family from the read instead: nobody is signed\n * in, every name in it is returned in `ambiguousNames` to be expired, and the\n * host's honest-screen banner says so with the one action that fixes it. A\n * wrong sign-out is recoverable in one click; a wrong identity is not.\n */\n reconcile(cookies: readonly AuthCookieValue[]): ReconciledAuthCookies;\n}\n\n/** The outcome of {@link AuthCookie.reconcile}. */\nexport interface ReconciledAuthCookies {\n /** One entry per name; auth names resolved to their non-empty value. */\n cookies: AuthCookieValue[];\n /** Auth cookie names that arrived more than once (i.e. at two scopes). */\n shadowedNames: string[];\n /**\n * Auth cookie names belonging to a storage key that arrived UNCHUNKED AND\n * CHUNKED at once — see {@link AuthCookie.reconcile}. Their values have been\n * dropped from `cookies`, and they must be expired on EVERY host, including\n * the ones that issue a host-only cookie: unlike a two-scope shadow there is\n * no copy here that could be the good one.\n */\n ambiguousNames: string[];\n /** The storage keys that arrived in both shapes. Named for the scream. */\n ambiguousKeys: string[];\n /** `true` when the jar is split and needs healing. */\n split: boolean;\n}\n\n/**\n * Parse a raw `Cookie` request header, **PRESERVING DUPLICATES AND ORDER**.\n *\n * 🚨 THIS IS THE ONLY WAY TO SEE A SPLIT JAR. Next's `RequestCookies` (and the\n * `cookies()` store built on it) parses the header into a Map keyed by NAME,\n * so two same-name cookies at two Domain scopes collapse to ONE before any\n * application code runs — and the survivor is whichever the browser happened\n * to send LAST. Measured against the real class:\n *\n * \"sb-…-v2.0=; sb-…-v2.0=base64-REAL\" -> [{ value: \"base64-REAL\" }] works\n * \"sb-…-v2.0=base64-REAL; sb-…-v2.0=\" -> [{ value: \"\" }] anonymous\n *\n * That coin flip IS the production break, and it is invisible from the\n * collapsed jar: reconciliation over `getAll()` is a no-op no matter how\n * carefully it is written. Every door must therefore reconcile over THIS.\n */\nexport function parseCookieHeader(\n header: string | null | undefined,\n): AuthCookieValue[] {\n if (!header) return [];\n const out: AuthCookieValue[] = [];\n for (const pair of header.split(\";\")) {\n const eq = pair.indexOf(\"=\");\n // A valueless segment is a name with an empty value — exactly the shadow\n // this exists to catch — so it must not be skipped.\n const name = (eq === -1 ? pair : pair.slice(0, eq)).trim();\n if (!name) continue;\n const raw = eq === -1 ? \"\" : pair.slice(eq + 1).trim();\n let value = raw;\n try {\n value = decodeURIComponent(raw);\n } catch {\n // A malformed escape is not a reason to lose the cookie; Next's parser\n // keeps the raw text too.\n }\n out.push({ name, value });\n }\n return out;\n}\n\n/**\n * Is `name` the storage key itself or one of its numbered chunks? The same\n * membership test `@supabase/ssr`'s `isChunkLike` uses, so a family is defined\n * here exactly as the writer defines it. A bare `startsWith` is wrong: it also\n * matches `<key>.anything`, i.e. unrelated cookies.\n */\nfunction belongsToStorageKey(name: string, storageKey: string): boolean {\n if (name === storageKey) return true;\n if (!name.startsWith(`${storageKey}.`)) return false;\n return /^(0|[1-9][0-9]*)$/.test(name.slice(storageKey.length + 1));\n}\n\n/** `<from>` → `<to>`, carrying Supabase's numbered chunk suffix across. */\nfunction renamedCookie(\n cookie: AuthCookieValue,\n from: string,\n to: string,\n): AuthCookieValue | null {\n if (cookie.name === from) return { ...cookie, name: to };\n const prefix = `${from}.`;\n if (!cookie.name.startsWith(prefix)) return null;\n const chunk = cookie.name.slice(prefix.length);\n if (!/^(0|[1-9][0-9]*)$/.test(chunk)) return null;\n return { ...cookie, name: `${to}.${chunk}` };\n}\n\nexport function createAuthCookie(args: CreateAuthCookieArgs): AuthCookie {\n const cookieName = args.cookieName ?? DEFAULT_AUTH_COOKIE_NAME;\n const legacyCookieName = args.legacyCookieName;\n const apex = args.apexDomain.toLowerCase();\n\n const optionsForHost = (\n host: string | null | undefined,\n ): AuthCookieOptions => {\n const normalized = (host ?? \"\").toLowerCase();\n const hostname = normalized.startsWith(\"[\")\n ? normalized.slice(1, normalized.indexOf(\"]\"))\n : normalized === \"::1\" ? normalized : normalized.split(\":\")[0] ?? \"\";\n const loopback = hostname === \"localhost\" || hostname.endsWith(\".localhost\") ||\n hostname === \"::1\" || /^127(?:\\.(?:0|[1-9]\\d{0,2})){3}$/.test(hostname) &&\n hostname.split(\".\").every((part) => Number(part) <= 255);\n const secure = !loopback;\n const onApex = hostname === apex || hostname.endsWith(`.${apex}`);\n return onApex\n ? { name: cookieName, domain: `.${apex}`, secure }\n : { name: cookieName, secure };\n };\n\n const isCurrentCookie = (name: string): boolean =>\n belongsToStorageKey(name, cookieName);\n\n const isAuthCookie = (name: string): boolean =>\n isCurrentCookie(name) ||\n (legacyCookieName !== undefined &&\n (name === legacyCookieName || name.startsWith(`${legacyCookieName}.`)));\n\n const reconcile = (\n cookies: readonly AuthCookieValue[],\n ): ReconciledAuthCookies => {\n const seen = new Map<string, number>();\n const out: AuthCookieValue[] = [];\n const shadowed = new Set<string>();\n\n for (const cookie of cookies) {\n const at = seen.get(cookie.name);\n if (at === undefined) {\n seen.set(cookie.name, out.length);\n out.push({ name: cookie.name, value: cookie.value });\n continue;\n }\n if (!isAuthCookie(cookie.name)) continue; // non-auth: first wins, as before\n shadowed.add(cookie.name);\n const held = out[at];\n // Non-empty wins. Later non-empty values do NOT displace an earlier\n // non-empty one — with no scope information, churning the choice would\n // make reassembly non-deterministic across requests.\n if (held !== undefined && held.value.length === 0) {\n out[at] = { name: cookie.name, value: cookie.value };\n }\n }\n\n // THE FAMILY PASS. A storage key may arrive unchunked AND chunked at once;\n // those are different NAMES, so the collapse above cannot have seen it.\n // `combineChunks` would take the unchunked value and never read the chunks,\n // which serves whichever person's session happens to be sitting in the base\n // cookie. Refuse the family instead of guessing — see `reconcile`'s docs.\n const ambiguous = new Set<string>();\n const ambiguousKeys: string[] = [];\n const byName = new Map(out.map((cookie) => [cookie.name, cookie] as const));\n const storageKeys = [cookieName, ...(legacyCookieName ? [legacyCookieName] : [])];\n for (const storageKey of storageKeys) {\n const unchunked = byName.get(storageKey);\n const firstChunk = byName.get(`${storageKey}.0`);\n if (!unchunked?.value || !firstChunk?.value) continue;\n ambiguousKeys.push(storageKey);\n for (const { name } of out) {\n if (belongsToStorageKey(name, storageKey)) ambiguous.add(name);\n }\n }\n\n const resolved =\n ambiguous.size === 0\n ? out\n : out.filter(({ name }) => !ambiguous.has(name));\n\n const shadowedNames = [...shadowed];\n const ambiguousNames = [...ambiguous];\n return {\n cookies: resolved,\n shadowedNames,\n ambiguousNames,\n ambiguousKeys,\n split: shadowedNames.length > 0 || ambiguousNames.length > 0,\n };\n };\n\n return {\n cookieName,\n legacyCookieName,\n optionsForHost,\n reconcile,\n browserOptions: () =>\n optionsForHost(\n typeof window !== \"undefined\" ? window.location.hostname : null,\n ),\n isCurrentCookie,\n migrateLegacyCookies: (cookies) => {\n if (!legacyCookieName) return [];\n if (cookies.some(({ name }) => isCurrentCookie(name))) return [];\n return cookies.flatMap((cookie) => {\n const renamed = renamedCookie(cookie, legacyCookieName, cookieName);\n return renamed ? [renamed] : [];\n });\n },\n };\n}\n"],"mappings":"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","names":["key"]}
1
+ {"version":3,"sources":["../src/next/supabase.ts","../src/db/auth-cookie.ts"],"sourcesContent":["/**\n * @ai-matrx/data/next — the Next.js Supabase binding.\n *\n * WHY THIS EXISTS (C22 / THE ALL-INCLUSIVE LAW): a Next.js app has FIVE\n * distinct places that must construct a Supabase client — the browser, a\n * Server Component, a Route Handler, the proxy/middleware, and an OAuth\n * callback — and every one of them must pass the SAME auth-cookie options or\n * it cannot see the session the others wrote. Before this module, matrx-frontend\n * carried that construction five times, each with its own env guard, its own\n * cookie adapter, and its own copy of the \"do not run code between\n * createServerClient and getUser()\" hazard. Every app on the platform would\n * have re-derived all five.\n *\n * The host now injects IDENTITY only — URL, publishable key, apex domain, the\n * cookie names — and the package owns every hard part:\n *\n * - env validation with a named, actionable error (never `process.env.X!`,\n * which fails as a cryptic `undefined.trim()` TypeError);\n * - the domain-wide cookie option construction on every single door;\n * - the SSR/browser split, including the Server-Component write that must be\n * swallowed because only the proxy may set cookies there;\n * - the browser singleton on a `globalThis` slot (module-level mutable state\n * is banned — dual ESM/CJS bundles would otherwise instantiate two clients\n * and split the session);\n * - the middleware session dance in ONE call: legacy-key migration, the\n * getUser() adjacency rule, session persistence under the new key, legacy\n * cookie clearing at the issuing scope, the no-store response headers, and\n * cookie carry-over onto redirects (dropping any one of these logs users\n * out at random, which is exactly how it was found).\n *\n * NO `next/*` IMPORT LIVES HERE. Next's own primitives (`cookies()`,\n * `NextResponse`) arrive through structural seams the host passes in, so this\n * module builds and tests outside Next and cannot drag the framework into a\n * non-Next consumer's graph.\n */\n\nimport {\n clearAuthCookiesAtScopes,\n createBrowserClient,\n createServerClient,\n DEFAULT_COOKIE_OPTIONS,\n} from \"@supabase/ssr\";\nimport {\n createAuthCookie,\n parseCookieHeader,\n type AuthCookie,\n type AuthCookieOptions,\n type AuthCookieValue,\n} from \"../db/auth-cookie\";\n\n/**\n * A `Database` type as generated by Supabase. Deliberately structural: the\n * package never imports a host's generated types, and a host that has not\n * generated any still gets a working (untyped-rows) client.\n */\nexport type DatabaseLike = Record<string, unknown>;\n\nexport type NextBrowserClient<DB extends DatabaseLike> = ReturnType<\n typeof createBrowserClient<DB>\n>;\nexport type NextServerClient<DB extends DatabaseLike> = ReturnType<\n typeof createServerClient<DB>\n>;\n\n/** A cookie to write, as `@supabase/ssr` hands it to `setAll`. */\nexport interface CookieToSet {\n name: string;\n value: string;\n options?: Record<string, unknown> | undefined;\n}\n\n/**\n * The subset of Next's `await cookies()` store this binding uses. Structural\n * on purpose — `ReadonlyRequestCookies` satisfies it by shape.\n */\nexport interface ServerCookieStore {\n getAll(): readonly AuthCookieValue[];\n set(name: string, value: string, options?: Record<string, unknown>): void;\n}\n\n/** The subset of `NextRequest[\"cookies\"]` the middleware binding uses. */\nexport interface RequestCookieJar {\n getAll(): readonly AuthCookieValue[];\n set(name: string, value: string): void;\n}\n\n/**\n * A cookie in Next's `ResponseCookies` shape: name, value, and every\n * attribute FLAT on the same object.\n *\n * 🚨 THIS SHAPE IS LOAD-BEARING, and modelling it as `never` cost the platform\n * a production outage (2026-09-01). `ResponseCookies.set` accepts either\n * `(name, value, attrs)` or ONE flat object — it does NOT accept `@supabase/ssr`'s\n * `{ name, value, options }`, and it does not throw on it either: it reads\n * `name`/`value`, ignores the unknown `options` key, and serializes\n * `sb-…-auth-v2.0=; Path=/`. Every attribute is lost. So:\n * · a token refresh wrote a HOST-ONLY session cookie shadowing the real\n * `.apex` one, and\n * · every chunk REMOVAL (`value: \"\"`, `maxAge: 0`) became a PERSISTENT EMPTY\n * host-only cookie instead of a deletion.\n * Two same-name cookies then reach the server in one `Cookie` header, the\n * empty one sorts first, `combineChunks`' `find()` takes it, and every server\n * read answers anonymous while the tab still shows a signed-in user.\n * `@supabase/ssr` cookies are converted with `toResponseCookie` at the single\n * boundary below; the `never` that hid this is gone.\n */\nexport interface ResponseCookie {\n name: string;\n value: string;\n domain?: string | undefined;\n path?: string | undefined;\n maxAge?: number | undefined;\n expires?: Date | number | undefined;\n httpOnly?: boolean | undefined;\n secure?: boolean | undefined;\n sameSite?: boolean | \"lax\" | \"strict\" | \"none\" | undefined;\n priority?: \"low\" | \"medium\" | \"high\" | undefined;\n partitioned?: boolean | undefined;\n}\n\n/**\n * Exactly the attributes Next's `ResponseCookies` serializes. Anything else on\n * an `@supabase/ssr` cookie is dropped by that class anyway — naming them is\n * what makes the drop visible instead of silent.\n */\nconst RESPONSE_COOKIE_ATTRIBUTES = [\n \"domain\",\n \"path\",\n \"maxAge\",\n \"expires\",\n \"httpOnly\",\n \"secure\",\n \"sameSite\",\n \"priority\",\n \"partitioned\",\n] as const satisfies readonly (keyof ResponseCookie)[];\n\n/**\n * The subset of `NextResponse[\"cookies\"]` the middleware binding uses.\n */\nexport interface ResponseCookieJar {\n getAll(): ResponseCookie[];\n set(cookie: ResponseCookie): void;\n}\n\n/** The subset of a `NextResponse` the middleware binding drives. */\nexport interface MiddlewareResponse {\n cookies: ResponseCookieJar;\n headers: {\n get(name: string): string | null;\n set(name: string, value: string): void;\n /**\n * Required for the split-jar heal. `ResponseCookies` keeps ONE entry per\n * cookie NAME, so the same name cannot be expired at two Domain scopes\n * through it — the second `set` replaces the first. The losing scope is\n * expired with raw appended `Set-Cookie` headers instead.\n */\n append(name: string, value: string): void;\n };\n}\n\nexport interface NextSupabaseConfig {\n /**\n * `process.env.NEXT_PUBLIC_SUPABASE_URL` — read STATICALLY at the call site.\n * Next only inlines `NEXT_PUBLIC_*` into client bundles for static member\n * accesses; a dynamic lookup inside this package would be `undefined` in\n * every browser bundle.\n */\n supabaseUrl: string | undefined;\n /** `process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`, read statically. */\n publishableKey: string | undefined;\n /** The apex domain one login must span, e.g. `\"aimatrx.com\"`. */\n apexDomain: string;\n /** Auth cookie name. Defaults to the package's `sb-matrx-auth`. */\n cookieName?: string;\n /** A superseded storage key to migrate from and then clear. */\n legacyCookieName?: string;\n /**\n * Host wrapper applied to the BROWSER client only — the one legitimate\n * host seam here (diagnostics/error capture). It must not change query\n * behavior; the package never inspects what it returns.\n *\n * `C extends object` because a Supabase client always IS one, and a real\n * capture wrapper is a `Proxy` that must constrain the same way. Leaving\n * `C` unconstrained made every honest host wrapper unassignable (found at\n * matrx-frontend adoption, 0.5.1).\n */\n wrapBrowserClient?: <C extends object>(client: C) => C;\n}\n\n/** Cache-defeating headers every auth-cookie-bearing response must carry. */\nexport const AUTH_COOKIE_RESPONSE_HEADERS: Readonly<Record<string, string>> =\n Object.freeze({\n \"Cache-Control\": \"private, no-cache, no-store, must-revalidate, max-age=0\",\n Expires: \"0\",\n Pragma: \"no-cache\",\n });\n\nconst CARRIED_HEADERS = [\"cache-control\", \"expires\", \"pragma\"] as const;\n\n/**\n * Stamped on any response whose auth cookie jar was found split and healed.\n * A shell that already rendered may be wearing an identity this pass could not\n * see, and must say so rather than paint a signed-in screen of anonymous data.\n */\nexport const SPLIT_COOKIE_JAR_HEADER = \"x-matrx-split-jar\";\n\n/**\n * Stamped when this request's auth cookie jar held one storage key BOTH\n * unchunked and chunked — see `AuthCookie.reconcile`. Distinct from\n * `SPLIT_COOKIE_JAR_HEADER` because the person's experience is different and\n * the screen must say the true thing:\n *\n * split jar → the tab still holds a session; the server just could not\n * read it. \"Sign in again\" restores the page.\n * ambiguous → we deliberately refused BOTH copies rather than risk\n * resolving the wrong person, so this person has just been\n * signed out BY US. That is an automatic intervention on\n * somebody's session and it must not happen quietly: the\n * browser's own read is anonymous too, so nothing else on the\n * page can notice, and without this header the app shows a\n * login screen with no explanation.\n */\nexport const AMBIGUOUS_AUTH_COOKIE_HEADER = \"x-matrx-auth-ambiguous\";\n\n/**\n * `@supabase/ssr`'s `{ name, value, options }` → Next's flat `ResponseCookie`.\n * The ONE conversion boundary; see {@link ResponseCookie} for what skipping it\n * cost.\n */\nexport function toResponseCookie(cookie: CookieToSet): ResponseCookie {\n const options = (cookie.options ?? {}) as Record<string, unknown>;\n // Built key by key, never spread: `options.name` would fight the real name,\n // and an unrecognized key would read as carried when Next drops it.\n const out: Record<string, unknown> = {\n name: cookie.name,\n value: cookie.value,\n };\n for (const attribute of RESPONSE_COOKIE_ATTRIBUTES) {\n const value = options[attribute];\n if (value !== undefined) out[attribute] = value;\n }\n return out as unknown as ResponseCookie;\n}\n\n/**\n * A raw `Set-Cookie` that EXPIRES `name` at the host-only scope — the scope a\n * response cannot reach through `ResponseCookies` once the same name is being\n * written at `.apex`. A browser ignores a `Set-Cookie` for a scope the current\n * host does not own, so this is a no-op on a healthy jar.\n */\nfunction hostOnlyExpiryHeader(name: string): string {\n return `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=Lax`;\n}\n\n/**\n * A `ResponseCookie` as a raw `Set-Cookie` header value, serialized the way\n * Next's `ResponseCookies` serializes it. Needed because a response can only\n * carry ONE entry per cookie NAME through `.cookies`, and `@supabase/ssr`\n * deliberately writes some names TWICE — see `applyCookies`.\n */\nexport function serializeSetCookie(cookie: ResponseCookie): string {\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n parts.push(`Path=${cookie.path ?? \"/\"}`);\n if (cookie.domain !== undefined) parts.push(`Domain=${cookie.domain}`);\n if (cookie.maxAge !== undefined) parts.push(`Max-Age=${cookie.maxAge}`);\n if (cookie.expires !== undefined) {\n const expires =\n cookie.expires instanceof Date\n ? cookie.expires\n : new Date(cookie.expires);\n parts.push(`Expires=${expires.toUTCString()}`);\n }\n if (cookie.httpOnly === true) parts.push(\"HttpOnly\");\n if (cookie.secure === true) parts.push(\"Secure\");\n if (typeof cookie.sameSite === \"string\") {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n return parts.join(\"; \");\n}\n\n/**\n * Thrown when identity is missing. Named and actionable — the alternative\n * (`process.env.X!`) surfaces as `Cannot read properties of undefined\n * (reading 'trim')` from inside Supabase, which taught nobody anything.\n */\nexport class MissingSupabaseIdentityError extends Error {\n readonly variable: string;\n constructor(variable: string) {\n super(\n `@ai-matrx/data/next: missing required Supabase identity \"${variable}\". ` +\n `Pass it as a STATIC member access (process.env.${variable}) to createNextSupabase().`,\n );\n this.name = \"MissingSupabaseIdentityError\";\n this.variable = variable;\n }\n}\n\nfunction requireIdentity(value: string | undefined, variable: string): string {\n const trimmed = value?.trim();\n if (!trimmed) throw new MissingSupabaseIdentityError(variable);\n return trimmed;\n}\n\n/**\n * THE AUTH RESOLVE BUDGET — the proxy is on the critical path of EVERY request\n * and nothing below it may hang forever.\n *\n * Next 16 runs `proxy.ts` in the Node.js runtime and that \"cannot be\n * configured\" (Next 16 upgrade guide), so on Vercel this pass is a Lambda with\n * a 15s task cap, concurrency of one per instance, and a real cold start. A\n * single `<Link>` prefetch storm — a chat sidebar is 20-40 links — spins up\n * that many instances at once, and an unbounded auth round trip in each one\n * turns a downstream blink into `504 MIDDLEWARE_INVOCATION_TIMEOUT` on the\n * page the person actually asked for. Measured on aimatrx.com, 2026-09-20: 19\n * such 504s in 48h, every one of them in a burst of simultaneous prefetches,\n * every one of them \"Task timed out after 15 seconds\".\n *\n * So the deadline is OURS, not the platform's: a budget short enough that a\n * stalled auth server costs one request its identity instead of costing the\n * whole page its response.\n */\nconst AUTH_RESOLVE_TIMEOUT_MS = 2_500;\n\n/**\n * The identity calls this budget covers, and ONLY these. Sign-in, sign-up,\n * OAuth code exchange and password reset also live under `/auth/v1/` and a\n * person is waiting on them; they keep their natural timeout.\n */\nconst IDENTITY_PATHS = [\n \"/auth/v1/user\",\n \"/auth/v1/token?grant_type=refresh_token\",\n \"/auth/v1/.well-known/jwks.json\",\n];\n\n/** Non-retryable by construction: not a 5xx, so auth-js stops retrying. */\nconst AUTH_BUDGET_EXHAUSTED_STATUS = 408;\nconst AUTH_BUDGET_EXHAUSTED_CODE = \"auth_budget_exhausted\";\n\n/**\n * ONE budget for the WHOLE identity resolve of a request, not one per socket.\n *\n * The 0.17.0 budget aborted each `/auth/v1/` socket at 2.5s. That was not\n * enough, and 2026-09-21 proved it: auth-js retries a token refresh with\n * exponential backoff for up to 30s (`AUTO_REFRESH_TICK_DURATION_MS`), and a\n * per-socket abort is exactly the retryable error that keeps that loop going —\n * five aborted attempts plus their pauses is ~15.5s, past Vercel's 15s task\n * cap, and the proxy answered `504` while believing it had a budget.\n *\n * This wrapper holds a deadline from the FIRST identity call. While the\n * deadline is open every identity call gets what is LEFT of it, aborting the\n * socket (a `Promise.race` would leave it hanging and holding the Lambda).\n * The moment an attempt aborts, or the deadline passes, the budget is\n * EXHAUSTED and every further identity call answers synchronously with a\n * synthetic `408 auth_budget_exhausted` — a non-retryable auth error — so\n * auth-js stops its loop at once instead of sleeping through it. The whole\n * resolve therefore costs at most the budget plus one 200ms backoff.\n *\n * `exhausted` is also the cookie gate: an exhausted budget must never write,\n * expire or heal a cookie, because auth-js clears the session on a\n * non-retryable refresh failure once the access token has truly expired, and\n * that write is a logout caused by a slow network.\n */\nexport interface AuthBudget {\n fetch: typeof fetch;\n readonly exhausted: boolean;\n}\n\n/**\n * 🚨 A REFRESH THAT WAS SENT IS NEVER ABANDONED AT THE BUDGET (2026-10-07).\n *\n * `grant_type=refresh_token` ROTATES the refresh token on the auth server the\n * moment it is processed. Aborting it at 2.5s did not stop the rotation — it only\n * threw away the new tokens, so no cookie was written and the browser kept a\n * refresh token the server had already spent. Past GoTrue's reuse window that\n * token is refused and the person is signed out (and a server-rendered read made\n * on the dropped session went out as `anon`: their own chats answered \"sign in\").\n * So a refresh, once on the wire, runs to completion and its answer is used,\n * bounded only by this ceiling — below Vercel's 15s proxy cap. A refresh that has\n * NOT been sent yet still obeys the budget (nothing rotated, nothing lost).\n */\nexport const AUTH_REFRESH_CEILING_MS = 8_000;\n\nconst REFRESH_GRANT_PATH = \"/auth/v1/token?grant_type=refresh_token\";\n\nexport function createAuthBudget(\n timeoutMs: number,\n options: { refreshCeilingMs?: number } = {},\n): AuthBudget {\n const refreshCeilingMs = Math.max(timeoutMs, options.refreshCeilingMs ?? AUTH_REFRESH_CEILING_MS);\n let deadline: number | null = null;\n let exhausted = false;\n\n const isIdentityCall = (url: string): boolean =>\n IDENTITY_PATHS.some((path) => url.includes(path));\n\n const exhaustedResponse = (): Response =>\n new Response(\n JSON.stringify({\n error_code: AUTH_BUDGET_EXHAUSTED_CODE,\n msg:\n `identity resolve budget of ${timeoutMs}ms is exhausted for this request; ` +\n `not retrying`,\n }),\n {\n status: AUTH_BUDGET_EXHAUSTED_STATUS,\n headers: { \"content-type\": \"application/json\" },\n },\n );\n\n const budgetedFetch: typeof fetch = async (input, init) => {\n const url =\n typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n if (!isIdentityCall(url) || init?.signal) {\n return fetch(input as RequestInfo, init);\n }\n const now = Date.now();\n if (deadline === null) deadline = now + timeoutMs;\n const remaining = deadline - now;\n if (exhausted || remaining <= 0) {\n exhausted = true;\n return exhaustedResponse();\n }\n // Once sent, a refresh is allowed the ceiling, not what is left of the budget.\n const allowed = url.includes(REFRESH_GRANT_PATH) ? refreshCeilingMs : remaining;\n try {\n return await fetch(input as RequestInfo, {\n ...init,\n signal: AbortSignal.timeout(allowed),\n });\n } catch (cause) {\n const name = String((cause as { name?: unknown })?.name ?? \"\");\n if (name === \"TimeoutError\" || name === \"AbortError\") exhausted = true;\n throw cause;\n }\n };\n\n return {\n fetch: budgetedFetch,\n get exhausted() {\n return exhausted;\n },\n };\n}\n\n/**\n * `true` for the errors that mean \"we could not REACH the auth authority\",\n * never for \"this person is not signed in\".\n *\n * The distinction is the whole point: a transport failure must not be read as\n * a logout, or a blink on the network signs people out (and, on a protected\n * route, bounces them to /login mid-session).\n *\n * Matched by NAME rather than by importing `isAuthRetryableFetchError`, so\n * this package keeps its single `@supabase/ssr` dependency and does not take\n * on `@supabase/auth-js` and its ESM/CJS dual-graph hazard.\n */\nfunction isAuthTransportFailure(error: unknown): boolean {\n if (!error || typeof error !== \"object\") return false;\n const name = String((error as { name?: unknown }).name ?? \"\");\n if (\n name === \"AuthRetryableFetchError\" ||\n name === \"TimeoutError\" ||\n name === \"AbortError\"\n ) {\n return true;\n }\n const status = (error as { status?: unknown }).status;\n if (status === 0 || status === 504 || status === 503) return true;\n // The synthetic answer this package hands auth-js once the budget is spent.\n const code = (error as { code?: unknown }).code;\n return status === AUTH_BUDGET_EXHAUSTED_STATUS || code === AUTH_BUDGET_EXHAUSTED_CODE;\n}\n\n/** What one identity resolve settled, and whether it could settle at all. */\ninterface ResolvedIdentity {\n user: ({ id: string } & Record<string, unknown>) | null;\n authUnavailable: boolean;\n}\n\n/**\n * THE ONE IDENTITY RESOLVE. `getClaims()`, never `getUser()`.\n *\n * `getUser()` sends a request to the Auth server for EVERY JWT — an auth-server\n * lookup on the critical path of every matched request. `getClaims()` verifies\n * the access token against the project's JWKS with WebCrypto, locally and\n * usually with no network call at all, and auth-js caches the key set in a\n * module-level `GLOBAL_JWKS` shared by every client in the process — so a warm\n * instance re-verifies with zero I/O and only a genuine token refresh or a\n * JWKS TTL expiry goes out to the network.\n *\n * This requires an ASYMMETRIC signing key on the project (ECC/RSA). With a\n * symmetric secret `getClaims()` silently degrades to a `getUser()`-shaped\n * request per call, which is correct but gives back none of the saving — so a\n * project that turns asymmetric keys off pays the old cost and nothing breaks.\n *\n * Verification is cryptographic, so the claims are as trusted as `getUser()`'s\n * answer; this is not the untrusted `getSession()` read.\n */\nasync function resolveIdentity(\n client: {\n auth: {\n getClaims: () => Promise<{\n data: { claims?: Record<string, unknown> } | null;\n error: unknown;\n }>;\n };\n },\n budget?: AuthBudget,\n): Promise<ResolvedIdentity> {\n const unreachable = (cause: unknown): ResolvedIdentity => {\n console.warn(\n `[@ai-matrx/data/next] AUTH UNREACHABLE within ${AUTH_RESOLVE_TIMEOUT_MS}ms — ` +\n `this request is being passed through WITHOUT an identity and WITHOUT ` +\n `touching a single auth cookie. Nobody is being signed out: the shell ` +\n `resolves auth again on the client, and the next request re-resolves it ` +\n `here. If this is not rare, the auth authority (or the network to it) is ` +\n `the thing to fix — raising this budget only trades it back for a 504. ` +\n `Cause: ${String(cause)}`,\n );\n return { user: null, authUnavailable: true };\n };\n\n try {\n const { data, error } = await client.auth.getClaims();\n if (error) {\n return isAuthTransportFailure(error) || budget?.exhausted\n ? unreachable(error)\n : { user: null, authUnavailable: false };\n }\n const claims = data?.claims;\n if (!claims || typeof claims.sub !== \"string\") {\n // No claims AND a spent budget is \"we could not tell\", not \"nobody\".\n return budget?.exhausted\n ? unreachable(\"identity budget exhausted before a token could be verified\")\n : { user: null, authUnavailable: false };\n }\n // The JWT carries the identity fields a proxy decides with. Kept as a\n // `{ id }`-shaped object so every existing `session.user` read — all of\n // which are truthiness or `.id` — is unchanged by the switch.\n return {\n user: { ...claims, id: claims.sub },\n authUnavailable: false,\n };\n } catch (cause) {\n return unreachable(cause);\n }\n}\n\nexport interface MiddlewareSessionArgs<Response> {\n /** `Host` header of the incoming request. */\n host: string | null | undefined;\n /** `request.cookies` */\n requestCookies: RequestCookieJar;\n /**\n * The raw `Cookie` header (`request.headers.get(\"cookie\")`). REQUIRED to\n * detect and heal a split jar — `request.cookies` has already collapsed the\n * duplicates. Without it this pass cannot see the shadow and cannot expire\n * it, which is exactly how the \"healed\" 0.7.x build still left poisoned\n * browsers poisoned.\n */\n cookieHeader?: string | null | undefined;\n /** `() => NextResponse.next({ request })` — a FRESH response each call. */\n createResponse: () => Response;\n /** `(url) => NextResponse.redirect(url)` */\n createRedirect: (url: URL) => Response;\n}\n\nexport interface MiddlewareSession<DB extends DatabaseLike, Response> {\n /**\n * The validated user, or `null`. Resolved via `getClaims()` — a local\n * cryptographic verification of the access token, not a `getSession()` read.\n *\n * `null` is ambiguous on its own: check `authUnavailable` before treating it\n * as \"signed out\". A transport failure produces `null` too, and acting on\n * that as a logout is how a network blink bounces a signed-in person.\n */\n user: ({ id: string } & Record<string, unknown>) | null;\n /** The client, in case the caller needs a further authenticated read. */\n client: NextServerClient<DB>;\n /** The response carrying every session cookie written during this pass. */\n response: Response;\n /**\n * A redirect that CARRIES the session cookies and cache headers. Building a\n * bare `NextResponse.redirect` here drops refreshed tokens on the floor and\n * logs the user out on the next request.\n */\n redirect(url: URL): Response;\n /**\n * `true` when this request arrived with the same auth cookie at two Domain\n * scopes. The response heals the jar, but the SHELL that already rendered\n * may be holding an identity this pass could not see — a host that renders\n * a signed-in shell must say so in words rather than paint a signed-in\n * screen full of anonymous data.\n */\n splitCookieJar: boolean;\n /**\n * `true` when a storage key arrived BOTH unchunked and chunked and this pass\n * therefore refused both copies rather than risk resolving the wrong person.\n * The person has been signed out by us and must be told so.\n */\n ambiguousAuthCookies: boolean;\n /**\n * `true` when the auth authority could not be REACHED inside the resolve\n * budget, so this pass has no opinion about who this is.\n *\n * The contract when it is set: `user` is `null` but the person is NOT signed\n * out, not one auth cookie was written, expired or healed, and the host must\n * neither bounce to login nor paint a signed-out screen as if it were\n * settled. Let the request through; the client resolves auth itself and the\n * next request re-resolves it here.\n */\n authUnavailable: boolean;\n}\n\nexport interface NextSupabase<DB extends DatabaseLike> {\n /** The auth-cookie primitive this binding is built on. */\n readonly authCookie: AuthCookie;\n readonly cookieName: string;\n optionsForHost(host: string | null | undefined): AuthCookieOptions;\n browserOptions(): AuthCookieOptions;\n\n /**\n * The browser client. Memoized on a `globalThis` slot keyed by identity, so\n * the ESM and CJS graphs of this package share ONE client and therefore one\n * auth state — a per-module singleton would silently split them.\n */\n browserClient(): NextBrowserClient<DB>;\n\n /**\n * A Server Component / Server Action client over `await cookies()`.\n * Cookie writes from a Server Component are swallowed by design: only the\n * proxy may set cookies there, and it refreshes the session anyway.\n */\n serverClient(args: {\n cookieStore: ServerCookieStore;\n host: string | null | undefined;\n /**\n * The raw `Cookie` request header (`(await headers()).get(\"cookie\")`).\n * REQUIRED to see a split jar: `cookies()` collapses two same-name cookies\n * to one before this code runs. Omitting it degrades this door to the\n * collapsed view, where the surviving copy is whichever the browser sent\n * last — a coin flip between the session and anonymous.\n */\n cookieHeader?: string | null | undefined;\n /**\n * See `routeClient`'s `appendSetCookie` — Next's `cookies()` store keys by\n * NAME exactly like `ResponseCookies`, so a two-scope removal collapses\n * here too. A Route Handler that signs somebody in MUST pass this (buffer\n * the headers and append them onto the response it returns, last).\n * A Server Component has no response to append to and its writes are\n * swallowed anyway, so it correctly passes nothing.\n */\n appendSetCookie?: (header: string) => void;\n }): NextServerClient<DB>;\n\n /**\n * A Route Handler client: reads the request's cookies, writes onto the\n * response you are about to return.\n */\n routeClient(args: {\n requestCookies: Pick<RequestCookieJar, \"getAll\">;\n setCookie: (cookie: CookieToSet) => void;\n host: string | null | undefined;\n /** See `serverClient` — same collapse, same requirement. */\n cookieHeader?: string | null | undefined;\n /**\n * `(header) => response.headers.append(\"Set-Cookie\", header)`.\n *\n * 🚨 REQUIRED ON ANY DOOR THAT SIGNS SOMEBODY IN. `@supabase/ssr` removes a\n * cookie at BOTH Domain scopes by emitting the SAME name twice, and every\n * jar a host can pass to `setCookie` — `NextResponse.cookies`, Next's\n * `cookies()` store — keeps ONE entry per NAME, so the second write\n * REPLACES the first and the `.apex` removal is silently lost. That is how\n * a stale UNCHUNKED auth cookie survives beside a fresh CHUNKED pair, and\n * `combineChunks` then serves the STALE one — the previous person's\n * session, to the person who just signed in (2026-09-08, R-O3).\n *\n * With this seam the duplicate goes out as a raw header instead. Without\n * it the drop is announced loudly rather than swallowed.\n *\n * THE CALLER'S OBLIGATION: `ResponseCookies.set` re-serializes the whole\n * `Set-Cookie` list from its own map and DISCARDS raw appends made since.\n * Nothing may touch `response.cookies` after a header appended here.\n */\n appendSetCookie?: (header: string) => void;\n }): NextServerClient<DB>;\n\n /**\n * THE middleware/proxy auth pass, entire. Runs legacy-key migration, builds\n * the client, resolves `getUser()` with nothing in between, persists a\n * migrated session under the current key, clears the superseded key at the\n * scope this host issued it on, and stamps no-store headers on any response\n * that carries cookies.\n */\n middlewareSession<Response extends MiddlewareResponse>(\n args: MiddlewareSessionArgs<Response>,\n ): Promise<MiddlewareSession<DB, Response>>;\n}\n\ninterface BrowserSlot {\n key: string;\n client: unknown;\n}\n\n/**\n * The browser-client singleton lives on `globalThis`, not in a module\n * variable: with dual ESM/CJS output each loader graph instantiates its own\n * copy of this module, and two Supabase browser clients means two auth\n * listeners racing on one cookie.\n */\nconst BROWSER_SLOT = Symbol.for(\"ai-matrx.data.next.browser-client\");\n\nfunction readBrowserSlot(): BrowserSlot | undefined {\n return (globalThis as Record<symbol, unknown>)[BROWSER_SLOT] as\n | BrowserSlot\n | undefined;\n}\n\nfunction writeBrowserSlot(slot: BrowserSlot): void {\n (globalThis as Record<symbol, unknown>)[BROWSER_SLOT] = slot;\n}\n\nexport function createNextSupabase<DB extends DatabaseLike = DatabaseLike>(\n config: NextSupabaseConfig,\n): NextSupabase<DB> {\n const url = requireIdentity(config.supabaseUrl, \"NEXT_PUBLIC_SUPABASE_URL\");\n const key = requireIdentity(\n config.publishableKey,\n \"NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY\",\n );\n const authCookie = createAuthCookie({\n apexDomain: config.apexDomain,\n ...(config.cookieName !== undefined\n ? { cookieName: config.cookieName }\n : {}),\n ...(config.legacyCookieName !== undefined\n ? { legacyCookieName: config.legacyCookieName }\n : {}),\n });\n const wrap = config.wrapBrowserClient;\n const slotKey = `${url}|${key}|${authCookie.cookieName}`;\n\n /**\n * `document.cookie` carries the SAME duplicates the server sees — and\n * `@supabase/ssr`'s default browser adapter resolves each chunk with\n * `find()`, so an empty shadow sorting first makes the BROWSER read\n * anonymous too. That is why the split-session banner never mounted in\n * production: the server said \"nobody\", and the client agreed, so nothing\n * looked inconsistent even though a real session was sitting right there.\n *\n * Reads are reconciled here (non-empty wins). Writes are NOT redirected:\n * they keep going through `document.cookie` with the canonical options, and\n * the shadow is expired by the proxy, which is the only side that can name a\n * cookie's scope.\n */\n const browserCookieAdapter = () => ({\n getAll: () =>\n authCookie.reconcile(\n typeof document === \"undefined\"\n ? []\n : parseCookieHeader(document.cookie),\n ).cookies,\n setAll: (cookiesToSet: readonly CookieToSet[]) => {\n if (typeof document === \"undefined\") return;\n for (const cookie of cookiesToSet) {\n const options = (cookie.options ?? {}) as Record<string, unknown>;\n const parts = [\n `${cookie.name}=${encodeURIComponent(cookie.value)}`,\n `Path=${typeof options.path === \"string\" ? options.path : \"/\"}`,\n ];\n if (typeof options.domain === \"string\") {\n parts.push(`Domain=${options.domain}`);\n }\n if (typeof options.maxAge === \"number\") {\n parts.push(`Max-Age=${options.maxAge}`);\n }\n if (typeof options.sameSite === \"string\") {\n parts.push(`SameSite=${options.sameSite}`);\n }\n if (options.secure === true || location.protocol === \"https:\") parts.push(\"Secure\");\n document.cookie = parts.join(\"; \");\n }\n },\n });\n\n const browserClient = (): NextBrowserClient<DB> => {\n const existing = readBrowserSlot();\n if (existing && existing.key === slotKey) {\n return existing.client as NextBrowserClient<DB>;\n }\n const raw = createBrowserClient<DB>(url, key, {\n cookieOptions: authCookie.browserOptions(),\n cookies: browserCookieAdapter(),\n });\n const client = wrap ? wrap(raw) : raw;\n writeBrowserSlot({ key: slotKey, client });\n return client;\n };\n\n /**\n * THE ONE READ SEAM. Every door reads the cookie jar through this — a\n * duplicated auth cookie name whose empty twin sorts first would otherwise\n * reassemble to \"\" and answer anonymous on that door alone (which is how\n * `manage` refused a valid Create while `www` worked).\n */\n const reconciledGetAll = (\n getAllRaw: () => readonly AuthCookieValue[],\n cookieHeader: string | null | undefined,\n ): (() => AuthCookieValue[]) => {\n // The raw header when we have it (duplicates intact), the collapsed jar\n // only as a fallback — see parseCookieHeader for why that matters.\n const parsed = parseCookieHeader(cookieHeader);\n if (parsed.length > 0) {\n const reconciled = authCookie.reconcile(parsed).cookies;\n return () => reconciled;\n }\n return () => authCookie.reconcile(getAllRaw()).cookies;\n };\n\n const serverClient: NextSupabase<DB>[\"serverClient\"] = ({\n cookieStore,\n host,\n cookieHeader,\n appendSetCookie,\n }) => {\n const written = new Set<string>();\n // A layout or page is a Lambda with the same task cap as the proxy, and a\n // `getClaims()` here can still trigger a token refresh. Same budget.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n return createServerClient<DB>(url, key, {\n cookieOptions: authCookie.optionsForHost(host),\n global: { fetch: budget.fetch },\n cookies: {\n getAll: reconciledGetAll(() => cookieStore.getAll(), cookieHeader),\n setAll: (cookiesToSet) => {\n try {\n // Two passes, never interleaved — see `routeClient`.\n const deferred: string[] = [];\n for (const cookie of cookiesToSet) {\n const { name, value, options } = cookie;\n if (!written.has(name)) {\n written.add(name);\n cookieStore.set(name, value, options as Record<string, unknown>);\n continue;\n }\n const header = serializeSetCookie(\n toResponseCookie(cookie as CookieToSet),\n );\n if (appendSetCookie) {\n deferred.push(header);\n continue;\n }\n console.warn(\n `[@ai-matrx/data/next] DROPPED a second Set-Cookie for \"${name}\" on host ` +\n `\"${host ?? \"?\"}\": Next's cookie store keeps one entry per name, so this ` +\n `write would have DELETED the first one. Cookie value omitted. ` +\n `A Route Handler here must pass appendSetCookie — an unremoved auth cookie ` +\n `at the other scope makes the next request resolve the PREVIOUS person.`,\n );\n }\n for (const header of deferred) appendSetCookie?.(header);\n } catch {\n // Called from a Server Component, where Next forbids cookie\n // writes. The proxy refreshes the session on every request, so\n // dropping this write is correct — not a swallowed failure.\n }\n },\n },\n });\n };\n\n const routeClient: NextSupabase<DB>[\"routeClient\"] = ({\n requestCookies,\n setCookie,\n host,\n cookieHeader,\n appendSetCookie,\n }) => {\n // See `appendSetCookie`: a name written twice cannot go through any\n // one-entry-per-NAME jar, and the second write is what deletes the first.\n const written = new Set<string>();\n // Same budget as the proxy: an API route is one Lambda, one identity resolve.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n return createServerClient<DB>(url, key, {\n cookieOptions: authCookie.optionsForHost(host),\n global: { fetch: budget.fetch },\n cookies: {\n getAll: reconciledGetAll(() => requestCookies.getAll(), cookieHeader),\n setAll: (cookiesToSet) => {\n if (budget.exhausted && cookiesToSet.length > 0) {\n // See middlewareSession.applyCookies — a spent budget writes nothing.\n console.warn(\n `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) in a ` +\n `route on host \"${host ?? \"?\"}\": identity budget exhausted; nobody is signed out.`,\n );\n return;\n }\n // 🚨 TWO PASSES, NEVER INTERLEAVED — the same rule the split-jar heal\n // learned the hard way. `ResponseCookies.set` re-serializes the whole\n // `Set-Cookie` list from its own map, DISCARDING every raw append made\n // since. Appending a duplicate in the middle of the batch and then\n // writing one more cookie eats it, which is a silently lost removal\n // all over again. Every `setCookie` first; every append after.\n const deferred: string[] = [];\n for (const cookie of cookiesToSet) {\n const { name } = cookie;\n if (!written.has(name)) {\n written.add(name);\n setCookie(cookie as CookieToSet);\n continue;\n }\n const header = serializeSetCookie(\n toResponseCookie(cookie as CookieToSet),\n );\n if (appendSetCookie) {\n deferred.push(header);\n continue;\n }\n // LOUD: a cookie write is being lost, and the loss is an identity\n // hazard, not a cosmetic one.\n console.warn(\n `[@ai-matrx/data/next] DROPPED a second Set-Cookie for \"${name}\" on host ` +\n `\"${host ?? \"?\"}\": this door passed no appendSetCookie, and the response jar ` +\n `keeps one entry per name, so this write would have DELETED the first one. ` +\n `Cookie value omitted. Pass appendSetCookie from this route — an ` +\n `unremoved auth cookie at the other scope makes the next request resolve the ` +\n `PREVIOUS person.`,\n );\n }\n for (const header of deferred) appendSetCookie?.(header);\n },\n },\n });\n };\n\n async function middlewareSession<Response extends MiddlewareResponse>(\n args: MiddlewareSessionArgs<Response>,\n ): Promise<MiddlewareSession<DB, Response>> {\n const { host, requestCookies, createResponse, createRedirect } = args;\n const hostCookieOptions = authCookie.optionsForHost(host);\n\n let response = createResponse();\n\n /**\n * THE BASELINE VIEW, taken from the RAW `Cookie` header so duplicates are\n * still there to be seen. `requestCookies.getAll()` has already collapsed\n * them (Next keys its jar by name), which is why reconciling over the jar\n * detected nothing in production.\n */\n const rawCookies = parseCookieHeader(args.cookieHeader);\n const baseline = authCookie.reconcile(\n rawCookies.length > 0 ? rawCookies : requestCookies.getAll(),\n );\n\n /**\n * Auth cookie names that arrived at TWO scopes. Only meaningful where this\n * host issues a Domain-scoped cookie; on localhost/preview the host-only\n * cookie IS the real one and must never be expired.\n */\n const shadowed = hostCookieOptions.domain\n ? new Set(baseline.shadowedNames)\n : new Set<string>();\n\n /**\n * An UNCHUNKED value and a CHUNKED pair for one storage key. Unlike a\n * two-scope shadow there is no copy here that could be the good one — the\n * reconciled view has already dropped the whole family — so it is expired\n * on EVERY host, host-only deployments included, and it is expired even\n * when `getUser()` resolved nobody: this is not a \"the auth server blinked\"\n * null, it is a jar the writer cannot produce.\n */\n for (const name of baseline.ambiguousNames) shadowed.add(name);\n if (baseline.ambiguousKeys.length > 0) {\n console.warn(\n `[@ai-matrx/data/next] AMBIGUOUS auth cookie jar on host \"${host ?? \"?\"}\": ` +\n `${baseline.ambiguousKeys.join(\", \")} arrived UNCHUNKED and CHUNKED at once. ` +\n `Serving either would risk resolving a DIFFERENT person's session, so this ` +\n `request is treated as signed out and every cookie in those families is being ` +\n `expired at both scopes. The person signs in once and the jar is clean.`,\n );\n }\n\n /** Writes made during this pass, layered over the baseline. */\n const overlay = new Map<string, string>();\n const readCookies = (): AuthCookieValue[] => {\n const merged = baseline.cookies.map((cookie) =>\n overlay.has(cookie.name)\n ? { name: cookie.name, value: overlay.get(cookie.name) as string }\n : cookie,\n );\n const seen = new Set(merged.map(({ name }) => name));\n for (const [name, value] of overlay) {\n if (!seen.has(name)) merged.push({ name, value });\n }\n return merged;\n };\n const noteWrite = (name: string, value: string): void => {\n overlay.set(name, value);\n requestCookies.set(name, value);\n };\n\n // Seed the current storage key from the superseded one BEFORE the client\n // reads cookies. Absent-only, so a live session is never overwritten.\n const migrated = authCookie.migrateLegacyCookies(readCookies());\n for (const { name, value } of migrated) noteWrite(name, value);\n\n /**\n * Names already written on this response through `.cookies`, and the\n * writes that could not go that way.\n *\n * 🚨 THE SECOND ONE-ENTRY-PER-NAME TRAP, and it is how the stale unchunked\n * auth cookie is MADE. `@supabase/ssr`'s `applyServerStorage` removes a\n * cookie at BOTH Domain scopes by emitting the SAME name twice —\n * `Domain=.apex; Max-Age=0` and then host-only `Max-Age=0`. Funnel both\n * through `ResponseCookies.set` and the second REPLACES the first, so the\n * `.apex` copy is never expired. Measured on the real class:\n *\n * set(\"sb-…-v2\", \"\", {domain: \".aimatrx.com\", maxAge: 0})\n * set(\"sb-…-v2\", \"\", {maxAge: 0})\n * set(\"sb-…-v2.0\", \"<new session>\", {domain: \".aimatrx.com\"})\n * -> [\"sb-…-v2=; Path=/; Max-Age=0; …\", host-only ONLY\n * \"sb-…-v2.0=<new session>; …; Domain=.aimatrx.com\"]\n *\n * So writing a CHUNKED session over an unchunked one leaves the old\n * unchunked cookie alive at `.apex` forever — and `combineChunks` returns\n * the unchunked value the moment it is non-empty, without ever reading the\n * chunks. Sign in as someone else and the app keeps resolving the previous\n * person. That is an identity leak, produced by a lost `Set-Cookie`.\n *\n * The heal already knew this class for its own two-scope expiry and used\n * raw appends. The ORDINARY write path did not, which is why the residue\n * kept being created. Every duplicate name now goes out as a raw appended\n * header too, flushed in the one final append pass below.\n */\n const writtenNames = new Set<string>();\n const duplicateWrites: string[] = [];\n\n // ONE budget for every identity call this pass makes — see createAuthBudget.\n const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);\n\n const applyCookies = (\n cookiesToSet: readonly CookieToSet[],\n headers: Record<string, string> = {},\n ): void => {\n // 🚨 A SPENT BUDGET WRITES NOTHING. auth-js clears the session when a\n // refresh fails non-retryably on an already-expired access token, and\n // our synthetic 408 IS that non-retryable failure. Letting that write\n // through would sign the person out because the network was slow.\n if (budget.exhausted && cookiesToSet.length > 0) {\n console.warn(\n `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) on host ` +\n `\"${host ?? \"?\"}\": the identity budget was exhausted on this request, so nothing ` +\n `learned here is trusted enough to change the jar. Nobody is signed out; the next ` +\n `request re-resolves. Names: ${cookiesToSet.map((c) => c.name).join(\", \")}`,\n );\n return;\n }\n for (const { name, value } of cookiesToSet) noteWrite(name, value);\n const next = createResponse();\n for (const cookie of response.cookies.getAll()) {\n next.cookies.set(cookie);\n }\n for (const cookie of cookiesToSet) {\n const responseCookie = toResponseCookie(cookie);\n if (writtenNames.has(responseCookie.name)) {\n // Raw, so it cannot displace the entry already on the response.\n // Appends are flushed LAST (see the two-pass rule in applyHeal), so a\n // genuine same-scope re-write still lands after the earlier value.\n duplicateWrites.push(serializeSetCookie(responseCookie));\n continue;\n }\n writtenNames.add(responseCookie.name);\n next.cookies.set(responseCookie);\n }\n const responseHeaders =\n cookiesToSet.length > 0\n ? { ...AUTH_COOKIE_RESPONSE_HEADERS, ...headers }\n : headers;\n for (const [name, value] of Object.entries(responseHeaders)) {\n next.headers.set(name, value);\n }\n response = next;\n };\n\n const client = createServerClient<DB>(url, key, {\n cookieOptions: hostCookieOptions,\n // Identity calls out of THIS pass share one deadline; see createAuthBudget.\n global: { fetch: budget.fetch },\n cookies: {\n getAll: () => readCookies(),\n setAll: (cookiesToSet, headers) => {\n applyCookies(\n cookiesToSet as readonly CookieToSet[],\n (headers ?? {}) as Record<string, string>,\n );\n },\n },\n });\n\n // NOTHING may run between constructing the client and the identity\n // resolve. Any await in between has produced random logouts that are close\n // to undebuggable. The rule is structural here instead of a comment a host\n // has to remember — `resolveIdentity` is one adjacent await.\n const { user, authUnavailable } = await resolveIdentity(client, budget);\n\n // A valid session that arrived under the superseded key is persisted ONCE\n // under the current key. A token the old authority issued fails getUser(),\n // is never persisted, and gets cleared below.\n if (user && migrated.length > 0) {\n const { name: _name, ...cookieOptions } = hostCookieOptions;\n applyCookies(\n readCookies()\n .filter(\n ({ name, value }) =>\n authCookie.isCurrentCookie(name) && value.length > 0,\n )\n .map(({ name, value }) => ({\n name,\n value,\n options: { ...DEFAULT_COOKIE_OPTIONS, ...cookieOptions },\n })),\n );\n }\n\n // The superseded storage key must die at BOTH scopes, and the clear below\n // can only reach one of them (one entry per NAME on a response). Its\n // host-only twin goes out as a raw header with the shadow heal.\n if (authCookie.legacyCookieName && hostCookieOptions.domain) {\n const legacy = authCookie.legacyCookieName;\n for (const { name } of rawCookies) {\n if (name === legacy || name.startsWith(`${legacy}.`)) shadowed.add(name);\n }\n }\n\n // 🚨 NEVER EXPIRE THE SUPERSEDED KEY ON A RESOLVE WE COULD NOT MAKE.\n // The migration above persists legacy -> current ONLY when a user resolved.\n // So on an unreachable auth server the copy under the new name was never\n // written, and expiring the legacy one here would destroy the only cookie\n // holding the session — a logout caused by a network blink, which is the\n // exact class this whole pass exists to prevent.\n if (authCookie.legacyCookieName && !authUnavailable) {\n await clearAuthCookiesAtScopes({\n getAll: () => readCookies(),\n setAll: (cookiesToSet, headers) => {\n applyCookies(\n cookiesToSet as readonly CookieToSet[],\n (headers ?? {}) as Record<string, string>,\n );\n },\n storageKey: authCookie.legacyCookieName,\n scopes: hostCookieOptions.domain\n ? [{ domain: hostCookieOptions.domain }]\n : [{}],\n });\n }\n\n /**\n * THE HEAL — CONVERGE ON THE CANONICAL SCOPE, never merely delete.\n *\n * A shadowed name means the same cookie exists host-only AND at `.apex`,\n * and NOTHING in a `Cookie` header says which scope holds which value. So\n * blindly expiring host-only can destroy the live session — if the good\n * token happened to be the host-only copy, that is a logout we caused.\n *\n * Instead: REWRITE the reconciled (non-empty) value at the canonical\n * `.apex` scope through `cookies.set`, and expire the host-only scope with\n * a raw appended `Set-Cookie`. Whichever scope held the good value, the\n * good value ends up at the canonical scope and the shadow dies. Idempotent\n * and safe to repeat. When every copy is empty there is no session to\n * preserve, so both scopes are simply expired.\n *\n * Raw headers because `ResponseCookies` keeps one entry per NAME — the\n * canonical write and the other scope's expiry cannot both go through it.\n * Applied ONCE, last, on whatever response actually leaves; nothing may\n * read `.cookies` afterwards or these get re-parsed into the one-per-name\n * map and the heal is undone.\n */\n const healed = new WeakSet<object>();\n const applyHeal = (target: Response): Response => {\n if (healed.has(target)) return target;\n // An unresolved identity is not evidence about the jar. Converging or\n // expiring a scope here would be acting on an answer we never got.\n if (authUnavailable) return target;\n if (shadowed.size === 0 && duplicateWrites.length === 0) return target;\n healed.add(target);\n const current = new Map(readCookies().map((c) => [c.name, c.value]));\n\n // 🚨 TWO PASSES, NEVER INTERLEAVED. `ResponseCookies.set` re-parses the\n // whole `Set-Cookie` header list into its one-entry-per-NAME map and\n // rewrites it — so a raw append made BEFORE a later `.set` is eaten.\n // Interleaving them shipped to production and silently dropped every\n // host-only expiry: the canonical re-issue went out, the shadow lived on,\n // and the heal did nothing. Proven with `curl` against the deployed build.\n // Every `.cookies` write happens here, and nothing may touch `.cookies`\n // after the append pass below.\n const expiries: string[] = [];\n for (const name of shadowed) {\n const value = current.get(name) ?? \"\";\n if (value.length > 0 && !user) {\n // 🚨 NEVER FREEZE AN UNVALIDATED VALUE. The surviving copy is only\n // known-good because `getUser()` above resolved a session FROM the\n // reconciled view on THIS request. Without that, a structurally clean\n // but STALE token — a heal racing a token refresh — would be pinned\n // at the canonical scope, and both hosts would then reject a\n // clean-looking jar until re-login (found on production by the walk's\n // rapid poison/reload cycles). A null user can equally mean the auth\n // server was briefly unreachable, so expiring here would log people\n // out over a blip. Touch nothing; `splitCookieJar` still reports, and\n // the banner says it in words.\n continue;\n }\n if (value.length > 0) {\n // The survivor, re-issued at the ONE canonical scope.\n target.cookies.set(\n toResponseCookie({\n name,\n value,\n options: {\n ...DEFAULT_COOKIE_OPTIONS,\n domain: hostCookieOptions.domain,\n path: \"/\",\n // A Domain-scoped auth cookie only ever exists on https here,\n // and `Secure` is not part of cookie identity, so re-issuing\n // with it set can only tighten what is already there.\n secure: true,\n },\n }),\n );\n } else if (hostCookieOptions.domain) {\n expiries.push(\n `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Domain=${hostCookieOptions.domain}; Secure; SameSite=Lax`,\n );\n }\n expiries.push(hostOnlyExpiryHeader(name));\n }\n // The ordinary writes that could not go through `.cookies` because the\n // name was already taken there. Flushed with the expiries, in the same\n // single append pass, AFTER every `.cookies` write on this response —\n // touching `.cookies` from here on re-parses the header list and eats\n // every one of these.\n for (const header of duplicateWrites) {\n target.headers.append(\"Set-Cookie\", header);\n }\n for (const header of expiries) {\n target.headers.append(\"Set-Cookie\", header);\n }\n if (shadowed.size === 0) {\n // Nothing was healed — only a two-scope write was rescued. No split\n // stamp, no scream, and no cache headers beyond what the write already\n // carried.\n return target;\n }\n\n // Stamped by the PACKAGE, not the host, so it rides EVERY healed\n // response — including redirects, which a host that stamps it at the end\n // of its own pass never reaches.\n target.headers.set(SPLIT_COOKIE_JAR_HEADER, \"1\");\n if (baseline.ambiguousKeys.length > 0) {\n target.headers.set(AMBIGUOUS_AUTH_COOKIE_HEADER, \"1\");\n }\n\n // A response that rewrites cookies must never be cached.\n for (const [key, value] of Object.entries(AUTH_COOKIE_RESPONSE_HEADERS)) {\n target.headers.set(key, value);\n }\n // LOUD: this is an automatic intervention on a user's cookie jar.\n console.warn(\n `[@ai-matrx/data/next] split auth cookie jar healed on host \"${host ?? \"?\"}\": ` +\n `${[...shadowed].join(\", \")} arrived at two scopes; the host-only copies ` +\n `were expired so the ${hostCookieOptions.domain} set is the only one left.`,\n );\n return target;\n };\n\n return {\n user: user as MiddlewareSession<DB, Response>[\"user\"],\n client,\n get response() {\n return applyHeal(response);\n },\n redirect: (targetUrl: URL): Response => {\n const redirect = createRedirect(targetUrl);\n for (const cookie of response.cookies.getAll()) {\n redirect.cookies.set(cookie);\n }\n for (const name of CARRIED_HEADERS) {\n const value = response.headers.get(name);\n if (value) redirect.headers.set(name, value);\n }\n return applyHeal(redirect);\n },\n splitCookieJar: shadowed.size > 0,\n ambiguousAuthCookies: baseline.ambiguousKeys.length > 0,\n authUnavailable,\n };\n }\n\n return {\n authCookie,\n cookieName: authCookie.cookieName,\n optionsForHost: authCookie.optionsForHost,\n browserOptions: authCookie.browserOptions,\n browserClient,\n serverClient,\n routeClient,\n middlewareSession,\n };\n}\n","/**\n * Shared auth-cookie options for EVERY Supabase client an application\n * constructs (browser, server, middleware, OAuth callbacks). All construction\n * sites MUST pass these options — a client using the default cookie name\n * cannot see the session the others wrote.\n *\n * WHY (the 2026-07 Matrx deployment split): one login must work on every\n * subdomain of the platform's apex (e.g. app.example.com, manage.example.com),\n * so on those hosts the auth cookie is issued domain-wide\n * (`Domain=.example.com`) instead of Supabase's default host-only cookie.\n *\n * The cookie is also RENAMED (default `sb-<project-ref>-auth-token` →\n * `sb-matrx-auth`): re-issuing an existing host-only cookie as domain-wide\n * under the SAME name leaves two same-name cookies in the browser (different\n * Domain attributes = distinct cookies), and whichever the browser sends\n * first wins — auth flaps. A new name makes the transition deterministic:\n * every user re-logs-in exactly once and the old cookie expires unused.\n * The `sb-` prefix is kept on purpose — cleanup paths that scrub session\n * cookies delete by that prefix.\n *\n * The Domain attribute is ONLY set on real apex hosts. localhost and preview\n * hosts get a host-only cookie — a browser rejects any Set-Cookie whose\n * Domain does not cover the current host, so setting it unconditionally would\n * silently break auth everywhere else.\n *\n * The apex domain is a VALUE the host passes in (never read from an env var\n * here): `createAuthCookie({ apexDomain: \"aimatrx.com\" })`.\n *\n * THE RENAME MIGRATION (0.5.0 — moved in from matrx-frontend, where it was\n * 44 lines of host code every other consumer would have had to re-derive):\n * replacing the auth authority (a new Supabase project) means a NEW cookie\n * name, and a tab still running the OLD bundle keeps writing the OLD name for\n * days. `migrateLegacyCookies` seeds the new storage key from the old one\n * ONLY when the new key is absent — never overwriting a live session — and\n * `isCurrentCookie` recognizes both the base name and Supabase's numbered\n * chunk suffixes (`<name>.0`, `<name>.1`, …), which a naive `startsWith`\n * check gets wrong for any cookie that merely shares the prefix.\n */\n\n/** Structurally compatible with @supabase/ssr's CookieOptionsWithName. */\nexport interface AuthCookieOptions {\n name: string;\n /** Remote hosts require HTTPS; loopback development remains usable over HTTP. */\n secure: boolean;\n domain?: string;\n}\n\n/** A cookie as read from a request — name + value, nothing else needed. */\nexport interface AuthCookieValue {\n name: string;\n value: string;\n}\n\nexport const DEFAULT_AUTH_COOKIE_NAME = \"sb-matrx-auth\";\n\nexport interface CreateAuthCookieArgs {\n /** The apex domain one login must span (e.g. `\"aimatrx.com\"`). */\n apexDomain: string;\n /** Override only for a second, unrelated deployment — never per-app. */\n cookieName?: string;\n /**\n * The storage key a PREVIOUS auth authority used. When present, cookies\n * under this name are seeded onto `cookieName` (absent-only) by\n * `migrateLegacyCookies`, and the middleware binding clears them at the\n * scope this host issued them on.\n */\n legacyCookieName?: string;\n}\n\nexport interface AuthCookie {\n cookieName: string;\n /** The superseded storage key, when this deployment has one. */\n legacyCookieName: string | undefined;\n /**\n * Cookie options for the host serving the current request.\n * @param host `Host` header or `window.location.hostname` (port tolerated).\n */\n optionsForHost(host: string | null | undefined): AuthCookieOptions;\n /** Browser variant — resolves the host from `window` (SSR-safe). */\n browserOptions(): AuthCookieOptions;\n /**\n * Is this cookie name the CURRENT auth cookie — base name or one of\n * Supabase's numbered chunks (`<name>.0`)? A prefix test is wrong: it\n * matches `<name>.anything`, including unrelated cookies.\n */\n isCurrentCookie(name: string): boolean;\n /**\n * Cookies to seed under the current name, derived from the legacy name.\n * Returns `[]` when any current cookie already exists (never overwrite a\n * live session) or when no legacy name is configured.\n */\n migrateLegacyCookies(\n cookies: readonly AuthCookieValue[],\n ): AuthCookieValue[];\n /**\n * THE POISONED-JAR READ. A browser can hold the SAME auth cookie name twice\n * — once host-only, once at `.apex` — because different Domain attributes\n * make distinct cookies, and it sends both in one `Cookie` header. Every\n * reassembler downstream (`@supabase/ssr`'s `combineChunks`) resolves a\n * chunk with `find()`, i.e. FIRST WINS, and the browser decides the order.\n * An empty shadow that sorts first therefore reassembles to `\"\"` and every\n * server read answers anonymous while the tab is signed in.\n *\n * This collapses each auth-cookie NAME to one entry, preferring the first\n * NON-EMPTY value. Non-auth cookies pass through untouched, in order.\n *\n * A `Cookie` header carries no Domain, so which scope a value came from is\n * genuinely unknowable here — non-empty-wins is the strongest correct rule\n * available, and `@supabase/ssr` already refuses a chunk set that decodes\n * incoherently. `shadowedNames` names every auth cookie that arrived more\n * than once, so the caller can EXPIRE the losing scope instead of leaving\n * the user to clear cookies by hand.\n *\n * THE SECOND POISON, AND IT IS AN IDENTITY LEAK: one storage key arriving\n * BOTH unchunked (`<name>`) and chunked (`<name>.0`, `<name>.1`). These are\n * DIFFERENT cookie names, so the per-name collapse above cannot see them,\n * and `@supabase/ssr`'s `combineChunks` returns the UNCHUNKED value the\n * moment it is non-empty — it never even reads the chunks. A stale base\n * cookie therefore shadows a freshly written chunked pair, and every read\n * resolves the PREVIOUS person while the new session sits unread beside it.\n * That is a different user's session being served, not merely a logout.\n *\n * `applyServerStorage` never writes that shape — it removes every chunk-like\n * sibling of whatever it writes — so a jar holding both is RESIDUE from a\n * removal that did not land (see `middlewareSession`: a two-scope removal\n * collapses on `ResponseCookies`, which is how this residue is made).\n *\n * WHICH COPY IS NEWER IS UNKNOWABLE HERE, and both directions of residue are\n * producible, so preferring either shape is a coin flip on WHOSE SESSION TO\n * SERVE. This drops the whole family from the read instead: nobody is signed\n * in, every name in it is returned in `ambiguousNames` to be expired, and the\n * host's honest-screen banner says so with the one action that fixes it. A\n * wrong sign-out is recoverable in one click; a wrong identity is not.\n */\n reconcile(cookies: readonly AuthCookieValue[]): ReconciledAuthCookies;\n}\n\n/** The outcome of {@link AuthCookie.reconcile}. */\nexport interface ReconciledAuthCookies {\n /** One entry per name; auth names resolved to their non-empty value. */\n cookies: AuthCookieValue[];\n /** Auth cookie names that arrived more than once (i.e. at two scopes). */\n shadowedNames: string[];\n /**\n * Auth cookie names belonging to a storage key that arrived UNCHUNKED AND\n * CHUNKED at once — see {@link AuthCookie.reconcile}. Their values have been\n * dropped from `cookies`, and they must be expired on EVERY host, including\n * the ones that issue a host-only cookie: unlike a two-scope shadow there is\n * no copy here that could be the good one.\n */\n ambiguousNames: string[];\n /** The storage keys that arrived in both shapes. Named for the scream. */\n ambiguousKeys: string[];\n /** `true` when the jar is split and needs healing. */\n split: boolean;\n}\n\n/**\n * Parse a raw `Cookie` request header, **PRESERVING DUPLICATES AND ORDER**.\n *\n * 🚨 THIS IS THE ONLY WAY TO SEE A SPLIT JAR. Next's `RequestCookies` (and the\n * `cookies()` store built on it) parses the header into a Map keyed by NAME,\n * so two same-name cookies at two Domain scopes collapse to ONE before any\n * application code runs — and the survivor is whichever the browser happened\n * to send LAST. Measured against the real class:\n *\n * \"sb-…-v2.0=; sb-…-v2.0=base64-REAL\" -> [{ value: \"base64-REAL\" }] works\n * \"sb-…-v2.0=base64-REAL; sb-…-v2.0=\" -> [{ value: \"\" }] anonymous\n *\n * That coin flip IS the production break, and it is invisible from the\n * collapsed jar: reconciliation over `getAll()` is a no-op no matter how\n * carefully it is written. Every door must therefore reconcile over THIS.\n */\nexport function parseCookieHeader(\n header: string | null | undefined,\n): AuthCookieValue[] {\n if (!header) return [];\n const out: AuthCookieValue[] = [];\n for (const pair of header.split(\";\")) {\n const eq = pair.indexOf(\"=\");\n // A valueless segment is a name with an empty value — exactly the shadow\n // this exists to catch — so it must not be skipped.\n const name = (eq === -1 ? pair : pair.slice(0, eq)).trim();\n if (!name) continue;\n const raw = eq === -1 ? \"\" : pair.slice(eq + 1).trim();\n let value = raw;\n try {\n value = decodeURIComponent(raw);\n } catch {\n // A malformed escape is not a reason to lose the cookie; Next's parser\n // keeps the raw text too.\n }\n out.push({ name, value });\n }\n return out;\n}\n\n/**\n * Is `name` the storage key itself or one of its numbered chunks? The same\n * membership test `@supabase/ssr`'s `isChunkLike` uses, so a family is defined\n * here exactly as the writer defines it. A bare `startsWith` is wrong: it also\n * matches `<key>.anything`, i.e. unrelated cookies.\n */\nfunction belongsToStorageKey(name: string, storageKey: string): boolean {\n if (name === storageKey) return true;\n if (!name.startsWith(`${storageKey}.`)) return false;\n return /^(0|[1-9][0-9]*)$/.test(name.slice(storageKey.length + 1));\n}\n\n/** `<from>` → `<to>`, carrying Supabase's numbered chunk suffix across. */\nfunction renamedCookie(\n cookie: AuthCookieValue,\n from: string,\n to: string,\n): AuthCookieValue | null {\n if (cookie.name === from) return { ...cookie, name: to };\n const prefix = `${from}.`;\n if (!cookie.name.startsWith(prefix)) return null;\n const chunk = cookie.name.slice(prefix.length);\n if (!/^(0|[1-9][0-9]*)$/.test(chunk)) return null;\n return { ...cookie, name: `${to}.${chunk}` };\n}\n\nexport function createAuthCookie(args: CreateAuthCookieArgs): AuthCookie {\n const cookieName = args.cookieName ?? DEFAULT_AUTH_COOKIE_NAME;\n const legacyCookieName = args.legacyCookieName;\n const apex = args.apexDomain.toLowerCase();\n\n const optionsForHost = (\n host: string | null | undefined,\n ): AuthCookieOptions => {\n const normalized = (host ?? \"\").toLowerCase();\n const hostname = normalized.startsWith(\"[\")\n ? normalized.slice(1, normalized.indexOf(\"]\"))\n : normalized === \"::1\" ? normalized : normalized.split(\":\")[0] ?? \"\";\n const loopback = hostname === \"localhost\" || hostname.endsWith(\".localhost\") ||\n hostname === \"::1\" || /^127(?:\\.(?:0|[1-9]\\d{0,2})){3}$/.test(hostname) &&\n hostname.split(\".\").every((part) => Number(part) <= 255);\n const secure = !loopback;\n const onApex = hostname === apex || hostname.endsWith(`.${apex}`);\n return onApex\n ? { name: cookieName, domain: `.${apex}`, secure }\n : { name: cookieName, secure };\n };\n\n const isCurrentCookie = (name: string): boolean =>\n belongsToStorageKey(name, cookieName);\n\n const isAuthCookie = (name: string): boolean =>\n isCurrentCookie(name) ||\n (legacyCookieName !== undefined &&\n (name === legacyCookieName || name.startsWith(`${legacyCookieName}.`)));\n\n const reconcile = (\n cookies: readonly AuthCookieValue[],\n ): ReconciledAuthCookies => {\n const seen = new Map<string, number>();\n const out: AuthCookieValue[] = [];\n const shadowed = new Set<string>();\n\n for (const cookie of cookies) {\n const at = seen.get(cookie.name);\n if (at === undefined) {\n seen.set(cookie.name, out.length);\n out.push({ name: cookie.name, value: cookie.value });\n continue;\n }\n if (!isAuthCookie(cookie.name)) continue; // non-auth: first wins, as before\n shadowed.add(cookie.name);\n const held = out[at];\n // Non-empty wins. Later non-empty values do NOT displace an earlier\n // non-empty one — with no scope information, churning the choice would\n // make reassembly non-deterministic across requests.\n if (held !== undefined && held.value.length === 0) {\n out[at] = { name: cookie.name, value: cookie.value };\n }\n }\n\n // THE FAMILY PASS. A storage key may arrive unchunked AND chunked at once;\n // those are different NAMES, so the collapse above cannot have seen it.\n // `combineChunks` would take the unchunked value and never read the chunks,\n // which serves whichever person's session happens to be sitting in the base\n // cookie. Refuse the family instead of guessing — see `reconcile`'s docs.\n const ambiguous = new Set<string>();\n const ambiguousKeys: string[] = [];\n const byName = new Map(out.map((cookie) => [cookie.name, cookie] as const));\n const storageKeys = [cookieName, ...(legacyCookieName ? [legacyCookieName] : [])];\n for (const storageKey of storageKeys) {\n const unchunked = byName.get(storageKey);\n const firstChunk = byName.get(`${storageKey}.0`);\n if (!unchunked?.value || !firstChunk?.value) continue;\n ambiguousKeys.push(storageKey);\n for (const { name } of out) {\n if (belongsToStorageKey(name, storageKey)) ambiguous.add(name);\n }\n }\n\n const resolved =\n ambiguous.size === 0\n ? out\n : out.filter(({ name }) => !ambiguous.has(name));\n\n const shadowedNames = [...shadowed];\n const ambiguousNames = [...ambiguous];\n return {\n cookies: resolved,\n shadowedNames,\n ambiguousNames,\n ambiguousKeys,\n split: shadowedNames.length > 0 || ambiguousNames.length > 0,\n };\n };\n\n return {\n cookieName,\n legacyCookieName,\n optionsForHost,\n reconcile,\n browserOptions: () =>\n optionsForHost(\n typeof window !== \"undefined\" ? window.location.hostname : null,\n ),\n isCurrentCookie,\n migrateLegacyCookies: (cookies) => {\n if (!legacyCookieName) return [];\n if (cookies.some(({ name }) => isCurrentCookie(name))) return [];\n return cookies.flatMap((cookie) => {\n const renamed = renamedCookie(cookie, legacyCookieName, cookieName);\n return renamed ? [renamed] : [];\n });\n },\n };\n}\n"],"mappings":"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","names":["key"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/data",
3
- "version": "0.22.20",
3
+ "version": "0.22.22",
4
4
  "description": "The opinionated AI Matrx data layer: the /db tier's hard-won Supabase primitives — optimistic concurrency, complete-list reads, JSONB merge, session-retry, the domain-wide auth cookie, realtime channel hygiene — the /net resilience trio, and the /files lane: the Matrx file-communication client (durable-ref resolution, the mx_files_session bootstrap, authenticated blob fetch, uploads, the public-link door).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -208,5 +208,5 @@
208
208
  "test:watch": "vitest",
209
209
  "verify:tarball": "node ./scripts/verify-tarball.mjs"
210
210
  },
211
- "gitHead": "bde4a2d0ac28eb6ad080485b579ef1b88b1ed1ac"
211
+ "gitHead": "8e202b66841e9174dacf715a76ff415dbee54310"
212
212
  }