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.
@@ -38594,12 +38594,27 @@ var __publicField = (obj, key, value) => {
38594
38594
  if (!parsed || typeof parsed.version !== "number" || typeof parsed.expiresAt !== "number" || !Array.isArray(parsed.flags) || typeof parsed.segments !== "object" || parsed.segments === null) {
38595
38595
  return null;
38596
38596
  }
38597
- return {
38597
+ const snapshot = {
38598
38598
  version: parsed.version,
38599
38599
  expiresAt: parsed.expiresAt,
38600
38600
  flags: parsed.flags,
38601
38601
  segments: parsed.segments || {}
38602
38602
  };
38603
+ if (Array.isArray(parsed.piiAttributes)) {
38604
+ snapshot.piiAttributes = parsed.piiAttributes.filter(
38605
+ (attr) => typeof attr === "string"
38606
+ );
38607
+ }
38608
+ if (typeof parsed.piiSalt === "string") {
38609
+ snapshot.piiSalt = parsed.piiSalt;
38610
+ }
38611
+ if (typeof parsed.piiValueEncoding === "string") {
38612
+ snapshot.piiValueEncoding = parsed.piiValueEncoding;
38613
+ }
38614
+ if (parsed.piiWireBroken === true) {
38615
+ snapshot.piiWireBroken = true;
38616
+ }
38617
+ return snapshot;
38603
38618
  } catch {
38604
38619
  return null;
38605
38620
  }
@@ -38623,6 +38638,12 @@ var __publicField = (obj, key, value) => {
38623
38638
  } catch {
38624
38639
  }
38625
38640
  }
38641
+ function clearCachedFlags(apiKey) {
38642
+ try {
38643
+ storage$1.removeItem?.(getFlagsKey$1(apiKey));
38644
+ } catch {
38645
+ }
38646
+ }
38626
38647
  function loadCachedContext$1(apiKey) {
38627
38648
  try {
38628
38649
  const raw = storage$1.getItem(getContextKey$1(apiKey));
@@ -38661,6 +38682,7 @@ var __publicField = (obj, key, value) => {
38661
38682
  }
38662
38683
  const cacheHelper = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
38663
38684
  __proto__: null,
38685
+ clearCachedFlags,
38664
38686
  clearCachedRulesSnapshot: clearCachedRulesSnapshot$1,
38665
38687
  loadCachedContext: loadCachedContext$1,
38666
38688
  loadCachedFlags: loadCachedFlags$1,
@@ -39095,7 +39117,7 @@ var __publicField = (obj, key, value) => {
39095
39117
  this.connectPromise = new Promise((resolve, reject) => {
39096
39118
  this.connectResolve = resolve;
39097
39119
  this.connectReject = reject;
39098
- const sdkVersion = "2.2.2";
39120
+ const sdkVersion = "2.2.3-rc.1";
39099
39121
  const platform2 = typeof window !== "undefined" ? "browser" : "nodejs";
39100
39122
  const wrapperName = this.configOptions?.wrapper?.name || "native-js";
39101
39123
  const wrapperVersion = this.configOptions?.wrapper?.version || "none";
@@ -41907,22 +41929,80 @@ var __publicField = (obj, key, value) => {
41907
41929
  return false;
41908
41930
  return payload.piiSalt !== void 0 || payload.piiAttributes !== void 0 || payload.piiValueEncoding !== void 0;
41909
41931
  }
41932
+ function piiAttributeKeysFromPayload(payload) {
41933
+ if (!payload || !Array.isArray(payload.piiAttributes))
41934
+ return [];
41935
+ return payload.piiAttributes.filter((k) => typeof k === "string" && k.length > 0);
41936
+ }
41937
+ function hasMalformedPiiAttributes(payload) {
41938
+ if (!payload || payload.piiAttributes === void 0)
41939
+ return false;
41940
+ if (!Array.isArray(payload.piiAttributes))
41941
+ return true;
41942
+ return payload.piiAttributes.some(
41943
+ (entry) => typeof entry !== "string" || entry.length === 0
41944
+ );
41945
+ }
41946
+ function hasExplicitInvalidPiiEncoding(payload) {
41947
+ if (!payload || payload.piiValueEncoding === void 0)
41948
+ return false;
41949
+ return payload.piiValueEncoding !== PII_VALUE_ENCODING;
41950
+ }
41951
+ function payloadRequiresUsablePiiWire(payload) {
41952
+ if (!payload)
41953
+ return false;
41954
+ if (hasExplicitInvalidPiiEncoding(payload))
41955
+ return true;
41956
+ if (hasMalformedPiiAttributes(payload))
41957
+ return true;
41958
+ if (piiAttributeKeysFromPayload(payload).length > 0)
41959
+ return true;
41960
+ return false;
41961
+ }
41962
+ function isExplicitPiiRequirementCleared(payload) {
41963
+ if (!payload || payload.piiAttributes === void 0)
41964
+ return false;
41965
+ if (!Array.isArray(payload.piiAttributes))
41966
+ return false;
41967
+ if (payload.piiAttributes.length !== 0)
41968
+ return false;
41969
+ if (hasExplicitInvalidPiiEncoding(payload))
41970
+ return false;
41971
+ return true;
41972
+ }
41910
41973
  function piiWireOptionsFromPayload(payload) {
41911
41974
  if (!payload)
41912
41975
  return null;
41976
+ if (hasExplicitInvalidPiiEncoding(payload))
41977
+ return null;
41978
+ if (hasMalformedPiiAttributes(payload))
41979
+ return null;
41913
41980
  const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
41914
41981
  if (!salt)
41915
41982
  return null;
41916
- const encoding = typeof payload.piiValueEncoding === "string" ? payload.piiValueEncoding : PII_VALUE_ENCODING;
41917
- if (encoding !== PII_VALUE_ENCODING)
41918
- return null;
41919
- const attrs = Array.isArray(payload.piiAttributes) ? payload.piiAttributes.filter((k) => typeof k === "string") : [];
41983
+ const attrs = piiAttributeKeysFromPayload(payload);
41920
41984
  return {
41921
41985
  piiAttributes: attrs,
41922
41986
  piiSalt: salt,
41923
41987
  piiValueEncoding: PII_VALUE_ENCODING
41924
41988
  };
41925
41989
  }
41990
+ function isPiiWireBroken(payload, resolved, previousPriorHadPiiAttrs = false) {
41991
+ if (hasExplicitInvalidPiiEncoding(payload) || hasMalformedPiiAttributes(payload)) {
41992
+ return true;
41993
+ }
41994
+ if (resolved) {
41995
+ return false;
41996
+ }
41997
+ if (isExplicitPiiRequirementCleared(payload))
41998
+ return false;
41999
+ if (payloadRequiresUsablePiiWire(payload))
42000
+ return true;
42001
+ if (previousPriorHadPiiAttrs && payloadHasExplicitPiiFields(payload)) {
42002
+ return true;
42003
+ }
42004
+ return false;
42005
+ }
41926
42006
  function resolvePiiWireForDelta(previous, action) {
41927
42007
  if (!payloadHasExplicitPiiFields(action)) {
41928
42008
  return previous;
@@ -41975,7 +42055,8 @@ var __publicField = (obj, key, value) => {
41975
42055
  segments: /* @__PURE__ */ new Map(),
41976
42056
  ready: false,
41977
42057
  needsFullConfig: true,
41978
- piiWire: null
42058
+ piiWire: null,
42059
+ piiWireBroken: false
41979
42060
  };
41980
42061
  }
41981
42062
  function cloneState(state) {
@@ -41990,7 +42071,8 @@ var __publicField = (obj, key, value) => {
41990
42071
  piiAttributes: [...state.piiWire.piiAttributes],
41991
42072
  piiSalt: state.piiWire.piiSalt,
41992
42073
  piiValueEncoding: state.piiWire.piiValueEncoding
41993
- } : null
42074
+ } : null,
42075
+ piiWireBroken: state.piiWireBroken
41994
42076
  };
41995
42077
  }
41996
42078
  function segmentsFromRecord(record) {
@@ -42017,14 +42099,18 @@ var __publicField = (obj, key, value) => {
42017
42099
  if (flag?.key)
42018
42100
  flags.set(flag.key, flag);
42019
42101
  }
42102
+ const piiWire = piiWireOptionsFromPayload(action);
42103
+ const piiWireBroken = isPiiWireBroken(action, piiWire, false);
42020
42104
  return {
42021
42105
  version: action.version,
42022
42106
  expiresAt: action.expiresAt,
42023
42107
  flags,
42024
42108
  segments: segmentsFromRecord(action.segments),
42025
- ready: true,
42026
- needsFullConfig: false,
42027
- piiWire: piiWireOptionsFromPayload(action)
42109
+ // Usable rules, but PII metadata must be valid when hashing is required.
42110
+ ready: !piiWireBroken,
42111
+ needsFullConfig: piiWireBroken,
42112
+ piiWire,
42113
+ piiWireBroken
42028
42114
  };
42029
42115
  }
42030
42116
  function applyPatch(state, action) {
@@ -42037,14 +42123,19 @@ var __publicField = (obj, key, value) => {
42037
42123
  flags.set(flag.key, flag);
42038
42124
  }
42039
42125
  const nextPii = resolvePiiWireForDelta(state.piiWire, action);
42126
+ const priorHadPiiAttrs = (state.piiWire?.piiAttributes.length ?? 0) > 0 || state.piiWireBroken;
42127
+ const computedBroken = isPiiWireBroken(action, nextPii, priorHadPiiAttrs);
42128
+ const recoveredWithRequiredAttrs = !!nextPii && nextPii.piiAttributes.length > 0;
42129
+ const piiWireBroken = isExplicitPiiRequirementCleared(action) ? false : recoveredWithRequiredAttrs ? false : computedBroken || state.piiWireBroken;
42040
42130
  return {
42041
42131
  version: action.toVersion,
42042
42132
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
42043
42133
  flags,
42044
42134
  segments: applySegmentUpserts(state.segments, action.segments),
42045
- ready: true,
42046
- needsFullConfig: false,
42047
- piiWire: nextPii
42135
+ ready: !piiWireBroken,
42136
+ needsFullConfig: piiWireBroken,
42137
+ piiWire: nextPii,
42138
+ piiWireBroken
42048
42139
  };
42049
42140
  }
42050
42141
  function reduceRules(state, action, now = Date.now()) {
@@ -42064,8 +42155,8 @@ var __publicField = (obj, key, value) => {
42064
42155
  ...cloneState(state),
42065
42156
  version: state.version,
42066
42157
  expiresAt: action.expiresAt,
42067
- ready: true,
42068
- needsFullConfig: state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
42158
+ ready: !state.piiWireBroken,
42159
+ needsFullConfig: state.piiWireBroken || state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
42069
42160
  }
42070
42161
  };
42071
42162
  }
@@ -42121,8 +42212,8 @@ var __publicField = (obj, key, value) => {
42121
42212
  ...current,
42122
42213
  version: action.toVersion,
42123
42214
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : current.expiresAt,
42124
- ready: true,
42125
- needsFullConfig: false
42215
+ ready: !current.piiWireBroken,
42216
+ needsFullConfig: current.piiWireBroken
42126
42217
  }
42127
42218
  };
42128
42219
  }
@@ -42145,9 +42236,12 @@ var __publicField = (obj, key, value) => {
42145
42236
  return this.state;
42146
42237
  }
42147
42238
  /**
42148
- * Fail-closed readiness: lease not expired and store marked ready.
42239
+ * Fail-closed readiness: lease not expired, store marked ready, and PII
42240
+ * wire metadata usable when hashing is required.
42149
42241
  */
42150
42242
  isReady(now = Date.now()) {
42243
+ if (this.state.piiWireBroken)
42244
+ return false;
42151
42245
  if (!this.state.ready)
42152
42246
  return false;
42153
42247
  if (!this.state.expiresAt)
@@ -42231,7 +42325,9 @@ var __publicField = (obj, key, value) => {
42231
42325
  piiAttributes: [...pii.piiAttributes],
42232
42326
  piiSalt: pii.piiSalt,
42233
42327
  piiValueEncoding: pii.piiValueEncoding
42234
- } : {}
42328
+ } : {},
42329
+ // Persist fail-closed even when piiWire is null (unusable salt/encoding).
42330
+ ...this.state.piiWireBroken ? { piiWireBroken: true } : {}
42235
42331
  };
42236
42332
  }
42237
42333
  hydrateFromSnapshot(snapshot, now = Date.now()) {
@@ -42241,14 +42337,17 @@ var __publicField = (obj, key, value) => {
42241
42337
  flags.set(flag.key, flag);
42242
42338
  }
42243
42339
  const expired = isConfigPayloadExpired(snapshot.expiresAt, now);
42340
+ const piiWire = piiWireOptionsFromPayload(snapshot);
42341
+ const piiWireBroken = snapshot.piiWireBroken === true || isPiiWireBroken(snapshot, piiWire, false);
42244
42342
  this.state = {
42245
42343
  version: snapshot.version,
42246
42344
  expiresAt: snapshot.expiresAt,
42247
42345
  flags,
42248
42346
  segments: segmentsFromRecord(snapshot.segments),
42249
- ready: !expired && flags.size > 0,
42250
- needsFullConfig: expired || flags.size === 0,
42251
- piiWire: piiWireOptionsFromPayload(snapshot)
42347
+ ready: !expired && flags.size > 0 && !piiWireBroken,
42348
+ needsFullConfig: expired || flags.size === 0 || piiWireBroken,
42349
+ piiWire,
42350
+ piiWireBroken
42252
42351
  };
42253
42352
  }
42254
42353
  }
@@ -43219,9 +43318,10 @@ var __publicField = (obj, key, value) => {
43219
43318
  }
43220
43319
  let hydratedRules = false;
43221
43320
  if (this.configSync && this.rulesStore && this.enableOfflineCache) {
43321
+ clearCachedFlags(this.apiKey);
43222
43322
  hydratedRules = await this.hydrateRulesFromCache();
43223
43323
  }
43224
- if (this.enableOfflineCache && !hydratedRules) {
43324
+ if (this.enableOfflineCache && !this.configSync && !hydratedRules) {
43225
43325
  const cached = await Promise.resolve(
43226
43326
  this.cacheAdapter.loadFlags(this.apiKey, this.cacheTTL)
43227
43327
  );
@@ -43463,7 +43563,7 @@ var __publicField = (obj, key, value) => {
43463
43563
  */
43464
43564
  updateFlags(newFlags, fromShare = false) {
43465
43565
  this.flags = newFlags;
43466
- if (this.enableOfflineCache) {
43566
+ if (this.enableOfflineCache && !this.configSync) {
43467
43567
  void Promise.resolve(
43468
43568
  this.cacheAdapter.saveFlags(this.apiKey, newFlags)
43469
43569
  );