flagmint-js-sdk 2.2.1 → 2.2.2

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.
@@ -39091,7 +39091,7 @@ class SseTransport {
39091
39091
  this.connectPromise = new Promise((resolve, reject) => {
39092
39092
  this.connectResolve = resolve;
39093
39093
  this.connectReject = reject;
39094
- const sdkVersion = "2.2.1";
39094
+ const sdkVersion = "2.2.2";
39095
39095
  const platform2 = typeof window !== "undefined" ? "browser" : "nodejs";
39096
39096
  const wrapperName = this.configOptions?.wrapper?.name || "native-js";
39097
39097
  const wrapperVersion = this.configOptions?.wrapper?.version || "none";
@@ -41777,7 +41777,7 @@ function expand2(hash3, prk, info, length = 32) {
41777
41777
  const hkdf = (hash3, ikm, salt, info, length) => expand2(hash3, extract(hash3, ikm, salt), info, length);
41778
41778
  const ASL_HKDF_INFO = "flagmint-asl-config-sync-mac-v1";
41779
41779
  const ASL_KEY_AGREEMENT = "x25519-hkdf-sha256";
41780
- const textEncoder$1 = new TextEncoder();
41780
+ const textEncoder$2 = new TextEncoder();
41781
41781
  function generateAslClientKeyPair() {
41782
41782
  const privateKey = x25519.utils.randomSecretKey();
41783
41783
  const publicKey = x25519.getPublicKey(privateKey);
@@ -41808,7 +41808,7 @@ function deriveAslMacKey(input) {
41808
41808
  sha256,
41809
41809
  shared,
41810
41810
  salt,
41811
- textEncoder$1.encode(ASL_HKDF_INFO),
41811
+ textEncoder$2.encode(ASL_HKDF_INFO),
41812
41812
  32
41813
41813
  );
41814
41814
  return { configMacKey };
@@ -41818,7 +41818,7 @@ function wipeKeyMaterial(key2) {
41818
41818
  return;
41819
41819
  key2.fill(0);
41820
41820
  }
41821
- const textEncoder = new TextEncoder();
41821
+ const textEncoder$1 = new TextEncoder();
41822
41822
  function canonicalizeForSigning(value) {
41823
41823
  return JSON.stringify(sortValue(value));
41824
41824
  }
@@ -41835,12 +41835,12 @@ function sortValue(value) {
41835
41835
  return sorted;
41836
41836
  }
41837
41837
  function normalizeMacKey(secret2) {
41838
- return typeof secret2 === "string" ? textEncoder.encode(secret2) : secret2;
41838
+ return typeof secret2 === "string" ? textEncoder$1.encode(secret2) : secret2;
41839
41839
  }
41840
41840
  function signConfigPayload(body, secret2) {
41841
41841
  const { signature: _ignored, ...unsigned } = body;
41842
41842
  const canonical = canonicalizeForSigning(unsigned);
41843
- const mac = hmac2(sha256, normalizeMacKey(secret2), textEncoder.encode(canonical));
41843
+ const mac = hmac2(sha256, normalizeMacKey(secret2), textEncoder$1.encode(canonical));
41844
41844
  return bytesToHex$1(mac);
41845
41845
  }
41846
41846
  function timingSafeEqualBytes(a, b) {
@@ -41862,13 +41862,107 @@ function verifyConfigPayloadSignature(body, secret2) {
41862
41862
  } catch {
41863
41863
  return false;
41864
41864
  }
41865
- const a = textEncoder.encode(body.signature);
41866
- const b = textEncoder.encode(expected);
41865
+ const a = textEncoder$1.encode(body.signature);
41866
+ const b = textEncoder$1.encode(expected);
41867
41867
  return timingSafeEqualBytes(a, b);
41868
41868
  }
41869
41869
  function isConfigPayloadExpired(expiresAt, now = Date.now()) {
41870
41870
  return !Number.isFinite(expiresAt) || now >= expiresAt;
41871
41871
  }
41872
+ const PII_VALUE_ENCODING = "hmac-v1";
41873
+ const textEncoder = new TextEncoder();
41874
+ function normalizePiiString(value) {
41875
+ return value.trim().toLowerCase();
41876
+ }
41877
+ function hashPiiValue(value, salt) {
41878
+ const mac = hmac2(
41879
+ sha256,
41880
+ textEncoder.encode(salt),
41881
+ textEncoder.encode(normalizePiiString(value))
41882
+ );
41883
+ return bytesToHex$1(mac);
41884
+ }
41885
+ function isPiiAttribute(attribute, piiAttributes) {
41886
+ if (!attribute || piiAttributes.length === 0)
41887
+ return false;
41888
+ const attr = attribute.trim();
41889
+ if (!attr)
41890
+ return false;
41891
+ for (const key2 of piiAttributes) {
41892
+ if (!key2)
41893
+ continue;
41894
+ if (attr === key2)
41895
+ return true;
41896
+ if (attr.endsWith(`.${key2}`))
41897
+ return true;
41898
+ }
41899
+ return false;
41900
+ }
41901
+ function payloadHasExplicitPiiFields(payload) {
41902
+ if (!payload)
41903
+ return false;
41904
+ return payload.piiSalt !== void 0 || payload.piiAttributes !== void 0 || payload.piiValueEncoding !== void 0;
41905
+ }
41906
+ function piiWireOptionsFromPayload(payload) {
41907
+ if (!payload)
41908
+ return null;
41909
+ const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
41910
+ if (!salt)
41911
+ 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") : [];
41916
+ return {
41917
+ piiAttributes: attrs,
41918
+ piiSalt: salt,
41919
+ piiValueEncoding: PII_VALUE_ENCODING
41920
+ };
41921
+ }
41922
+ function resolvePiiWireForDelta(previous, action) {
41923
+ if (!payloadHasExplicitPiiFields(action)) {
41924
+ return previous;
41925
+ }
41926
+ const merged = {
41927
+ piiSalt: action.piiSalt !== void 0 ? action.piiSalt : previous?.piiSalt,
41928
+ piiAttributes: action.piiAttributes !== void 0 ? action.piiAttributes : previous?.piiAttributes,
41929
+ piiValueEncoding: action.piiValueEncoding !== void 0 ? action.piiValueEncoding : previous?.piiValueEncoding
41930
+ };
41931
+ return piiWireOptionsFromPayload(merged);
41932
+ }
41933
+ function applyPiiHashToFlatContext(context, options, mode) {
41934
+ if (!options || !options.piiSalt || !options.piiAttributes.length || options.piiValueEncoding !== PII_VALUE_ENCODING) {
41935
+ return context;
41936
+ }
41937
+ if (mode?.alreadyHashed) {
41938
+ return context;
41939
+ }
41940
+ let changed = false;
41941
+ const out = { ...context };
41942
+ for (const [key2, value] of Object.entries(context)) {
41943
+ if (!isPiiAttribute(key2, options.piiAttributes))
41944
+ continue;
41945
+ if (typeof value === "string") {
41946
+ out[key2] = hashPiiValue(value, options.piiSalt);
41947
+ changed = true;
41948
+ continue;
41949
+ }
41950
+ if (Array.isArray(value)) {
41951
+ let arrayChanged = false;
41952
+ const next = value.map((entry) => {
41953
+ if (typeof entry !== "string")
41954
+ return entry;
41955
+ arrayChanged = true;
41956
+ return hashPiiValue(entry, options.piiSalt);
41957
+ });
41958
+ if (arrayChanged) {
41959
+ out[key2] = next;
41960
+ changed = true;
41961
+ }
41962
+ }
41963
+ }
41964
+ return changed ? out : context;
41965
+ }
41872
41966
  function createEmptyRulesState() {
41873
41967
  return {
41874
41968
  version: 0,
@@ -41876,7 +41970,8 @@ function createEmptyRulesState() {
41876
41970
  flags: /* @__PURE__ */ new Map(),
41877
41971
  segments: /* @__PURE__ */ new Map(),
41878
41972
  ready: false,
41879
- needsFullConfig: true
41973
+ needsFullConfig: true,
41974
+ piiWire: null
41880
41975
  };
41881
41976
  }
41882
41977
  function cloneState(state) {
@@ -41886,7 +41981,12 @@ function cloneState(state) {
41886
41981
  flags: new Map(state.flags),
41887
41982
  segments: new Map(state.segments),
41888
41983
  ready: state.ready,
41889
- needsFullConfig: state.needsFullConfig
41984
+ needsFullConfig: state.needsFullConfig,
41985
+ piiWire: state.piiWire ? {
41986
+ piiAttributes: [...state.piiWire.piiAttributes],
41987
+ piiSalt: state.piiWire.piiSalt,
41988
+ piiValueEncoding: state.piiWire.piiValueEncoding
41989
+ } : null
41890
41990
  };
41891
41991
  }
41892
41992
  function segmentsFromRecord(record) {
@@ -41919,7 +42019,8 @@ function hydrateFromFull(action) {
41919
42019
  flags,
41920
42020
  segments: segmentsFromRecord(action.segments),
41921
42021
  ready: true,
41922
- needsFullConfig: false
42022
+ needsFullConfig: false,
42023
+ piiWire: piiWireOptionsFromPayload(action)
41923
42024
  };
41924
42025
  }
41925
42026
  function applyPatch(state, action) {
@@ -41931,13 +42032,15 @@ function applyPatch(state, action) {
41931
42032
  if (flag?.key)
41932
42033
  flags.set(flag.key, flag);
41933
42034
  }
42035
+ const nextPii = resolvePiiWireForDelta(state.piiWire, action);
41934
42036
  return {
41935
42037
  version: action.toVersion,
41936
42038
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
41937
42039
  flags,
41938
42040
  segments: applySegmentUpserts(state.segments, action.segments),
41939
42041
  ready: true,
41940
- needsFullConfig: false
42042
+ needsFullConfig: false,
42043
+ piiWire: nextPii
41941
42044
  };
41942
42045
  }
41943
42046
  function reduceRules(state, action, now = Date.now()) {
@@ -42114,11 +42217,17 @@ class RulesStore {
42114
42217
  this.state.segments.forEach((segment, id) => {
42115
42218
  segments[id] = segment;
42116
42219
  });
42220
+ const pii = this.state.piiWire;
42117
42221
  return {
42118
42222
  version: this.state.version,
42119
42223
  expiresAt: this.state.expiresAt,
42120
42224
  flags: Array.from(this.state.flags.values()),
42121
- segments
42225
+ segments,
42226
+ ...pii ? {
42227
+ piiAttributes: [...pii.piiAttributes],
42228
+ piiSalt: pii.piiSalt,
42229
+ piiValueEncoding: pii.piiValueEncoding
42230
+ } : {}
42122
42231
  };
42123
42232
  }
42124
42233
  hydrateFromSnapshot(snapshot, now = Date.now()) {
@@ -42134,7 +42243,8 @@ class RulesStore {
42134
42243
  flags,
42135
42244
  segments: segmentsFromRecord(snapshot.segments),
42136
42245
  ready: !expired && flags.size > 0,
42137
- needsFullConfig: expired || flags.size === 0
42246
+ needsFullConfig: expired || flags.size === 0,
42247
+ piiWire: piiWireOptionsFromPayload(snapshot)
42138
42248
  };
42139
42249
  }
42140
42250
  }
@@ -42336,7 +42446,7 @@ function flattenEvaluationContext(context) {
42336
42446
  function toComparableString(value) {
42337
42447
  if (value === null || value === void 0)
42338
42448
  return "";
42339
- return String(value);
42449
+ return String(value).trim().toLowerCase();
42340
42450
  }
42341
42451
  function toComparableArray(value) {
42342
42452
  if (Array.isArray(value)) {
@@ -42898,8 +43008,13 @@ function prepareContextForEvaluator(context) {
42898
43008
  }
42899
43009
  return context;
42900
43010
  }
42901
- function evaluateSdkFlag(flag, context, segments) {
42902
- const evalContext = prepareContextForEvaluator(context);
43011
+ function prepareContextForLocalEval(context, pii) {
43012
+ const prepared = prepareContextForEvaluator(context);
43013
+ const flat = flattenEvaluationContext(prepared);
43014
+ return applyPiiHashToFlatContext(flat, pii);
43015
+ }
43016
+ function evaluateSdkFlag(flag, context, segments, pii) {
43017
+ const evalContext = prepareContextForLocalEval(context, pii);
42903
43018
  const variationsById = {};
42904
43019
  for (const v of flag.variations || []) {
42905
43020
  if (v?.id)
@@ -42959,7 +43074,7 @@ function evaluateSdkFlag(flag, context, segments) {
42959
43074
  }
42960
43075
  function evaluateAllSdkFlags(flags, context, segments, options) {
42961
43076
  const list = flags instanceof Map ? Array.from(flags.values()) : flags;
42962
- const prepared = prepareContextForEvaluator(context);
43077
+ const prepared = prepareContextForLocalEval(context, options?.pii);
42963
43078
  const out = {};
42964
43079
  for (const flag of list) {
42965
43080
  if (!flag?.key)
@@ -42968,7 +43083,7 @@ function evaluateAllSdkFlags(flags, context, segments, options) {
42968
43083
  out[flag.key] = coerceType(flag.default_value, flag.type);
42969
43084
  continue;
42970
43085
  }
42971
- out[flag.key] = evaluateSdkFlag(flag, prepared, segments);
43086
+ out[flag.key] = evaluateSdkFlag(flag, prepared, segments, null);
42972
43087
  }
42973
43088
  return out;
42974
43089
  }
@@ -43413,7 +43528,8 @@ class FlagClient {
43413
43528
  value = evaluateSdkFlag(
43414
43529
  flag,
43415
43530
  this.context,
43416
- this.rulesStore.getState().segments
43531
+ this.rulesStore.getState().segments,
43532
+ this.rulesStore.getState().piiWire
43417
43533
  ) ?? fallback;
43418
43534
  }
43419
43535
  } else {
@@ -43893,7 +44009,7 @@ class FlagClient {
43893
44009
  state.flags,
43894
44010
  context ?? this.context,
43895
44011
  state.segments,
43896
- { failClosedDefaultsOnly: failClosed }
44012
+ { failClosedDefaultsOnly: failClosed, pii: state.piiWire }
43897
44013
  );
43898
44014
  }
43899
44015
  analyticsMapFromRulesStore() {
@@ -44023,6 +44139,7 @@ export {
44023
44139
  LongPollingTransport,
44024
44140
  RulesStore,
44025
44141
  SseTransport,
44142
+ applyPiiHashToFlatContext,
44026
44143
  applyRolloutStrategy,
44027
44144
  cacheHelper_async as asyncCache,
44028
44145
  canonicalizeForSigning,
@@ -44040,11 +44157,13 @@ export {
44040
44157
  flattenEvaluationContext,
44041
44158
  generateAslClientKeyPair,
44042
44159
  hashPercent,
44160
+ hashPiiValue,
44043
44161
  hashToPercentage,
44044
44162
  isConfigPayloadExpired,
44045
44163
  isInSegment,
44046
44164
  performAslHandshake,
44047
44165
  pickVariant,
44166
+ prepareContextForLocalEval,
44048
44167
  reduceRules,
44049
44168
  signConfigPayload,
44050
44169
  stringHash,