@palbase/web 7.2.2 → 7.3.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/dist/{analytics-facade-DLfnVVwL.d.ts → analytics-facade-BER0EyYT.d.ts} +8 -3
  2. package/dist/{analytics-facade-jyXv7Cu4.d.cts → analytics-facade-CQXCQSoF.d.cts} +8 -3
  3. package/dist/{chunk-ACCJV6FV.js → chunk-53TN7KYR.js} +58 -5
  4. package/dist/chunk-53TN7KYR.js.map +1 -0
  5. package/dist/{chunk-ZC6Q3GPX.js → chunk-CBN3PWNL.js} +2 -2
  6. package/dist/{chunk-IREKQSHT.js → chunk-RRHJV6YD.js} +22 -15
  7. package/dist/chunk-RRHJV6YD.js.map +1 -0
  8. package/dist/gen/cli.cjs +6 -12
  9. package/dist/gen/cli.cjs.map +1 -1
  10. package/dist/gen/cli.js +6 -12
  11. package/dist/gen/cli.js.map +1 -1
  12. package/dist/index.cjs +49 -4
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +2 -2
  17. package/dist/internal.cjs +73 -17
  18. package/dist/internal.cjs.map +1 -1
  19. package/dist/internal.d.cts +3 -3
  20. package/dist/internal.d.ts +3 -3
  21. package/dist/internal.js +2 -2
  22. package/dist/next/client.cjs +81 -25
  23. package/dist/next/client.cjs.map +1 -1
  24. package/dist/next/client.js +6 -4
  25. package/dist/next/client.js.map +1 -1
  26. package/dist/next/index.cjs +87 -31
  27. package/dist/next/index.cjs.map +1 -1
  28. package/dist/next/index.d.cts +2 -22
  29. package/dist/next/index.d.ts +2 -22
  30. package/dist/next/index.js +7 -6
  31. package/dist/next/index.js.map +1 -1
  32. package/dist/next/proxy.js +2 -2
  33. package/dist/{pb-DdzpEkPY.d.ts → pb-BRlmBIAm.d.ts} +1 -1
  34. package/dist/{pb-CfYQGEn0.d.cts → pb-WAQvwCfM.d.cts} +1 -1
  35. package/dist/react/index.cjs +48 -3
  36. package/dist/react/index.cjs.map +1 -1
  37. package/dist/react/index.d.cts +1 -1
  38. package/dist/react/index.d.ts +1 -1
  39. package/dist/react/index.js +2 -2
  40. package/package.json +13 -13
  41. package/LICENSE +0 -21
  42. package/dist/chunk-ACCJV6FV.js.map +0 -1
  43. package/dist/chunk-IREKQSHT.js.map +0 -1
  44. /package/dist/{chunk-ZC6Q3GPX.js.map → chunk-CBN3PWNL.js.map} +0 -0
@@ -6,9 +6,11 @@ import {
6
6
  import {
7
7
  __configure,
8
8
  getRuntime
9
- } from "../chunk-IREKQSHT.js";
10
- import "../chunk-ACCJV6FV.js";
11
- import "../chunk-CFDU23TB.js";
9
+ } from "../chunk-RRHJV6YD.js";
10
+ import "../chunk-53TN7KYR.js";
11
+ import {
12
+ environmentRefFromPublishableApiKey
13
+ } from "../chunk-CFDU23TB.js";
12
14
  import "../chunk-PZ5AY32C.js";
13
15
 
14
16
  // src/next/client.ts
@@ -70,7 +72,7 @@ function setupPalbeNext() {
70
72
  const config = getRuntime().config;
71
73
  __configure({
72
74
  ...config,
73
- storage: cookieSessionStorage(config.environmentRef)
75
+ storage: cookieSessionStorage(environmentRefFromPublishableApiKey(config.apiKey))
74
76
  });
75
77
  }
76
78
  export {
@@ -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 { environmentRefFromPublishableApiKey } from '../api-key.js';\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(environmentRefFromPublishableApiKey(config.apiKey)),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,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,oCAAoC,OAAO,MAAM,CAAC;AAAA,EAClF,CAAC;AACH;","names":[]}
@@ -42,6 +42,18 @@ __export(next_exports, {
42
42
  });
43
43
  module.exports = __toCommonJS(next_exports);
44
44
 
45
+ // src/api-key.ts
46
+ var API_KEY_RE = /^pb_([a-z0-9]{4,24})_[cs][A-Za-z0-9]{20}$/;
47
+ var PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]{4,24})_c[A-Za-z0-9]{20}$/;
48
+ function environmentRefFromApiKey(apiKey) {
49
+ const m = API_KEY_RE.exec(apiKey);
50
+ return m ? m[1] ?? "" : "";
51
+ }
52
+ function environmentRefFromPublishableApiKey(apiKey) {
53
+ const match = PUBLISHABLE_API_KEY_RE.exec(apiKey);
54
+ return match ? match[1] ?? "" : "";
55
+ }
56
+
45
57
  // src/auth-wire.ts
46
58
  function asWireAuthResult(raw) {
47
59
  if (typeof raw !== "object" || raw === null) return null;
@@ -55,18 +67,6 @@ function asWireAuthResult(raw) {
55
67
  return raw;
56
68
  }
57
69
 
58
- // src/api-key.ts
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}$/;
61
- function environmentRefFromApiKey(apiKey) {
62
- const m = API_KEY_RE.exec(apiKey);
63
- return m ? m[1] ?? "" : "";
64
- }
65
- function environmentRefFromPublishableApiKey(apiKey) {
66
- const match = PUBLISHABLE_API_KEY_RE.exec(apiKey);
67
- return match ? match[1] ?? "" : "";
68
- }
69
-
70
70
  // src/next/cookie-codec.ts
71
71
  var MAX_COOKIE_VALUE = 3500;
72
72
  var SESSION_COOKIE_ATTRS = {
@@ -166,6 +166,45 @@ var PalbaseError = class extends Error {
166
166
  this.details = details;
167
167
  }
168
168
  };
169
+ var POW_CHALLENGE_ID_HEADER = "X-PoW-Challenge-ID";
170
+ var POW_NONCE_HEADER = "X-PoW-Nonce";
171
+ function asPowChallenge(details) {
172
+ if (typeof details !== "object" || details === null) return null;
173
+ const env = details;
174
+ if (env.error !== "pow_required") return null;
175
+ const c = env.challenge;
176
+ if (typeof c !== "object" || c === null) return null;
177
+ const { id, prefix, difficulty } = c;
178
+ if (typeof id !== "string" || typeof prefix !== "string") return null;
179
+ if (typeof difficulty !== "number" || !Number.isInteger(difficulty) || difficulty < 0) return null;
180
+ return { id, prefix, difficulty };
181
+ }
182
+ var encoder = new TextEncoder();
183
+ function leadingZeroBits(hash) {
184
+ let bits = 0;
185
+ for (const byte of hash) {
186
+ if (byte === 0) {
187
+ bits += 8;
188
+ continue;
189
+ }
190
+ return bits + Math.clz32(byte) - 24;
191
+ }
192
+ return bits;
193
+ }
194
+ async function solvePowChallenge(challenge, maxIterations = 1 << 24) {
195
+ for (let nonce = 0; nonce < maxIterations; nonce++) {
196
+ const digest = await crypto.subtle.digest("SHA-256", encoder.encode(challenge.prefix + nonce));
197
+ if (leadingZeroBits(new Uint8Array(digest)) >= challenge.difficulty) {
198
+ return {
199
+ [POW_CHALLENGE_ID_HEADER]: challenge.id,
200
+ [POW_NONCE_HEADER]: String(nonce)
201
+ };
202
+ }
203
+ }
204
+ throw new Error(
205
+ `proof-of-work: no nonce found for difficulty ${challenge.difficulty} within ${maxIterations} attempts`
206
+ );
207
+ }
169
208
  function detectPlatform() {
170
209
  if (typeof Deno !== "undefined") {
171
210
  return "deno";
@@ -302,9 +341,9 @@ var HttpClient = class _HttpClient {
302
341
  }
303
342
  return headers;
304
343
  }
305
- async executeWithRetry(method, path, options, attempt) {
344
+ async executeWithRetry(method, path, options, attempt, earned) {
306
345
  const url = `${this.getBaseUrl()}${path}`;
307
- const headers = this.buildHeaders(options);
346
+ const headers = { ...this.buildHeaders(options), ...earned };
308
347
  for (const interceptor of this.interceptors) {
309
348
  await interceptor({ headers, method, path });
310
349
  }
@@ -323,7 +362,7 @@ var HttpClient = class _HttpClient {
323
362
  if (attempt < MAX_RETRIES - 1) {
324
363
  const backoff = INITIAL_BACKOFF_MS * 2 ** attempt;
325
364
  await this.delay(backoff);
326
- return this.executeWithRetry(method, path, options, attempt + 1);
365
+ return this.executeWithRetry(method, path, options, attempt + 1, earned);
327
366
  }
328
367
  throw new PalbaseError(
329
368
  "network_error",
@@ -337,7 +376,7 @@ var HttpClient = class _HttpClient {
337
376
  const parsed = retryAfter ? Number.parseInt(retryAfter, 10) : Number.NaN;
338
377
  const delayMs = Number.isNaN(parsed) ? INITIAL_BACKOFF_MS * 2 ** attempt : Math.min(parsed * 1e3, MAX_RETRY_DELAY_MS);
339
378
  await this.delay(delayMs);
340
- return this.executeWithRetry(method, path, options, attempt + 1);
379
+ return this.executeWithRetry(method, path, options, attempt + 1, earned);
341
380
  }
342
381
  }
343
382
  let data = null;
@@ -351,6 +390,18 @@ var HttpClient = class _HttpClient {
351
390
  errorBody = body;
352
391
  }
353
392
  }
393
+ if (response.status === 403 && !earned) {
394
+ const challenge = asPowChallenge(errorBody);
395
+ if (challenge) {
396
+ return this.executeWithRetry(
397
+ method,
398
+ path,
399
+ options,
400
+ attempt,
401
+ await solvePowChallenge(challenge)
402
+ );
403
+ }
404
+ }
354
405
  if (!response.ok) {
355
406
  return {
356
407
  data: null,
@@ -591,11 +642,11 @@ async function palbeRequest(rt, method, path, spec = {}) {
591
642
  if (MUTATING.has(method) && !callerHasKey) {
592
643
  headers["Idempotency-Key"] = crypto.randomUUID();
593
644
  }
594
- const attempt = async () => {
645
+ const attempt = async (extra) => {
595
646
  try {
596
647
  return await rt.http.request(method, path, {
597
648
  body: spec.body,
598
- headers,
649
+ headers: extra ? { ...headers, ...extra } : headers,
599
650
  signal: spec.signal
600
651
  });
601
652
  } catch (e) {
@@ -630,6 +681,12 @@ async function palbeRequest(rt, method, path, spec = {}) {
630
681
  }
631
682
  res = await attempt();
632
683
  }
684
+ if (res.error?.status === 403 && res.error.code === "pow_required") {
685
+ const challenge = asPowChallenge(res.error.details);
686
+ if (challenge) {
687
+ res = await attempt(await solvePowChallenge(challenge));
688
+ }
689
+ }
633
690
  } catch (e) {
634
691
  if (!isAbort(e)) record(asPalbaseError(e)?.status ?? 0);
635
692
  throw e;
@@ -1287,7 +1344,7 @@ var AnalyticsState = class {
1287
1344
  idKey;
1288
1345
  optOutKey;
1289
1346
  constructor(rt) {
1290
- const ref = rt.config.environmentRef;
1347
+ const ref = rt.projectRef;
1291
1348
  this.idKey = ref ? `palbe.analytics.id.${ref}` : "palbe.analytics.id";
1292
1349
  this.optOutKey = ref ? `palbe.analytics.optout.${ref}` : "palbe.analytics.optout";
1293
1350
  rt.auth.onAuthEvent((event) => {
@@ -2908,7 +2965,7 @@ var PalbeFlags = class {
2908
2965
  constructor(rt) {
2909
2966
  this.transport = new FlagsClient(rt.http);
2910
2967
  const browser = typeof document !== "undefined";
2911
- const ref = rt.config.environmentRef;
2968
+ const ref = rt.projectRef;
2912
2969
  const options = {
2913
2970
  auth: palbeAuthAdapter(rt.auth),
2914
2971
  ...ref ? { storageKey: `palbe.flags.${ref}` } : {},
@@ -9307,15 +9364,13 @@ function defaultSessionStorage(key) {
9307
9364
  }
9308
9365
 
9309
9366
  // src/version.ts
9310
- var VERSION = "7.2.2";
9367
+ var VERSION = "7.3.1";
9311
9368
 
9312
9369
  // src/runtime.ts
9313
9370
  function buildRuntime(config) {
9314
- const keyEnvironmentRef = environmentRefFromPublishableApiKey(config.apiKey);
9315
- if (keyEnvironmentRef === "") {
9316
- throw new Error("PalbeConfig.apiKey does not contain a valid publishable Environment identity");
9317
- } else if (config.environmentRef !== keyEnvironmentRef) {
9318
- throw new Error("PalbeConfig.environmentRef must match the Environment identity in apiKey");
9371
+ const projectRef = environmentRefFromPublishableApiKey(config.apiKey);
9372
+ if (projectRef === "") {
9373
+ throw new Error("PalbeConfig.apiKey does not contain a valid publishable project identity");
9319
9374
  }
9320
9375
  let runtimeURL;
9321
9376
  try {
@@ -9323,8 +9378,8 @@ function buildRuntime(config) {
9323
9378
  } catch {
9324
9379
  throw new Error("PalbeConfig.url must be a valid URL");
9325
9380
  }
9326
- if (runtimeURL.hostname.endsWith(".palbase.studio") && runtimeURL.hostname.split(".")[0] !== config.environmentRef) {
9327
- throw new Error("PalbeConfig.url must match environmentRef for palbase.studio");
9381
+ if (runtimeURL.hostname.endsWith(".palbase.studio") && runtimeURL.hostname.split(".")[0] !== projectRef) {
9382
+ throw new Error("PalbeConfig.url must match the project identity in apiKey for palbase.studio");
9328
9383
  }
9329
9384
  const http = new HttpClient(config.apiKey, {
9330
9385
  url: config.url,
@@ -9334,7 +9389,7 @@ function buildRuntime(config) {
9334
9389
  const tokenManager = new TokenManager();
9335
9390
  http.tokenManager = tokenManager;
9336
9391
  const authClient = new AuthClient(http, tokenManager);
9337
- const ref = config.environmentRef;
9392
+ const ref = projectRef;
9338
9393
  const storage = config.storage ?? defaultSessionStorage(ref ? `palbe.session.${ref}` : void 0);
9339
9394
  const persisted = storage.load();
9340
9395
  if (persisted) {
@@ -9379,6 +9434,7 @@ function buildRuntime(config) {
9379
9434
  let perf;
9380
9435
  const rt = {
9381
9436
  config,
9437
+ projectRef,
9382
9438
  http,
9383
9439
  tokenManager,
9384
9440
  authClient,
@@ -9790,7 +9846,7 @@ function handleAuthCallback(opts) {
9790
9846
  const wire = asWireAuthResult(raw);
9791
9847
  if (!wire) return redirect(fallback, { auth_error: "oauth_exchange_failed" });
9792
9848
  const response = redirect(safeNextPath(params.get("next"), request.nextUrl.origin) ?? fallback);
9793
- const write = encodeSessionCookiesDecoded(config.environmentRef, {
9849
+ const write = encodeSessionCookiesDecoded(environmentRefFromPublishableApiKey(config.apiKey), {
9794
9850
  accessToken: wire.access_token,
9795
9851
  refreshToken: wire.refresh_token,
9796
9852
  expiresAt: Date.now() + wire.expires_in * 1e3
@@ -9881,7 +9937,7 @@ async function pbServer(opts) {
9881
9937
  "import the generated palbe.gen.ts once in app/layout.tsx \u2014 the root-layout import configures Server Components and Route Handlers too."
9882
9938
  );
9883
9939
  const store = opts?.cookies ?? await nextCookieStore();
9884
- const ref = config.environmentRef;
9940
+ const ref = environmentRefFromPublishableApiKey(config.apiKey);
9885
9941
  const rt = buildRuntime({ ...config, storage: serverCookieAdapter(store, ref) });
9886
9942
  return createBoundClient(rt);
9887
9943
  }