@ai-matrx/auth 0.0.0 → 0.1.1

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +31 -1
  4. package/dist/auth-cookie-DWx6Q3xo.d.cts +168 -0
  5. package/dist/auth-cookie-DWx6Q3xo.d.ts +168 -0
  6. package/dist/cookies.cjs +233 -0
  7. package/dist/cookies.d.cts +109 -0
  8. package/dist/cookies.d.ts +109 -0
  9. package/dist/cookies.js +210 -0
  10. package/dist/credentials.cjs +68 -0
  11. package/dist/credentials.d.cts +82 -0
  12. package/dist/credentials.d.ts +82 -0
  13. package/dist/credentials.js +47 -0
  14. package/dist/index.cjs +189 -0
  15. package/dist/index.d.cts +86 -0
  16. package/dist/index.d.ts +86 -0
  17. package/dist/index.js +166 -0
  18. package/dist/next.cjs +655 -0
  19. package/dist/next.d.cts +389 -0
  20. package/dist/next.d.ts +389 -0
  21. package/dist/next.js +637 -0
  22. package/dist/node.cjs +61 -0
  23. package/dist/node.d.cts +33 -0
  24. package/dist/node.d.ts +33 -0
  25. package/dist/node.js +36 -0
  26. package/dist/organizations.cjs +389 -0
  27. package/dist/organizations.d.cts +226 -0
  28. package/dist/organizations.d.ts +226 -0
  29. package/dist/organizations.js +366 -0
  30. package/dist/react.cjs +206 -0
  31. package/dist/react.d.cts +163 -0
  32. package/dist/react.d.ts +163 -0
  33. package/dist/react.js +186 -0
  34. package/dist/session-DhRO4euL.d.cts +102 -0
  35. package/dist/session-DhRO4euL.d.ts +102 -0
  36. package/dist/session-identity.cjs +125 -0
  37. package/dist/session-identity.d.cts +100 -0
  38. package/dist/session-identity.d.ts +100 -0
  39. package/dist/session-identity.js +104 -0
  40. package/dist/testing.cjs +67 -0
  41. package/dist/testing.d.cts +38 -0
  42. package/dist/testing.d.ts +38 -0
  43. package/dist/testing.js +46 -0
  44. package/package.json +186 -6
package/dist/next.cjs ADDED
@@ -0,0 +1,655 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/next.ts
21
+ var next_exports = {};
22
+ __export(next_exports, {
23
+ AMBIGUOUS_AUTH_COOKIE_HEADER: () => AMBIGUOUS_AUTH_COOKIE_HEADER,
24
+ AUTH_COOKIE_RESPONSE_HEADERS: () => AUTH_COOKIE_RESPONSE_HEADERS,
25
+ AUTH_REFRESH_CEILING_MS: () => AUTH_REFRESH_CEILING_MS,
26
+ DEFAULT_AUTH_COOKIE_NAME: () => DEFAULT_AUTH_COOKIE_NAME,
27
+ MissingSupabaseIdentityError: () => MissingSupabaseIdentityError,
28
+ SPLIT_COOKIE_JAR_HEADER: () => SPLIT_COOKIE_JAR_HEADER,
29
+ createAuthBudget: () => createAuthBudget,
30
+ createAuthCookie: () => createAuthCookie,
31
+ createNextSupabase: () => createNextSupabase,
32
+ serializeSetCookie: () => serializeSetCookie,
33
+ toResponseCookie: () => toResponseCookie
34
+ });
35
+ module.exports = __toCommonJS(next_exports);
36
+
37
+ // src/next/supabase.ts
38
+ var import_ssr = require("@supabase/ssr");
39
+
40
+ // src/cookies/auth-cookie.ts
41
+ var DEFAULT_AUTH_COOKIE_NAME = "sb-matrx-auth";
42
+ function parseCookieHeader(header) {
43
+ if (!header) return [];
44
+ const out = [];
45
+ for (const pair of header.split(";")) {
46
+ const eq = pair.indexOf("=");
47
+ const name = (eq === -1 ? pair : pair.slice(0, eq)).trim();
48
+ if (!name) continue;
49
+ const raw = eq === -1 ? "" : pair.slice(eq + 1).trim();
50
+ let value = raw;
51
+ try {
52
+ value = decodeURIComponent(raw);
53
+ } catch {
54
+ }
55
+ out.push({ name, value });
56
+ }
57
+ return out;
58
+ }
59
+ function belongsToStorageKey(name, storageKey) {
60
+ if (name === storageKey) return true;
61
+ if (!name.startsWith(`${storageKey}.`)) return false;
62
+ return /^(0|[1-9][0-9]*)$/.test(name.slice(storageKey.length + 1));
63
+ }
64
+ function renamedCookie(cookie, from, to) {
65
+ if (cookie.name === from) return { ...cookie, name: to };
66
+ const prefix = `${from}.`;
67
+ if (!cookie.name.startsWith(prefix)) return null;
68
+ const chunk = cookie.name.slice(prefix.length);
69
+ if (!/^(0|[1-9][0-9]*)$/.test(chunk)) return null;
70
+ return { ...cookie, name: `${to}.${chunk}` };
71
+ }
72
+ function createAuthCookie(args) {
73
+ const cookieName = args.cookieName ?? DEFAULT_AUTH_COOKIE_NAME;
74
+ const legacyCookieName = args.legacyCookieName;
75
+ const apex = args.apexDomain.toLowerCase();
76
+ const optionsForHost = (host) => {
77
+ const normalized = (host ?? "").toLowerCase();
78
+ const hostname = normalized.startsWith("[") ? normalized.slice(1, normalized.indexOf("]")) : normalized === "::1" ? normalized : normalized.split(":")[0] ?? "";
79
+ const loopback = hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "::1" || /^127(?:\.(?:0|[1-9]\d{0,2})){3}$/.test(hostname) && hostname.split(".").every((part) => Number(part) <= 255);
80
+ const secure = !loopback;
81
+ const onApex = hostname === apex || hostname.endsWith(`.${apex}`);
82
+ return onApex ? { name: cookieName, domain: `.${apex}`, secure } : { name: cookieName, secure };
83
+ };
84
+ const isCurrentCookie = (name) => belongsToStorageKey(name, cookieName);
85
+ const isAuthCookie = (name) => isCurrentCookie(name) || legacyCookieName !== void 0 && (name === legacyCookieName || name.startsWith(`${legacyCookieName}.`));
86
+ const reconcile = (cookies) => {
87
+ const seen = /* @__PURE__ */ new Map();
88
+ const out = [];
89
+ const shadowed = /* @__PURE__ */ new Set();
90
+ for (const cookie of cookies) {
91
+ const at = seen.get(cookie.name);
92
+ if (at === void 0) {
93
+ seen.set(cookie.name, out.length);
94
+ out.push({ name: cookie.name, value: cookie.value });
95
+ continue;
96
+ }
97
+ if (!isAuthCookie(cookie.name)) continue;
98
+ shadowed.add(cookie.name);
99
+ const held = out[at];
100
+ if (held !== void 0 && held.value.length === 0) {
101
+ out[at] = { name: cookie.name, value: cookie.value };
102
+ }
103
+ }
104
+ const ambiguous = /* @__PURE__ */ new Set();
105
+ const ambiguousKeys = [];
106
+ const byName = new Map(out.map((cookie) => [cookie.name, cookie]));
107
+ const storageKeys = [cookieName, ...legacyCookieName ? [legacyCookieName] : []];
108
+ for (const storageKey of storageKeys) {
109
+ const unchunked = byName.get(storageKey);
110
+ const firstChunk = byName.get(`${storageKey}.0`);
111
+ if (!unchunked?.value || !firstChunk?.value) continue;
112
+ ambiguousKeys.push(storageKey);
113
+ for (const { name } of out) {
114
+ if (belongsToStorageKey(name, storageKey)) ambiguous.add(name);
115
+ }
116
+ }
117
+ const resolved = ambiguous.size === 0 ? out : out.filter(({ name }) => !ambiguous.has(name));
118
+ const shadowedNames = [...shadowed];
119
+ const ambiguousNames = [...ambiguous];
120
+ return {
121
+ cookies: resolved,
122
+ shadowedNames,
123
+ ambiguousNames,
124
+ ambiguousKeys,
125
+ split: shadowedNames.length > 0 || ambiguousNames.length > 0
126
+ };
127
+ };
128
+ return {
129
+ cookieName,
130
+ legacyCookieName,
131
+ optionsForHost,
132
+ reconcile,
133
+ browserOptions: () => optionsForHost(
134
+ typeof window !== "undefined" ? window.location.hostname : null
135
+ ),
136
+ isCurrentCookie,
137
+ migrateLegacyCookies: (cookies) => {
138
+ if (!legacyCookieName) return [];
139
+ if (cookies.some(({ name }) => isCurrentCookie(name))) return [];
140
+ return cookies.flatMap((cookie) => {
141
+ const renamed = renamedCookie(cookie, legacyCookieName, cookieName);
142
+ return renamed ? [renamed] : [];
143
+ });
144
+ }
145
+ };
146
+ }
147
+
148
+ // src/next/supabase.ts
149
+ var RESPONSE_COOKIE_ATTRIBUTES = [
150
+ "domain",
151
+ "path",
152
+ "maxAge",
153
+ "expires",
154
+ "httpOnly",
155
+ "secure",
156
+ "sameSite",
157
+ "priority",
158
+ "partitioned"
159
+ ];
160
+ var AUTH_COOKIE_RESPONSE_HEADERS = Object.freeze({
161
+ "Cache-Control": "private, no-cache, no-store, must-revalidate, max-age=0",
162
+ Expires: "0",
163
+ Pragma: "no-cache"
164
+ });
165
+ var CARRIED_HEADERS = ["cache-control", "expires", "pragma"];
166
+ var SPLIT_COOKIE_JAR_HEADER = "x-matrx-split-jar";
167
+ var AMBIGUOUS_AUTH_COOKIE_HEADER = "x-matrx-auth-ambiguous";
168
+ function toResponseCookie(cookie) {
169
+ const options = cookie.options ?? {};
170
+ const out = {
171
+ name: cookie.name,
172
+ value: cookie.value
173
+ };
174
+ for (const attribute of RESPONSE_COOKIE_ATTRIBUTES) {
175
+ const value = options[attribute];
176
+ if (value !== void 0) out[attribute] = value;
177
+ }
178
+ return out;
179
+ }
180
+ function hostOnlyExpiryHeader(name) {
181
+ return `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=Lax`;
182
+ }
183
+ function serializeSetCookie(cookie) {
184
+ const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];
185
+ parts.push(`Path=${cookie.path ?? "/"}`);
186
+ if (cookie.domain !== void 0) parts.push(`Domain=${cookie.domain}`);
187
+ if (cookie.maxAge !== void 0) parts.push(`Max-Age=${cookie.maxAge}`);
188
+ if (cookie.expires !== void 0) {
189
+ const expires = cookie.expires instanceof Date ? cookie.expires : new Date(cookie.expires);
190
+ parts.push(`Expires=${expires.toUTCString()}`);
191
+ }
192
+ if (cookie.httpOnly === true) parts.push("HttpOnly");
193
+ if (cookie.secure === true) parts.push("Secure");
194
+ if (typeof cookie.sameSite === "string") {
195
+ parts.push(`SameSite=${cookie.sameSite}`);
196
+ }
197
+ return parts.join("; ");
198
+ }
199
+ var MissingSupabaseIdentityError = class extends Error {
200
+ variable;
201
+ constructor(variable) {
202
+ super(
203
+ `@ai-matrx/data/next: missing required Supabase identity "${variable}". Pass it as a STATIC member access (process.env.${variable}) to createNextSupabase().`
204
+ );
205
+ this.name = "MissingSupabaseIdentityError";
206
+ this.variable = variable;
207
+ }
208
+ };
209
+ function requireIdentity(value, variable) {
210
+ const trimmed = value?.trim();
211
+ if (!trimmed) throw new MissingSupabaseIdentityError(variable);
212
+ return trimmed;
213
+ }
214
+ var AUTH_RESOLVE_TIMEOUT_MS = 2500;
215
+ var IDENTITY_PATHS = [
216
+ "/auth/v1/user",
217
+ "/auth/v1/token?grant_type=refresh_token",
218
+ "/auth/v1/.well-known/jwks.json"
219
+ ];
220
+ var AUTH_BUDGET_EXHAUSTED_STATUS = 408;
221
+ var AUTH_BUDGET_EXHAUSTED_CODE = "auth_budget_exhausted";
222
+ var AUTH_REFRESH_CEILING_MS = 8e3;
223
+ var REFRESH_GRANT_PATH = "/auth/v1/token?grant_type=refresh_token";
224
+ function createAuthBudget(timeoutMs, options = {}) {
225
+ const refreshCeilingMs = Math.max(timeoutMs, options.refreshCeilingMs ?? AUTH_REFRESH_CEILING_MS);
226
+ let deadline = null;
227
+ let exhausted = false;
228
+ const isIdentityCall = (url) => IDENTITY_PATHS.some((path) => url.includes(path));
229
+ const exhaustedResponse = () => new Response(
230
+ JSON.stringify({
231
+ error_code: AUTH_BUDGET_EXHAUSTED_CODE,
232
+ msg: `identity resolve budget of ${timeoutMs}ms is exhausted for this request; not retrying`
233
+ }),
234
+ {
235
+ status: AUTH_BUDGET_EXHAUSTED_STATUS,
236
+ headers: { "content-type": "application/json" }
237
+ }
238
+ );
239
+ const budgetedFetch = async (input, init) => {
240
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
241
+ if (!isIdentityCall(url) || init?.signal) {
242
+ return fetch(input, init);
243
+ }
244
+ const now = Date.now();
245
+ if (deadline === null) deadline = now + timeoutMs;
246
+ const remaining = deadline - now;
247
+ if (exhausted || remaining <= 0) {
248
+ exhausted = true;
249
+ return exhaustedResponse();
250
+ }
251
+ const allowed = url.includes(REFRESH_GRANT_PATH) ? refreshCeilingMs : remaining;
252
+ try {
253
+ return await fetch(input, {
254
+ ...init,
255
+ signal: AbortSignal.timeout(allowed)
256
+ });
257
+ } catch (cause) {
258
+ const name = String(cause?.name ?? "");
259
+ if (name === "TimeoutError" || name === "AbortError") exhausted = true;
260
+ throw cause;
261
+ }
262
+ };
263
+ return {
264
+ fetch: budgetedFetch,
265
+ get exhausted() {
266
+ return exhausted;
267
+ }
268
+ };
269
+ }
270
+ function isAuthTransportFailure(error) {
271
+ if (!error || typeof error !== "object") return false;
272
+ const name = String(error.name ?? "");
273
+ if (name === "AuthRetryableFetchError" || name === "TimeoutError" || name === "AbortError") {
274
+ return true;
275
+ }
276
+ const status = error.status;
277
+ if (status === 0 || status === 504 || status === 503) return true;
278
+ const code = error.code;
279
+ return status === AUTH_BUDGET_EXHAUSTED_STATUS || code === AUTH_BUDGET_EXHAUSTED_CODE;
280
+ }
281
+ async function resolveIdentity(client, budget) {
282
+ const unreachable = (cause) => {
283
+ console.warn(
284
+ `[@ai-matrx/data/next] AUTH UNREACHABLE within ${AUTH_RESOLVE_TIMEOUT_MS}ms \u2014 this request is being passed through WITHOUT an identity and WITHOUT touching a single auth cookie. Nobody is being signed out: the shell resolves auth again on the client, and the next request re-resolves it here. If this is not rare, the auth authority (or the network to it) is the thing to fix \u2014 raising this budget only trades it back for a 504. Cause: ${String(cause)}`
285
+ );
286
+ return { user: null, authUnavailable: true };
287
+ };
288
+ try {
289
+ const { data, error } = await client.auth.getClaims();
290
+ if (error) {
291
+ return isAuthTransportFailure(error) || budget?.exhausted ? unreachable(error) : { user: null, authUnavailable: false };
292
+ }
293
+ const claims = data?.claims;
294
+ if (!claims || typeof claims.sub !== "string") {
295
+ return budget?.exhausted ? unreachable("identity budget exhausted before a token could be verified") : { user: null, authUnavailable: false };
296
+ }
297
+ return {
298
+ user: { ...claims, id: claims.sub },
299
+ authUnavailable: false
300
+ };
301
+ } catch (cause) {
302
+ return unreachable(cause);
303
+ }
304
+ }
305
+ var BROWSER_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.data.next.browser-client");
306
+ function readBrowserSlot() {
307
+ return globalThis[BROWSER_SLOT];
308
+ }
309
+ function writeBrowserSlot(slot) {
310
+ globalThis[BROWSER_SLOT] = slot;
311
+ }
312
+ function createNextSupabase(config) {
313
+ const url = requireIdentity(config.supabaseUrl, "NEXT_PUBLIC_SUPABASE_URL");
314
+ const key = requireIdentity(
315
+ config.publishableKey,
316
+ "NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY"
317
+ );
318
+ const authCookie = createAuthCookie({
319
+ apexDomain: config.apexDomain,
320
+ ...config.cookieName !== void 0 ? { cookieName: config.cookieName } : {},
321
+ ...config.legacyCookieName !== void 0 ? { legacyCookieName: config.legacyCookieName } : {}
322
+ });
323
+ const wrap = config.wrapBrowserClient;
324
+ const slotKey = `${url}|${key}|${authCookie.cookieName}`;
325
+ const browserCookieAdapter = () => ({
326
+ getAll: () => authCookie.reconcile(
327
+ typeof document === "undefined" ? [] : parseCookieHeader(document.cookie)
328
+ ).cookies,
329
+ setAll: (cookiesToSet) => {
330
+ if (typeof document === "undefined") return;
331
+ for (const cookie of cookiesToSet) {
332
+ const options = cookie.options ?? {};
333
+ const parts = [
334
+ `${cookie.name}=${encodeURIComponent(cookie.value)}`,
335
+ `Path=${typeof options.path === "string" ? options.path : "/"}`
336
+ ];
337
+ if (typeof options.domain === "string") {
338
+ parts.push(`Domain=${options.domain}`);
339
+ }
340
+ if (typeof options.maxAge === "number") {
341
+ parts.push(`Max-Age=${options.maxAge}`);
342
+ }
343
+ if (typeof options.sameSite === "string") {
344
+ parts.push(`SameSite=${options.sameSite}`);
345
+ }
346
+ if (options.secure === true || location.protocol === "https:") parts.push("Secure");
347
+ document.cookie = parts.join("; ");
348
+ }
349
+ }
350
+ });
351
+ const browserClient = () => {
352
+ const existing = readBrowserSlot();
353
+ if (existing && existing.key === slotKey) {
354
+ return existing.client;
355
+ }
356
+ const raw = (0, import_ssr.createBrowserClient)(url, key, {
357
+ cookieOptions: authCookie.browserOptions(),
358
+ cookies: browserCookieAdapter()
359
+ });
360
+ const client = wrap ? wrap(raw) : raw;
361
+ writeBrowserSlot({ key: slotKey, client });
362
+ return client;
363
+ };
364
+ const reconciledGetAll = (getAllRaw, cookieHeader) => {
365
+ const parsed = parseCookieHeader(cookieHeader);
366
+ if (parsed.length > 0) {
367
+ const reconciled = authCookie.reconcile(parsed).cookies;
368
+ return () => reconciled;
369
+ }
370
+ return () => authCookie.reconcile(getAllRaw()).cookies;
371
+ };
372
+ const serverClient = ({
373
+ cookieStore,
374
+ host,
375
+ cookieHeader,
376
+ appendSetCookie
377
+ }) => {
378
+ const written = /* @__PURE__ */ new Set();
379
+ const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);
380
+ return (0, import_ssr.createServerClient)(url, key, {
381
+ cookieOptions: authCookie.optionsForHost(host),
382
+ global: { fetch: budget.fetch },
383
+ cookies: {
384
+ getAll: reconciledGetAll(() => cookieStore.getAll(), cookieHeader),
385
+ setAll: (cookiesToSet) => {
386
+ try {
387
+ const deferred = [];
388
+ for (const cookie of cookiesToSet) {
389
+ const { name, value, options } = cookie;
390
+ if (!written.has(name)) {
391
+ written.add(name);
392
+ cookieStore.set(name, value, options);
393
+ continue;
394
+ }
395
+ const header = serializeSetCookie(
396
+ toResponseCookie(cookie)
397
+ );
398
+ if (appendSetCookie) {
399
+ deferred.push(header);
400
+ continue;
401
+ }
402
+ console.warn(
403
+ `[@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.`
404
+ );
405
+ }
406
+ for (const header of deferred) appendSetCookie?.(header);
407
+ } catch {
408
+ }
409
+ }
410
+ }
411
+ });
412
+ };
413
+ const routeClient = ({
414
+ requestCookies,
415
+ setCookie,
416
+ host,
417
+ cookieHeader,
418
+ appendSetCookie
419
+ }) => {
420
+ const written = /* @__PURE__ */ new Set();
421
+ const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);
422
+ return (0, import_ssr.createServerClient)(url, key, {
423
+ cookieOptions: authCookie.optionsForHost(host),
424
+ global: { fetch: budget.fetch },
425
+ cookies: {
426
+ getAll: reconciledGetAll(() => requestCookies.getAll(), cookieHeader),
427
+ setAll: (cookiesToSet) => {
428
+ if (budget.exhausted && cookiesToSet.length > 0) {
429
+ console.warn(
430
+ `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) in a route on host "${host ?? "?"}": identity budget exhausted; nobody is signed out.`
431
+ );
432
+ return;
433
+ }
434
+ const deferred = [];
435
+ for (const cookie of cookiesToSet) {
436
+ const { name } = cookie;
437
+ if (!written.has(name)) {
438
+ written.add(name);
439
+ setCookie(cookie);
440
+ continue;
441
+ }
442
+ const header = serializeSetCookie(
443
+ toResponseCookie(cookie)
444
+ );
445
+ if (appendSetCookie) {
446
+ deferred.push(header);
447
+ continue;
448
+ }
449
+ console.warn(
450
+ `[@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.`
451
+ );
452
+ }
453
+ for (const header of deferred) appendSetCookie?.(header);
454
+ }
455
+ }
456
+ });
457
+ };
458
+ async function middlewareSession(args) {
459
+ const { host, requestCookies, createResponse, createRedirect } = args;
460
+ const hostCookieOptions = authCookie.optionsForHost(host);
461
+ let response = createResponse();
462
+ const rawCookies = parseCookieHeader(args.cookieHeader);
463
+ const baseline = authCookie.reconcile(
464
+ rawCookies.length > 0 ? rawCookies : requestCookies.getAll()
465
+ );
466
+ const shadowed = hostCookieOptions.domain ? new Set(baseline.shadowedNames) : /* @__PURE__ */ new Set();
467
+ for (const name of baseline.ambiguousNames) shadowed.add(name);
468
+ if (baseline.ambiguousKeys.length > 0) {
469
+ console.warn(
470
+ `[@ai-matrx/data/next] AMBIGUOUS auth cookie jar on host "${host ?? "?"}": ${baseline.ambiguousKeys.join(", ")} arrived UNCHUNKED and CHUNKED at once. Serving either would risk resolving a DIFFERENT person's session, so this request is treated as signed out and every cookie in those families is being expired at both scopes. The person signs in once and the jar is clean.`
471
+ );
472
+ }
473
+ const overlay = /* @__PURE__ */ new Map();
474
+ const readCookies = () => {
475
+ const merged = baseline.cookies.map(
476
+ (cookie) => overlay.has(cookie.name) ? { name: cookie.name, value: overlay.get(cookie.name) } : cookie
477
+ );
478
+ const seen = new Set(merged.map(({ name }) => name));
479
+ for (const [name, value] of overlay) {
480
+ if (!seen.has(name)) merged.push({ name, value });
481
+ }
482
+ return merged;
483
+ };
484
+ const noteWrite = (name, value) => {
485
+ overlay.set(name, value);
486
+ requestCookies.set(name, value);
487
+ };
488
+ const migrated = authCookie.migrateLegacyCookies(readCookies());
489
+ for (const { name, value } of migrated) noteWrite(name, value);
490
+ const writtenNames = /* @__PURE__ */ new Set();
491
+ const duplicateWrites = [];
492
+ const budget = createAuthBudget(AUTH_RESOLVE_TIMEOUT_MS);
493
+ const applyCookies = (cookiesToSet, headers = {}) => {
494
+ if (budget.exhausted && cookiesToSet.length > 0) {
495
+ console.warn(
496
+ `[@ai-matrx/data/next] DROPPED ${cookiesToSet.length} auth cookie write(s) on host "${host ?? "?"}": the identity budget was exhausted on this request, so nothing learned here is trusted enough to change the jar. Nobody is signed out; the next request re-resolves. Names: ${cookiesToSet.map((c) => c.name).join(", ")}`
497
+ );
498
+ return;
499
+ }
500
+ for (const { name, value } of cookiesToSet) noteWrite(name, value);
501
+ const next = createResponse();
502
+ for (const cookie of response.cookies.getAll()) {
503
+ next.cookies.set(cookie);
504
+ }
505
+ for (const cookie of cookiesToSet) {
506
+ const responseCookie = toResponseCookie(cookie);
507
+ if (writtenNames.has(responseCookie.name)) {
508
+ duplicateWrites.push(serializeSetCookie(responseCookie));
509
+ continue;
510
+ }
511
+ writtenNames.add(responseCookie.name);
512
+ next.cookies.set(responseCookie);
513
+ }
514
+ const responseHeaders = cookiesToSet.length > 0 ? { ...AUTH_COOKIE_RESPONSE_HEADERS, ...headers } : headers;
515
+ for (const [name, value] of Object.entries(responseHeaders)) {
516
+ next.headers.set(name, value);
517
+ }
518
+ response = next;
519
+ };
520
+ const client = (0, import_ssr.createServerClient)(url, key, {
521
+ cookieOptions: hostCookieOptions,
522
+ // Identity calls out of THIS pass share one deadline; see createAuthBudget.
523
+ global: { fetch: budget.fetch },
524
+ cookies: {
525
+ getAll: () => readCookies(),
526
+ setAll: (cookiesToSet, headers) => {
527
+ applyCookies(
528
+ cookiesToSet,
529
+ headers ?? {}
530
+ );
531
+ }
532
+ }
533
+ });
534
+ const { user, authUnavailable } = await resolveIdentity(client, budget);
535
+ if (user && migrated.length > 0) {
536
+ const { name: _name, ...cookieOptions } = hostCookieOptions;
537
+ applyCookies(
538
+ readCookies().filter(
539
+ ({ name, value }) => authCookie.isCurrentCookie(name) && value.length > 0
540
+ ).map(({ name, value }) => ({
541
+ name,
542
+ value,
543
+ options: { ...import_ssr.DEFAULT_COOKIE_OPTIONS, ...cookieOptions }
544
+ }))
545
+ );
546
+ }
547
+ if (authCookie.legacyCookieName && hostCookieOptions.domain) {
548
+ const legacy = authCookie.legacyCookieName;
549
+ for (const { name } of rawCookies) {
550
+ if (name === legacy || name.startsWith(`${legacy}.`)) shadowed.add(name);
551
+ }
552
+ }
553
+ if (authCookie.legacyCookieName && !authUnavailable) {
554
+ await (0, import_ssr.clearAuthCookiesAtScopes)({
555
+ getAll: () => readCookies(),
556
+ setAll: (cookiesToSet, headers) => {
557
+ applyCookies(
558
+ cookiesToSet,
559
+ headers ?? {}
560
+ );
561
+ },
562
+ storageKey: authCookie.legacyCookieName,
563
+ scopes: hostCookieOptions.domain ? [{ domain: hostCookieOptions.domain }] : [{}]
564
+ });
565
+ }
566
+ const healed = /* @__PURE__ */ new WeakSet();
567
+ const applyHeal = (target) => {
568
+ if (healed.has(target)) return target;
569
+ if (authUnavailable) return target;
570
+ if (shadowed.size === 0 && duplicateWrites.length === 0) return target;
571
+ healed.add(target);
572
+ const current = new Map(readCookies().map((c) => [c.name, c.value]));
573
+ const expiries = [];
574
+ for (const name of shadowed) {
575
+ const value = current.get(name) ?? "";
576
+ if (value.length > 0 && !user) {
577
+ continue;
578
+ }
579
+ if (value.length > 0) {
580
+ target.cookies.set(
581
+ toResponseCookie({
582
+ name,
583
+ value,
584
+ options: {
585
+ ...import_ssr.DEFAULT_COOKIE_OPTIONS,
586
+ domain: hostCookieOptions.domain,
587
+ path: "/",
588
+ // A Domain-scoped auth cookie only ever exists on https here,
589
+ // and `Secure` is not part of cookie identity, so re-issuing
590
+ // with it set can only tighten what is already there.
591
+ secure: true
592
+ }
593
+ })
594
+ );
595
+ } else if (hostCookieOptions.domain) {
596
+ expiries.push(
597
+ `${name}=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Domain=${hostCookieOptions.domain}; Secure; SameSite=Lax`
598
+ );
599
+ }
600
+ expiries.push(hostOnlyExpiryHeader(name));
601
+ }
602
+ for (const header of duplicateWrites) {
603
+ target.headers.append("Set-Cookie", header);
604
+ }
605
+ for (const header of expiries) {
606
+ target.headers.append("Set-Cookie", header);
607
+ }
608
+ if (shadowed.size === 0) {
609
+ return target;
610
+ }
611
+ target.headers.set(SPLIT_COOKIE_JAR_HEADER, "1");
612
+ if (baseline.ambiguousKeys.length > 0) {
613
+ target.headers.set(AMBIGUOUS_AUTH_COOKIE_HEADER, "1");
614
+ }
615
+ for (const [key2, value] of Object.entries(AUTH_COOKIE_RESPONSE_HEADERS)) {
616
+ target.headers.set(key2, value);
617
+ }
618
+ console.warn(
619
+ `[@ai-matrx/data/next] split auth cookie jar healed on host "${host ?? "?"}": ${[...shadowed].join(", ")} arrived at two scopes; the host-only copies were expired so the ${hostCookieOptions.domain} set is the only one left.`
620
+ );
621
+ return target;
622
+ };
623
+ return {
624
+ user,
625
+ client,
626
+ get response() {
627
+ return applyHeal(response);
628
+ },
629
+ redirect: (targetUrl) => {
630
+ const redirect = createRedirect(targetUrl);
631
+ for (const cookie of response.cookies.getAll()) {
632
+ redirect.cookies.set(cookie);
633
+ }
634
+ for (const name of CARRIED_HEADERS) {
635
+ const value = response.headers.get(name);
636
+ if (value) redirect.headers.set(name, value);
637
+ }
638
+ return applyHeal(redirect);
639
+ },
640
+ splitCookieJar: shadowed.size > 0,
641
+ ambiguousAuthCookies: baseline.ambiguousKeys.length > 0,
642
+ authUnavailable
643
+ };
644
+ }
645
+ return {
646
+ authCookie,
647
+ cookieName: authCookie.cookieName,
648
+ optionsForHost: authCookie.optionsForHost,
649
+ browserOptions: authCookie.browserOptions,
650
+ browserClient,
651
+ serverClient,
652
+ routeClient,
653
+ middlewareSession
654
+ };
655
+ }