flagmint-js-sdk 2.2.2-rc.0 → 2.2.3-rc.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.
@@ -38619,6 +38619,12 @@ function saveCachedFlags$1(apiKey, data) {
38619
38619
  } catch {
38620
38620
  }
38621
38621
  }
38622
+ function clearCachedFlags(apiKey) {
38623
+ try {
38624
+ storage$1.removeItem?.(getFlagsKey$1(apiKey));
38625
+ } catch {
38626
+ }
38627
+ }
38622
38628
  function loadCachedContext$1(apiKey) {
38623
38629
  try {
38624
38630
  const raw = storage$1.getItem(getContextKey$1(apiKey));
@@ -38657,6 +38663,7 @@ function clearCachedRulesSnapshot$1(apiKey) {
38657
38663
  }
38658
38664
  const cacheHelper = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
38659
38665
  __proto__: null,
38666
+ clearCachedFlags,
38660
38667
  clearCachedRulesSnapshot: clearCachedRulesSnapshot$1,
38661
38668
  loadCachedContext: loadCachedContext$1,
38662
38669
  loadCachedFlags: loadCachedFlags$1,
@@ -39091,7 +39098,7 @@ class SseTransport {
39091
39098
  this.connectPromise = new Promise((resolve, reject) => {
39092
39099
  this.connectResolve = resolve;
39093
39100
  this.connectReject = reject;
39094
- const sdkVersion = "2.2.2-rc.0";
39101
+ const sdkVersion = "2.2.3-rc.0";
39095
39102
  const platform2 = typeof window !== "undefined" ? "browser" : "nodejs";
39096
39103
  const wrapperName = this.configOptions?.wrapper?.name || "native-js";
39097
39104
  const wrapperVersion = this.configOptions?.wrapper?.version || "none";
@@ -41871,7 +41878,6 @@ function isConfigPayloadExpired(expiresAt, now = Date.now()) {
41871
41878
  }
41872
41879
  const PII_VALUE_ENCODING = "hmac-v1";
41873
41880
  const textEncoder = new TextEncoder();
41874
- const HASH_HEX_PATTERN = /^[0-9a-f]{64}$/;
41875
41881
  function normalizePiiString(value) {
41876
41882
  return value.trim().toLowerCase();
41877
41883
  }
@@ -41899,34 +41905,109 @@ function isPiiAttribute(attribute, piiAttributes) {
41899
41905
  }
41900
41906
  return false;
41901
41907
  }
41908
+ function payloadHasExplicitPiiFields(payload) {
41909
+ if (!payload)
41910
+ return false;
41911
+ return payload.piiSalt !== void 0 || payload.piiAttributes !== void 0 || payload.piiValueEncoding !== void 0;
41912
+ }
41913
+ function piiAttributeKeysFromPayload(payload) {
41914
+ if (!payload || !Array.isArray(payload.piiAttributes))
41915
+ return [];
41916
+ return payload.piiAttributes.filter((k) => typeof k === "string" && k.length > 0);
41917
+ }
41918
+ function hasMalformedPiiAttributes(payload) {
41919
+ if (!payload || payload.piiAttributes === void 0)
41920
+ return false;
41921
+ if (!Array.isArray(payload.piiAttributes))
41922
+ return true;
41923
+ return payload.piiAttributes.some(
41924
+ (entry) => typeof entry !== "string" || entry.length === 0
41925
+ );
41926
+ }
41927
+ function hasExplicitInvalidPiiEncoding(payload) {
41928
+ if (!payload || payload.piiValueEncoding === void 0)
41929
+ return false;
41930
+ return payload.piiValueEncoding !== PII_VALUE_ENCODING;
41931
+ }
41932
+ function payloadRequiresUsablePiiWire(payload) {
41933
+ if (!payload)
41934
+ return false;
41935
+ if (hasExplicitInvalidPiiEncoding(payload))
41936
+ return true;
41937
+ if (hasMalformedPiiAttributes(payload))
41938
+ return true;
41939
+ if (piiAttributeKeysFromPayload(payload).length > 0)
41940
+ return true;
41941
+ return false;
41942
+ }
41943
+ function isExplicitPiiRequirementCleared(payload) {
41944
+ if (!payload || payload.piiAttributes === void 0)
41945
+ return false;
41946
+ if (!Array.isArray(payload.piiAttributes))
41947
+ return false;
41948
+ if (payload.piiAttributes.length !== 0)
41949
+ return false;
41950
+ if (hasExplicitInvalidPiiEncoding(payload))
41951
+ return false;
41952
+ return true;
41953
+ }
41902
41954
  function piiWireOptionsFromPayload(payload) {
41903
41955
  if (!payload)
41904
41956
  return null;
41957
+ if (hasExplicitInvalidPiiEncoding(payload))
41958
+ return null;
41959
+ if (hasMalformedPiiAttributes(payload))
41960
+ return null;
41905
41961
  const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
41906
41962
  if (!salt)
41907
41963
  return null;
41908
- const encoding = typeof payload.piiValueEncoding === "string" ? payload.piiValueEncoding : PII_VALUE_ENCODING;
41909
- if (encoding !== PII_VALUE_ENCODING)
41910
- return null;
41911
- const attrs = Array.isArray(payload.piiAttributes) ? payload.piiAttributes.filter((k) => typeof k === "string") : [];
41964
+ const attrs = piiAttributeKeysFromPayload(payload);
41912
41965
  return {
41913
41966
  piiAttributes: attrs,
41914
41967
  piiSalt: salt,
41915
41968
  piiValueEncoding: PII_VALUE_ENCODING
41916
41969
  };
41917
41970
  }
41918
- function applyPiiHashToFlatContext(context, options) {
41971
+ function isPiiWireBroken(payload, resolved, previousPriorHadPiiAttrs = false) {
41972
+ if (hasExplicitInvalidPiiEncoding(payload) || hasMalformedPiiAttributes(payload)) {
41973
+ return true;
41974
+ }
41975
+ if (resolved) {
41976
+ return false;
41977
+ }
41978
+ if (isExplicitPiiRequirementCleared(payload))
41979
+ return false;
41980
+ if (payloadRequiresUsablePiiWire(payload))
41981
+ return true;
41982
+ if (previousPriorHadPiiAttrs && payloadHasExplicitPiiFields(payload)) {
41983
+ return true;
41984
+ }
41985
+ return false;
41986
+ }
41987
+ function resolvePiiWireForDelta(previous, action) {
41988
+ if (!payloadHasExplicitPiiFields(action)) {
41989
+ return previous;
41990
+ }
41991
+ const merged = {
41992
+ piiSalt: action.piiSalt !== void 0 ? action.piiSalt : previous?.piiSalt,
41993
+ piiAttributes: action.piiAttributes !== void 0 ? action.piiAttributes : previous?.piiAttributes,
41994
+ piiValueEncoding: action.piiValueEncoding !== void 0 ? action.piiValueEncoding : previous?.piiValueEncoding
41995
+ };
41996
+ return piiWireOptionsFromPayload(merged);
41997
+ }
41998
+ function applyPiiHashToFlatContext(context, options, mode) {
41919
41999
  if (!options || !options.piiSalt || !options.piiAttributes.length || options.piiValueEncoding !== PII_VALUE_ENCODING) {
41920
42000
  return context;
41921
42001
  }
42002
+ if (mode?.alreadyHashed) {
42003
+ return context;
42004
+ }
41922
42005
  let changed = false;
41923
42006
  const out = { ...context };
41924
42007
  for (const [key2, value] of Object.entries(context)) {
41925
42008
  if (!isPiiAttribute(key2, options.piiAttributes))
41926
42009
  continue;
41927
42010
  if (typeof value === "string") {
41928
- if (HASH_HEX_PATTERN.test(value))
41929
- continue;
41930
42011
  out[key2] = hashPiiValue(value, options.piiSalt);
41931
42012
  changed = true;
41932
42013
  continue;
@@ -41936,8 +42017,6 @@ function applyPiiHashToFlatContext(context, options) {
41936
42017
  const next = value.map((entry) => {
41937
42018
  if (typeof entry !== "string")
41938
42019
  return entry;
41939
- if (HASH_HEX_PATTERN.test(entry))
41940
- return entry;
41941
42020
  arrayChanged = true;
41942
42021
  return hashPiiValue(entry, options.piiSalt);
41943
42022
  });
@@ -41957,7 +42036,8 @@ function createEmptyRulesState() {
41957
42036
  segments: /* @__PURE__ */ new Map(),
41958
42037
  ready: false,
41959
42038
  needsFullConfig: true,
41960
- piiWire: null
42039
+ piiWire: null,
42040
+ piiWireBroken: false
41961
42041
  };
41962
42042
  }
41963
42043
  function cloneState(state) {
@@ -41972,7 +42052,8 @@ function cloneState(state) {
41972
42052
  piiAttributes: [...state.piiWire.piiAttributes],
41973
42053
  piiSalt: state.piiWire.piiSalt,
41974
42054
  piiValueEncoding: state.piiWire.piiValueEncoding
41975
- } : null
42055
+ } : null,
42056
+ piiWireBroken: state.piiWireBroken
41976
42057
  };
41977
42058
  }
41978
42059
  function segmentsFromRecord(record) {
@@ -41999,14 +42080,18 @@ function hydrateFromFull(action) {
41999
42080
  if (flag?.key)
42000
42081
  flags.set(flag.key, flag);
42001
42082
  }
42083
+ const piiWire = piiWireOptionsFromPayload(action);
42084
+ const piiWireBroken = isPiiWireBroken(action, piiWire, false);
42002
42085
  return {
42003
42086
  version: action.version,
42004
42087
  expiresAt: action.expiresAt,
42005
42088
  flags,
42006
42089
  segments: segmentsFromRecord(action.segments),
42007
- ready: true,
42008
- needsFullConfig: false,
42009
- piiWire: piiWireOptionsFromPayload(action)
42090
+ // Usable rules, but PII metadata must be valid when hashing is required.
42091
+ ready: !piiWireBroken,
42092
+ needsFullConfig: piiWireBroken,
42093
+ piiWire,
42094
+ piiWireBroken
42010
42095
  };
42011
42096
  }
42012
42097
  function applyPatch(state, action) {
@@ -42018,15 +42103,20 @@ function applyPatch(state, action) {
42018
42103
  if (flag?.key)
42019
42104
  flags.set(flag.key, flag);
42020
42105
  }
42021
- const nextPii = piiWireOptionsFromPayload(action) ?? state.piiWire;
42106
+ const nextPii = resolvePiiWireForDelta(state.piiWire, action);
42107
+ const priorHadPiiAttrs = (state.piiWire?.piiAttributes.length ?? 0) > 0 || state.piiWireBroken;
42108
+ const computedBroken = isPiiWireBroken(action, nextPii, priorHadPiiAttrs);
42109
+ const recoveredWithRequiredAttrs = !!nextPii && nextPii.piiAttributes.length > 0;
42110
+ const piiWireBroken = isExplicitPiiRequirementCleared(action) ? false : recoveredWithRequiredAttrs ? false : computedBroken || state.piiWireBroken;
42022
42111
  return {
42023
42112
  version: action.toVersion,
42024
42113
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
42025
42114
  flags,
42026
42115
  segments: applySegmentUpserts(state.segments, action.segments),
42027
- ready: true,
42028
- needsFullConfig: false,
42029
- piiWire: nextPii
42116
+ ready: !piiWireBroken,
42117
+ needsFullConfig: piiWireBroken,
42118
+ piiWire: nextPii,
42119
+ piiWireBroken
42030
42120
  };
42031
42121
  }
42032
42122
  function reduceRules(state, action, now = Date.now()) {
@@ -42046,8 +42136,8 @@ function reduceRules(state, action, now = Date.now()) {
42046
42136
  ...cloneState(state),
42047
42137
  version: state.version,
42048
42138
  expiresAt: action.expiresAt,
42049
- ready: true,
42050
- needsFullConfig: state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
42139
+ ready: !state.piiWireBroken,
42140
+ needsFullConfig: state.piiWireBroken || state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
42051
42141
  }
42052
42142
  };
42053
42143
  }
@@ -42103,8 +42193,8 @@ function reduceRules(state, action, now = Date.now()) {
42103
42193
  ...current,
42104
42194
  version: action.toVersion,
42105
42195
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : current.expiresAt,
42106
- ready: true,
42107
- needsFullConfig: false
42196
+ ready: !current.piiWireBroken,
42197
+ needsFullConfig: current.piiWireBroken
42108
42198
  }
42109
42199
  };
42110
42200
  }
@@ -42127,9 +42217,12 @@ class RulesStore {
42127
42217
  return this.state;
42128
42218
  }
42129
42219
  /**
42130
- * Fail-closed readiness: lease not expired and store marked ready.
42220
+ * Fail-closed readiness: lease not expired, store marked ready, and PII
42221
+ * wire metadata usable when hashing is required.
42131
42222
  */
42132
42223
  isReady(now = Date.now()) {
42224
+ if (this.state.piiWireBroken)
42225
+ return false;
42133
42226
  if (!this.state.ready)
42134
42227
  return false;
42135
42228
  if (!this.state.expiresAt)
@@ -42213,7 +42306,9 @@ class RulesStore {
42213
42306
  piiAttributes: [...pii.piiAttributes],
42214
42307
  piiSalt: pii.piiSalt,
42215
42308
  piiValueEncoding: pii.piiValueEncoding
42216
- } : {}
42309
+ } : {},
42310
+ // Persist fail-closed even when piiWire is null (unusable salt/encoding).
42311
+ ...this.state.piiWireBroken ? { piiWireBroken: true } : {}
42217
42312
  };
42218
42313
  }
42219
42314
  hydrateFromSnapshot(snapshot, now = Date.now()) {
@@ -42223,14 +42318,17 @@ class RulesStore {
42223
42318
  flags.set(flag.key, flag);
42224
42319
  }
42225
42320
  const expired = isConfigPayloadExpired(snapshot.expiresAt, now);
42321
+ const piiWire = piiWireOptionsFromPayload(snapshot);
42322
+ const piiWireBroken = snapshot.piiWireBroken === true || isPiiWireBroken(snapshot, piiWire, false);
42226
42323
  this.state = {
42227
42324
  version: snapshot.version,
42228
42325
  expiresAt: snapshot.expiresAt,
42229
42326
  flags,
42230
42327
  segments: segmentsFromRecord(snapshot.segments),
42231
- ready: !expired && flags.size > 0,
42232
- needsFullConfig: expired || flags.size === 0,
42233
- piiWire: piiWireOptionsFromPayload(snapshot)
42328
+ ready: !expired && flags.size > 0 && !piiWireBroken,
42329
+ needsFullConfig: expired || flags.size === 0 || piiWireBroken,
42330
+ piiWire,
42331
+ piiWireBroken
42234
42332
  };
42235
42333
  }
42236
42334
  }
@@ -42432,7 +42530,7 @@ function flattenEvaluationContext(context) {
42432
42530
  function toComparableString(value) {
42433
42531
  if (value === null || value === void 0)
42434
42532
  return "";
42435
- return String(value);
42533
+ return String(value).trim().toLowerCase();
42436
42534
  }
42437
42535
  function toComparableArray(value) {
42438
42536
  if (Array.isArray(value)) {
@@ -43201,9 +43299,10 @@ class FlagClient {
43201
43299
  }
43202
43300
  let hydratedRules = false;
43203
43301
  if (this.configSync && this.rulesStore && this.enableOfflineCache) {
43302
+ clearCachedFlags(this.apiKey);
43204
43303
  hydratedRules = await this.hydrateRulesFromCache();
43205
43304
  }
43206
- if (this.enableOfflineCache && !hydratedRules) {
43305
+ if (this.enableOfflineCache && !this.configSync && !hydratedRules) {
43207
43306
  const cached = await Promise.resolve(
43208
43307
  this.cacheAdapter.loadFlags(this.apiKey, this.cacheTTL)
43209
43308
  );
@@ -43445,7 +43544,7 @@ class FlagClient {
43445
43544
  */
43446
43545
  updateFlags(newFlags, fromShare = false) {
43447
43546
  this.flags = newFlags;
43448
- if (this.enableOfflineCache) {
43547
+ if (this.enableOfflineCache && !this.configSync) {
43449
43548
  void Promise.resolve(
43450
43549
  this.cacheAdapter.saveFlags(this.apiKey, newFlags)
43451
43550
  );