@palbase/web 2.0.0 → 3.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.
Files changed (47) hide show
  1. package/README.md +11 -9
  2. package/dist/{analytics-facade-ovP0pF2P.d.cts → analytics-facade-BOxQ8IP2.d.cts} +11 -10
  3. package/dist/{analytics-facade-DMtGDGfd.d.ts → analytics-facade-yfMJ69vw.d.ts} +11 -10
  4. package/dist/{chunk-V6VTX65P.js → chunk-3HVTEZ7N.js} +80 -58
  5. package/dist/chunk-3HVTEZ7N.js.map +1 -0
  6. package/dist/{chunk-VJXFABBW.js → chunk-6VGKMNXO.js} +11 -11
  7. package/dist/chunk-6VGKMNXO.js.map +1 -0
  8. package/dist/chunk-S7NAY3MW.js +24 -0
  9. package/dist/chunk-S7NAY3MW.js.map +1 -0
  10. package/dist/gen/cli.cjs +33 -8
  11. package/dist/gen/cli.cjs.map +1 -1
  12. package/dist/gen/cli.js +29 -9
  13. package/dist/gen/cli.js.map +1 -1
  14. package/dist/index.cjs +53 -32
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +4 -4
  17. package/dist/index.d.ts +4 -4
  18. package/dist/index.js +2 -2
  19. package/dist/internal.cjs +83 -54
  20. package/dist/internal.cjs.map +1 -1
  21. package/dist/internal.d.cts +4 -4
  22. package/dist/internal.d.ts +4 -4
  23. package/dist/internal.js +2 -2
  24. package/dist/next/client.cjs +100 -68
  25. package/dist/next/client.cjs.map +1 -1
  26. package/dist/next/client.d.cts +1 -1
  27. package/dist/next/client.d.ts +1 -1
  28. package/dist/next/client.js +12 -10
  29. package/dist/next/client.js.map +1 -1
  30. package/dist/next/index.cjs +103 -69
  31. package/dist/next/index.cjs.map +1 -1
  32. package/dist/next/index.d.cts +11 -11
  33. package/dist/next/index.d.ts +11 -11
  34. package/dist/next/index.js +14 -13
  35. package/dist/next/index.js.map +1 -1
  36. package/dist/{pb-DDM_mCQB.d.cts → pb-B3h3NIPE.d.cts} +1 -1
  37. package/dist/{pb-CvpcQero.d.ts → pb-QRISxPd1.d.ts} +1 -1
  38. package/dist/react/index.cjs +1 -1
  39. package/dist/react/index.cjs.map +1 -1
  40. package/dist/react/index.d.cts +2 -2
  41. package/dist/react/index.d.ts +2 -2
  42. package/dist/react/index.js +2 -2
  43. package/package.json +4 -4
  44. package/dist/chunk-PZ5AY32C.js +0 -10
  45. package/dist/chunk-PZ5AY32C.js.map +0 -1
  46. package/dist/chunk-V6VTX65P.js.map +0 -1
  47. package/dist/chunk-VJXFABBW.js.map +0 -1
@@ -1,10 +1,10 @@
1
- import { P as PalbeConfig, d as PalbeRuntime } from './analytics-facade-ovP0pF2P.cjs';
2
- export { e as buildRuntime } from './analytics-facade-ovP0pF2P.cjs';
1
+ import { P as PalbeConfig, d as PalbeRuntime } from './analytics-facade-BOxQ8IP2.cjs';
2
+ export { e as buildRuntime } from './analytics-facade-BOxQ8IP2.cjs';
3
3
  import { B as BackendError } from './errors-fDoNdTrJ.cjs';
4
- export { P as PB, c as createBoundClient } from './pb-DDM_mCQB.cjs';
4
+ export { P as PB, c as createBoundClient } from './pb-B3h3NIPE.cjs';
5
5
  import 'livekit-client';
6
6
  import './storage-BPaeSG8K.cjs';
7
- import './pooled-flags-Bwq4usn0.js';
7
+ import './pooled-flags-4GtDtsiu.js';
8
8
 
9
9
  interface EndpointDescriptor {
10
10
  method: string;
@@ -1,10 +1,10 @@
1
- import { P as PalbeConfig, d as PalbeRuntime } from './analytics-facade-DMtGDGfd.js';
2
- export { e as buildRuntime } from './analytics-facade-DMtGDGfd.js';
1
+ import { P as PalbeConfig, d as PalbeRuntime } from './analytics-facade-yfMJ69vw.js';
2
+ export { e as buildRuntime } from './analytics-facade-yfMJ69vw.js';
3
3
  import { B as BackendError } from './errors-fDoNdTrJ.js';
4
- export { P as PB, c as createBoundClient } from './pb-CvpcQero.js';
4
+ export { P as PB, c as createBoundClient } from './pb-QRISxPd1.js';
5
5
  import 'livekit-client';
6
6
  import './storage-BPaeSG8K.js';
7
- import './pooled-flags-Bwq4usn0.js';
7
+ import './pooled-flags-4GtDtsiu.js';
8
8
 
9
9
  interface EndpointDescriptor {
10
10
  method: string;
package/dist/internal.js CHANGED
@@ -6,8 +6,8 @@ import {
6
6
  createBoundClient,
7
7
  getRuntime,
8
8
  onConfigured
9
- } from "./chunk-V6VTX65P.js";
10
- import "./chunk-PZ5AY32C.js";
9
+ } from "./chunk-3HVTEZ7N.js";
10
+ import "./chunk-S7NAY3MW.js";
11
11
  export {
12
12
  __configure,
13
13
  __registerNamespaces,
@@ -40,18 +40,9 @@ var PalbaseError = class extends Error {
40
40
  }
41
41
  };
42
42
  var PALBASE_DEFAULT_HOST = "api.palbase.studio";
43
- var REF_LEN = 8;
44
- var BASE62_RE = /^[0-9A-Za-z]+$/;
45
- function parseProjectRef(apiKey) {
46
- if (apiKey.length < 13) return null;
47
- if (!apiKey.startsWith("pb_")) return null;
48
- if (apiKey[11] !== "_") return null;
49
- const ref = apiKey.slice(3, 11);
50
- if (ref.length !== REF_LEN) return null;
51
- if (!BASE62_RE.test(ref)) return null;
52
- const scope = apiKey[12];
53
- if (scope !== "c") return null;
54
- return ref;
43
+ var API_KEY_RE = /^pb_([a-z0-9]+)_c[A-Za-z0-9]{20}$/;
44
+ function parseEnvironmentRef(apiKey) {
45
+ return API_KEY_RE.exec(apiKey)?.[1] ?? null;
55
46
  }
56
47
  var MAX_RETRIES = 3;
57
48
  var INITIAL_BACKOFF_MS = 200;
@@ -81,8 +72,7 @@ var HttpClient = class _HttpClient {
81
72
  * with the parent at runtime — later changes on the parent propagate to
82
73
  * the scope and vice versa.
83
74
  *
84
- * Typical use: tagging admin calls with `x-palbase-project: <ref>` so the
85
- * gateway can route them to the correct project's data plane.
75
+ * Typical use: adding an Environment-routing header for an admin call.
86
76
  */
87
77
  withHeaders(extra) {
88
78
  const mergedHeaders = { ...this.options?.headers ?? {}, ...extra };
@@ -123,10 +113,10 @@ var HttpClient = class _HttpClient {
123
113
  if (this.options?.url) {
124
114
  return this.options.url;
125
115
  }
126
- if (this.apiKey && parseProjectRef(this.apiKey) === null) {
116
+ if (this.apiKey && parseEnvironmentRef(this.apiKey) === null) {
127
117
  throw new PalbaseError(
128
118
  "invalid_api_key",
129
- 'Invalid API key format. Expected pb_{ref}_{scope}{random}. For dev/staging pass `url: "https://api.dev.palbase.studio"` via options.',
119
+ 'Invalid API key format. Expected pb_{environment_ref}_c{20_base62_chars}. For dev/staging pass `url: "https://api.dev.palbase.studio"` via options.',
130
120
  0
131
121
  );
132
122
  }
@@ -139,10 +129,6 @@ var HttpClient = class _HttpClient {
139
129
  const effectiveKey = this.apiKey;
140
130
  if (effectiveKey) {
141
131
  headers["apikey"] = effectiveKey;
142
- const ref = parseProjectRef(effectiveKey);
143
- if (ref) {
144
- headers["X-Project-Ref"] = ref;
145
- }
146
132
  }
147
133
  const token = this.tokenManager?.getAccessToken();
148
134
  if (token) {
@@ -953,13 +939,6 @@ var AuthClient = class {
953
939
  }
954
940
  };
955
941
 
956
- // src/api-key.ts
957
- var API_KEY_RE = /^pb_([^_]+)_[cs][A-Za-z0-9]{20}$/;
958
- function endpointRefFromApiKey(apiKey) {
959
- const m = API_KEY_RE.exec(apiKey);
960
- return m ? m[1] ?? "" : "";
961
- }
962
-
963
942
  // src/analytics-facade.ts
964
943
  var EVENT_NAME_RE = /^[a-zA-Z$][a-zA-Z0-9_.:$-]{0,63}$/;
965
944
  var FLUSH_AT = 20;
@@ -1017,7 +996,7 @@ var AnalyticsState = class {
1017
996
  idKey;
1018
997
  optOutKey;
1019
998
  constructor(rt) {
1020
- const ref = endpointRefFromApiKey(rt.config.apiKey);
999
+ const ref = rt.config.environmentRef;
1021
1000
  this.idKey = ref ? `palbe.analytics.id.${ref}` : "palbe.analytics.id";
1022
1001
  this.optOutKey = ref ? `palbe.analytics.optout.${ref}` : "palbe.analytics.optout";
1023
1002
  rt.auth.onAuthEvent((event) => {
@@ -1231,6 +1210,13 @@ var PalbeAnalytics = class {
1231
1210
  }
1232
1211
  };
1233
1212
 
1213
+ // src/api-key.ts
1214
+ var PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]+)_c[A-Za-z0-9]{20}$/;
1215
+ function environmentRefFromPublishableApiKey(apiKey) {
1216
+ const match = PUBLISHABLE_API_KEY_RE.exec(apiKey);
1217
+ return match ? match[1] ?? "" : "";
1218
+ }
1219
+
1234
1220
  // src/auth-wire.ts
1235
1221
  function asWireAuthResult(raw) {
1236
1222
  if (typeof raw !== "object" || raw === null) return null;
@@ -1792,6 +1778,19 @@ var PalbeCalls = class {
1792
1778
 
1793
1779
  // ../modules/flags/dist/index.js
1794
1780
  var FLAG_NAME_RE = /^[a-zA-Z0-9_-]+$/;
1781
+ function flagEnabled(value) {
1782
+ if (typeof value === "boolean") return value;
1783
+ return value != null && value !== 0 && value !== "";
1784
+ }
1785
+ function flagVariant(value) {
1786
+ return typeof value === "string" ? { name: value } : null;
1787
+ }
1788
+ function mapResponse(res, fn) {
1789
+ if (res.error || res.data == null) {
1790
+ return { ...res, data: null };
1791
+ }
1792
+ return { ...res, data: fn(res.data) };
1793
+ }
1795
1794
  var FlagsClient = class {
1796
1795
  httpClient;
1797
1796
  getCurrentUserId;
@@ -1811,10 +1810,11 @@ var FlagsClient = class {
1811
1810
  `Invalid flag name: "${flagName}". Flag names must match ${FLAG_NAME_RE.source}`
1812
1811
  );
1813
1812
  }
1814
- const params = this.buildContextParams(context);
1815
- const query = params.toString();
1816
- const path = `/v1/flags/${flagName}/enabled${query ? `?${query}` : ""}`;
1817
- return this.httpClient.request("GET", path);
1813
+ const res = await this.httpClient.request(
1814
+ "GET",
1815
+ this.mergedPath(context)
1816
+ );
1817
+ return mapResponse(res, (snap) => flagEnabled(snap.values?.[flagName]));
1818
1818
  }
1819
1819
  async getVariant(flagName, context) {
1820
1820
  if (!FLAG_NAME_RE.test(flagName)) {
@@ -1822,16 +1822,29 @@ var FlagsClient = class {
1822
1822
  `Invalid flag name: "${flagName}". Flag names must match ${FLAG_NAME_RE.source}`
1823
1823
  );
1824
1824
  }
1825
- const params = this.buildContextParams(context);
1826
- const query = params.toString();
1827
- const path = `/v1/flags/${flagName}/variant${query ? `?${query}` : ""}`;
1828
- return this.httpClient.request("GET", path);
1825
+ const res = await this.httpClient.request(
1826
+ "GET",
1827
+ this.mergedPath(context)
1828
+ );
1829
+ if (res.error || res.data == null) {
1830
+ return { ...res, data: null };
1831
+ }
1832
+ return { ...res, data: flagVariant(res.data.values?.[flagName]) };
1829
1833
  }
1830
1834
  async getAll(context) {
1831
- const params = this.buildContextParams(context);
1832
- const query = params.toString();
1833
- const path = `/v1/flags${query ? `?${query}` : ""}`;
1834
- return this.httpClient.request("GET", path);
1835
+ const res = await this.httpClient.request(
1836
+ "GET",
1837
+ this.mergedPath(context)
1838
+ );
1839
+ return mapResponse(res, (snap) => {
1840
+ const values = snap.values ?? {};
1841
+ return Object.keys(values).map((name) => {
1842
+ const value = values[name];
1843
+ const flag = { name, enabled: flagEnabled(value) };
1844
+ if (typeof value === "string") flag.variant = { name: value };
1845
+ return flag;
1846
+ });
1847
+ });
1835
1848
  }
1836
1849
  /**
1837
1850
  * Cold-start read: the full merged flag set for the current auth identity.
@@ -1930,15 +1943,16 @@ var FlagsClient = class {
1930
1943
  }
1931
1944
  };
1932
1945
  }
1933
- buildContextParams(context) {
1934
- const params = new URLSearchParams();
1935
- if (context?.userId) {
1936
- params.set("userId", context.userId);
1937
- }
1938
- if (context?.properties) {
1939
- params.set("properties", JSON.stringify(context.properties));
1940
- }
1941
- return params;
1946
+ /**
1947
+ * Resolve the merged-read path for a context. With `context.userId` present the
1948
+ * read targets that specific user's merged view (`/v1/user-flags/users/{id}`, a
1949
+ * privileged cross-user read); otherwise the current auth user's view
1950
+ * (`/v1/user-flags`). `context.properties` has no server-side targeting support
1951
+ * in the user-flags module and is ignored.
1952
+ */
1953
+ mergedPath(context) {
1954
+ const uid = context?.userId;
1955
+ return uid ? `/v1/user-flags/users/${encodeURIComponent(uid)}` : "/v1/user-flags";
1942
1956
  }
1943
1957
  };
1944
1958
  var DEFAULT_POLL_MS = 3e4;
@@ -2343,7 +2357,7 @@ var PalbeFlags = class {
2343
2357
  constructor(rt) {
2344
2358
  this.transport = new FlagsClient(rt.http);
2345
2359
  const browser = typeof document !== "undefined";
2346
- const ref = endpointRefFromApiKey(rt.config.apiKey);
2360
+ const ref = rt.config.environmentRef;
2347
2361
  const options = {
2348
2362
  auth: palbeAuthAdapter(rt.auth),
2349
2363
  ...ref ? { storageKey: `palbe.flags.${ref}` } : {},
@@ -2400,7 +2414,7 @@ var PalbeFlags = class {
2400
2414
  *
2401
2415
  * DEVIATION from iOS sync-cache parity: the platform does not propagate
2402
2416
  * variant metadata into the user-flags snapshot/delta cache, so this is an
2403
- * ASYNC transport read (`GET /v1/flags/{key}/variant`), not a cache lookup.
2417
+ * ASYNC transport read (derived from `GET /v1/user-flags`), not a cache lookup.
2404
2418
  */
2405
2419
  async getVariant(key) {
2406
2420
  let res;
@@ -8083,7 +8097,7 @@ var AnonTokenProvider = class {
8083
8097
  }
8084
8098
  /**
8085
8099
  * Raw fetch, deliberately NOT through HttpClient/palbeRequest: the mint
8086
- * must carry the project apikey and NEVER a Bearer (iOS keeps
8100
+ * must carry the Environment API key and NEVER a Bearer (iOS keeps
8087
8101
  * `/auth/anonymous` on the unauthenticated-path list), and HttpClient's
8088
8102
  * pre-flight would try to refresh a stale session before an anon mint —
8089
8103
  * exactly the entanglement this provider exists to avoid.
@@ -8740,10 +8754,25 @@ function defaultSessionStorage(key) {
8740
8754
  }
8741
8755
 
8742
8756
  // src/version.ts
8743
- var VERSION = "2.0.0";
8757
+ var VERSION = "3.0.0";
8744
8758
 
8745
8759
  // src/runtime.ts
8746
8760
  function buildRuntime(config) {
8761
+ const keyEnvironmentRef = environmentRefFromPublishableApiKey(config.apiKey);
8762
+ if (keyEnvironmentRef === "") {
8763
+ throw new Error("PalbeConfig.apiKey does not contain a valid publishable Environment identity");
8764
+ } else if (config.environmentRef !== keyEnvironmentRef) {
8765
+ throw new Error("PalbeConfig.environmentRef must match the Environment identity in apiKey");
8766
+ }
8767
+ let runtimeURL;
8768
+ try {
8769
+ runtimeURL = new URL(config.url);
8770
+ } catch {
8771
+ throw new Error("PalbeConfig.url must be a valid URL");
8772
+ }
8773
+ if (runtimeURL.hostname.endsWith(".palbase.studio") && runtimeURL.hostname.split(".")[0] !== config.environmentRef) {
8774
+ throw new Error("PalbeConfig.url must match environmentRef for palbase.studio");
8775
+ }
8747
8776
  const http = new HttpClient(config.apiKey, {
8748
8777
  url: config.url,
8749
8778
  headers: { "X-Client-Info": `palbe-web/${VERSION}`, ...config.headers }
@@ -8752,7 +8781,7 @@ function buildRuntime(config) {
8752
8781
  const tokenManager = new TokenManager();
8753
8782
  http.tokenManager = tokenManager;
8754
8783
  const authClient = new AuthClient(http, tokenManager);
8755
- const ref = endpointRefFromApiKey(config.apiKey);
8784
+ const ref = config.environmentRef;
8756
8785
  const storage = config.storage ?? defaultSessionStorage(ref ? `palbe.session.${ref}` : void 0);
8757
8786
  const persisted = storage.load();
8758
8787
  if (persisted) {
@@ -8910,11 +8939,11 @@ function getRuntime() {
8910
8939
 
8911
8940
  // src/next/cookie-codec.ts
8912
8941
  var MAX_COOKIE_VALUE = 3500;
8913
- function sessionCookieName(endpointRef) {
8914
- return `palbe-session-${endpointRef}`;
8942
+ function sessionCookieName(environmentRef) {
8943
+ return `palbe-session-${environmentRef}`;
8915
8944
  }
8916
- function encodeSessionCookies(endpointRef, session) {
8917
- const name = sessionCookieName(endpointRef);
8945
+ function encodeSessionCookies(environmentRef, session) {
8946
+ const name = sessionCookieName(environmentRef);
8918
8947
  const value = encodeURIComponent(
8919
8948
  JSON.stringify({ a: session.accessToken, r: session.refreshToken, e: session.expiresAt })
8920
8949
  );
@@ -8933,8 +8962,8 @@ function encodeSessionCookies(endpointRef, session) {
8933
8962
  }
8934
8963
  return { set: chunks, clear: [`${name}.${chunks.length}`] };
8935
8964
  }
8936
- function decodeSessionCookies(get, endpointRef) {
8937
- const base = sessionCookieName(endpointRef);
8965
+ function decodeSessionCookies(get, environmentRef) {
8966
+ const base = sessionCookieName(environmentRef);
8938
8967
  let encoded = get(base);
8939
8968
  if (encoded === void 0) {
8940
8969
  let joined = "";
@@ -8968,8 +8997,8 @@ function parseStoredSession(json) {
8968
8997
  return null;
8969
8998
  }
8970
8999
  }
8971
- function clearedSessionCookieNames(endpointRef, present) {
8972
- const base = sessionCookieName(endpointRef);
9000
+ function clearedSessionCookieNames(environmentRef, present) {
9001
+ const base = sessionCookieName(environmentRef);
8973
9002
  const names = [];
8974
9003
  if (present(base)) names.push(base);
8975
9004
  for (let i = 0; ; i++) {
@@ -8996,12 +9025,12 @@ function cookieJar() {
8996
9025
  function deleteCookie(name) {
8997
9026
  document.cookie = `${name}=; ${WRITE_ATTRS}; Max-Age=0`;
8998
9027
  }
8999
- function cookieSessionStorage(endpointRef) {
9028
+ function cookieSessionStorage(environmentRef) {
9000
9029
  return {
9001
9030
  load() {
9002
9031
  if (typeof document === "undefined") return null;
9003
9032
  const jar = cookieJar();
9004
- const stored = decodeSessionCookies((name) => jar.get(name), endpointRef);
9033
+ const stored = decodeSessionCookies((name) => jar.get(name), environmentRef);
9005
9034
  if (!stored) return null;
9006
9035
  return stored.accessToken && stored.expiresAt > 0 ? {
9007
9036
  refreshToken: stored.refreshToken,
@@ -9012,10 +9041,10 @@ function cookieSessionStorage(endpointRef) {
9012
9041
  save(session) {
9013
9042
  if (typeof document === "undefined") return;
9014
9043
  const jar = cookieJar();
9015
- for (const name of clearedSessionCookieNames(endpointRef, (n) => jar.has(n))) {
9044
+ for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {
9016
9045
  deleteCookie(name);
9017
9046
  }
9018
- const { set, clear } = encodeSessionCookies(endpointRef, {
9047
+ const { set, clear } = encodeSessionCookies(environmentRef, {
9019
9048
  accessToken: session.accessToken ?? "",
9020
9049
  refreshToken: session.refreshToken,
9021
9050
  expiresAt: session.expiresAt ?? 0
@@ -9028,7 +9057,7 @@ function cookieSessionStorage(endpointRef) {
9028
9057
  clear() {
9029
9058
  if (typeof document === "undefined") return;
9030
9059
  const jar = cookieJar();
9031
- for (const name of clearedSessionCookieNames(endpointRef, (n) => jar.has(n))) {
9060
+ for (const name of clearedSessionCookieNames(environmentRef, (n) => jar.has(n))) {
9032
9061
  deleteCookie(name);
9033
9062
  }
9034
9063
  }
@@ -9037,7 +9066,10 @@ function cookieSessionStorage(endpointRef) {
9037
9066
  function setupPalbeNext() {
9038
9067
  if (typeof document === "undefined") return;
9039
9068
  const config = getRuntime().config;
9040
- __configure({ ...config, storage: cookieSessionStorage(endpointRefFromApiKey(config.apiKey)) });
9069
+ __configure({
9070
+ ...config,
9071
+ storage: cookieSessionStorage(config.environmentRef)
9072
+ });
9041
9073
  }
9042
9074
  // Annotate the CommonJS export names for ESM import in node:
9043
9075
  0 && (module.exports = {