@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/dist/index.cjs CHANGED
@@ -196,12 +196,6 @@ async function palbeRequest(rt, method, path, spec = {}) {
196
196
  if (MUTATING.has(method) && !callerHasKey) {
197
197
  headers["Idempotency-Key"] = crypto.randomUUID();
198
198
  }
199
- if (rt.appIdentifier !== "") {
200
- const callerHasBundle = Object.keys(headers).some(
201
- (k) => k.toLowerCase() === "x-palbase-bundle"
202
- );
203
- if (!callerHasBundle) headers["X-Palbase-Bundle"] = rt.appIdentifier;
204
- }
205
199
  const attempt = async () => {
206
200
  try {
207
201
  return await rt.http.request(method, path, {
@@ -992,6 +986,19 @@ var PalbeCalls = class {
992
986
 
993
987
  // ../modules/flags/dist/index.js
994
988
  var FLAG_NAME_RE = /^[a-zA-Z0-9_-]+$/;
989
+ function flagEnabled(value) {
990
+ if (typeof value === "boolean") return value;
991
+ return value != null && value !== 0 && value !== "";
992
+ }
993
+ function flagVariant(value) {
994
+ return typeof value === "string" ? { name: value } : null;
995
+ }
996
+ function mapResponse(res, fn) {
997
+ if (res.error || res.data == null) {
998
+ return { ...res, data: null };
999
+ }
1000
+ return { ...res, data: fn(res.data) };
1001
+ }
995
1002
  var FlagsClient = class {
996
1003
  httpClient;
997
1004
  getCurrentUserId;
@@ -1011,10 +1018,11 @@ var FlagsClient = class {
1011
1018
  `Invalid flag name: "${flagName}". Flag names must match ${FLAG_NAME_RE.source}`
1012
1019
  );
1013
1020
  }
1014
- const params = this.buildContextParams(context);
1015
- const query = params.toString();
1016
- const path = `/v1/flags/${flagName}/enabled${query ? `?${query}` : ""}`;
1017
- return this.httpClient.request("GET", path);
1021
+ const res = await this.httpClient.request(
1022
+ "GET",
1023
+ this.mergedPath(context)
1024
+ );
1025
+ return mapResponse(res, (snap) => flagEnabled(snap.values?.[flagName]));
1018
1026
  }
1019
1027
  async getVariant(flagName, context) {
1020
1028
  if (!FLAG_NAME_RE.test(flagName)) {
@@ -1022,16 +1030,29 @@ var FlagsClient = class {
1022
1030
  `Invalid flag name: "${flagName}". Flag names must match ${FLAG_NAME_RE.source}`
1023
1031
  );
1024
1032
  }
1025
- const params = this.buildContextParams(context);
1026
- const query = params.toString();
1027
- const path = `/v1/flags/${flagName}/variant${query ? `?${query}` : ""}`;
1028
- return this.httpClient.request("GET", path);
1033
+ const res = await this.httpClient.request(
1034
+ "GET",
1035
+ this.mergedPath(context)
1036
+ );
1037
+ if (res.error || res.data == null) {
1038
+ return { ...res, data: null };
1039
+ }
1040
+ return { ...res, data: flagVariant(res.data.values?.[flagName]) };
1029
1041
  }
1030
1042
  async getAll(context) {
1031
- const params = this.buildContextParams(context);
1032
- const query = params.toString();
1033
- const path = `/v1/flags${query ? `?${query}` : ""}`;
1034
- return this.httpClient.request("GET", path);
1043
+ const res = await this.httpClient.request(
1044
+ "GET",
1045
+ this.mergedPath(context)
1046
+ );
1047
+ return mapResponse(res, (snap) => {
1048
+ const values = snap.values ?? {};
1049
+ return Object.keys(values).map((name) => {
1050
+ const value = values[name];
1051
+ const flag = { name, enabled: flagEnabled(value) };
1052
+ if (typeof value === "string") flag.variant = { name: value };
1053
+ return flag;
1054
+ });
1055
+ });
1035
1056
  }
1036
1057
  /**
1037
1058
  * Cold-start read: the full merged flag set for the current auth identity.
@@ -1130,15 +1151,16 @@ var FlagsClient = class {
1130
1151
  }
1131
1152
  };
1132
1153
  }
1133
- buildContextParams(context) {
1134
- const params = new URLSearchParams();
1135
- if (context?.userId) {
1136
- params.set("userId", context.userId);
1137
- }
1138
- if (context?.properties) {
1139
- params.set("properties", JSON.stringify(context.properties));
1140
- }
1141
- return params;
1154
+ /**
1155
+ * Resolve the merged-read path for a context. With `context.userId` present the
1156
+ * read targets that specific user's merged view (`/v1/user-flags/users/{id}`, a
1157
+ * privileged cross-user read); otherwise the current auth user's view
1158
+ * (`/v1/user-flags`). `context.properties` has no server-side targeting support
1159
+ * in the user-flags module and is ignored.
1160
+ */
1161
+ mergedPath(context) {
1162
+ const uid = context?.userId;
1163
+ return uid ? `/v1/user-flags/users/${encodeURIComponent(uid)}` : "/v1/user-flags";
1142
1164
  }
1143
1165
  };
1144
1166
  var DEFAULT_POLL_MS = 3e4;
@@ -1600,7 +1622,7 @@ var PalbeFlags = class {
1600
1622
  *
1601
1623
  * DEVIATION from iOS sync-cache parity: the platform does not propagate
1602
1624
  * variant metadata into the user-flags snapshot/delta cache, so this is an
1603
- * ASYNC transport read (`GET /v1/flags/{key}/variant`), not a cache lookup.
1625
+ * ASYNC transport read (derived from `GET /v1/user-flags`), not a cache lookup.
1604
1626
  */
1605
1627
  async getVariant(key) {
1606
1628
  let res;
@@ -6900,6 +6922,14 @@ function getNamespace(name) {
6900
6922
  return ns;
6901
6923
  }
6902
6924
 
6925
+ // src/runtime-metadata.ts
6926
+ var APP_METADATA_HEADER = "X-Palbase-Bundle";
6927
+ function applyBrowserOriginHeader(headers) {
6928
+ if (Object.keys(headers).some((key) => key.toLowerCase() === "x-palbase-bundle")) return;
6929
+ const origin = typeof window !== "undefined" && typeof window.location !== "undefined" ? window.location.origin : "";
6930
+ if (origin !== "") headers[APP_METADATA_HEADER] = origin;
6931
+ }
6932
+
6903
6933
  // src/realtime/anon-token.ts
6904
6934
  var REFRESH_SKEW_MS = 6e4;
6905
6935
  var AnonTokenProvider = class {
@@ -6942,11 +6972,16 @@ var AnonTokenProvider = class {
6942
6972
  */
6943
6973
  async mint() {
6944
6974
  const base = this.rt.config.url.replace(/\/+$/, "");
6975
+ const headers = {
6976
+ apikey: this.rt.config.apiKey,
6977
+ ...this.rt.config.headers
6978
+ };
6979
+ applyBrowserOriginHeader(headers);
6945
6980
  let response;
6946
6981
  try {
6947
6982
  response = await fetch(`${base}/auth/anonymous`, {
6948
6983
  method: "POST",
6949
- headers: { apikey: this.rt.config.apiKey }
6984
+ headers
6950
6985
  });
6951
6986
  } catch (e) {
6952
6987
  throw new BackendError("network", {
@@ -7583,7 +7618,7 @@ function localStorageSessionStorage(key = DEFAULT_KEY) {
7583
7618
  }
7584
7619
 
7585
7620
  // src/version.ts
7586
- var VERSION = "1.10.0";
7621
+ var VERSION = "2.0.1";
7587
7622
 
7588
7623
  // src/internal.ts
7589
7624
  function getRuntime() {
@@ -7640,7 +7675,9 @@ async function buildHeaders(rt, extra) {
7640
7675
  if (token) headers.Authorization = `Bearer ${token}`;
7641
7676
  const callerHasKey = Object.keys(extra ?? {}).some((k) => k.toLowerCase() === "idempotency-key");
7642
7677
  if (!callerHasKey) headers["Idempotency-Key"] = crypto.randomUUID();
7643
- return { ...headers, ...extra };
7678
+ const merged = { ...headers, ...extra };
7679
+ applyBrowserOriginHeader(merged);
7680
+ return merged;
7644
7681
  }
7645
7682
  function buildForm(options) {
7646
7683
  const form = new FormData();