flagmint-js-sdk 2.2.2 → 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";
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";
@@ -41903,22 +41910,80 @@ function payloadHasExplicitPiiFields(payload) {
41903
41910
  return false;
41904
41911
  return payload.piiSalt !== void 0 || payload.piiAttributes !== void 0 || payload.piiValueEncoding !== void 0;
41905
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
+ }
41906
41954
  function piiWireOptionsFromPayload(payload) {
41907
41955
  if (!payload)
41908
41956
  return null;
41957
+ if (hasExplicitInvalidPiiEncoding(payload))
41958
+ return null;
41959
+ if (hasMalformedPiiAttributes(payload))
41960
+ return null;
41909
41961
  const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
41910
41962
  if (!salt)
41911
41963
  return null;
41912
- const encoding = typeof payload.piiValueEncoding === "string" ? payload.piiValueEncoding : PII_VALUE_ENCODING;
41913
- if (encoding !== PII_VALUE_ENCODING)
41914
- return null;
41915
- const attrs = Array.isArray(payload.piiAttributes) ? payload.piiAttributes.filter((k) => typeof k === "string") : [];
41964
+ const attrs = piiAttributeKeysFromPayload(payload);
41916
41965
  return {
41917
41966
  piiAttributes: attrs,
41918
41967
  piiSalt: salt,
41919
41968
  piiValueEncoding: PII_VALUE_ENCODING
41920
41969
  };
41921
41970
  }
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
+ }
41922
41987
  function resolvePiiWireForDelta(previous, action) {
41923
41988
  if (!payloadHasExplicitPiiFields(action)) {
41924
41989
  return previous;
@@ -41971,7 +42036,8 @@ function createEmptyRulesState() {
41971
42036
  segments: /* @__PURE__ */ new Map(),
41972
42037
  ready: false,
41973
42038
  needsFullConfig: true,
41974
- piiWire: null
42039
+ piiWire: null,
42040
+ piiWireBroken: false
41975
42041
  };
41976
42042
  }
41977
42043
  function cloneState(state) {
@@ -41986,7 +42052,8 @@ function cloneState(state) {
41986
42052
  piiAttributes: [...state.piiWire.piiAttributes],
41987
42053
  piiSalt: state.piiWire.piiSalt,
41988
42054
  piiValueEncoding: state.piiWire.piiValueEncoding
41989
- } : null
42055
+ } : null,
42056
+ piiWireBroken: state.piiWireBroken
41990
42057
  };
41991
42058
  }
41992
42059
  function segmentsFromRecord(record) {
@@ -42013,14 +42080,18 @@ function hydrateFromFull(action) {
42013
42080
  if (flag?.key)
42014
42081
  flags.set(flag.key, flag);
42015
42082
  }
42083
+ const piiWire = piiWireOptionsFromPayload(action);
42084
+ const piiWireBroken = isPiiWireBroken(action, piiWire, false);
42016
42085
  return {
42017
42086
  version: action.version,
42018
42087
  expiresAt: action.expiresAt,
42019
42088
  flags,
42020
42089
  segments: segmentsFromRecord(action.segments),
42021
- ready: true,
42022
- needsFullConfig: false,
42023
- 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
42024
42095
  };
42025
42096
  }
42026
42097
  function applyPatch(state, action) {
@@ -42033,14 +42104,19 @@ function applyPatch(state, action) {
42033
42104
  flags.set(flag.key, flag);
42034
42105
  }
42035
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;
42036
42111
  return {
42037
42112
  version: action.toVersion,
42038
42113
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
42039
42114
  flags,
42040
42115
  segments: applySegmentUpserts(state.segments, action.segments),
42041
- ready: true,
42042
- needsFullConfig: false,
42043
- piiWire: nextPii
42116
+ ready: !piiWireBroken,
42117
+ needsFullConfig: piiWireBroken,
42118
+ piiWire: nextPii,
42119
+ piiWireBroken
42044
42120
  };
42045
42121
  }
42046
42122
  function reduceRules(state, action, now = Date.now()) {
@@ -42060,8 +42136,8 @@ function reduceRules(state, action, now = Date.now()) {
42060
42136
  ...cloneState(state),
42061
42137
  version: state.version,
42062
42138
  expiresAt: action.expiresAt,
42063
- ready: true,
42064
- 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
42065
42141
  }
42066
42142
  };
42067
42143
  }
@@ -42117,8 +42193,8 @@ function reduceRules(state, action, now = Date.now()) {
42117
42193
  ...current,
42118
42194
  version: action.toVersion,
42119
42195
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : current.expiresAt,
42120
- ready: true,
42121
- needsFullConfig: false
42196
+ ready: !current.piiWireBroken,
42197
+ needsFullConfig: current.piiWireBroken
42122
42198
  }
42123
42199
  };
42124
42200
  }
@@ -42141,9 +42217,12 @@ class RulesStore {
42141
42217
  return this.state;
42142
42218
  }
42143
42219
  /**
42144
- * 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.
42145
42222
  */
42146
42223
  isReady(now = Date.now()) {
42224
+ if (this.state.piiWireBroken)
42225
+ return false;
42147
42226
  if (!this.state.ready)
42148
42227
  return false;
42149
42228
  if (!this.state.expiresAt)
@@ -42227,7 +42306,9 @@ class RulesStore {
42227
42306
  piiAttributes: [...pii.piiAttributes],
42228
42307
  piiSalt: pii.piiSalt,
42229
42308
  piiValueEncoding: pii.piiValueEncoding
42230
- } : {}
42309
+ } : {},
42310
+ // Persist fail-closed even when piiWire is null (unusable salt/encoding).
42311
+ ...this.state.piiWireBroken ? { piiWireBroken: true } : {}
42231
42312
  };
42232
42313
  }
42233
42314
  hydrateFromSnapshot(snapshot, now = Date.now()) {
@@ -42237,14 +42318,17 @@ class RulesStore {
42237
42318
  flags.set(flag.key, flag);
42238
42319
  }
42239
42320
  const expired = isConfigPayloadExpired(snapshot.expiresAt, now);
42321
+ const piiWire = piiWireOptionsFromPayload(snapshot);
42322
+ const piiWireBroken = snapshot.piiWireBroken === true || isPiiWireBroken(snapshot, piiWire, false);
42240
42323
  this.state = {
42241
42324
  version: snapshot.version,
42242
42325
  expiresAt: snapshot.expiresAt,
42243
42326
  flags,
42244
42327
  segments: segmentsFromRecord(snapshot.segments),
42245
- ready: !expired && flags.size > 0,
42246
- needsFullConfig: expired || flags.size === 0,
42247
- piiWire: piiWireOptionsFromPayload(snapshot)
42328
+ ready: !expired && flags.size > 0 && !piiWireBroken,
42329
+ needsFullConfig: expired || flags.size === 0 || piiWireBroken,
42330
+ piiWire,
42331
+ piiWireBroken
42248
42332
  };
42249
42333
  }
42250
42334
  }
@@ -43215,9 +43299,10 @@ class FlagClient {
43215
43299
  }
43216
43300
  let hydratedRules = false;
43217
43301
  if (this.configSync && this.rulesStore && this.enableOfflineCache) {
43302
+ clearCachedFlags(this.apiKey);
43218
43303
  hydratedRules = await this.hydrateRulesFromCache();
43219
43304
  }
43220
- if (this.enableOfflineCache && !hydratedRules) {
43305
+ if (this.enableOfflineCache && !this.configSync && !hydratedRules) {
43221
43306
  const cached = await Promise.resolve(
43222
43307
  this.cacheAdapter.loadFlags(this.apiKey, this.cacheTTL)
43223
43308
  );
@@ -43459,7 +43544,7 @@ class FlagClient {
43459
43544
  */
43460
43545
  updateFlags(newFlags, fromShare = false) {
43461
43546
  this.flags = newFlags;
43462
- if (this.enableOfflineCache) {
43547
+ if (this.enableOfflineCache && !this.configSync) {
43463
43548
  void Promise.resolve(
43464
43549
  this.cacheAdapter.saveFlags(this.apiKey, newFlags)
43465
43550
  );