flagmint-js-sdk 2.2.2 → 2.2.3-rc.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.
@@ -38590,12 +38590,27 @@ function parseRulesSnapshot$1(raw) {
38590
38590
  if (!parsed || typeof parsed.version !== "number" || typeof parsed.expiresAt !== "number" || !Array.isArray(parsed.flags) || typeof parsed.segments !== "object" || parsed.segments === null) {
38591
38591
  return null;
38592
38592
  }
38593
- return {
38593
+ const snapshot = {
38594
38594
  version: parsed.version,
38595
38595
  expiresAt: parsed.expiresAt,
38596
38596
  flags: parsed.flags,
38597
38597
  segments: parsed.segments || {}
38598
38598
  };
38599
+ if (Array.isArray(parsed.piiAttributes)) {
38600
+ snapshot.piiAttributes = parsed.piiAttributes.filter(
38601
+ (attr) => typeof attr === "string"
38602
+ );
38603
+ }
38604
+ if (typeof parsed.piiSalt === "string") {
38605
+ snapshot.piiSalt = parsed.piiSalt;
38606
+ }
38607
+ if (typeof parsed.piiValueEncoding === "string") {
38608
+ snapshot.piiValueEncoding = parsed.piiValueEncoding;
38609
+ }
38610
+ if (parsed.piiWireBroken === true) {
38611
+ snapshot.piiWireBroken = true;
38612
+ }
38613
+ return snapshot;
38599
38614
  } catch {
38600
38615
  return null;
38601
38616
  }
@@ -38619,6 +38634,12 @@ function saveCachedFlags$1(apiKey, data) {
38619
38634
  } catch {
38620
38635
  }
38621
38636
  }
38637
+ function clearCachedFlags(apiKey) {
38638
+ try {
38639
+ storage$1.removeItem?.(getFlagsKey$1(apiKey));
38640
+ } catch {
38641
+ }
38642
+ }
38622
38643
  function loadCachedContext$1(apiKey) {
38623
38644
  try {
38624
38645
  const raw = storage$1.getItem(getContextKey$1(apiKey));
@@ -38657,6 +38678,7 @@ function clearCachedRulesSnapshot$1(apiKey) {
38657
38678
  }
38658
38679
  const cacheHelper = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
38659
38680
  __proto__: null,
38681
+ clearCachedFlags,
38660
38682
  clearCachedRulesSnapshot: clearCachedRulesSnapshot$1,
38661
38683
  loadCachedContext: loadCachedContext$1,
38662
38684
  loadCachedFlags: loadCachedFlags$1,
@@ -39091,7 +39113,7 @@ class SseTransport {
39091
39113
  this.connectPromise = new Promise((resolve, reject) => {
39092
39114
  this.connectResolve = resolve;
39093
39115
  this.connectReject = reject;
39094
- const sdkVersion = "2.2.2";
39116
+ const sdkVersion = "2.2.3-rc.1";
39095
39117
  const platform2 = typeof window !== "undefined" ? "browser" : "nodejs";
39096
39118
  const wrapperName = this.configOptions?.wrapper?.name || "native-js";
39097
39119
  const wrapperVersion = this.configOptions?.wrapper?.version || "none";
@@ -41903,22 +41925,80 @@ function payloadHasExplicitPiiFields(payload) {
41903
41925
  return false;
41904
41926
  return payload.piiSalt !== void 0 || payload.piiAttributes !== void 0 || payload.piiValueEncoding !== void 0;
41905
41927
  }
41928
+ function piiAttributeKeysFromPayload(payload) {
41929
+ if (!payload || !Array.isArray(payload.piiAttributes))
41930
+ return [];
41931
+ return payload.piiAttributes.filter((k) => typeof k === "string" && k.length > 0);
41932
+ }
41933
+ function hasMalformedPiiAttributes(payload) {
41934
+ if (!payload || payload.piiAttributes === void 0)
41935
+ return false;
41936
+ if (!Array.isArray(payload.piiAttributes))
41937
+ return true;
41938
+ return payload.piiAttributes.some(
41939
+ (entry) => typeof entry !== "string" || entry.length === 0
41940
+ );
41941
+ }
41942
+ function hasExplicitInvalidPiiEncoding(payload) {
41943
+ if (!payload || payload.piiValueEncoding === void 0)
41944
+ return false;
41945
+ return payload.piiValueEncoding !== PII_VALUE_ENCODING;
41946
+ }
41947
+ function payloadRequiresUsablePiiWire(payload) {
41948
+ if (!payload)
41949
+ return false;
41950
+ if (hasExplicitInvalidPiiEncoding(payload))
41951
+ return true;
41952
+ if (hasMalformedPiiAttributes(payload))
41953
+ return true;
41954
+ if (piiAttributeKeysFromPayload(payload).length > 0)
41955
+ return true;
41956
+ return false;
41957
+ }
41958
+ function isExplicitPiiRequirementCleared(payload) {
41959
+ if (!payload || payload.piiAttributes === void 0)
41960
+ return false;
41961
+ if (!Array.isArray(payload.piiAttributes))
41962
+ return false;
41963
+ if (payload.piiAttributes.length !== 0)
41964
+ return false;
41965
+ if (hasExplicitInvalidPiiEncoding(payload))
41966
+ return false;
41967
+ return true;
41968
+ }
41906
41969
  function piiWireOptionsFromPayload(payload) {
41907
41970
  if (!payload)
41908
41971
  return null;
41972
+ if (hasExplicitInvalidPiiEncoding(payload))
41973
+ return null;
41974
+ if (hasMalformedPiiAttributes(payload))
41975
+ return null;
41909
41976
  const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
41910
41977
  if (!salt)
41911
41978
  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") : [];
41979
+ const attrs = piiAttributeKeysFromPayload(payload);
41916
41980
  return {
41917
41981
  piiAttributes: attrs,
41918
41982
  piiSalt: salt,
41919
41983
  piiValueEncoding: PII_VALUE_ENCODING
41920
41984
  };
41921
41985
  }
41986
+ function isPiiWireBroken(payload, resolved, previousPriorHadPiiAttrs = false) {
41987
+ if (hasExplicitInvalidPiiEncoding(payload) || hasMalformedPiiAttributes(payload)) {
41988
+ return true;
41989
+ }
41990
+ if (resolved) {
41991
+ return false;
41992
+ }
41993
+ if (isExplicitPiiRequirementCleared(payload))
41994
+ return false;
41995
+ if (payloadRequiresUsablePiiWire(payload))
41996
+ return true;
41997
+ if (previousPriorHadPiiAttrs && payloadHasExplicitPiiFields(payload)) {
41998
+ return true;
41999
+ }
42000
+ return false;
42001
+ }
41922
42002
  function resolvePiiWireForDelta(previous, action) {
41923
42003
  if (!payloadHasExplicitPiiFields(action)) {
41924
42004
  return previous;
@@ -41971,7 +42051,8 @@ function createEmptyRulesState() {
41971
42051
  segments: /* @__PURE__ */ new Map(),
41972
42052
  ready: false,
41973
42053
  needsFullConfig: true,
41974
- piiWire: null
42054
+ piiWire: null,
42055
+ piiWireBroken: false
41975
42056
  };
41976
42057
  }
41977
42058
  function cloneState(state) {
@@ -41986,7 +42067,8 @@ function cloneState(state) {
41986
42067
  piiAttributes: [...state.piiWire.piiAttributes],
41987
42068
  piiSalt: state.piiWire.piiSalt,
41988
42069
  piiValueEncoding: state.piiWire.piiValueEncoding
41989
- } : null
42070
+ } : null,
42071
+ piiWireBroken: state.piiWireBroken
41990
42072
  };
41991
42073
  }
41992
42074
  function segmentsFromRecord(record) {
@@ -42013,14 +42095,18 @@ function hydrateFromFull(action) {
42013
42095
  if (flag?.key)
42014
42096
  flags.set(flag.key, flag);
42015
42097
  }
42098
+ const piiWire = piiWireOptionsFromPayload(action);
42099
+ const piiWireBroken = isPiiWireBroken(action, piiWire, false);
42016
42100
  return {
42017
42101
  version: action.version,
42018
42102
  expiresAt: action.expiresAt,
42019
42103
  flags,
42020
42104
  segments: segmentsFromRecord(action.segments),
42021
- ready: true,
42022
- needsFullConfig: false,
42023
- piiWire: piiWireOptionsFromPayload(action)
42105
+ // Usable rules, but PII metadata must be valid when hashing is required.
42106
+ ready: !piiWireBroken,
42107
+ needsFullConfig: piiWireBroken,
42108
+ piiWire,
42109
+ piiWireBroken
42024
42110
  };
42025
42111
  }
42026
42112
  function applyPatch(state, action) {
@@ -42033,14 +42119,19 @@ function applyPatch(state, action) {
42033
42119
  flags.set(flag.key, flag);
42034
42120
  }
42035
42121
  const nextPii = resolvePiiWireForDelta(state.piiWire, action);
42122
+ const priorHadPiiAttrs = (state.piiWire?.piiAttributes.length ?? 0) > 0 || state.piiWireBroken;
42123
+ const computedBroken = isPiiWireBroken(action, nextPii, priorHadPiiAttrs);
42124
+ const recoveredWithRequiredAttrs = !!nextPii && nextPii.piiAttributes.length > 0;
42125
+ const piiWireBroken = isExplicitPiiRequirementCleared(action) ? false : recoveredWithRequiredAttrs ? false : computedBroken || state.piiWireBroken;
42036
42126
  return {
42037
42127
  version: action.toVersion,
42038
42128
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
42039
42129
  flags,
42040
42130
  segments: applySegmentUpserts(state.segments, action.segments),
42041
- ready: true,
42042
- needsFullConfig: false,
42043
- piiWire: nextPii
42131
+ ready: !piiWireBroken,
42132
+ needsFullConfig: piiWireBroken,
42133
+ piiWire: nextPii,
42134
+ piiWireBroken
42044
42135
  };
42045
42136
  }
42046
42137
  function reduceRules(state, action, now = Date.now()) {
@@ -42060,8 +42151,8 @@ function reduceRules(state, action, now = Date.now()) {
42060
42151
  ...cloneState(state),
42061
42152
  version: state.version,
42062
42153
  expiresAt: action.expiresAt,
42063
- ready: true,
42064
- needsFullConfig: state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
42154
+ ready: !state.piiWireBroken,
42155
+ needsFullConfig: state.piiWireBroken || state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
42065
42156
  }
42066
42157
  };
42067
42158
  }
@@ -42117,8 +42208,8 @@ function reduceRules(state, action, now = Date.now()) {
42117
42208
  ...current,
42118
42209
  version: action.toVersion,
42119
42210
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : current.expiresAt,
42120
- ready: true,
42121
- needsFullConfig: false
42211
+ ready: !current.piiWireBroken,
42212
+ needsFullConfig: current.piiWireBroken
42122
42213
  }
42123
42214
  };
42124
42215
  }
@@ -42141,9 +42232,12 @@ class RulesStore {
42141
42232
  return this.state;
42142
42233
  }
42143
42234
  /**
42144
- * Fail-closed readiness: lease not expired and store marked ready.
42235
+ * Fail-closed readiness: lease not expired, store marked ready, and PII
42236
+ * wire metadata usable when hashing is required.
42145
42237
  */
42146
42238
  isReady(now = Date.now()) {
42239
+ if (this.state.piiWireBroken)
42240
+ return false;
42147
42241
  if (!this.state.ready)
42148
42242
  return false;
42149
42243
  if (!this.state.expiresAt)
@@ -42227,7 +42321,9 @@ class RulesStore {
42227
42321
  piiAttributes: [...pii.piiAttributes],
42228
42322
  piiSalt: pii.piiSalt,
42229
42323
  piiValueEncoding: pii.piiValueEncoding
42230
- } : {}
42324
+ } : {},
42325
+ // Persist fail-closed even when piiWire is null (unusable salt/encoding).
42326
+ ...this.state.piiWireBroken ? { piiWireBroken: true } : {}
42231
42327
  };
42232
42328
  }
42233
42329
  hydrateFromSnapshot(snapshot, now = Date.now()) {
@@ -42237,14 +42333,17 @@ class RulesStore {
42237
42333
  flags.set(flag.key, flag);
42238
42334
  }
42239
42335
  const expired = isConfigPayloadExpired(snapshot.expiresAt, now);
42336
+ const piiWire = piiWireOptionsFromPayload(snapshot);
42337
+ const piiWireBroken = snapshot.piiWireBroken === true || isPiiWireBroken(snapshot, piiWire, false);
42240
42338
  this.state = {
42241
42339
  version: snapshot.version,
42242
42340
  expiresAt: snapshot.expiresAt,
42243
42341
  flags,
42244
42342
  segments: segmentsFromRecord(snapshot.segments),
42245
- ready: !expired && flags.size > 0,
42246
- needsFullConfig: expired || flags.size === 0,
42247
- piiWire: piiWireOptionsFromPayload(snapshot)
42343
+ ready: !expired && flags.size > 0 && !piiWireBroken,
42344
+ needsFullConfig: expired || flags.size === 0 || piiWireBroken,
42345
+ piiWire,
42346
+ piiWireBroken
42248
42347
  };
42249
42348
  }
42250
42349
  }
@@ -43215,9 +43314,10 @@ class FlagClient {
43215
43314
  }
43216
43315
  let hydratedRules = false;
43217
43316
  if (this.configSync && this.rulesStore && this.enableOfflineCache) {
43317
+ clearCachedFlags(this.apiKey);
43218
43318
  hydratedRules = await this.hydrateRulesFromCache();
43219
43319
  }
43220
- if (this.enableOfflineCache && !hydratedRules) {
43320
+ if (this.enableOfflineCache && !this.configSync && !hydratedRules) {
43221
43321
  const cached = await Promise.resolve(
43222
43322
  this.cacheAdapter.loadFlags(this.apiKey, this.cacheTTL)
43223
43323
  );
@@ -43459,7 +43559,7 @@ class FlagClient {
43459
43559
  */
43460
43560
  updateFlags(newFlags, fromShare = false) {
43461
43561
  this.flags = newFlags;
43462
- if (this.enableOfflineCache) {
43562
+ if (this.enableOfflineCache && !this.configSync) {
43463
43563
  void Promise.resolve(
43464
43564
  this.cacheAdapter.saveFlags(this.apiKey, newFlags)
43465
43565
  );