@palbase/web 1.10.0 → 2.0.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.
package/README.md CHANGED
@@ -34,8 +34,9 @@ wires `import './palbe.gen';` into your app entry, and adds a `predev`/`prebuild
34
34
  hook that keeps the types in sync. **Commit `palbe.gen.ts`** — it's the typed
35
35
  client your app imports, not a build artifact.
36
36
 
37
- The generated file calls `__configure(...)` with your project's URL and anon API
38
- key at module load, so importing it once at startup is all the setup there is.
37
+ The generated file calls `__configure(...)` with your project's URL, app ID, and
38
+ anon API key at module load, so importing it once at startup is all the setup
39
+ there is.
39
40
  If you forget to import it, every `pb` call throws a guided `BackendError`
40
41
  (`kind: 'notConfigured'`).
41
42
 
@@ -1,6 +1,6 @@
1
1
  import { Room, ExternalE2EEKeyProvider } from 'livekit-client';
2
2
  import { S as SessionStorageAdapter } from './storage-BPaeSG8K.cjs';
3
- import { F } from './pooled-flags-Bwq4usn0.js';
3
+ import { F } from './pooled-flags-4GtDtsiu.js';
4
4
 
5
5
  declare class PalbaseError$1 extends Error {
6
6
  readonly code: string;
@@ -620,18 +620,19 @@ interface PalbeOAuthConfig {
620
620
  interface PalbeConfig {
621
621
  url: string;
622
622
  apiKey: string;
623
- /** Informational url+apiKey are already branch-specific (endpointRef embeds the branch slug); gen bakes all three consistently. */
624
- branch?: string;
623
+ /** Registered app identity persisted by `palbase web link`. */
624
+ appId: string;
625
625
  /**
626
- * F2 app-registration: the registered app `identifier` from
627
- * `palbase-config.json` — for web this is the declared ORIGIN (e.g.
628
- * `https://app.example.com`). When non-empty, `configure` REFUSES to build the
629
- * runtime if `window.location.origin` does not match (SDK-REFUSES), and the
630
- * value rides on `X-Palbase-Bundle` on EVERY request so the Kong gate can match
631
- * it (the IDENTICAL header + value the iOS SDK sends). Empty/absent legacy /
632
- * tooling: no guard, no header.
626
+ * The ENVIRONMENT this config targets informational only.
627
+ *
628
+ * `url` and `apiKey` ALREADY identify the environment (the ref is the host
629
+ * label and is embedded in the key), so nothing at runtime reads this: it is
630
+ * here so a developer looking at the generated file can see WHICH runtime it
631
+ * points at. It replaces the old `branch` field the Palbase branch is gone
632
+ * as a resource, and a second, drift-prone name for the same runtime is
633
+ * exactly how a build ends up talking to production from a staging checkout.
633
634
  */
634
- identifier?: string;
635
+ environmentRef?: string;
635
636
  oauth?: PalbeOAuthConfig;
636
637
  /** Refresh-token persistence. Default: endpoint-scoped localStorage in browsers, memory elsewhere. */
637
638
  storage?: SessionStorageAdapter;
@@ -687,7 +688,7 @@ declare class PalbeFlags {
687
688
  *
688
689
  * DEVIATION from iOS sync-cache parity: the platform does not propagate
689
690
  * variant metadata into the user-flags snapshot/delta cache, so this is an
690
- * ASYNC transport read (`GET /v1/flags/{key}/variant`), not a cache lookup.
691
+ * ASYNC transport read (derived from `GET /v1/user-flags`), not a cache lookup.
691
692
  */
692
693
  getVariant(key: string): Promise<string | null>;
693
694
  /** Subscribe to any change in the cached flag set. */
@@ -1690,14 +1691,6 @@ declare class PalbeRealtime {
1690
1691
 
1691
1692
  interface PalbeRuntime {
1692
1693
  config: PalbeConfig;
1693
- /**
1694
- * F2 app-registration: the registered app `identifier` (web ORIGIN) stashed at
1695
- * configure. Rides on `X-Palbase-Bundle` on EVERY request (request.ts) so the
1696
- * Kong gate can match it. Empty string when the binding is unconfigured/tooling
1697
- * (no header is sent). Origin-match is enforced in `buildRuntime` BEFORE this
1698
- * runtime is returned — a mismatch throws there and no runtime is built.
1699
- */
1700
- appIdentifier: string;
1701
1694
  http: HttpClient$1;
1702
1695
  tokenManager: TokenManager$1;
1703
1696
  authClient: AuthClient;
@@ -1,6 +1,6 @@
1
1
  import { Room, ExternalE2EEKeyProvider } from 'livekit-client';
2
2
  import { S as SessionStorageAdapter } from './storage-BPaeSG8K.js';
3
- import { F } from './pooled-flags-Bwq4usn0.js';
3
+ import { F } from './pooled-flags-4GtDtsiu.js';
4
4
 
5
5
  declare class PalbaseError$1 extends Error {
6
6
  readonly code: string;
@@ -620,18 +620,19 @@ interface PalbeOAuthConfig {
620
620
  interface PalbeConfig {
621
621
  url: string;
622
622
  apiKey: string;
623
- /** Informational url+apiKey are already branch-specific (endpointRef embeds the branch slug); gen bakes all three consistently. */
624
- branch?: string;
623
+ /** Registered app identity persisted by `palbase web link`. */
624
+ appId: string;
625
625
  /**
626
- * F2 app-registration: the registered app `identifier` from
627
- * `palbase-config.json` — for web this is the declared ORIGIN (e.g.
628
- * `https://app.example.com`). When non-empty, `configure` REFUSES to build the
629
- * runtime if `window.location.origin` does not match (SDK-REFUSES), and the
630
- * value rides on `X-Palbase-Bundle` on EVERY request so the Kong gate can match
631
- * it (the IDENTICAL header + value the iOS SDK sends). Empty/absent legacy /
632
- * tooling: no guard, no header.
626
+ * The ENVIRONMENT this config targets informational only.
627
+ *
628
+ * `url` and `apiKey` ALREADY identify the environment (the ref is the host
629
+ * label and is embedded in the key), so nothing at runtime reads this: it is
630
+ * here so a developer looking at the generated file can see WHICH runtime it
631
+ * points at. It replaces the old `branch` field the Palbase branch is gone
632
+ * as a resource, and a second, drift-prone name for the same runtime is
633
+ * exactly how a build ends up talking to production from a staging checkout.
633
634
  */
634
- identifier?: string;
635
+ environmentRef?: string;
635
636
  oauth?: PalbeOAuthConfig;
636
637
  /** Refresh-token persistence. Default: endpoint-scoped localStorage in browsers, memory elsewhere. */
637
638
  storage?: SessionStorageAdapter;
@@ -687,7 +688,7 @@ declare class PalbeFlags {
687
688
  *
688
689
  * DEVIATION from iOS sync-cache parity: the platform does not propagate
689
690
  * variant metadata into the user-flags snapshot/delta cache, so this is an
690
- * ASYNC transport read (`GET /v1/flags/{key}/variant`), not a cache lookup.
691
+ * ASYNC transport read (derived from `GET /v1/user-flags`), not a cache lookup.
691
692
  */
692
693
  getVariant(key: string): Promise<string | null>;
693
694
  /** Subscribe to any change in the cached flag set. */
@@ -1690,14 +1691,6 @@ declare class PalbeRealtime {
1690
1691
 
1691
1692
  interface PalbeRuntime {
1692
1693
  config: PalbeConfig;
1693
- /**
1694
- * F2 app-registration: the registered app `identifier` (web ORIGIN) stashed at
1695
- * configure. Rides on `X-Palbase-Bundle` on EVERY request (request.ts) so the
1696
- * Kong gate can match it. Empty string when the binding is unconfigured/tooling
1697
- * (no header is sent). Origin-match is enforced in `buildRuntime` BEFORE this
1698
- * runtime is returned — a mismatch throws there and no runtime is built.
1699
- */
1700
- appIdentifier: string;
1701
1694
  http: HttpClient$1;
1702
1695
  tokenManager: TokenManager$1;
1703
1696
  authClient: AuthClient;
@@ -420,12 +420,6 @@ async function palbeRequest(rt, method, path, spec = {}) {
420
420
  if (MUTATING.has(method) && !callerHasKey) {
421
421
  headers["Idempotency-Key"] = crypto.randomUUID();
422
422
  }
423
- if (rt.appIdentifier !== "") {
424
- const callerHasBundle = Object.keys(headers).some(
425
- (k) => k.toLowerCase() === "x-palbase-bundle"
426
- );
427
- if (!callerHasBundle) headers["X-Palbase-Bundle"] = rt.appIdentifier;
428
- }
429
423
  const attempt = async () => {
430
424
  try {
431
425
  return await rt.http.request(method, path, {
@@ -1409,23 +1403,6 @@ var PalbeAnalytics = class {
1409
1403
  }
1410
1404
  };
1411
1405
 
1412
- // src/app-config.ts
1413
- function loadAppConfig(raw) {
1414
- if (typeof raw !== "object" || raw === null) {
1415
- throw new Error("app_config_invalid: expected a JSON object");
1416
- }
1417
- const r = raw;
1418
- const str = (k) => typeof r[k] === "string" ? r[k] : "";
1419
- return { appId: str("app_id"), identifier: str("identifier"), envPreset: str("env_preset") };
1420
- }
1421
- function assertOriginMatches(cfg, runtimeOrigin) {
1422
- if (cfg.identifier === "") return;
1423
- if (runtimeOrigin === "") return;
1424
- if (runtimeOrigin !== cfg.identifier) {
1425
- throw new Error(`app_config_mismatch: expected origin ${cfg.identifier}, got ${runtimeOrigin}`);
1426
- }
1427
- }
1428
-
1429
1406
  // src/auth-wire.ts
1430
1407
  function asWireAuthResult(raw) {
1431
1408
  if (typeof raw !== "object" || raw === null) return null;
@@ -1992,6 +1969,19 @@ var PalbeCalls = class {
1992
1969
 
1993
1970
  // ../modules/flags/dist/index.js
1994
1971
  var FLAG_NAME_RE = /^[a-zA-Z0-9_-]+$/;
1972
+ function flagEnabled(value) {
1973
+ if (typeof value === "boolean") return value;
1974
+ return value != null && value !== 0 && value !== "";
1975
+ }
1976
+ function flagVariant(value) {
1977
+ return typeof value === "string" ? { name: value } : null;
1978
+ }
1979
+ function mapResponse(res, fn) {
1980
+ if (res.error || res.data == null) {
1981
+ return { ...res, data: null };
1982
+ }
1983
+ return { ...res, data: fn(res.data) };
1984
+ }
1995
1985
  var FlagsClient = class {
1996
1986
  httpClient;
1997
1987
  getCurrentUserId;
@@ -2011,10 +2001,11 @@ var FlagsClient = class {
2011
2001
  `Invalid flag name: "${flagName}". Flag names must match ${FLAG_NAME_RE.source}`
2012
2002
  );
2013
2003
  }
2014
- const params = this.buildContextParams(context);
2015
- const query = params.toString();
2016
- const path = `/v1/flags/${flagName}/enabled${query ? `?${query}` : ""}`;
2017
- return this.httpClient.request("GET", path);
2004
+ const res = await this.httpClient.request(
2005
+ "GET",
2006
+ this.mergedPath(context)
2007
+ );
2008
+ return mapResponse(res, (snap) => flagEnabled(snap.values?.[flagName]));
2018
2009
  }
2019
2010
  async getVariant(flagName, context) {
2020
2011
  if (!FLAG_NAME_RE.test(flagName)) {
@@ -2022,16 +2013,29 @@ var FlagsClient = class {
2022
2013
  `Invalid flag name: "${flagName}". Flag names must match ${FLAG_NAME_RE.source}`
2023
2014
  );
2024
2015
  }
2025
- const params = this.buildContextParams(context);
2026
- const query = params.toString();
2027
- const path = `/v1/flags/${flagName}/variant${query ? `?${query}` : ""}`;
2028
- return this.httpClient.request("GET", path);
2016
+ const res = await this.httpClient.request(
2017
+ "GET",
2018
+ this.mergedPath(context)
2019
+ );
2020
+ if (res.error || res.data == null) {
2021
+ return { ...res, data: null };
2022
+ }
2023
+ return { ...res, data: flagVariant(res.data.values?.[flagName]) };
2029
2024
  }
2030
2025
  async getAll(context) {
2031
- const params = this.buildContextParams(context);
2032
- const query = params.toString();
2033
- const path = `/v1/flags${query ? `?${query}` : ""}`;
2034
- return this.httpClient.request("GET", path);
2026
+ const res = await this.httpClient.request(
2027
+ "GET",
2028
+ this.mergedPath(context)
2029
+ );
2030
+ return mapResponse(res, (snap) => {
2031
+ const values = snap.values ?? {};
2032
+ return Object.keys(values).map((name) => {
2033
+ const value = values[name];
2034
+ const flag = { name, enabled: flagEnabled(value) };
2035
+ if (typeof value === "string") flag.variant = { name: value };
2036
+ return flag;
2037
+ });
2038
+ });
2035
2039
  }
2036
2040
  /**
2037
2041
  * Cold-start read: the full merged flag set for the current auth identity.
@@ -2130,15 +2134,16 @@ var FlagsClient = class {
2130
2134
  }
2131
2135
  };
2132
2136
  }
2133
- buildContextParams(context) {
2134
- const params = new URLSearchParams();
2135
- if (context?.userId) {
2136
- params.set("userId", context.userId);
2137
- }
2138
- if (context?.properties) {
2139
- params.set("properties", JSON.stringify(context.properties));
2140
- }
2141
- return params;
2137
+ /**
2138
+ * Resolve the merged-read path for a context. With `context.userId` present the
2139
+ * read targets that specific user's merged view (`/v1/user-flags/users/{id}`, a
2140
+ * privileged cross-user read); otherwise the current auth user's view
2141
+ * (`/v1/user-flags`). `context.properties` has no server-side targeting support
2142
+ * in the user-flags module and is ignored.
2143
+ */
2144
+ mergedPath(context) {
2145
+ const uid = context?.userId;
2146
+ return uid ? `/v1/user-flags/users/${encodeURIComponent(uid)}` : "/v1/user-flags";
2142
2147
  }
2143
2148
  };
2144
2149
  var DEFAULT_POLL_MS = 3e4;
@@ -2600,7 +2605,7 @@ var PalbeFlags = class {
2600
2605
  *
2601
2606
  * DEVIATION from iOS sync-cache parity: the platform does not propagate
2602
2607
  * variant metadata into the user-flags snapshot/delta cache, so this is an
2603
- * ASYNC transport read (`GET /v1/flags/{key}/variant`), not a cache lookup.
2608
+ * ASYNC transport read (derived from `GET /v1/user-flags`), not a cache lookup.
2604
2609
  */
2605
2610
  async getVariant(key) {
2606
2611
  let res;
@@ -8239,6 +8244,14 @@ function observeWebVitals(record) {
8239
8244
  };
8240
8245
  }
8241
8246
 
8247
+ // src/runtime-metadata.ts
8248
+ var APP_METADATA_HEADER = "X-Palbase-Bundle";
8249
+ function applyBrowserOriginHeader(headers) {
8250
+ if (Object.keys(headers).some((key) => key.toLowerCase() === "x-palbase-bundle")) return;
8251
+ const origin = typeof window !== "undefined" && typeof window.location !== "undefined" ? window.location.origin : "";
8252
+ if (origin !== "") headers[APP_METADATA_HEADER] = origin;
8253
+ }
8254
+
8242
8255
  // src/realtime/anon-token.ts
8243
8256
  var REFRESH_SKEW_MS = 6e4;
8244
8257
  var AnonTokenProvider = class {
@@ -8281,11 +8294,16 @@ var AnonTokenProvider = class {
8281
8294
  */
8282
8295
  async mint() {
8283
8296
  const base = this.rt.config.url.replace(/\/+$/, "");
8297
+ const headers = {
8298
+ apikey: this.rt.config.apiKey,
8299
+ ...this.rt.config.headers
8300
+ };
8301
+ applyBrowserOriginHeader(headers);
8284
8302
  let response;
8285
8303
  try {
8286
8304
  response = await fetch(`${base}/auth/anonymous`, {
8287
8305
  method: "POST",
8288
- headers: { apikey: this.rt.config.apiKey }
8306
+ headers
8289
8307
  });
8290
8308
  } catch (e) {
8291
8309
  throw new BackendError("network", {
@@ -8926,17 +8944,15 @@ function defaultSessionStorage(key) {
8926
8944
  }
8927
8945
 
8928
8946
  // src/version.ts
8929
- var VERSION = "1.10.0";
8947
+ var VERSION = "2.0.1";
8930
8948
 
8931
8949
  // src/runtime.ts
8932
8950
  function buildRuntime(config) {
8933
- const appIdentifier = config.identifier ?? "";
8934
- const runtimeOrigin = typeof window !== "undefined" && typeof window.location !== "undefined" ? window.location.origin : "";
8935
- assertOriginMatches(loadAppConfig({ identifier: appIdentifier }), runtimeOrigin);
8936
8951
  const http = new HttpClient(config.apiKey, {
8937
8952
  url: config.url,
8938
8953
  headers: { "X-Client-Info": `palbe-web/${VERSION}`, ...config.headers }
8939
8954
  });
8955
+ http.addInterceptor(({ headers }) => applyBrowserOriginHeader(headers));
8940
8956
  const tokenManager = new TokenManager();
8941
8957
  http.tokenManager = tokenManager;
8942
8958
  const authClient = new AuthClient(http, tokenManager);
@@ -8985,7 +9001,6 @@ function buildRuntime(config) {
8985
9001
  let perf;
8986
9002
  const rt = {
8987
9003
  config,
8988
- appIdentifier,
8989
9004
  http,
8990
9005
  tokenManager,
8991
9006
  authClient,
@@ -9144,7 +9159,9 @@ async function buildHeaders(rt, extra) {
9144
9159
  if (token) headers.Authorization = `Bearer ${token}`;
9145
9160
  const callerHasKey = Object.keys(extra ?? {}).some((k) => k.toLowerCase() === "idempotency-key");
9146
9161
  if (!callerHasKey) headers["Idempotency-Key"] = crypto.randomUUID();
9147
- return { ...headers, ...extra };
9162
+ const merged = { ...headers, ...extra };
9163
+ applyBrowserOriginHeader(merged);
9164
+ return merged;
9148
9165
  }
9149
9166
  function buildForm(options) {
9150
9167
  const form = new FormData();
@@ -9340,4 +9357,4 @@ export {
9340
9357
  pb,
9341
9358
  createBoundClient
9342
9359
  };
9343
- //# sourceMappingURL=chunk-4ZJHHXD3.js.map
9360
+ //# sourceMappingURL=chunk-AIPUO4QX.js.map