@palbase/web 4.0.0 → 4.0.2

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 (48) hide show
  1. package/dist/_mls-wasm.cjs +32 -0
  2. package/dist/_mls-wasm.cjs.map +1 -0
  3. package/dist/_mls-wasm.js +11 -0
  4. package/dist/_mls-wasm.js.map +1 -0
  5. package/dist/{analytics-facade-DwOtlXPP.d.cts → analytics-facade-CaluA4bW.d.cts} +26 -1
  6. package/dist/{analytics-facade-Nk6J9Ncm.d.ts → analytics-facade-Cp3d4QI-.d.ts} +26 -1
  7. package/dist/{chunk-S7NAY3MW.js → chunk-CFDU23TB.js} +3 -10
  8. package/dist/chunk-CFDU23TB.js.map +1 -0
  9. package/dist/chunk-PZ5AY32C.js +10 -0
  10. package/dist/chunk-PZ5AY32C.js.map +1 -0
  11. package/dist/{chunk-NP4NIY7A.js → chunk-TW6YN354.js} +84 -27
  12. package/dist/chunk-TW6YN354.js.map +1 -0
  13. package/dist/gen/cli.cjs +5 -5
  14. package/dist/gen/cli.cjs.map +1 -1
  15. package/dist/gen/cli.js +6 -5
  16. package/dist/gen/cli.js.map +1 -1
  17. package/dist/index.cjs +91 -27
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +3 -3
  20. package/dist/index.d.ts +3 -3
  21. package/dist/index.js +3 -2
  22. package/dist/internal.cjs +90 -26
  23. package/dist/internal.cjs.map +1 -1
  24. package/dist/internal.d.cts +3 -3
  25. package/dist/internal.d.ts +3 -3
  26. package/dist/internal.js +3 -2
  27. package/dist/next/client.cjs +90 -26
  28. package/dist/next/client.cjs.map +1 -1
  29. package/dist/next/client.js +3 -2
  30. package/dist/next/client.js.map +1 -1
  31. package/dist/next/index.cjs +81 -27
  32. package/dist/next/index.cjs.map +1 -1
  33. package/dist/next/index.d.cts +2 -2
  34. package/dist/next/index.d.ts +2 -2
  35. package/dist/next/index.js +3 -2
  36. package/dist/next/index.js.map +1 -1
  37. package/dist/{pb-FG_nV7NR.d.ts → pb-DUK5qy81.d.ts} +1 -1
  38. package/dist/{pb-C9v5dEO6.d.cts → pb-DvV6ftmf.d.cts} +1 -1
  39. package/dist/react/index.cjs +10 -2
  40. package/dist/react/index.cjs.map +1 -1
  41. package/dist/react/index.d.cts +1 -1
  42. package/dist/react/index.d.ts +1 -1
  43. package/dist/react/index.js +3 -2
  44. package/dist/react/index.js.map +1 -1
  45. package/package.json +10 -3
  46. package/dist/chunk-NP4NIY7A.js.map +0 -1
  47. package/dist/chunk-S7NAY3MW.js.map +0 -1
  48. package/dist/pkg/palbe_mls_bg.wasm +0 -0
@@ -6,8 +6,9 @@ import {
6
6
  import {
7
7
  __configure,
8
8
  getRuntime
9
- } from "../chunk-NP4NIY7A.js";
10
- import "../chunk-S7NAY3MW.js";
9
+ } from "../chunk-TW6YN354.js";
10
+ import "../chunk-CFDU23TB.js";
11
+ import "../chunk-PZ5AY32C.js";
11
12
 
12
13
  // src/next/client.ts
13
14
  var SESSION_MAX_AGE_S = 2592e3;
@@ -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":";;;;;;;;;;;;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":[]}
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":[]}
@@ -57,8 +57,8 @@ function asWireAuthResult(raw) {
57
57
  }
58
58
 
59
59
  // src/api-key.ts
60
- var API_KEY_RE = /^pb_([a-z0-9]+)_[cs][A-Za-z0-9]{20}$/;
61
- var PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]+)_c[A-Za-z0-9]{20}$/;
60
+ var API_KEY_RE = /^pb_([a-z0-9]{4,24})_[cs][A-Za-z0-9]{20}$/;
61
+ var PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]{4,24})_c[A-Za-z0-9]{20}$/;
62
62
  function environmentRefFromApiKey(apiKey) {
63
63
  const m = API_KEY_RE.exec(apiKey);
64
64
  return m ? m[1] ?? "" : "";
@@ -168,7 +168,7 @@ var PalbaseError = class extends Error {
168
168
  }
169
169
  };
170
170
  var PALBASE_DEFAULT_HOST = "api.palbase.studio";
171
- var API_KEY_RE2 = /^pb_([a-z0-9]+)_c[A-Za-z0-9]{20}$/;
171
+ var API_KEY_RE2 = /^pb_([a-z0-9]{4,24})_c[A-Za-z0-9]{20}$/;
172
172
  function parseEnvironmentRef(apiKey) {
173
173
  return API_KEY_RE2.exec(apiKey)?.[1] ?? null;
174
174
  }
@@ -545,6 +545,11 @@ var PERF_EXCLUDED_PREFIX = "/v1/analytics/";
545
545
  function isSelfTraced(path) {
546
546
  return path.startsWith(PERF_EXCLUDED_PREFIX);
547
547
  }
548
+ function isSessionKilled(status, code) {
549
+ if (status === 401) return code === "session_revoked";
550
+ if (status === 403) return code === "subject_fenced" || code === "subject_erased";
551
+ return false;
552
+ }
548
553
  function nowMs() {
549
554
  return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
550
555
  }
@@ -580,7 +585,7 @@ async function palbeRequest(rt, method, path, spec = {}) {
580
585
  let res;
581
586
  try {
582
587
  res = await attempt();
583
- if (res.error?.status === 401 && rt.tokenManager.getRefreshToken() && rt.tokenManager.refreshFunction) {
588
+ if (res.error?.status === 401 && res.error.code !== "session_revoked" && rt.tokenManager.getRefreshToken() && rt.tokenManager.refreshFunction) {
584
589
  try {
585
590
  await rt.tokenManager.refreshSession();
586
591
  } catch (refreshErr) {
@@ -600,6 +605,9 @@ async function palbeRequest(rt, method, path, spec = {}) {
600
605
  if (!isAbort(e)) record(asPalbaseError(e)?.status ?? 0);
601
606
  throw e;
602
607
  }
608
+ if (res.error && isSessionKilled(res.error.status, res.error.code)) {
609
+ rt.auth.reactiveTeardown();
610
+ }
603
611
  record(res.error?.status ?? 200);
604
612
  return unwrap(res);
605
613
  }
@@ -1478,7 +1486,8 @@ var PalbeAuth = class {
1478
1486
  if (!this.signedInState) return;
1479
1487
  this.signedInState = false;
1480
1488
  this.cachedUser = null;
1481
- const reason = this.signingOut ? "userInitiated" : "sessionExpired";
1489
+ const reason = this.nextSignOutReason ?? (this.signingOut ? "userInitiated" : "sessionExpired");
1490
+ this.nextSignOutReason = null;
1482
1491
  this.emitState({ status: "signedOut" });
1483
1492
  this.emitEvent({ type: "signedOut", reason });
1484
1493
  } else if (event === "TOKEN_REFRESHED") {
@@ -1491,6 +1500,10 @@ var PalbeAuth = class {
1491
1500
  rt;
1492
1501
  cachedUser = null;
1493
1502
  signingOut = false;
1503
+ // Explicit signedOut reason pinned for the NEXT session clear (deleteAccount /
1504
+ // reactiveTeardown set it before clearSession; the SIGNED_OUT handler consumes
1505
+ // it exactly once, then resets to null → normal signOut/expiry split resumes).
1506
+ nextSignOutReason = null;
1494
1507
  signingIn = false;
1495
1508
  // suppresses AuthClient's TOKEN_REFRESHED during re-signIn
1496
1509
  signedInState = false;
@@ -1538,6 +1551,45 @@ var PalbeAuth = class {
1538
1551
  this.cachedUser = null;
1539
1552
  }
1540
1553
  }
1554
+ /**
1555
+ * Self-service account erasure ("right to be forgotten"). Calls palauth
1556
+ * `DELETE /auth/user` (session-authed + fresh re-auth): password users pass
1557
+ * `{ password }`; passwordless/OAuth users rely on a freshly stepped-up
1558
+ * session. On 202 the erasure workflow is durably queued AND every session is
1559
+ * already revoked server-side — so we tear down local state (mirroring
1560
+ * `signOut()`'s clear, minus the pointless `/logout`) and emit
1561
+ * `signedOut{reason:'accountDeleted'}`.
1562
+ *
1563
+ * On 401 `reauth_required` / 503 `not_configured` (or `erasure_unavailable`)
1564
+ * the request throws BEFORE the teardown line, so the local session is left
1565
+ * fully intact — the deletion did not happen and the user is still signed in.
1566
+ */
1567
+ async deleteAccount(params = {}) {
1568
+ const res = await palbeRequest(this.rt, "DELETE", "/auth/user", {
1569
+ body: params.password ? { password: params.password } : {}
1570
+ });
1571
+ this.nextSignOutReason = "accountDeleted";
1572
+ this.rt.tokenManager.clearSession();
1573
+ this.rt.storage.clear();
1574
+ this.cachedUser = null;
1575
+ return { erasureId: res.erasure_id };
1576
+ }
1577
+ /**
1578
+ * @internal — invoked by the transport choke point (`request.ts`), not app code.
1579
+ * Reactive teardown when the server reports the session is gone: a 403
1580
+ * `subject_fenced`/`subject_erased` (subject being erased) or a 401
1581
+ * `session_revoked`. Clears local state and emits
1582
+ * `signedOut{reason:'sessionInvalid'}` exactly once (deduped by
1583
+ * `signedInState`). An ordinary 403 authz denial does NOT reach here —
1584
+ * `request.ts` matches only the exact session-kill codes.
1585
+ */
1586
+ reactiveTeardown() {
1587
+ if (!this.signedInState) return;
1588
+ this.nextSignOutReason = "sessionInvalid";
1589
+ this.rt.tokenManager.clearSession();
1590
+ this.rt.storage.clear();
1591
+ this.cachedUser = null;
1592
+ }
1541
1593
  async getUser() {
1542
1594
  const raw = await palbeRequest(this.rt, "GET", "/auth/user");
1543
1595
  return mapWireUser(raw);
@@ -6477,31 +6529,29 @@ function date_now() {
6477
6529
  }
6478
6530
 
6479
6531
  // src/messaging/wasm/loader.ts
6480
- var import_meta = {};
6481
6532
  var GLUE_MODULE = "./palbe_mls_bg.js";
6482
6533
  var SNIPPET_MODULE = "./snippets/mls-rs-core-f99cdecbb456b09c/inline0.js";
6483
6534
  var inflight = null;
6484
6535
  var ready = null;
6485
- async function loadWasmModule() {
6486
- const wasmUrl = new URL("./pkg/palbe_mls_bg.wasm", import_meta.url);
6487
- if (wasmUrl.protocol === "file:") {
6488
- const fsSpecifier = ["node", "fs/promises"].join(":");
6489
- const { readFile } = await import(
6490
- /* webpackIgnore: true */
6491
- fsSpecifier
6492
- );
6493
- const bytes = await readFile(wasmUrl);
6494
- return WebAssembly.compile(bytes);
6536
+ function decodeBase64(encoded) {
6537
+ if (typeof encoded !== "string" || encoded.length === 0 || typeof globalThis.atob !== "function") {
6538
+ throw new Error("palbe-mls WASM asset is invalid");
6495
6539
  }
6496
- const response = await fetch(wasmUrl);
6497
- if (typeof WebAssembly.compileStreaming === "function") {
6498
- try {
6499
- return await WebAssembly.compileStreaming(Promise.resolve(response));
6500
- } catch {
6501
- }
6540
+ let binary;
6541
+ try {
6542
+ binary = globalThis.atob(encoded);
6543
+ } catch {
6544
+ throw new Error("palbe-mls WASM asset is invalid");
6545
+ }
6546
+ const bytes = new Uint8Array(binary.length);
6547
+ for (let index = 0; index < binary.length; index += 1) {
6548
+ bytes[index] = binary.charCodeAt(index);
6502
6549
  }
6503
- const buffer = await response.arrayBuffer();
6504
- return WebAssembly.compile(buffer);
6550
+ return bytes.buffer;
6551
+ }
6552
+ async function loadWasmModule() {
6553
+ const asset = await import("#palbase-mls-wasm");
6554
+ return WebAssembly.compile(decodeBase64(asset.default));
6505
6555
  }
6506
6556
  function buildImports() {
6507
6557
  return {
@@ -6512,7 +6562,7 @@ function buildImports() {
6512
6562
  function initMls() {
6513
6563
  if (ready) return Promise.resolve(ready);
6514
6564
  if (inflight) return inflight;
6515
- inflight = (async () => {
6565
+ const attempt = (async () => {
6516
6566
  const module2 = await loadWasmModule();
6517
6567
  const instance = await WebAssembly.instantiate(module2, buildImports());
6518
6568
  const setWasm = __wbg_set_wasm;
@@ -6531,7 +6581,11 @@ function initMls() {
6531
6581
  ready = g;
6532
6582
  return g;
6533
6583
  })();
6534
- return inflight;
6584
+ inflight = attempt;
6585
+ void attempt.catch(() => {
6586
+ if (inflight === attempt) inflight = null;
6587
+ });
6588
+ return attempt;
6535
6589
  }
6536
6590
  function requireMls() {
6537
6591
  if (!ready) {
@@ -8984,7 +9038,7 @@ function defaultSessionStorage(key) {
8984
9038
  }
8985
9039
 
8986
9040
  // src/version.ts
8987
- var VERSION = "4.0.0";
9041
+ var VERSION = "4.0.2";
8988
9042
 
8989
9043
  // src/runtime.ts
8990
9044
  function buildRuntime(config) {