flagmint-js-sdk 2.2.2-rc.0 → 2.2.3-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.
- package/README.md +2 -2
- package/dist/flagmint.cjs.js +131 -32
- package/dist/flagmint.cjs.js.map +1 -1
- package/dist/flagmint.es.js +131 -32
- package/dist/flagmint.es.js.map +1 -1
- package/dist/flagmint.umd.js +131 -32
- package/dist/flagmint.umd.js.map +1 -1
- package/dist/sdk/core/config-sync/index.d.ts +2 -2
- package/dist/sdk/core/config-sync/piiHash.d.ts +86 -6
- package/dist/sdk/core/config-sync/rulesStore.d.ts +7 -1
- package/dist/sdk/core/config-sync/types.d.ts +5 -0
- package/dist/sdk/core/helpers/cacheHelper.d.ts +7 -0
- package/package.json +1 -1
package/dist/flagmint.es.js
CHANGED
|
@@ -38619,6 +38619,12 @@ function saveCachedFlags$1(apiKey, data) {
|
|
|
38619
38619
|
} catch {
|
|
38620
38620
|
}
|
|
38621
38621
|
}
|
|
38622
|
+
function clearCachedFlags(apiKey) {
|
|
38623
|
+
try {
|
|
38624
|
+
storage$1.removeItem?.(getFlagsKey$1(apiKey));
|
|
38625
|
+
} catch {
|
|
38626
|
+
}
|
|
38627
|
+
}
|
|
38622
38628
|
function loadCachedContext$1(apiKey) {
|
|
38623
38629
|
try {
|
|
38624
38630
|
const raw = storage$1.getItem(getContextKey$1(apiKey));
|
|
@@ -38657,6 +38663,7 @@ function clearCachedRulesSnapshot$1(apiKey) {
|
|
|
38657
38663
|
}
|
|
38658
38664
|
const cacheHelper = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
38659
38665
|
__proto__: null,
|
|
38666
|
+
clearCachedFlags,
|
|
38660
38667
|
clearCachedRulesSnapshot: clearCachedRulesSnapshot$1,
|
|
38661
38668
|
loadCachedContext: loadCachedContext$1,
|
|
38662
38669
|
loadCachedFlags: loadCachedFlags$1,
|
|
@@ -39091,7 +39098,7 @@ class SseTransport {
|
|
|
39091
39098
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
39092
39099
|
this.connectResolve = resolve;
|
|
39093
39100
|
this.connectReject = reject;
|
|
39094
|
-
const sdkVersion = "2.2.
|
|
39101
|
+
const sdkVersion = "2.2.3-rc.0";
|
|
39095
39102
|
const platform2 = typeof window !== "undefined" ? "browser" : "nodejs";
|
|
39096
39103
|
const wrapperName = this.configOptions?.wrapper?.name || "native-js";
|
|
39097
39104
|
const wrapperVersion = this.configOptions?.wrapper?.version || "none";
|
|
@@ -41871,7 +41878,6 @@ function isConfigPayloadExpired(expiresAt, now = Date.now()) {
|
|
|
41871
41878
|
}
|
|
41872
41879
|
const PII_VALUE_ENCODING = "hmac-v1";
|
|
41873
41880
|
const textEncoder = new TextEncoder();
|
|
41874
|
-
const HASH_HEX_PATTERN = /^[0-9a-f]{64}$/;
|
|
41875
41881
|
function normalizePiiString(value) {
|
|
41876
41882
|
return value.trim().toLowerCase();
|
|
41877
41883
|
}
|
|
@@ -41899,34 +41905,109 @@ function isPiiAttribute(attribute, piiAttributes) {
|
|
|
41899
41905
|
}
|
|
41900
41906
|
return false;
|
|
41901
41907
|
}
|
|
41908
|
+
function payloadHasExplicitPiiFields(payload) {
|
|
41909
|
+
if (!payload)
|
|
41910
|
+
return false;
|
|
41911
|
+
return payload.piiSalt !== void 0 || payload.piiAttributes !== void 0 || payload.piiValueEncoding !== void 0;
|
|
41912
|
+
}
|
|
41913
|
+
function piiAttributeKeysFromPayload(payload) {
|
|
41914
|
+
if (!payload || !Array.isArray(payload.piiAttributes))
|
|
41915
|
+
return [];
|
|
41916
|
+
return payload.piiAttributes.filter((k) => typeof k === "string" && k.length > 0);
|
|
41917
|
+
}
|
|
41918
|
+
function hasMalformedPiiAttributes(payload) {
|
|
41919
|
+
if (!payload || payload.piiAttributes === void 0)
|
|
41920
|
+
return false;
|
|
41921
|
+
if (!Array.isArray(payload.piiAttributes))
|
|
41922
|
+
return true;
|
|
41923
|
+
return payload.piiAttributes.some(
|
|
41924
|
+
(entry) => typeof entry !== "string" || entry.length === 0
|
|
41925
|
+
);
|
|
41926
|
+
}
|
|
41927
|
+
function hasExplicitInvalidPiiEncoding(payload) {
|
|
41928
|
+
if (!payload || payload.piiValueEncoding === void 0)
|
|
41929
|
+
return false;
|
|
41930
|
+
return payload.piiValueEncoding !== PII_VALUE_ENCODING;
|
|
41931
|
+
}
|
|
41932
|
+
function payloadRequiresUsablePiiWire(payload) {
|
|
41933
|
+
if (!payload)
|
|
41934
|
+
return false;
|
|
41935
|
+
if (hasExplicitInvalidPiiEncoding(payload))
|
|
41936
|
+
return true;
|
|
41937
|
+
if (hasMalformedPiiAttributes(payload))
|
|
41938
|
+
return true;
|
|
41939
|
+
if (piiAttributeKeysFromPayload(payload).length > 0)
|
|
41940
|
+
return true;
|
|
41941
|
+
return false;
|
|
41942
|
+
}
|
|
41943
|
+
function isExplicitPiiRequirementCleared(payload) {
|
|
41944
|
+
if (!payload || payload.piiAttributes === void 0)
|
|
41945
|
+
return false;
|
|
41946
|
+
if (!Array.isArray(payload.piiAttributes))
|
|
41947
|
+
return false;
|
|
41948
|
+
if (payload.piiAttributes.length !== 0)
|
|
41949
|
+
return false;
|
|
41950
|
+
if (hasExplicitInvalidPiiEncoding(payload))
|
|
41951
|
+
return false;
|
|
41952
|
+
return true;
|
|
41953
|
+
}
|
|
41902
41954
|
function piiWireOptionsFromPayload(payload) {
|
|
41903
41955
|
if (!payload)
|
|
41904
41956
|
return null;
|
|
41957
|
+
if (hasExplicitInvalidPiiEncoding(payload))
|
|
41958
|
+
return null;
|
|
41959
|
+
if (hasMalformedPiiAttributes(payload))
|
|
41960
|
+
return null;
|
|
41905
41961
|
const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
|
|
41906
41962
|
if (!salt)
|
|
41907
41963
|
return null;
|
|
41908
|
-
const
|
|
41909
|
-
if (encoding !== PII_VALUE_ENCODING)
|
|
41910
|
-
return null;
|
|
41911
|
-
const attrs = Array.isArray(payload.piiAttributes) ? payload.piiAttributes.filter((k) => typeof k === "string") : [];
|
|
41964
|
+
const attrs = piiAttributeKeysFromPayload(payload);
|
|
41912
41965
|
return {
|
|
41913
41966
|
piiAttributes: attrs,
|
|
41914
41967
|
piiSalt: salt,
|
|
41915
41968
|
piiValueEncoding: PII_VALUE_ENCODING
|
|
41916
41969
|
};
|
|
41917
41970
|
}
|
|
41918
|
-
function
|
|
41971
|
+
function isPiiWireBroken(payload, resolved, previousPriorHadPiiAttrs = false) {
|
|
41972
|
+
if (hasExplicitInvalidPiiEncoding(payload) || hasMalformedPiiAttributes(payload)) {
|
|
41973
|
+
return true;
|
|
41974
|
+
}
|
|
41975
|
+
if (resolved) {
|
|
41976
|
+
return false;
|
|
41977
|
+
}
|
|
41978
|
+
if (isExplicitPiiRequirementCleared(payload))
|
|
41979
|
+
return false;
|
|
41980
|
+
if (payloadRequiresUsablePiiWire(payload))
|
|
41981
|
+
return true;
|
|
41982
|
+
if (previousPriorHadPiiAttrs && payloadHasExplicitPiiFields(payload)) {
|
|
41983
|
+
return true;
|
|
41984
|
+
}
|
|
41985
|
+
return false;
|
|
41986
|
+
}
|
|
41987
|
+
function resolvePiiWireForDelta(previous, action) {
|
|
41988
|
+
if (!payloadHasExplicitPiiFields(action)) {
|
|
41989
|
+
return previous;
|
|
41990
|
+
}
|
|
41991
|
+
const merged = {
|
|
41992
|
+
piiSalt: action.piiSalt !== void 0 ? action.piiSalt : previous?.piiSalt,
|
|
41993
|
+
piiAttributes: action.piiAttributes !== void 0 ? action.piiAttributes : previous?.piiAttributes,
|
|
41994
|
+
piiValueEncoding: action.piiValueEncoding !== void 0 ? action.piiValueEncoding : previous?.piiValueEncoding
|
|
41995
|
+
};
|
|
41996
|
+
return piiWireOptionsFromPayload(merged);
|
|
41997
|
+
}
|
|
41998
|
+
function applyPiiHashToFlatContext(context, options, mode) {
|
|
41919
41999
|
if (!options || !options.piiSalt || !options.piiAttributes.length || options.piiValueEncoding !== PII_VALUE_ENCODING) {
|
|
41920
42000
|
return context;
|
|
41921
42001
|
}
|
|
42002
|
+
if (mode?.alreadyHashed) {
|
|
42003
|
+
return context;
|
|
42004
|
+
}
|
|
41922
42005
|
let changed = false;
|
|
41923
42006
|
const out = { ...context };
|
|
41924
42007
|
for (const [key2, value] of Object.entries(context)) {
|
|
41925
42008
|
if (!isPiiAttribute(key2, options.piiAttributes))
|
|
41926
42009
|
continue;
|
|
41927
42010
|
if (typeof value === "string") {
|
|
41928
|
-
if (HASH_HEX_PATTERN.test(value))
|
|
41929
|
-
continue;
|
|
41930
42011
|
out[key2] = hashPiiValue(value, options.piiSalt);
|
|
41931
42012
|
changed = true;
|
|
41932
42013
|
continue;
|
|
@@ -41936,8 +42017,6 @@ function applyPiiHashToFlatContext(context, options) {
|
|
|
41936
42017
|
const next = value.map((entry) => {
|
|
41937
42018
|
if (typeof entry !== "string")
|
|
41938
42019
|
return entry;
|
|
41939
|
-
if (HASH_HEX_PATTERN.test(entry))
|
|
41940
|
-
return entry;
|
|
41941
42020
|
arrayChanged = true;
|
|
41942
42021
|
return hashPiiValue(entry, options.piiSalt);
|
|
41943
42022
|
});
|
|
@@ -41957,7 +42036,8 @@ function createEmptyRulesState() {
|
|
|
41957
42036
|
segments: /* @__PURE__ */ new Map(),
|
|
41958
42037
|
ready: false,
|
|
41959
42038
|
needsFullConfig: true,
|
|
41960
|
-
piiWire: null
|
|
42039
|
+
piiWire: null,
|
|
42040
|
+
piiWireBroken: false
|
|
41961
42041
|
};
|
|
41962
42042
|
}
|
|
41963
42043
|
function cloneState(state) {
|
|
@@ -41972,7 +42052,8 @@ function cloneState(state) {
|
|
|
41972
42052
|
piiAttributes: [...state.piiWire.piiAttributes],
|
|
41973
42053
|
piiSalt: state.piiWire.piiSalt,
|
|
41974
42054
|
piiValueEncoding: state.piiWire.piiValueEncoding
|
|
41975
|
-
} : null
|
|
42055
|
+
} : null,
|
|
42056
|
+
piiWireBroken: state.piiWireBroken
|
|
41976
42057
|
};
|
|
41977
42058
|
}
|
|
41978
42059
|
function segmentsFromRecord(record) {
|
|
@@ -41999,14 +42080,18 @@ function hydrateFromFull(action) {
|
|
|
41999
42080
|
if (flag?.key)
|
|
42000
42081
|
flags.set(flag.key, flag);
|
|
42001
42082
|
}
|
|
42083
|
+
const piiWire = piiWireOptionsFromPayload(action);
|
|
42084
|
+
const piiWireBroken = isPiiWireBroken(action, piiWire, false);
|
|
42002
42085
|
return {
|
|
42003
42086
|
version: action.version,
|
|
42004
42087
|
expiresAt: action.expiresAt,
|
|
42005
42088
|
flags,
|
|
42006
42089
|
segments: segmentsFromRecord(action.segments),
|
|
42007
|
-
|
|
42008
|
-
|
|
42009
|
-
|
|
42090
|
+
// Usable rules, but PII metadata must be valid when hashing is required.
|
|
42091
|
+
ready: !piiWireBroken,
|
|
42092
|
+
needsFullConfig: piiWireBroken,
|
|
42093
|
+
piiWire,
|
|
42094
|
+
piiWireBroken
|
|
42010
42095
|
};
|
|
42011
42096
|
}
|
|
42012
42097
|
function applyPatch(state, action) {
|
|
@@ -42018,15 +42103,20 @@ function applyPatch(state, action) {
|
|
|
42018
42103
|
if (flag?.key)
|
|
42019
42104
|
flags.set(flag.key, flag);
|
|
42020
42105
|
}
|
|
42021
|
-
const nextPii =
|
|
42106
|
+
const nextPii = resolvePiiWireForDelta(state.piiWire, action);
|
|
42107
|
+
const priorHadPiiAttrs = (state.piiWire?.piiAttributes.length ?? 0) > 0 || state.piiWireBroken;
|
|
42108
|
+
const computedBroken = isPiiWireBroken(action, nextPii, priorHadPiiAttrs);
|
|
42109
|
+
const recoveredWithRequiredAttrs = !!nextPii && nextPii.piiAttributes.length > 0;
|
|
42110
|
+
const piiWireBroken = isExplicitPiiRequirementCleared(action) ? false : recoveredWithRequiredAttrs ? false : computedBroken || state.piiWireBroken;
|
|
42022
42111
|
return {
|
|
42023
42112
|
version: action.toVersion,
|
|
42024
42113
|
expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
|
|
42025
42114
|
flags,
|
|
42026
42115
|
segments: applySegmentUpserts(state.segments, action.segments),
|
|
42027
|
-
ready:
|
|
42028
|
-
needsFullConfig:
|
|
42029
|
-
piiWire: nextPii
|
|
42116
|
+
ready: !piiWireBroken,
|
|
42117
|
+
needsFullConfig: piiWireBroken,
|
|
42118
|
+
piiWire: nextPii,
|
|
42119
|
+
piiWireBroken
|
|
42030
42120
|
};
|
|
42031
42121
|
}
|
|
42032
42122
|
function reduceRules(state, action, now = Date.now()) {
|
|
@@ -42046,8 +42136,8 @@ function reduceRules(state, action, now = Date.now()) {
|
|
|
42046
42136
|
...cloneState(state),
|
|
42047
42137
|
version: state.version,
|
|
42048
42138
|
expiresAt: action.expiresAt,
|
|
42049
|
-
ready:
|
|
42050
|
-
needsFullConfig: state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
|
|
42139
|
+
ready: !state.piiWireBroken,
|
|
42140
|
+
needsFullConfig: state.piiWireBroken || state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
|
|
42051
42141
|
}
|
|
42052
42142
|
};
|
|
42053
42143
|
}
|
|
@@ -42103,8 +42193,8 @@ function reduceRules(state, action, now = Date.now()) {
|
|
|
42103
42193
|
...current,
|
|
42104
42194
|
version: action.toVersion,
|
|
42105
42195
|
expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : current.expiresAt,
|
|
42106
|
-
ready:
|
|
42107
|
-
needsFullConfig:
|
|
42196
|
+
ready: !current.piiWireBroken,
|
|
42197
|
+
needsFullConfig: current.piiWireBroken
|
|
42108
42198
|
}
|
|
42109
42199
|
};
|
|
42110
42200
|
}
|
|
@@ -42127,9 +42217,12 @@ class RulesStore {
|
|
|
42127
42217
|
return this.state;
|
|
42128
42218
|
}
|
|
42129
42219
|
/**
|
|
42130
|
-
* Fail-closed readiness: lease not expired
|
|
42220
|
+
* Fail-closed readiness: lease not expired, store marked ready, and PII
|
|
42221
|
+
* wire metadata usable when hashing is required.
|
|
42131
42222
|
*/
|
|
42132
42223
|
isReady(now = Date.now()) {
|
|
42224
|
+
if (this.state.piiWireBroken)
|
|
42225
|
+
return false;
|
|
42133
42226
|
if (!this.state.ready)
|
|
42134
42227
|
return false;
|
|
42135
42228
|
if (!this.state.expiresAt)
|
|
@@ -42213,7 +42306,9 @@ class RulesStore {
|
|
|
42213
42306
|
piiAttributes: [...pii.piiAttributes],
|
|
42214
42307
|
piiSalt: pii.piiSalt,
|
|
42215
42308
|
piiValueEncoding: pii.piiValueEncoding
|
|
42216
|
-
} : {}
|
|
42309
|
+
} : {},
|
|
42310
|
+
// Persist fail-closed even when piiWire is null (unusable salt/encoding).
|
|
42311
|
+
...this.state.piiWireBroken ? { piiWireBroken: true } : {}
|
|
42217
42312
|
};
|
|
42218
42313
|
}
|
|
42219
42314
|
hydrateFromSnapshot(snapshot, now = Date.now()) {
|
|
@@ -42223,14 +42318,17 @@ class RulesStore {
|
|
|
42223
42318
|
flags.set(flag.key, flag);
|
|
42224
42319
|
}
|
|
42225
42320
|
const expired = isConfigPayloadExpired(snapshot.expiresAt, now);
|
|
42321
|
+
const piiWire = piiWireOptionsFromPayload(snapshot);
|
|
42322
|
+
const piiWireBroken = snapshot.piiWireBroken === true || isPiiWireBroken(snapshot, piiWire, false);
|
|
42226
42323
|
this.state = {
|
|
42227
42324
|
version: snapshot.version,
|
|
42228
42325
|
expiresAt: snapshot.expiresAt,
|
|
42229
42326
|
flags,
|
|
42230
42327
|
segments: segmentsFromRecord(snapshot.segments),
|
|
42231
|
-
ready: !expired && flags.size > 0,
|
|
42232
|
-
needsFullConfig: expired || flags.size === 0,
|
|
42233
|
-
piiWire
|
|
42328
|
+
ready: !expired && flags.size > 0 && !piiWireBroken,
|
|
42329
|
+
needsFullConfig: expired || flags.size === 0 || piiWireBroken,
|
|
42330
|
+
piiWire,
|
|
42331
|
+
piiWireBroken
|
|
42234
42332
|
};
|
|
42235
42333
|
}
|
|
42236
42334
|
}
|
|
@@ -42432,7 +42530,7 @@ function flattenEvaluationContext(context) {
|
|
|
42432
42530
|
function toComparableString(value) {
|
|
42433
42531
|
if (value === null || value === void 0)
|
|
42434
42532
|
return "";
|
|
42435
|
-
return String(value);
|
|
42533
|
+
return String(value).trim().toLowerCase();
|
|
42436
42534
|
}
|
|
42437
42535
|
function toComparableArray(value) {
|
|
42438
42536
|
if (Array.isArray(value)) {
|
|
@@ -43201,9 +43299,10 @@ class FlagClient {
|
|
|
43201
43299
|
}
|
|
43202
43300
|
let hydratedRules = false;
|
|
43203
43301
|
if (this.configSync && this.rulesStore && this.enableOfflineCache) {
|
|
43302
|
+
clearCachedFlags(this.apiKey);
|
|
43204
43303
|
hydratedRules = await this.hydrateRulesFromCache();
|
|
43205
43304
|
}
|
|
43206
|
-
if (this.enableOfflineCache && !hydratedRules) {
|
|
43305
|
+
if (this.enableOfflineCache && !this.configSync && !hydratedRules) {
|
|
43207
43306
|
const cached = await Promise.resolve(
|
|
43208
43307
|
this.cacheAdapter.loadFlags(this.apiKey, this.cacheTTL)
|
|
43209
43308
|
);
|
|
@@ -43445,7 +43544,7 @@ class FlagClient {
|
|
|
43445
43544
|
*/
|
|
43446
43545
|
updateFlags(newFlags, fromShare = false) {
|
|
43447
43546
|
this.flags = newFlags;
|
|
43448
|
-
if (this.enableOfflineCache) {
|
|
43547
|
+
if (this.enableOfflineCache && !this.configSync) {
|
|
43449
43548
|
void Promise.resolve(
|
|
43450
43549
|
this.cacheAdapter.saveFlags(this.apiKey, newFlags)
|
|
43451
43550
|
);
|