flagmint-js-sdk 2.2.1 → 2.2.2-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.
@@ -39093,7 +39093,7 @@ class SseTransport {
39093
39093
  this.connectPromise = new Promise((resolve, reject) => {
39094
39094
  this.connectResolve = resolve;
39095
39095
  this.connectReject = reject;
39096
- const sdkVersion = "2.2.1";
39096
+ const sdkVersion = "2.2.2-rc.0";
39097
39097
  const platform2 = typeof window !== "undefined" ? "browser" : "nodejs";
39098
39098
  const wrapperName = this.configOptions?.wrapper?.name || "native-js";
39099
39099
  const wrapperVersion = this.configOptions?.wrapper?.version || "none";
@@ -41779,7 +41779,7 @@ function expand2(hash3, prk, info, length = 32) {
41779
41779
  const hkdf = (hash3, ikm, salt, info, length) => expand2(hash3, extract(hash3, ikm, salt), info, length);
41780
41780
  const ASL_HKDF_INFO = "flagmint-asl-config-sync-mac-v1";
41781
41781
  const ASL_KEY_AGREEMENT = "x25519-hkdf-sha256";
41782
- const textEncoder$1 = new TextEncoder();
41782
+ const textEncoder$2 = new TextEncoder();
41783
41783
  function generateAslClientKeyPair() {
41784
41784
  const privateKey = x25519.utils.randomSecretKey();
41785
41785
  const publicKey = x25519.getPublicKey(privateKey);
@@ -41810,7 +41810,7 @@ function deriveAslMacKey(input) {
41810
41810
  sha256,
41811
41811
  shared,
41812
41812
  salt,
41813
- textEncoder$1.encode(ASL_HKDF_INFO),
41813
+ textEncoder$2.encode(ASL_HKDF_INFO),
41814
41814
  32
41815
41815
  );
41816
41816
  return { configMacKey };
@@ -41820,7 +41820,7 @@ function wipeKeyMaterial(key2) {
41820
41820
  return;
41821
41821
  key2.fill(0);
41822
41822
  }
41823
- const textEncoder = new TextEncoder();
41823
+ const textEncoder$1 = new TextEncoder();
41824
41824
  function canonicalizeForSigning(value) {
41825
41825
  return JSON.stringify(sortValue(value));
41826
41826
  }
@@ -41837,12 +41837,12 @@ function sortValue(value) {
41837
41837
  return sorted;
41838
41838
  }
41839
41839
  function normalizeMacKey(secret2) {
41840
- return typeof secret2 === "string" ? textEncoder.encode(secret2) : secret2;
41840
+ return typeof secret2 === "string" ? textEncoder$1.encode(secret2) : secret2;
41841
41841
  }
41842
41842
  function signConfigPayload(body, secret2) {
41843
41843
  const { signature: _ignored, ...unsigned } = body;
41844
41844
  const canonical = canonicalizeForSigning(unsigned);
41845
- const mac = hmac2(sha256, normalizeMacKey(secret2), textEncoder.encode(canonical));
41845
+ const mac = hmac2(sha256, normalizeMacKey(secret2), textEncoder$1.encode(canonical));
41846
41846
  return bytesToHex$1(mac);
41847
41847
  }
41848
41848
  function timingSafeEqualBytes(a, b) {
@@ -41864,13 +41864,93 @@ function verifyConfigPayloadSignature(body, secret2) {
41864
41864
  } catch {
41865
41865
  return false;
41866
41866
  }
41867
- const a = textEncoder.encode(body.signature);
41868
- const b = textEncoder.encode(expected);
41867
+ const a = textEncoder$1.encode(body.signature);
41868
+ const b = textEncoder$1.encode(expected);
41869
41869
  return timingSafeEqualBytes(a, b);
41870
41870
  }
41871
41871
  function isConfigPayloadExpired(expiresAt, now = Date.now()) {
41872
41872
  return !Number.isFinite(expiresAt) || now >= expiresAt;
41873
41873
  }
41874
+ const PII_VALUE_ENCODING = "hmac-v1";
41875
+ const textEncoder = new TextEncoder();
41876
+ const HASH_HEX_PATTERN = /^[0-9a-f]{64}$/;
41877
+ function normalizePiiString(value) {
41878
+ return value.trim().toLowerCase();
41879
+ }
41880
+ function hashPiiValue(value, salt) {
41881
+ const mac = hmac2(
41882
+ sha256,
41883
+ textEncoder.encode(salt),
41884
+ textEncoder.encode(normalizePiiString(value))
41885
+ );
41886
+ return bytesToHex$1(mac);
41887
+ }
41888
+ function isPiiAttribute(attribute, piiAttributes) {
41889
+ if (!attribute || piiAttributes.length === 0)
41890
+ return false;
41891
+ const attr = attribute.trim();
41892
+ if (!attr)
41893
+ return false;
41894
+ for (const key2 of piiAttributes) {
41895
+ if (!key2)
41896
+ continue;
41897
+ if (attr === key2)
41898
+ return true;
41899
+ if (attr.endsWith(`.${key2}`))
41900
+ return true;
41901
+ }
41902
+ return false;
41903
+ }
41904
+ function piiWireOptionsFromPayload(payload) {
41905
+ if (!payload)
41906
+ return null;
41907
+ const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
41908
+ if (!salt)
41909
+ return null;
41910
+ const encoding = typeof payload.piiValueEncoding === "string" ? payload.piiValueEncoding : PII_VALUE_ENCODING;
41911
+ if (encoding !== PII_VALUE_ENCODING)
41912
+ return null;
41913
+ const attrs = Array.isArray(payload.piiAttributes) ? payload.piiAttributes.filter((k) => typeof k === "string") : [];
41914
+ return {
41915
+ piiAttributes: attrs,
41916
+ piiSalt: salt,
41917
+ piiValueEncoding: PII_VALUE_ENCODING
41918
+ };
41919
+ }
41920
+ function applyPiiHashToFlatContext(context, options) {
41921
+ if (!options || !options.piiSalt || !options.piiAttributes.length || options.piiValueEncoding !== PII_VALUE_ENCODING) {
41922
+ return context;
41923
+ }
41924
+ let changed = false;
41925
+ const out = { ...context };
41926
+ for (const [key2, value] of Object.entries(context)) {
41927
+ if (!isPiiAttribute(key2, options.piiAttributes))
41928
+ continue;
41929
+ if (typeof value === "string") {
41930
+ if (HASH_HEX_PATTERN.test(value))
41931
+ continue;
41932
+ out[key2] = hashPiiValue(value, options.piiSalt);
41933
+ changed = true;
41934
+ continue;
41935
+ }
41936
+ if (Array.isArray(value)) {
41937
+ let arrayChanged = false;
41938
+ const next = value.map((entry) => {
41939
+ if (typeof entry !== "string")
41940
+ return entry;
41941
+ if (HASH_HEX_PATTERN.test(entry))
41942
+ return entry;
41943
+ arrayChanged = true;
41944
+ return hashPiiValue(entry, options.piiSalt);
41945
+ });
41946
+ if (arrayChanged) {
41947
+ out[key2] = next;
41948
+ changed = true;
41949
+ }
41950
+ }
41951
+ }
41952
+ return changed ? out : context;
41953
+ }
41874
41954
  function createEmptyRulesState() {
41875
41955
  return {
41876
41956
  version: 0,
@@ -41878,7 +41958,8 @@ function createEmptyRulesState() {
41878
41958
  flags: /* @__PURE__ */ new Map(),
41879
41959
  segments: /* @__PURE__ */ new Map(),
41880
41960
  ready: false,
41881
- needsFullConfig: true
41961
+ needsFullConfig: true,
41962
+ piiWire: null
41882
41963
  };
41883
41964
  }
41884
41965
  function cloneState(state) {
@@ -41888,7 +41969,12 @@ function cloneState(state) {
41888
41969
  flags: new Map(state.flags),
41889
41970
  segments: new Map(state.segments),
41890
41971
  ready: state.ready,
41891
- needsFullConfig: state.needsFullConfig
41972
+ needsFullConfig: state.needsFullConfig,
41973
+ piiWire: state.piiWire ? {
41974
+ piiAttributes: [...state.piiWire.piiAttributes],
41975
+ piiSalt: state.piiWire.piiSalt,
41976
+ piiValueEncoding: state.piiWire.piiValueEncoding
41977
+ } : null
41892
41978
  };
41893
41979
  }
41894
41980
  function segmentsFromRecord(record) {
@@ -41921,7 +42007,8 @@ function hydrateFromFull(action) {
41921
42007
  flags,
41922
42008
  segments: segmentsFromRecord(action.segments),
41923
42009
  ready: true,
41924
- needsFullConfig: false
42010
+ needsFullConfig: false,
42011
+ piiWire: piiWireOptionsFromPayload(action)
41925
42012
  };
41926
42013
  }
41927
42014
  function applyPatch(state, action) {
@@ -41933,13 +42020,15 @@ function applyPatch(state, action) {
41933
42020
  if (flag?.key)
41934
42021
  flags.set(flag.key, flag);
41935
42022
  }
42023
+ const nextPii = piiWireOptionsFromPayload(action) ?? state.piiWire;
41936
42024
  return {
41937
42025
  version: action.toVersion,
41938
42026
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
41939
42027
  flags,
41940
42028
  segments: applySegmentUpserts(state.segments, action.segments),
41941
42029
  ready: true,
41942
- needsFullConfig: false
42030
+ needsFullConfig: false,
42031
+ piiWire: nextPii
41943
42032
  };
41944
42033
  }
41945
42034
  function reduceRules(state, action, now = Date.now()) {
@@ -42116,11 +42205,17 @@ class RulesStore {
42116
42205
  this.state.segments.forEach((segment, id) => {
42117
42206
  segments[id] = segment;
42118
42207
  });
42208
+ const pii = this.state.piiWire;
42119
42209
  return {
42120
42210
  version: this.state.version,
42121
42211
  expiresAt: this.state.expiresAt,
42122
42212
  flags: Array.from(this.state.flags.values()),
42123
- segments
42213
+ segments,
42214
+ ...pii ? {
42215
+ piiAttributes: [...pii.piiAttributes],
42216
+ piiSalt: pii.piiSalt,
42217
+ piiValueEncoding: pii.piiValueEncoding
42218
+ } : {}
42124
42219
  };
42125
42220
  }
42126
42221
  hydrateFromSnapshot(snapshot, now = Date.now()) {
@@ -42136,7 +42231,8 @@ class RulesStore {
42136
42231
  flags,
42137
42232
  segments: segmentsFromRecord(snapshot.segments),
42138
42233
  ready: !expired && flags.size > 0,
42139
- needsFullConfig: expired || flags.size === 0
42234
+ needsFullConfig: expired || flags.size === 0,
42235
+ piiWire: piiWireOptionsFromPayload(snapshot)
42140
42236
  };
42141
42237
  }
42142
42238
  }
@@ -42900,8 +42996,13 @@ function prepareContextForEvaluator(context) {
42900
42996
  }
42901
42997
  return context;
42902
42998
  }
42903
- function evaluateSdkFlag(flag, context, segments) {
42904
- const evalContext = prepareContextForEvaluator(context);
42999
+ function prepareContextForLocalEval(context, pii) {
43000
+ const prepared = prepareContextForEvaluator(context);
43001
+ const flat = flattenEvaluationContext(prepared);
43002
+ return applyPiiHashToFlatContext(flat, pii);
43003
+ }
43004
+ function evaluateSdkFlag(flag, context, segments, pii) {
43005
+ const evalContext = prepareContextForLocalEval(context, pii);
42905
43006
  const variationsById = {};
42906
43007
  for (const v of flag.variations || []) {
42907
43008
  if (v?.id)
@@ -42961,7 +43062,7 @@ function evaluateSdkFlag(flag, context, segments) {
42961
43062
  }
42962
43063
  function evaluateAllSdkFlags(flags, context, segments, options) {
42963
43064
  const list = flags instanceof Map ? Array.from(flags.values()) : flags;
42964
- const prepared = prepareContextForEvaluator(context);
43065
+ const prepared = prepareContextForLocalEval(context, options?.pii);
42965
43066
  const out = {};
42966
43067
  for (const flag of list) {
42967
43068
  if (!flag?.key)
@@ -42970,7 +43071,7 @@ function evaluateAllSdkFlags(flags, context, segments, options) {
42970
43071
  out[flag.key] = coerceType(flag.default_value, flag.type);
42971
43072
  continue;
42972
43073
  }
42973
- out[flag.key] = evaluateSdkFlag(flag, prepared, segments);
43074
+ out[flag.key] = evaluateSdkFlag(flag, prepared, segments, null);
42974
43075
  }
42975
43076
  return out;
42976
43077
  }
@@ -43415,7 +43516,8 @@ class FlagClient {
43415
43516
  value = evaluateSdkFlag(
43416
43517
  flag,
43417
43518
  this.context,
43418
- this.rulesStore.getState().segments
43519
+ this.rulesStore.getState().segments,
43520
+ this.rulesStore.getState().piiWire
43419
43521
  ) ?? fallback;
43420
43522
  }
43421
43523
  } else {
@@ -43895,7 +43997,7 @@ class FlagClient {
43895
43997
  state.flags,
43896
43998
  context ?? this.context,
43897
43999
  state.segments,
43898
- { failClosedDefaultsOnly: failClosed }
44000
+ { failClosedDefaultsOnly: failClosed, pii: state.piiWire }
43899
44001
  );
43900
44002
  }
43901
44003
  analyticsMapFromRulesStore() {
@@ -44024,6 +44126,7 @@ exports.FlagClient = FlagClient;
44024
44126
  exports.LongPollingTransport = LongPollingTransport;
44025
44127
  exports.RulesStore = RulesStore;
44026
44128
  exports.SseTransport = SseTransport;
44129
+ exports.applyPiiHashToFlatContext = applyPiiHashToFlatContext;
44027
44130
  exports.applyRolloutStrategy = applyRolloutStrategy;
44028
44131
  exports.asyncCache = cacheHelper_async;
44029
44132
  exports.canonicalizeForSigning = canonicalizeForSigning;
@@ -44041,11 +44144,13 @@ exports.evaluateSdkFlag = evaluateSdkFlag;
44041
44144
  exports.flattenEvaluationContext = flattenEvaluationContext;
44042
44145
  exports.generateAslClientKeyPair = generateAslClientKeyPair;
44043
44146
  exports.hashPercent = hashPercent;
44147
+ exports.hashPiiValue = hashPiiValue;
44044
44148
  exports.hashToPercentage = hashToPercentage;
44045
44149
  exports.isConfigPayloadExpired = isConfigPayloadExpired;
44046
44150
  exports.isInSegment = isInSegment;
44047
44151
  exports.performAslHandshake = performAslHandshake;
44048
44152
  exports.pickVariant = pickVariant;
44153
+ exports.prepareContextForLocalEval = prepareContextForLocalEval;
44049
44154
  exports.reduceRules = reduceRules;
44050
44155
  exports.signConfigPayload = signConfigPayload;
44051
44156
  exports.stringHash = stringHash;