flagmint-js-sdk 2.2.2 → 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 +1 -1
- package/dist/flagmint.cjs.js +109 -24
- package/dist/flagmint.cjs.js.map +1 -1
- package/dist/flagmint.es.js +109 -24
- package/dist/flagmint.es.js.map +1 -1
- package/dist/flagmint.umd.js +109 -24
- package/dist/flagmint.umd.js.map +1 -1
- package/dist/sdk/core/config-sync/index.d.ts +1 -1
- package/dist/sdk/core/config-sync/piiHash.d.ts +46 -0
- 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/README.md
CHANGED
|
@@ -133,7 +133,7 @@ After `ready()`:
|
|
|
133
133
|
| `transportMode` | `'auto' \| 'sse' \| 'long-polling'` | `'auto'` | `auto` tries SSE, then long-polling |
|
|
134
134
|
| `EventSourceImpl` | EventSource | browser global | Required in Node |
|
|
135
135
|
| `shareConnection` | boolean | `true` in browsers with `BroadcastChannel`, `false` in Node | One stream per API key across same-origin documents |
|
|
136
|
-
| `enableOfflineCache` | boolean | `true` |
|
|
136
|
+
| `enableOfflineCache` | boolean | `true` | Server-eval: load/save evaluated flags. Config-sync: load/save **rules** only (evaluated maps are not cached — they are per-user and would collide across tabs). |
|
|
137
137
|
| `persistContext` | boolean | `false` | Persist context in the cache adapter |
|
|
138
138
|
| `cacheAdapter` | CacheAdapter | localStorage helpers | Custom cache (Redis, files, …) |
|
|
139
139
|
| `restEndpoint` | string | env default | Override the long-polling / evaluate URL |
|
package/dist/flagmint.cjs.js
CHANGED
|
@@ -38621,6 +38621,12 @@ function saveCachedFlags$1(apiKey, data) {
|
|
|
38621
38621
|
} catch {
|
|
38622
38622
|
}
|
|
38623
38623
|
}
|
|
38624
|
+
function clearCachedFlags(apiKey) {
|
|
38625
|
+
try {
|
|
38626
|
+
storage$1.removeItem?.(getFlagsKey$1(apiKey));
|
|
38627
|
+
} catch {
|
|
38628
|
+
}
|
|
38629
|
+
}
|
|
38624
38630
|
function loadCachedContext$1(apiKey) {
|
|
38625
38631
|
try {
|
|
38626
38632
|
const raw = storage$1.getItem(getContextKey$1(apiKey));
|
|
@@ -38659,6 +38665,7 @@ function clearCachedRulesSnapshot$1(apiKey) {
|
|
|
38659
38665
|
}
|
|
38660
38666
|
const cacheHelper = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
38661
38667
|
__proto__: null,
|
|
38668
|
+
clearCachedFlags,
|
|
38662
38669
|
clearCachedRulesSnapshot: clearCachedRulesSnapshot$1,
|
|
38663
38670
|
loadCachedContext: loadCachedContext$1,
|
|
38664
38671
|
loadCachedFlags: loadCachedFlags$1,
|
|
@@ -39093,7 +39100,7 @@ class SseTransport {
|
|
|
39093
39100
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
39094
39101
|
this.connectResolve = resolve;
|
|
39095
39102
|
this.connectReject = reject;
|
|
39096
|
-
const sdkVersion = "2.2.
|
|
39103
|
+
const sdkVersion = "2.2.3-rc.0";
|
|
39097
39104
|
const platform2 = typeof window !== "undefined" ? "browser" : "nodejs";
|
|
39098
39105
|
const wrapperName = this.configOptions?.wrapper?.name || "native-js";
|
|
39099
39106
|
const wrapperVersion = this.configOptions?.wrapper?.version || "none";
|
|
@@ -41905,22 +41912,80 @@ function payloadHasExplicitPiiFields(payload) {
|
|
|
41905
41912
|
return false;
|
|
41906
41913
|
return payload.piiSalt !== void 0 || payload.piiAttributes !== void 0 || payload.piiValueEncoding !== void 0;
|
|
41907
41914
|
}
|
|
41915
|
+
function piiAttributeKeysFromPayload(payload) {
|
|
41916
|
+
if (!payload || !Array.isArray(payload.piiAttributes))
|
|
41917
|
+
return [];
|
|
41918
|
+
return payload.piiAttributes.filter((k) => typeof k === "string" && k.length > 0);
|
|
41919
|
+
}
|
|
41920
|
+
function hasMalformedPiiAttributes(payload) {
|
|
41921
|
+
if (!payload || payload.piiAttributes === void 0)
|
|
41922
|
+
return false;
|
|
41923
|
+
if (!Array.isArray(payload.piiAttributes))
|
|
41924
|
+
return true;
|
|
41925
|
+
return payload.piiAttributes.some(
|
|
41926
|
+
(entry) => typeof entry !== "string" || entry.length === 0
|
|
41927
|
+
);
|
|
41928
|
+
}
|
|
41929
|
+
function hasExplicitInvalidPiiEncoding(payload) {
|
|
41930
|
+
if (!payload || payload.piiValueEncoding === void 0)
|
|
41931
|
+
return false;
|
|
41932
|
+
return payload.piiValueEncoding !== PII_VALUE_ENCODING;
|
|
41933
|
+
}
|
|
41934
|
+
function payloadRequiresUsablePiiWire(payload) {
|
|
41935
|
+
if (!payload)
|
|
41936
|
+
return false;
|
|
41937
|
+
if (hasExplicitInvalidPiiEncoding(payload))
|
|
41938
|
+
return true;
|
|
41939
|
+
if (hasMalformedPiiAttributes(payload))
|
|
41940
|
+
return true;
|
|
41941
|
+
if (piiAttributeKeysFromPayload(payload).length > 0)
|
|
41942
|
+
return true;
|
|
41943
|
+
return false;
|
|
41944
|
+
}
|
|
41945
|
+
function isExplicitPiiRequirementCleared(payload) {
|
|
41946
|
+
if (!payload || payload.piiAttributes === void 0)
|
|
41947
|
+
return false;
|
|
41948
|
+
if (!Array.isArray(payload.piiAttributes))
|
|
41949
|
+
return false;
|
|
41950
|
+
if (payload.piiAttributes.length !== 0)
|
|
41951
|
+
return false;
|
|
41952
|
+
if (hasExplicitInvalidPiiEncoding(payload))
|
|
41953
|
+
return false;
|
|
41954
|
+
return true;
|
|
41955
|
+
}
|
|
41908
41956
|
function piiWireOptionsFromPayload(payload) {
|
|
41909
41957
|
if (!payload)
|
|
41910
41958
|
return null;
|
|
41959
|
+
if (hasExplicitInvalidPiiEncoding(payload))
|
|
41960
|
+
return null;
|
|
41961
|
+
if (hasMalformedPiiAttributes(payload))
|
|
41962
|
+
return null;
|
|
41911
41963
|
const salt = typeof payload.piiSalt === "string" ? payload.piiSalt : "";
|
|
41912
41964
|
if (!salt)
|
|
41913
41965
|
return null;
|
|
41914
|
-
const
|
|
41915
|
-
if (encoding !== PII_VALUE_ENCODING)
|
|
41916
|
-
return null;
|
|
41917
|
-
const attrs = Array.isArray(payload.piiAttributes) ? payload.piiAttributes.filter((k) => typeof k === "string") : [];
|
|
41966
|
+
const attrs = piiAttributeKeysFromPayload(payload);
|
|
41918
41967
|
return {
|
|
41919
41968
|
piiAttributes: attrs,
|
|
41920
41969
|
piiSalt: salt,
|
|
41921
41970
|
piiValueEncoding: PII_VALUE_ENCODING
|
|
41922
41971
|
};
|
|
41923
41972
|
}
|
|
41973
|
+
function isPiiWireBroken(payload, resolved, previousPriorHadPiiAttrs = false) {
|
|
41974
|
+
if (hasExplicitInvalidPiiEncoding(payload) || hasMalformedPiiAttributes(payload)) {
|
|
41975
|
+
return true;
|
|
41976
|
+
}
|
|
41977
|
+
if (resolved) {
|
|
41978
|
+
return false;
|
|
41979
|
+
}
|
|
41980
|
+
if (isExplicitPiiRequirementCleared(payload))
|
|
41981
|
+
return false;
|
|
41982
|
+
if (payloadRequiresUsablePiiWire(payload))
|
|
41983
|
+
return true;
|
|
41984
|
+
if (previousPriorHadPiiAttrs && payloadHasExplicitPiiFields(payload)) {
|
|
41985
|
+
return true;
|
|
41986
|
+
}
|
|
41987
|
+
return false;
|
|
41988
|
+
}
|
|
41924
41989
|
function resolvePiiWireForDelta(previous, action) {
|
|
41925
41990
|
if (!payloadHasExplicitPiiFields(action)) {
|
|
41926
41991
|
return previous;
|
|
@@ -41973,7 +42038,8 @@ function createEmptyRulesState() {
|
|
|
41973
42038
|
segments: /* @__PURE__ */ new Map(),
|
|
41974
42039
|
ready: false,
|
|
41975
42040
|
needsFullConfig: true,
|
|
41976
|
-
piiWire: null
|
|
42041
|
+
piiWire: null,
|
|
42042
|
+
piiWireBroken: false
|
|
41977
42043
|
};
|
|
41978
42044
|
}
|
|
41979
42045
|
function cloneState(state) {
|
|
@@ -41988,7 +42054,8 @@ function cloneState(state) {
|
|
|
41988
42054
|
piiAttributes: [...state.piiWire.piiAttributes],
|
|
41989
42055
|
piiSalt: state.piiWire.piiSalt,
|
|
41990
42056
|
piiValueEncoding: state.piiWire.piiValueEncoding
|
|
41991
|
-
} : null
|
|
42057
|
+
} : null,
|
|
42058
|
+
piiWireBroken: state.piiWireBroken
|
|
41992
42059
|
};
|
|
41993
42060
|
}
|
|
41994
42061
|
function segmentsFromRecord(record) {
|
|
@@ -42015,14 +42082,18 @@ function hydrateFromFull(action) {
|
|
|
42015
42082
|
if (flag?.key)
|
|
42016
42083
|
flags.set(flag.key, flag);
|
|
42017
42084
|
}
|
|
42085
|
+
const piiWire = piiWireOptionsFromPayload(action);
|
|
42086
|
+
const piiWireBroken = isPiiWireBroken(action, piiWire, false);
|
|
42018
42087
|
return {
|
|
42019
42088
|
version: action.version,
|
|
42020
42089
|
expiresAt: action.expiresAt,
|
|
42021
42090
|
flags,
|
|
42022
42091
|
segments: segmentsFromRecord(action.segments),
|
|
42023
|
-
|
|
42024
|
-
|
|
42025
|
-
|
|
42092
|
+
// Usable rules, but PII metadata must be valid when hashing is required.
|
|
42093
|
+
ready: !piiWireBroken,
|
|
42094
|
+
needsFullConfig: piiWireBroken,
|
|
42095
|
+
piiWire,
|
|
42096
|
+
piiWireBroken
|
|
42026
42097
|
};
|
|
42027
42098
|
}
|
|
42028
42099
|
function applyPatch(state, action) {
|
|
@@ -42035,14 +42106,19 @@ function applyPatch(state, action) {
|
|
|
42035
42106
|
flags.set(flag.key, flag);
|
|
42036
42107
|
}
|
|
42037
42108
|
const nextPii = resolvePiiWireForDelta(state.piiWire, action);
|
|
42109
|
+
const priorHadPiiAttrs = (state.piiWire?.piiAttributes.length ?? 0) > 0 || state.piiWireBroken;
|
|
42110
|
+
const computedBroken = isPiiWireBroken(action, nextPii, priorHadPiiAttrs);
|
|
42111
|
+
const recoveredWithRequiredAttrs = !!nextPii && nextPii.piiAttributes.length > 0;
|
|
42112
|
+
const piiWireBroken = isExplicitPiiRequirementCleared(action) ? false : recoveredWithRequiredAttrs ? false : computedBroken || state.piiWireBroken;
|
|
42038
42113
|
return {
|
|
42039
42114
|
version: action.toVersion,
|
|
42040
42115
|
expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : state.expiresAt,
|
|
42041
42116
|
flags,
|
|
42042
42117
|
segments: applySegmentUpserts(state.segments, action.segments),
|
|
42043
|
-
ready:
|
|
42044
|
-
needsFullConfig:
|
|
42045
|
-
piiWire: nextPii
|
|
42118
|
+
ready: !piiWireBroken,
|
|
42119
|
+
needsFullConfig: piiWireBroken,
|
|
42120
|
+
piiWire: nextPii,
|
|
42121
|
+
piiWireBroken
|
|
42046
42122
|
};
|
|
42047
42123
|
}
|
|
42048
42124
|
function reduceRules(state, action, now = Date.now()) {
|
|
@@ -42062,8 +42138,8 @@ function reduceRules(state, action, now = Date.now()) {
|
|
|
42062
42138
|
...cloneState(state),
|
|
42063
42139
|
version: state.version,
|
|
42064
42140
|
expiresAt: action.expiresAt,
|
|
42065
|
-
ready:
|
|
42066
|
-
needsFullConfig: state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
|
|
42141
|
+
ready: !state.piiWireBroken,
|
|
42142
|
+
needsFullConfig: state.piiWireBroken || state.flags.size === 0 || versionMismatch ? true : state.needsFullConfig
|
|
42067
42143
|
}
|
|
42068
42144
|
};
|
|
42069
42145
|
}
|
|
@@ -42119,8 +42195,8 @@ function reduceRules(state, action, now = Date.now()) {
|
|
|
42119
42195
|
...current,
|
|
42120
42196
|
version: action.toVersion,
|
|
42121
42197
|
expiresAt: typeof action.expiresAt === "number" ? action.expiresAt : current.expiresAt,
|
|
42122
|
-
ready:
|
|
42123
|
-
needsFullConfig:
|
|
42198
|
+
ready: !current.piiWireBroken,
|
|
42199
|
+
needsFullConfig: current.piiWireBroken
|
|
42124
42200
|
}
|
|
42125
42201
|
};
|
|
42126
42202
|
}
|
|
@@ -42143,9 +42219,12 @@ class RulesStore {
|
|
|
42143
42219
|
return this.state;
|
|
42144
42220
|
}
|
|
42145
42221
|
/**
|
|
42146
|
-
* Fail-closed readiness: lease not expired
|
|
42222
|
+
* Fail-closed readiness: lease not expired, store marked ready, and PII
|
|
42223
|
+
* wire metadata usable when hashing is required.
|
|
42147
42224
|
*/
|
|
42148
42225
|
isReady(now = Date.now()) {
|
|
42226
|
+
if (this.state.piiWireBroken)
|
|
42227
|
+
return false;
|
|
42149
42228
|
if (!this.state.ready)
|
|
42150
42229
|
return false;
|
|
42151
42230
|
if (!this.state.expiresAt)
|
|
@@ -42229,7 +42308,9 @@ class RulesStore {
|
|
|
42229
42308
|
piiAttributes: [...pii.piiAttributes],
|
|
42230
42309
|
piiSalt: pii.piiSalt,
|
|
42231
42310
|
piiValueEncoding: pii.piiValueEncoding
|
|
42232
|
-
} : {}
|
|
42311
|
+
} : {},
|
|
42312
|
+
// Persist fail-closed even when piiWire is null (unusable salt/encoding).
|
|
42313
|
+
...this.state.piiWireBroken ? { piiWireBroken: true } : {}
|
|
42233
42314
|
};
|
|
42234
42315
|
}
|
|
42235
42316
|
hydrateFromSnapshot(snapshot, now = Date.now()) {
|
|
@@ -42239,14 +42320,17 @@ class RulesStore {
|
|
|
42239
42320
|
flags.set(flag.key, flag);
|
|
42240
42321
|
}
|
|
42241
42322
|
const expired = isConfigPayloadExpired(snapshot.expiresAt, now);
|
|
42323
|
+
const piiWire = piiWireOptionsFromPayload(snapshot);
|
|
42324
|
+
const piiWireBroken = snapshot.piiWireBroken === true || isPiiWireBroken(snapshot, piiWire, false);
|
|
42242
42325
|
this.state = {
|
|
42243
42326
|
version: snapshot.version,
|
|
42244
42327
|
expiresAt: snapshot.expiresAt,
|
|
42245
42328
|
flags,
|
|
42246
42329
|
segments: segmentsFromRecord(snapshot.segments),
|
|
42247
|
-
ready: !expired && flags.size > 0,
|
|
42248
|
-
needsFullConfig: expired || flags.size === 0,
|
|
42249
|
-
piiWire
|
|
42330
|
+
ready: !expired && flags.size > 0 && !piiWireBroken,
|
|
42331
|
+
needsFullConfig: expired || flags.size === 0 || piiWireBroken,
|
|
42332
|
+
piiWire,
|
|
42333
|
+
piiWireBroken
|
|
42250
42334
|
};
|
|
42251
42335
|
}
|
|
42252
42336
|
}
|
|
@@ -43217,9 +43301,10 @@ class FlagClient {
|
|
|
43217
43301
|
}
|
|
43218
43302
|
let hydratedRules = false;
|
|
43219
43303
|
if (this.configSync && this.rulesStore && this.enableOfflineCache) {
|
|
43304
|
+
clearCachedFlags(this.apiKey);
|
|
43220
43305
|
hydratedRules = await this.hydrateRulesFromCache();
|
|
43221
43306
|
}
|
|
43222
|
-
if (this.enableOfflineCache && !hydratedRules) {
|
|
43307
|
+
if (this.enableOfflineCache && !this.configSync && !hydratedRules) {
|
|
43223
43308
|
const cached = await Promise.resolve(
|
|
43224
43309
|
this.cacheAdapter.loadFlags(this.apiKey, this.cacheTTL)
|
|
43225
43310
|
);
|
|
@@ -43461,7 +43546,7 @@ class FlagClient {
|
|
|
43461
43546
|
*/
|
|
43462
43547
|
updateFlags(newFlags, fromShare = false) {
|
|
43463
43548
|
this.flags = newFlags;
|
|
43464
|
-
if (this.enableOfflineCache) {
|
|
43549
|
+
if (this.enableOfflineCache && !this.configSync) {
|
|
43465
43550
|
void Promise.resolve(
|
|
43466
43551
|
this.cacheAdapter.saveFlags(this.apiKey, newFlags)
|
|
43467
43552
|
);
|