@palbase/web 4.0.1 → 5.0.0
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/README.md +20 -3
- package/dist/{analytics-facade-DwOtlXPP.d.cts → analytics-facade-CaluA4bW.d.cts} +26 -1
- package/dist/{analytics-facade-Nk6J9Ncm.d.ts → analytics-facade-Cp3d4QI-.d.ts} +26 -1
- package/dist/chunk-BNAGRUIQ.js +381 -0
- package/dist/chunk-BNAGRUIQ.js.map +1 -0
- package/dist/{chunk-NXEPAEF5.js → chunk-CFDU23TB.js} +3 -3
- package/dist/chunk-CFDU23TB.js.map +1 -0
- package/dist/chunk-OWLTZG2V.js +24 -0
- package/dist/chunk-OWLTZG2V.js.map +1 -0
- package/dist/{chunk-TGDS6VMT.js → chunk-XTQXFZUP.js} +68 -376
- package/dist/chunk-XTQXFZUP.js.map +1 -0
- package/dist/gen/cli.cjs +5 -5
- package/dist/gen/cli.cjs.map +1 -1
- package/dist/gen/cli.js +5 -5
- package/dist/gen/cli.js.map +1 -1
- package/dist/index.cjs +55 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +6 -4
- package/dist/internal.cjs +57 -5
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +3 -3
- package/dist/internal.d.ts +3 -3
- package/dist/internal.js +3 -2
- package/dist/next/client.cjs +57 -5
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +3 -2
- package/dist/next/client.js.map +1 -1
- package/dist/next/index.cjs +61 -93
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +4 -64
- package/dist/next/index.d.ts +4 -64
- package/dist/next/index.js +9 -103
- package/dist/next/index.js.map +1 -1
- package/dist/next/middleware.cjs +263 -0
- package/dist/next/middleware.cjs.map +1 -0
- package/dist/next/middleware.d.cts +101 -0
- package/dist/next/middleware.d.ts +101 -0
- package/dist/next/middleware.js +107 -0
- package/dist/next/middleware.js.map +1 -0
- package/dist/{pb-FG_nV7NR.d.ts → pb-DUK5qy81.d.ts} +1 -1
- package/dist/{pb-C9v5dEO6.d.cts → pb-DvV6ftmf.d.cts} +1 -1
- package/dist/react/index.cjs +10 -2
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +3 -2
- package/dist/react/index.js.map +1 -1
- package/package.json +11 -1
- package/dist/chunk-NXEPAEF5.js.map +0 -1
- package/dist/chunk-TGDS6VMT.js.map +0 -1
package/dist/next/client.js
CHANGED
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
__configure,
|
|
8
8
|
getRuntime
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-XTQXFZUP.js";
|
|
10
|
+
import "../chunk-BNAGRUIQ.js";
|
|
11
|
+
import "../chunk-CFDU23TB.js";
|
|
11
12
|
import "../chunk-PZ5AY32C.js";
|
|
12
13
|
|
|
13
14
|
// src/next/client.ts
|
package/dist/next/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/client.ts"],"sourcesContent":["/**\n * '@palbase/web/next/client' — the browser half of the Next.js adapter. This entry\n * must NEVER import 'next' (it runs in plain client bundles); the cookie jar\n * is document.cookie itself, written with the shared codec so the server\n * side (pbServer / middleware / callback) reads the same bytes.\n */\nimport { __configure, getRuntime } from '../internal.js';\nimport type { PersistedSession, SessionStorageAdapter } from '../storage.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookies,\n} from './cookie-codec.js';\n\n/** 30 days — the refresh-token TTL (P3 design contract). */\nconst SESSION_MAX_AGE_S = 2_592_000;\n\n// Secure is unconditional, INCLUDING http://localhost: browsers treat\n// localhost as a potentially-trustworthy origin, so document.cookie accepts\n// Secure cookies there — no dev-mode special case needed. KNOWN LIMITATION:\n// plain-http origins OTHER than localhost — e.g. LAN-IP device testing on\n// http://192.168.x.x — are NOT trustworthy, so the browser silently DROPS\n// these Secure cookie writes and the session won't persist; use https (or a\n// localhost tunnel/port-forward) for on-device testing. NOT HttpOnly by\n// design: the browser SDK must read/write the session (Supabase-paradigm\n// tradeoff, documented in the P3 plan).\nconst WRITE_ATTRS = 'Path=/; SameSite=Lax; Secure';\n\n/** Parse document.cookie (\"a=1; b=2\") into a name → raw-value map. */\nfunction cookieJar(): Map<string, string> {\n const jar = new Map<string, string>();\n for (const part of document.cookie.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n const name = part.slice(0, eq).trim();\n if (name) jar.set(name, part.slice(eq + 1).trim());\n }\n return jar;\n}\n\nfunction deleteCookie(name: string): void {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=; ${WRITE_ATTRS}; Max-Age=0`;\n}\n\n/**\n * document.cookie-backed SessionStorageAdapter. `load` returns the EXTENDED\n * PersistedSession (access token + expiry ride along) so hydration adopts\n * the full session without a refresh round-trip.\n */\nexport function cookieSessionStorage(environmentRef: string): SessionStorageAdapter {\n return {\n load(): PersistedSession | null {\n if (typeof document === 'undefined') return null;\n const jar = cookieJar();\n const stored = decodeSessionCookies((name) => jar.get(name), environmentRef);\n if (!stored) return null;\n // A refresh-only save round-trips as a:'' / e:0 — normalize back to\n // the legacy shape so hydration takes the expired-trick path cleanly.\n return stored.accessToken && stored.expiresAt > 0\n ? {\n refreshToken: stored.refreshToken,\n accessToken: stored.accessToken,\n expiresAt: stored.expiresAt,\n }\n : { refreshToken: stored.refreshToken };\n },\n save(session: PersistedSession): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n // Delete every currently-present session cookie first (stale chunks of\n // a previously-larger session, or a stale base when the new write\n // chunks), then set the new cookie(s).\n for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n const { set, clear } = encodeSessionCookies(environmentRef, {\n accessToken: session.accessToken ?? '',\n refreshToken: session.refreshToken,\n expiresAt: session.expiresAt ?? 0,\n });\n for (const { name, value } of set) {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=${value}; ${WRITE_ATTRS}; Max-Age=${SESSION_MAX_AGE_S}`;\n }\n // Overflow guard (codec contract): delete one-past-the-end so a stale\n // orphan chunk behind a gap can never join a future chunk run.\n for (const name of clear) deleteCookie(name);\n },\n clear(): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n },\n };\n}\n\n/**\n * One-liner for a client component/provider: re-configure the already-loaded\n * gen config (palbe.gen.ts must be imported first — throws the guided\n * notConfigured error otherwise) with cookie-backed session storage so the\n * browser and the server share the session. Calling it again simply\n * re-configures (safe, e.g. under fast refresh). When Next evaluates the\n * client component module server-side there is no document — no-op.\n */\nexport function setupPalbeNext(): void {\n if (typeof document === 'undefined') return;\n const config = getRuntime().config;\n __configure({\n ...config,\n storage: cookieSessionStorage(config.environmentRef),\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/next/client.ts"],"sourcesContent":["/**\n * '@palbase/web/next/client' — the browser half of the Next.js adapter. This entry\n * must NEVER import 'next' (it runs in plain client bundles); the cookie jar\n * is document.cookie itself, written with the shared codec so the server\n * side (pbServer / middleware / callback) reads the same bytes.\n */\nimport { __configure, getRuntime } from '../internal.js';\nimport type { PersistedSession, SessionStorageAdapter } from '../storage.js';\nimport {\n clearedSessionCookieNames,\n decodeSessionCookies,\n encodeSessionCookies,\n} from './cookie-codec.js';\n\n/** 30 days — the refresh-token TTL (P3 design contract). */\nconst SESSION_MAX_AGE_S = 2_592_000;\n\n// Secure is unconditional, INCLUDING http://localhost: browsers treat\n// localhost as a potentially-trustworthy origin, so document.cookie accepts\n// Secure cookies there — no dev-mode special case needed. KNOWN LIMITATION:\n// plain-http origins OTHER than localhost — e.g. LAN-IP device testing on\n// http://192.168.x.x — are NOT trustworthy, so the browser silently DROPS\n// these Secure cookie writes and the session won't persist; use https (or a\n// localhost tunnel/port-forward) for on-device testing. NOT HttpOnly by\n// design: the browser SDK must read/write the session (Supabase-paradigm\n// tradeoff, documented in the P3 plan).\nconst WRITE_ATTRS = 'Path=/; SameSite=Lax; Secure';\n\n/** Parse document.cookie (\"a=1; b=2\") into a name → raw-value map. */\nfunction cookieJar(): Map<string, string> {\n const jar = new Map<string, string>();\n for (const part of document.cookie.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n const name = part.slice(0, eq).trim();\n if (name) jar.set(name, part.slice(eq + 1).trim());\n }\n return jar;\n}\n\nfunction deleteCookie(name: string): void {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=; ${WRITE_ATTRS}; Max-Age=0`;\n}\n\n/**\n * document.cookie-backed SessionStorageAdapter. `load` returns the EXTENDED\n * PersistedSession (access token + expiry ride along) so hydration adopts\n * the full session without a refresh round-trip.\n */\nexport function cookieSessionStorage(environmentRef: string): SessionStorageAdapter {\n return {\n load(): PersistedSession | null {\n if (typeof document === 'undefined') return null;\n const jar = cookieJar();\n const stored = decodeSessionCookies((name) => jar.get(name), environmentRef);\n if (!stored) return null;\n // A refresh-only save round-trips as a:'' / e:0 — normalize back to\n // the legacy shape so hydration takes the expired-trick path cleanly.\n return stored.accessToken && stored.expiresAt > 0\n ? {\n refreshToken: stored.refreshToken,\n accessToken: stored.accessToken,\n expiresAt: stored.expiresAt,\n }\n : { refreshToken: stored.refreshToken };\n },\n save(session: PersistedSession): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n // Delete every currently-present session cookie first (stale chunks of\n // a previously-larger session, or a stale base when the new write\n // chunks), then set the new cookie(s).\n for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n const { set, clear } = encodeSessionCookies(environmentRef, {\n accessToken: session.accessToken ?? '',\n refreshToken: session.refreshToken,\n expiresAt: session.expiresAt ?? 0,\n });\n for (const { name, value } of set) {\n // biome-ignore lint/suspicious/noDocumentCookie: SessionStorageAdapter is a SYNC contract; the async Cookie Store API can't back it (and isn't universal).\n document.cookie = `${name}=${value}; ${WRITE_ATTRS}; Max-Age=${SESSION_MAX_AGE_S}`;\n }\n // Overflow guard (codec contract): delete one-past-the-end so a stale\n // orphan chunk behind a gap can never join a future chunk run.\n for (const name of clear) deleteCookie(name);\n },\n clear(): void {\n if (typeof document === 'undefined') return;\n const jar = cookieJar();\n for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {\n deleteCookie(name);\n }\n },\n };\n}\n\n/**\n * One-liner for a client component/provider: re-configure the already-loaded\n * gen config (palbe.gen.ts must be imported first — throws the guided\n * notConfigured error otherwise) with cookie-backed session storage so the\n * browser and the server share the session. Calling it again simply\n * re-configures (safe, e.g. under fast refresh). When Next evaluates the\n * client component module server-side there is no document — no-op.\n */\nexport function setupPalbeNext(): void {\n if (typeof document === 'undefined') return;\n const config = getRuntime().config;\n __configure({\n ...config,\n storage: cookieSessionStorage(config.environmentRef),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAM,oBAAoB;AAW1B,IAAM,cAAc;AAGpB,SAAS,YAAiC;AACxC,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,QAAQ,SAAS,OAAO,MAAM,GAAG,GAAG;AAC7C,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,OAAO,GAAI;AACf,UAAM,OAAO,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACpC,QAAI,KAAM,KAAI,IAAI,MAAM,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK,CAAC;AAAA,EACnD;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAoB;AAExC,WAAS,SAAS,GAAG,IAAI,MAAM,WAAW;AAC5C;AAOO,SAAS,qBAAqB,gBAA+C;AAClF,SAAO;AAAA,IACL,OAAgC;AAC9B,UAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,YAAM,MAAM,UAAU;AACtB,YAAM,SAAS,qBAAqB,CAAC,SAAS,IAAI,IAAI,IAAI,GAAG,cAAc;AAC3E,UAAI,CAAC,OAAQ,QAAO;AAGpB,aAAO,OAAO,eAAe,OAAO,YAAY,IAC5C;AAAA,QACE,cAAc,OAAO;AAAA,QACrB,aAAa,OAAO;AAAA,QACpB,WAAW,OAAO;AAAA,MACpB,IACA,EAAE,cAAc,OAAO,aAAa;AAAA,IAC1C;AAAA,IACA,KAAK,SAAiC;AACpC,UAAI,OAAO,aAAa,YAAa;AACrC,YAAM,MAAM,UAAU;AAItB,iBAAW,QAAQ,0BAA0B,gBAAgB,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,GAAG;AAC/E,qBAAa,IAAI;AAAA,MACnB;AACA,YAAM,EAAE,KAAK,MAAM,IAAI,qBAAqB,gBAAgB;AAAA,QAC1D,aAAa,QAAQ,eAAe;AAAA,QACpC,cAAc,QAAQ;AAAA,QACtB,WAAW,QAAQ,aAAa;AAAA,MAClC,CAAC;AACD,iBAAW,EAAE,MAAM,MAAM,KAAK,KAAK;AAEjC,iBAAS,SAAS,GAAG,IAAI,IAAI,KAAK,KAAK,WAAW,aAAa,iBAAiB;AAAA,MAClF;AAGA,iBAAW,QAAQ,MAAO,cAAa,IAAI;AAAA,IAC7C;AAAA,IACA,QAAc;AACZ,UAAI,OAAO,aAAa,YAAa;AACrC,YAAM,MAAM,UAAU;AACtB,iBAAW,QAAQ,0BAA0B,gBAAgB,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,GAAG;AAC/E,qBAAa,IAAI;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;AAUO,SAAS,iBAAuB;AACrC,MAAI,OAAO,aAAa,YAAa;AACrC,QAAM,SAAS,WAAW,EAAE;AAC5B,cAAY;AAAA,IACV,GAAG;AAAA,IACH,SAAS,qBAAqB,OAAO,cAAc;AAAA,EACrD,CAAC;AACH;","names":[]}
|
package/dist/next/index.cjs
CHANGED
|
@@ -37,7 +37,6 @@ __export(next_exports, {
|
|
|
37
37
|
encodeSessionCookiesDecoded: () => encodeSessionCookiesDecoded,
|
|
38
38
|
environmentRefFromApiKey: () => environmentRefFromApiKey,
|
|
39
39
|
handleAuthCallback: () => handleAuthCallback,
|
|
40
|
-
palbeMiddleware: () => palbeMiddleware,
|
|
41
40
|
pbServer: () => pbServer,
|
|
42
41
|
sessionCookieName: () => sessionCookieName
|
|
43
42
|
});
|
|
@@ -57,8 +56,8 @@ function asWireAuthResult(raw) {
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
// src/api-key.ts
|
|
60
|
-
var API_KEY_RE = /^pb_([a-z0-9]
|
|
61
|
-
var PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]
|
|
59
|
+
var API_KEY_RE = /^pb_([a-z0-9]{4,24})_[cs][A-Za-z0-9]{20}$/;
|
|
60
|
+
var PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]{4,24})_c[A-Za-z0-9]{20}$/;
|
|
62
61
|
function environmentRefFromApiKey(apiKey) {
|
|
63
62
|
const m = API_KEY_RE.exec(apiKey);
|
|
64
63
|
return m ? m[1] ?? "" : "";
|
|
@@ -168,7 +167,7 @@ var PalbaseError = class extends Error {
|
|
|
168
167
|
}
|
|
169
168
|
};
|
|
170
169
|
var PALBASE_DEFAULT_HOST = "api.palbase.studio";
|
|
171
|
-
var API_KEY_RE2 = /^pb_([a-z0-9]
|
|
170
|
+
var API_KEY_RE2 = /^pb_([a-z0-9]{4,24})_c[A-Za-z0-9]{20}$/;
|
|
172
171
|
function parseEnvironmentRef(apiKey) {
|
|
173
172
|
return API_KEY_RE2.exec(apiKey)?.[1] ?? null;
|
|
174
173
|
}
|
|
@@ -545,6 +544,11 @@ var PERF_EXCLUDED_PREFIX = "/v1/analytics/";
|
|
|
545
544
|
function isSelfTraced(path) {
|
|
546
545
|
return path.startsWith(PERF_EXCLUDED_PREFIX);
|
|
547
546
|
}
|
|
547
|
+
function isSessionKilled(status, code) {
|
|
548
|
+
if (status === 401) return code === "session_revoked";
|
|
549
|
+
if (status === 403) return code === "subject_fenced" || code === "subject_erased";
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
548
552
|
function nowMs() {
|
|
549
553
|
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
550
554
|
}
|
|
@@ -580,7 +584,7 @@ async function palbeRequest(rt, method, path, spec = {}) {
|
|
|
580
584
|
let res;
|
|
581
585
|
try {
|
|
582
586
|
res = await attempt();
|
|
583
|
-
if (res.error?.status === 401 && rt.tokenManager.getRefreshToken() && rt.tokenManager.refreshFunction) {
|
|
587
|
+
if (res.error?.status === 401 && res.error.code !== "session_revoked" && rt.tokenManager.getRefreshToken() && rt.tokenManager.refreshFunction) {
|
|
584
588
|
try {
|
|
585
589
|
await rt.tokenManager.refreshSession();
|
|
586
590
|
} catch (refreshErr) {
|
|
@@ -600,6 +604,9 @@ async function palbeRequest(rt, method, path, spec = {}) {
|
|
|
600
604
|
if (!isAbort(e)) record(asPalbaseError(e)?.status ?? 0);
|
|
601
605
|
throw e;
|
|
602
606
|
}
|
|
607
|
+
if (res.error && isSessionKilled(res.error.status, res.error.code)) {
|
|
608
|
+
rt.auth.reactiveTeardown();
|
|
609
|
+
}
|
|
603
610
|
record(res.error?.status ?? 200);
|
|
604
611
|
return unwrap(res);
|
|
605
612
|
}
|
|
@@ -1478,7 +1485,8 @@ var PalbeAuth = class {
|
|
|
1478
1485
|
if (!this.signedInState) return;
|
|
1479
1486
|
this.signedInState = false;
|
|
1480
1487
|
this.cachedUser = null;
|
|
1481
|
-
const reason = this.signingOut ? "userInitiated" : "sessionExpired";
|
|
1488
|
+
const reason = this.nextSignOutReason ?? (this.signingOut ? "userInitiated" : "sessionExpired");
|
|
1489
|
+
this.nextSignOutReason = null;
|
|
1482
1490
|
this.emitState({ status: "signedOut" });
|
|
1483
1491
|
this.emitEvent({ type: "signedOut", reason });
|
|
1484
1492
|
} else if (event === "TOKEN_REFRESHED") {
|
|
@@ -1491,6 +1499,10 @@ var PalbeAuth = class {
|
|
|
1491
1499
|
rt;
|
|
1492
1500
|
cachedUser = null;
|
|
1493
1501
|
signingOut = false;
|
|
1502
|
+
// Explicit signedOut reason pinned for the NEXT session clear (deleteAccount /
|
|
1503
|
+
// reactiveTeardown set it before clearSession; the SIGNED_OUT handler consumes
|
|
1504
|
+
// it exactly once, then resets to null → normal signOut/expiry split resumes).
|
|
1505
|
+
nextSignOutReason = null;
|
|
1494
1506
|
signingIn = false;
|
|
1495
1507
|
// suppresses AuthClient's TOKEN_REFRESHED during re-signIn
|
|
1496
1508
|
signedInState = false;
|
|
@@ -1538,6 +1550,45 @@ var PalbeAuth = class {
|
|
|
1538
1550
|
this.cachedUser = null;
|
|
1539
1551
|
}
|
|
1540
1552
|
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Self-service account erasure ("right to be forgotten"). Calls palauth
|
|
1555
|
+
* `DELETE /auth/user` (session-authed + fresh re-auth): password users pass
|
|
1556
|
+
* `{ password }`; passwordless/OAuth users rely on a freshly stepped-up
|
|
1557
|
+
* session. On 202 the erasure workflow is durably queued AND every session is
|
|
1558
|
+
* already revoked server-side — so we tear down local state (mirroring
|
|
1559
|
+
* `signOut()`'s clear, minus the pointless `/logout`) and emit
|
|
1560
|
+
* `signedOut{reason:'accountDeleted'}`.
|
|
1561
|
+
*
|
|
1562
|
+
* On 401 `reauth_required` / 503 `not_configured` (or `erasure_unavailable`)
|
|
1563
|
+
* the request throws BEFORE the teardown line, so the local session is left
|
|
1564
|
+
* fully intact — the deletion did not happen and the user is still signed in.
|
|
1565
|
+
*/
|
|
1566
|
+
async deleteAccount(params = {}) {
|
|
1567
|
+
const res = await palbeRequest(this.rt, "DELETE", "/auth/user", {
|
|
1568
|
+
body: params.password ? { password: params.password } : {}
|
|
1569
|
+
});
|
|
1570
|
+
this.nextSignOutReason = "accountDeleted";
|
|
1571
|
+
this.rt.tokenManager.clearSession();
|
|
1572
|
+
this.rt.storage.clear();
|
|
1573
|
+
this.cachedUser = null;
|
|
1574
|
+
return { erasureId: res.erasure_id };
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* @internal — invoked by the transport choke point (`request.ts`), not app code.
|
|
1578
|
+
* Reactive teardown when the server reports the session is gone: a 403
|
|
1579
|
+
* `subject_fenced`/`subject_erased` (subject being erased) or a 401
|
|
1580
|
+
* `session_revoked`. Clears local state and emits
|
|
1581
|
+
* `signedOut{reason:'sessionInvalid'}` exactly once (deduped by
|
|
1582
|
+
* `signedInState`). An ordinary 403 authz denial does NOT reach here —
|
|
1583
|
+
* `request.ts` matches only the exact session-kill codes.
|
|
1584
|
+
*/
|
|
1585
|
+
reactiveTeardown() {
|
|
1586
|
+
if (!this.signedInState) return;
|
|
1587
|
+
this.nextSignOutReason = "sessionInvalid";
|
|
1588
|
+
this.rt.tokenManager.clearSession();
|
|
1589
|
+
this.rt.storage.clear();
|
|
1590
|
+
this.cachedUser = null;
|
|
1591
|
+
}
|
|
1541
1592
|
async getUser() {
|
|
1542
1593
|
const raw = await palbeRequest(this.rt, "GET", "/auth/user");
|
|
1543
1594
|
return mapWireUser(raw);
|
|
@@ -8986,7 +9037,7 @@ function defaultSessionStorage(key) {
|
|
|
8986
9037
|
}
|
|
8987
9038
|
|
|
8988
9039
|
// src/version.ts
|
|
8989
|
-
var VERSION = "
|
|
9040
|
+
var VERSION = "5.0.0";
|
|
8990
9041
|
|
|
8991
9042
|
// src/runtime.ts
|
|
8992
9043
|
function buildRuntime(config) {
|
|
@@ -9366,7 +9417,7 @@ function getRuntime() {
|
|
|
9366
9417
|
return rt;
|
|
9367
9418
|
}
|
|
9368
9419
|
|
|
9369
|
-
// src/next/
|
|
9420
|
+
// src/next/global-config.ts
|
|
9370
9421
|
function requireGlobalConfig(hint) {
|
|
9371
9422
|
try {
|
|
9372
9423
|
return getRuntime().config;
|
|
@@ -9377,6 +9428,8 @@ function requireGlobalConfig(hint) {
|
|
|
9377
9428
|
});
|
|
9378
9429
|
}
|
|
9379
9430
|
}
|
|
9431
|
+
|
|
9432
|
+
// src/next/shared.ts
|
|
9380
9433
|
var nextServerModule;
|
|
9381
9434
|
async function importNextServer(caller) {
|
|
9382
9435
|
nextServerModule ??= import("next/server");
|
|
@@ -9482,90 +9535,6 @@ function handleAuthCallback(opts) {
|
|
|
9482
9535
|
};
|
|
9483
9536
|
}
|
|
9484
9537
|
|
|
9485
|
-
// src/next/middleware.ts
|
|
9486
|
-
var DEFAULT_REFRESH_MARGIN_MS = 6e4;
|
|
9487
|
-
var inflightRefreshes = /* @__PURE__ */ new Map();
|
|
9488
|
-
function refreshSingleFlight(config, refreshToken) {
|
|
9489
|
-
const existing = inflightRefreshes.get(refreshToken);
|
|
9490
|
-
if (existing) return existing;
|
|
9491
|
-
const pending = refresh(config, refreshToken).finally(() => {
|
|
9492
|
-
inflightRefreshes.delete(refreshToken);
|
|
9493
|
-
});
|
|
9494
|
-
inflightRefreshes.set(refreshToken, pending);
|
|
9495
|
-
return pending;
|
|
9496
|
-
}
|
|
9497
|
-
async function refresh(config, refreshToken) {
|
|
9498
|
-
try {
|
|
9499
|
-
const res = await fetch(`${config.url}/auth/token/refresh`, {
|
|
9500
|
-
method: "POST",
|
|
9501
|
-
headers: { apikey: config.apiKey, "content-type": "application/json" },
|
|
9502
|
-
body: JSON.stringify({ refresh_token: refreshToken })
|
|
9503
|
-
});
|
|
9504
|
-
if (res.status === 400 || res.status === 401 || res.status === 403) {
|
|
9505
|
-
return { kind: "terminal" };
|
|
9506
|
-
}
|
|
9507
|
-
if (!res.ok) return { kind: "transient" };
|
|
9508
|
-
const raw = await res.json();
|
|
9509
|
-
if (typeof raw === "object" && raw !== null) {
|
|
9510
|
-
const obj = raw;
|
|
9511
|
-
if (typeof obj.access_token === "string" && typeof obj.refresh_token === "string" && typeof obj.expires_in === "number") {
|
|
9512
|
-
return {
|
|
9513
|
-
kind: "rotated",
|
|
9514
|
-
session: {
|
|
9515
|
-
accessToken: obj.access_token,
|
|
9516
|
-
refreshToken: obj.refresh_token,
|
|
9517
|
-
expiresAt: Date.now() + obj.expires_in * 1e3
|
|
9518
|
-
}
|
|
9519
|
-
};
|
|
9520
|
-
}
|
|
9521
|
-
}
|
|
9522
|
-
return { kind: "transient" };
|
|
9523
|
-
} catch {
|
|
9524
|
-
return { kind: "transient" };
|
|
9525
|
-
}
|
|
9526
|
-
}
|
|
9527
|
-
async function palbeMiddleware(request, opts) {
|
|
9528
|
-
const config = requireGlobalConfig(
|
|
9529
|
-
"import the generated palbe.gen.ts at the top of middleware.ts \u2014 Next bundles middleware as its own module graph, so the layout.tsx import does not reach it."
|
|
9530
|
-
);
|
|
9531
|
-
const { NextResponse } = await importNextServer("palbeMiddleware");
|
|
9532
|
-
const passThrough = () => opts?.response ?? NextResponse.next({ request });
|
|
9533
|
-
const ref = config.environmentRef;
|
|
9534
|
-
const session = decodeSessionCookies((name) => request.cookies.get(name)?.value, ref);
|
|
9535
|
-
if (!session) return passThrough();
|
|
9536
|
-
if (session.expiresAt - Date.now() > (opts?.refreshMarginMs ?? DEFAULT_REFRESH_MARGIN_MS)) {
|
|
9537
|
-
return passThrough();
|
|
9538
|
-
}
|
|
9539
|
-
const outcome = await refreshSingleFlight(config, session.refreshToken);
|
|
9540
|
-
if (outcome.kind === "transient") return passThrough();
|
|
9541
|
-
const staleNames = clearedSessionCookieNames(ref, (name) => request.cookies.has(name));
|
|
9542
|
-
if (outcome.kind === "terminal") {
|
|
9543
|
-
for (const name of staleNames) request.cookies.delete(name);
|
|
9544
|
-
const response2 = opts?.response ?? NextResponse.next({ request });
|
|
9545
|
-
for (const name of staleNames) {
|
|
9546
|
-
response2.cookies.set(name, "", { ...SESSION_COOKIE_ATTRS, maxAge: 0 });
|
|
9547
|
-
}
|
|
9548
|
-
response2.headers.set("cache-control", "private, no-store");
|
|
9549
|
-
return response2;
|
|
9550
|
-
}
|
|
9551
|
-
const write = encodeSessionCookiesDecoded(ref, outcome.session);
|
|
9552
|
-
for (const name of staleNames) request.cookies.delete(name);
|
|
9553
|
-
for (const { name, value } of write.set) request.cookies.set(name, value);
|
|
9554
|
-
for (const name of write.clear) request.cookies.delete(name);
|
|
9555
|
-
const response = opts?.response ?? NextResponse.next({ request });
|
|
9556
|
-
const written = new Set(write.set.map((c) => c.name));
|
|
9557
|
-
for (const { name, value } of write.set) {
|
|
9558
|
-
response.cookies.set(name, value, SESSION_COOKIE_ATTRS);
|
|
9559
|
-
}
|
|
9560
|
-
for (const name of /* @__PURE__ */ new Set([...write.clear, ...staleNames])) {
|
|
9561
|
-
if (!written.has(name)) {
|
|
9562
|
-
response.cookies.set(name, "", { ...SESSION_COOKIE_ATTRS, maxAge: 0 });
|
|
9563
|
-
}
|
|
9564
|
-
}
|
|
9565
|
-
response.headers.set("cache-control", "private, no-store");
|
|
9566
|
-
return response;
|
|
9567
|
-
}
|
|
9568
|
-
|
|
9569
9538
|
// src/next/server.ts
|
|
9570
9539
|
function nextRequired(cause) {
|
|
9571
9540
|
const detail = cause instanceof Error && cause.message ? ` (${cause.message})` : "";
|
|
@@ -9655,7 +9624,6 @@ async function pbServer(opts) {
|
|
|
9655
9624
|
encodeSessionCookiesDecoded,
|
|
9656
9625
|
environmentRefFromApiKey,
|
|
9657
9626
|
handleAuthCallback,
|
|
9658
|
-
palbeMiddleware,
|
|
9659
9627
|
pbServer,
|
|
9660
9628
|
sessionCookieName
|
|
9661
9629
|
});
|