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.
package/README.md CHANGED
@@ -162,7 +162,7 @@ transportMode: 'long-polling' // POST evaluate on an interval
162
162
 
163
163
  # Connection sharing
164
164
 
165
- Used when the same app is embedded twice on one origin (for example two WeSeeDo iframes in a host page).
165
+ Used when the same app is embedded twice on one origin (for example two iframes in a host page).
166
166
 
167
167
  - **Leader** performs handshake and holds EventSource.
168
168
  - **Followers** receive flag snapshots over `BroadcastChannel` (`flagmint-share:<apiKey>`). Leadership is a `localStorage` lock (`flagmint_share_lock:<apiKey>`).
@@ -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";
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,107 @@ 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
+ function normalizePiiString(value) {
41877
+ return value.trim().toLowerCase();
41878
+ }
41879
+ function hashPiiValue(value, salt) {
41880
+ const mac = hmac2(
41881
+ sha256,
41882
+ textEncoder.encode(salt),
41883
+ textEncoder.encode(normalizePiiString(value))
41884
+ );
41885
+ return bytesToHex$1(mac);
41886
+ }
41887
+ function isPiiAttribute(attribute, piiAttributes) {
41888
+ if (!attribute || piiAttributes.length === 0)
41889
+ return false;
41890
+ const attr = attribute.trim();
41891
+ if (!attr)
41892
+ return false;
41893
+ for (const key2 of piiAttributes) {
41894
+ if (!key2)
41895
+ continue;
41896
+ if (attr === key2)
41897
+ return true;
41898
+ if (attr.endsWith(`.${key2}`))
41899
+ return true;
41900
+ }
41901
+ return false;
41902
+ }
41903
+ function payloadHasExplicitPiiFields(payload) {
41904
+ if (!payload)
41905
+ return false;
41906
+ return payload.piiSalt !== void 0 || payload.piiAttributes !== void 0 || payload.piiValueEncoding !== void 0;
41907
+ }
41908
+ function piiWireOptionsFromPayload(payload) {
41909
+ if (!payload)
41910
+ return null;
41911
+ const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
41912
+ if (!salt)
41913
+ return null;
41914
+ const encoding = typeof payload.piiValueEncoding === "string" ? payload.piiValueEncoding : PII_VALUE_ENCODING;
41915
+ if (encoding !== PII_VALUE_ENCODING)
41916
+ return null;
41917
+ const attrs = Array.isArray(payload.piiAttributes) ? payload.piiAttributes.filter((k) => typeof k === "string") : [];
41918
+ return {
41919
+ piiAttributes: attrs,
41920
+ piiSalt: salt,
41921
+ piiValueEncoding: PII_VALUE_ENCODING
41922
+ };
41923
+ }
41924
+ function resolvePiiWireForDelta(previous, action) {
41925
+ if (!payloadHasExplicitPiiFields(action)) {
41926
+ return previous;
41927
+ }
41928
+ const merged = {
41929
+ piiSalt: action.piiSalt !== void 0 ? action.piiSalt : previous?.piiSalt,
41930
+ piiAttributes: action.piiAttributes !== void 0 ? action.piiAttributes : previous?.piiAttributes,
41931
+ piiValueEncoding: action.piiValueEncoding !== void 0 ? action.piiValueEncoding : previous?.piiValueEncoding
41932
+ };
41933
+ return piiWireOptionsFromPayload(merged);
41934
+ }
41935
+ function applyPiiHashToFlatContext(context, options, mode) {
41936
+ if (!options || !options.piiSalt || !options.piiAttributes.length || options.piiValueEncoding !== PII_VALUE_ENCODING) {
41937
+ return context;
41938
+ }
41939
+ if (mode?.alreadyHashed) {
41940
+ return context;
41941
+ }
41942
+ let changed = false;
41943
+ const out = { ...context };
41944
+ for (const [key2, value] of Object.entries(context)) {
41945
+ if (!isPiiAttribute(key2, options.piiAttributes))
41946
+ continue;
41947
+ if (typeof value === "string") {
41948
+ out[key2] = hashPiiValue(value, options.piiSalt);
41949
+ changed = true;
41950
+ continue;
41951
+ }
41952
+ if (Array.isArray(value)) {
41953
+ let arrayChanged = false;
41954
+ const next = value.map((entry) => {
41955
+ if (typeof entry !== "string")
41956
+ return entry;
41957
+ arrayChanged = true;
41958
+ return hashPiiValue(entry, options.piiSalt);
41959
+ });
41960
+ if (arrayChanged) {
41961
+ out[key2] = next;
41962
+ changed = true;
41963
+ }
41964
+ }
41965
+ }
41966
+ return changed ? out : context;
41967
+ }
41874
41968
  function createEmptyRulesState() {
41875
41969
  return {
41876
41970
  version: 0,
@@ -41878,7 +41972,8 @@ function createEmptyRulesState() {
41878
41972
  flags: /* @__PURE__ */ new Map(),
41879
41973
  segments: /* @__PURE__ */ new Map(),
41880
41974
  ready: false,
41881
- needsFullConfig: true
41975
+ needsFullConfig: true,
41976
+ piiWire: null
41882
41977
  };
41883
41978
  }
41884
41979
  function cloneState(state) {
@@ -41888,7 +41983,12 @@ function cloneState(state) {
41888
41983
  flags: new Map(state.flags),
41889
41984
  segments: new Map(state.segments),
41890
41985
  ready: state.ready,
41891
- needsFullConfig: state.needsFullConfig
41986
+ needsFullConfig: state.needsFullConfig,
41987
+ piiWire: state.piiWire ? {
41988
+ piiAttributes: [...state.piiWire.piiAttributes],
41989
+ piiSalt: state.piiWire.piiSalt,
41990
+ piiValueEncoding: state.piiWire.piiValueEncoding
41991
+ } : null
41892
41992
  };
41893
41993
  }
41894
41994
  function segmentsFromRecord(record) {
@@ -41921,7 +42021,8 @@ function hydrateFromFull(action) {
41921
42021
  flags,
41922
42022
  segments: segmentsFromRecord(action.segments),
41923
42023
  ready: true,
41924
- needsFullConfig: false
42024
+ needsFullConfig: false,
42025
+ piiWire: piiWireOptionsFromPayload(action)
41925
42026
  };
41926
42027
  }
41927
42028
  function applyPatch(state, action) {
@@ -41933,13 +42034,15 @@ function applyPatch(state, action) {
41933
42034
  if (flag?.key)
41934
42035
  flags.set(flag.key, flag);
41935
42036
  }
42037
+ const nextPii = resolvePiiWireForDelta(state.piiWire, action);
41936
42038
  return {
41937
42039
  version: action.toVersion,
41938
42040
  expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
41939
42041
  flags,
41940
42042
  segments: applySegmentUpserts(state.segments, action.segments),
41941
42043
  ready: true,
41942
- needsFullConfig: false
42044
+ needsFullConfig: false,
42045
+ piiWire: nextPii
41943
42046
  };
41944
42047
  }
41945
42048
  function reduceRules(state, action, now = Date.now()) {
@@ -42116,11 +42219,17 @@ class RulesStore {
42116
42219
  this.state.segments.forEach((segment, id) => {
42117
42220
  segments[id] = segment;
42118
42221
  });
42222
+ const pii = this.state.piiWire;
42119
42223
  return {
42120
42224
  version: this.state.version,
42121
42225
  expiresAt: this.state.expiresAt,
42122
42226
  flags: Array.from(this.state.flags.values()),
42123
- segments
42227
+ segments,
42228
+ ...pii ? {
42229
+ piiAttributes: [...pii.piiAttributes],
42230
+ piiSalt: pii.piiSalt,
42231
+ piiValueEncoding: pii.piiValueEncoding
42232
+ } : {}
42124
42233
  };
42125
42234
  }
42126
42235
  hydrateFromSnapshot(snapshot, now = Date.now()) {
@@ -42136,7 +42245,8 @@ class RulesStore {
42136
42245
  flags,
42137
42246
  segments: segmentsFromRecord(snapshot.segments),
42138
42247
  ready: !expired && flags.size > 0,
42139
- needsFullConfig: expired || flags.size === 0
42248
+ needsFullConfig: expired || flags.size === 0,
42249
+ piiWire: piiWireOptionsFromPayload(snapshot)
42140
42250
  };
42141
42251
  }
42142
42252
  }
@@ -42338,7 +42448,7 @@ function flattenEvaluationContext(context) {
42338
42448
  function toComparableString(value) {
42339
42449
  if (value === null || value === void 0)
42340
42450
  return "";
42341
- return String(value);
42451
+ return String(value).trim().toLowerCase();
42342
42452
  }
42343
42453
  function toComparableArray(value) {
42344
42454
  if (Array.isArray(value)) {
@@ -42900,8 +43010,13 @@ function prepareContextForEvaluator(context) {
42900
43010
  }
42901
43011
  return context;
42902
43012
  }
42903
- function evaluateSdkFlag(flag, context, segments) {
42904
- const evalContext = prepareContextForEvaluator(context);
43013
+ function prepareContextForLocalEval(context, pii) {
43014
+ const prepared = prepareContextForEvaluator(context);
43015
+ const flat = flattenEvaluationContext(prepared);
43016
+ return applyPiiHashToFlatContext(flat, pii);
43017
+ }
43018
+ function evaluateSdkFlag(flag, context, segments, pii) {
43019
+ const evalContext = prepareContextForLocalEval(context, pii);
42905
43020
  const variationsById = {};
42906
43021
  for (const v of flag.variations || []) {
42907
43022
  if (v?.id)
@@ -42961,7 +43076,7 @@ function evaluateSdkFlag(flag, context, segments) {
42961
43076
  }
42962
43077
  function evaluateAllSdkFlags(flags, context, segments, options) {
42963
43078
  const list = flags instanceof Map ? Array.from(flags.values()) : flags;
42964
- const prepared = prepareContextForEvaluator(context);
43079
+ const prepared = prepareContextForLocalEval(context, options?.pii);
42965
43080
  const out = {};
42966
43081
  for (const flag of list) {
42967
43082
  if (!flag?.key)
@@ -42970,7 +43085,7 @@ function evaluateAllSdkFlags(flags, context, segments, options) {
42970
43085
  out[flag.key] = coerceType(flag.default_value, flag.type);
42971
43086
  continue;
42972
43087
  }
42973
- out[flag.key] = evaluateSdkFlag(flag, prepared, segments);
43088
+ out[flag.key] = evaluateSdkFlag(flag, prepared, segments, null);
42974
43089
  }
42975
43090
  return out;
42976
43091
  }
@@ -43415,7 +43530,8 @@ class FlagClient {
43415
43530
  value = evaluateSdkFlag(
43416
43531
  flag,
43417
43532
  this.context,
43418
- this.rulesStore.getState().segments
43533
+ this.rulesStore.getState().segments,
43534
+ this.rulesStore.getState().piiWire
43419
43535
  ) ?? fallback;
43420
43536
  }
43421
43537
  } else {
@@ -43895,7 +44011,7 @@ class FlagClient {
43895
44011
  state.flags,
43896
44012
  context ?? this.context,
43897
44013
  state.segments,
43898
- { failClosedDefaultsOnly: failClosed }
44014
+ { failClosedDefaultsOnly: failClosed, pii: state.piiWire }
43899
44015
  );
43900
44016
  }
43901
44017
  analyticsMapFromRulesStore() {
@@ -44024,6 +44140,7 @@ exports.FlagClient = FlagClient;
44024
44140
  exports.LongPollingTransport = LongPollingTransport;
44025
44141
  exports.RulesStore = RulesStore;
44026
44142
  exports.SseTransport = SseTransport;
44143
+ exports.applyPiiHashToFlatContext = applyPiiHashToFlatContext;
44027
44144
  exports.applyRolloutStrategy = applyRolloutStrategy;
44028
44145
  exports.asyncCache = cacheHelper_async;
44029
44146
  exports.canonicalizeForSigning = canonicalizeForSigning;
@@ -44041,11 +44158,13 @@ exports.evaluateSdkFlag = evaluateSdkFlag;
44041
44158
  exports.flattenEvaluationContext = flattenEvaluationContext;
44042
44159
  exports.generateAslClientKeyPair = generateAslClientKeyPair;
44043
44160
  exports.hashPercent = hashPercent;
44161
+ exports.hashPiiValue = hashPiiValue;
44044
44162
  exports.hashToPercentage = hashToPercentage;
44045
44163
  exports.isConfigPayloadExpired = isConfigPayloadExpired;
44046
44164
  exports.isInSegment = isInSegment;
44047
44165
  exports.performAslHandshake = performAslHandshake;
44048
44166
  exports.pickVariant = pickVariant;
44167
+ exports.prepareContextForLocalEval = prepareContextForLocalEval;
44049
44168
  exports.reduceRules = reduceRules;
44050
44169
  exports.signConfigPayload = signConfigPayload;
44051
44170
  exports.stringHash = stringHash;