@launchdarkly/js-sdk-common 2.17.0 → 2.19.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/CHANGELOG.md +14 -0
- package/dist/cjs/AttributeReference.d.ts +1 -0
- package/dist/cjs/AttributeReference.d.ts.map +1 -1
- package/dist/cjs/Context.d.ts +9 -0
- package/dist/cjs/Context.d.ts.map +1 -1
- package/dist/cjs/api/platform/Requests.d.ts +1 -0
- package/dist/cjs/api/platform/Requests.d.ts.map +1 -1
- package/dist/cjs/api/subsystem/DataSystem/CallbackHandler.d.ts.map +1 -1
- package/dist/cjs/api/subsystem/DataSystem/DataSource.d.ts +18 -2
- package/dist/cjs/api/subsystem/DataSystem/DataSource.d.ts.map +1 -1
- package/dist/cjs/datasource/CompositeDataSource.d.ts +7 -2
- package/dist/cjs/datasource/CompositeDataSource.d.ts.map +1 -1
- package/dist/cjs/datasource/errors.d.ts +9 -0
- package/dist/cjs/datasource/errors.d.ts.map +1 -1
- package/dist/cjs/datasource/index.d.ts +2 -2
- package/dist/cjs/datasource/index.d.ts.map +1 -1
- package/dist/cjs/index.cjs +201 -47
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/internal/events/EventProcessor.d.ts +1 -1
- package/dist/cjs/internal/events/EventProcessor.d.ts.map +1 -1
- package/dist/cjs/internal/events/LDEventSummarizer.d.ts +32 -0
- package/dist/cjs/internal/events/LDEventSummarizer.d.ts.map +1 -0
- package/dist/cjs/internal/events/MultiEventSummarizer.d.ts +13 -0
- package/dist/cjs/internal/events/MultiEventSummarizer.d.ts.map +1 -0
- package/dist/cjs/internal/fdv2/payloadProcessor.d.ts.map +1 -1
- package/dist/cjs/internal/fdv2/proto.d.ts +1 -1
- package/dist/cjs/internal/fdv2/proto.d.ts.map +1 -1
- package/dist/cjs/internal/index.d.ts +1 -0
- package/dist/cjs/internal/index.d.ts.map +1 -1
- package/dist/cjs/internal/json/canonicalize.d.ts +10 -0
- package/dist/cjs/internal/json/canonicalize.d.ts.map +1 -0
- package/dist/cjs/internal/json/index.d.ts +2 -0
- package/dist/cjs/internal/json/index.d.ts.map +1 -0
- package/dist/cjs/options/ServiceEndpoints.d.ts +2 -2
- package/dist/cjs/options/ServiceEndpoints.d.ts.map +1 -1
- package/dist/esm/AttributeReference.d.ts +1 -0
- package/dist/esm/AttributeReference.d.ts.map +1 -1
- package/dist/esm/Context.d.ts +9 -0
- package/dist/esm/Context.d.ts.map +1 -1
- package/dist/esm/api/platform/Requests.d.ts +1 -0
- package/dist/esm/api/platform/Requests.d.ts.map +1 -1
- package/dist/esm/api/subsystem/DataSystem/CallbackHandler.d.ts.map +1 -1
- package/dist/esm/api/subsystem/DataSystem/DataSource.d.ts +18 -2
- package/dist/esm/api/subsystem/DataSystem/DataSource.d.ts.map +1 -1
- package/dist/esm/datasource/CompositeDataSource.d.ts +7 -2
- package/dist/esm/datasource/CompositeDataSource.d.ts.map +1 -1
- package/dist/esm/datasource/errors.d.ts +9 -0
- package/dist/esm/datasource/errors.d.ts.map +1 -1
- package/dist/esm/datasource/index.d.ts +2 -2
- package/dist/esm/datasource/index.d.ts.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.mjs +201 -48
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/internal/events/EventProcessor.d.ts +1 -1
- package/dist/esm/internal/events/EventProcessor.d.ts.map +1 -1
- package/dist/esm/internal/events/LDEventSummarizer.d.ts +32 -0
- package/dist/esm/internal/events/LDEventSummarizer.d.ts.map +1 -0
- package/dist/esm/internal/events/MultiEventSummarizer.d.ts +13 -0
- package/dist/esm/internal/events/MultiEventSummarizer.d.ts.map +1 -0
- package/dist/esm/internal/fdv2/payloadProcessor.d.ts.map +1 -1
- package/dist/esm/internal/fdv2/proto.d.ts +1 -1
- package/dist/esm/internal/fdv2/proto.d.ts.map +1 -1
- package/dist/esm/internal/index.d.ts +1 -0
- package/dist/esm/internal/index.d.ts.map +1 -1
- package/dist/esm/internal/json/canonicalize.d.ts +10 -0
- package/dist/esm/internal/json/canonicalize.d.ts.map +1 -0
- package/dist/esm/internal/json/index.d.ts +2 -0
- package/dist/esm/internal/json/index.d.ts.map +1 -0
- package/dist/esm/options/ServiceEndpoints.d.ts +2 -2
- package/dist/esm/options/ServiceEndpoints.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/esm/index.mjs
CHANGED
|
@@ -115,6 +115,9 @@ class AttributeReference {
|
|
|
115
115
|
return (this.depth === other.depth &&
|
|
116
116
|
this._components.every((value, index) => value === other.getComponent(index)));
|
|
117
117
|
}
|
|
118
|
+
get components() {
|
|
119
|
+
return [...this._components];
|
|
120
|
+
}
|
|
118
121
|
}
|
|
119
122
|
/**
|
|
120
123
|
* For use as invalid references when deserializing Flag/Segment data.
|
|
@@ -314,6 +317,41 @@ function isLegacyUser(context) {
|
|
|
314
317
|
return !('kind' in context) || context.kind === null || context.kind === undefined;
|
|
315
318
|
}
|
|
316
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Given some object to serialize product a canonicalized JSON string.
|
|
322
|
+
* https://www.rfc-editor.org/rfc/rfc8785.html
|
|
323
|
+
*
|
|
324
|
+
* We do not support custom toJSON methods on objects. Objects should be limited to basic types.
|
|
325
|
+
*
|
|
326
|
+
* @param object The object to serialize.
|
|
327
|
+
*/
|
|
328
|
+
function canonicalize(object, visited = []) {
|
|
329
|
+
// For JavaScript the default JSON serialization will produce canonicalized output for basic types.
|
|
330
|
+
if (object === null || typeof object !== 'object') {
|
|
331
|
+
return JSON.stringify(object);
|
|
332
|
+
}
|
|
333
|
+
if (visited.includes(object)) {
|
|
334
|
+
throw new Error('Cycle detected');
|
|
335
|
+
}
|
|
336
|
+
if (Array.isArray(object)) {
|
|
337
|
+
const values = object
|
|
338
|
+
.map((item) => canonicalize(item, [...visited, object]))
|
|
339
|
+
.map((item) => (item === undefined ? 'null' : item));
|
|
340
|
+
return `[${values.join(',')}]`;
|
|
341
|
+
}
|
|
342
|
+
const values = Object.keys(object)
|
|
343
|
+
.sort()
|
|
344
|
+
.map((key) => {
|
|
345
|
+
const value = canonicalize(object[key], [...visited, object]);
|
|
346
|
+
if (value !== undefined) {
|
|
347
|
+
return `${JSON.stringify(key)}:${value}`;
|
|
348
|
+
}
|
|
349
|
+
return undefined;
|
|
350
|
+
})
|
|
351
|
+
.filter((item) => item !== undefined);
|
|
352
|
+
return `{${values.join(',')}}`;
|
|
353
|
+
}
|
|
354
|
+
|
|
317
355
|
// The general strategy for the context is to transform the passed in context
|
|
318
356
|
// as little as possible. We do convert the legacy users to a single kind
|
|
319
357
|
// context, but we do not translate all passed contexts into a rigid structure.
|
|
@@ -688,6 +726,28 @@ class Context {
|
|
|
688
726
|
get legacy() {
|
|
689
727
|
return this._wasLegacy;
|
|
690
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* Get the serialized canonical JSON for this context. This is not filtered for use in events.
|
|
731
|
+
*
|
|
732
|
+
* This method will cache the result.
|
|
733
|
+
*
|
|
734
|
+
* @returns The serialized canonical JSON or undefined if it cannot be serialized.
|
|
735
|
+
*/
|
|
736
|
+
canonicalUnfilteredJson() {
|
|
737
|
+
if (!this.valid) {
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
if (this._cachedCanonicalJson) {
|
|
741
|
+
return this._cachedCanonicalJson;
|
|
742
|
+
}
|
|
743
|
+
try {
|
|
744
|
+
this._cachedCanonicalJson = canonicalize(Context.toLDContext(this));
|
|
745
|
+
}
|
|
746
|
+
catch {
|
|
747
|
+
// Indicated by undefined being returned.
|
|
748
|
+
}
|
|
749
|
+
return this._cachedCanonicalJson;
|
|
750
|
+
}
|
|
691
751
|
}
|
|
692
752
|
Context.UserKind = DEFAULT_KIND;
|
|
693
753
|
|
|
@@ -889,8 +949,6 @@ class CallbackHandler {
|
|
|
889
949
|
if (this._disabled) {
|
|
890
950
|
return;
|
|
891
951
|
}
|
|
892
|
-
// TODO: SDK-1044 track selector for future synchronizer to use
|
|
893
|
-
// report data up
|
|
894
952
|
this._dataCallback(basis, data);
|
|
895
953
|
}
|
|
896
954
|
async statusHandler(status, err) {
|
|
@@ -902,6 +960,11 @@ class CallbackHandler {
|
|
|
902
960
|
}
|
|
903
961
|
|
|
904
962
|
// TODO: refactor client-sdk to use this enum
|
|
963
|
+
/**
|
|
964
|
+
* @experimental
|
|
965
|
+
* This feature is not stable and not subject to any backwards compatibility guarantees or semantic
|
|
966
|
+
* versioning. It is not suitable for production usage.
|
|
967
|
+
*/
|
|
905
968
|
var DataSourceState;
|
|
906
969
|
(function (DataSourceState) {
|
|
907
970
|
// Positive confirmation of connection/data receipt
|
|
@@ -1002,6 +1065,43 @@ class DataSourceList {
|
|
|
1002
1065
|
}
|
|
1003
1066
|
}
|
|
1004
1067
|
|
|
1068
|
+
class LDFileDataSourceError extends Error {
|
|
1069
|
+
constructor(message) {
|
|
1070
|
+
super(message);
|
|
1071
|
+
this.name = 'LaunchDarklyFileDataSourceError';
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
class LDPollingError extends Error {
|
|
1075
|
+
constructor(kind, message, status, recoverable = true) {
|
|
1076
|
+
super(message);
|
|
1077
|
+
this.kind = kind;
|
|
1078
|
+
this.status = status;
|
|
1079
|
+
this.name = 'LaunchDarklyPollingError';
|
|
1080
|
+
this.recoverable = recoverable;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
class LDStreamingError extends Error {
|
|
1084
|
+
constructor(kind, message, code, recoverable = true) {
|
|
1085
|
+
super(message);
|
|
1086
|
+
this.kind = kind;
|
|
1087
|
+
this.code = code;
|
|
1088
|
+
this.name = 'LaunchDarklyStreamingError';
|
|
1089
|
+
this.recoverable = recoverable;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* This is a short term error and will be removed once FDv2 adoption is sufficient.
|
|
1094
|
+
*/
|
|
1095
|
+
class LDFlagDeliveryFallbackError extends Error {
|
|
1096
|
+
constructor(kind, message, code) {
|
|
1097
|
+
super(message);
|
|
1098
|
+
this.kind = kind;
|
|
1099
|
+
this.code = code;
|
|
1100
|
+
this.name = 'LDFlagDeliveryFallbackError';
|
|
1101
|
+
this.recoverable = false;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1005
1105
|
const DEFAULT_FALLBACK_TIME_MS = 2 * 60 * 1000;
|
|
1006
1106
|
const DEFAULT_RECOVERY_TIME_MS = 5 * 60 * 1000;
|
|
1007
1107
|
/**
|
|
@@ -1012,8 +1112,12 @@ class CompositeDataSource {
|
|
|
1012
1112
|
/**
|
|
1013
1113
|
* @param initializers factories to create {@link DataSystemInitializer}s, in priority order.
|
|
1014
1114
|
* @param synchronizers factories to create {@link DataSystemSynchronizer}s, in priority order.
|
|
1115
|
+
* @param fdv1Synchronizers factories to fallback to if we need to fallback to FDv1.
|
|
1116
|
+
* @param _logger for logging
|
|
1117
|
+
* @param _transitionConditions to control automated transition between datasources. Typically only used for testing.
|
|
1118
|
+
* @param _backoff to control delay between transitions. Typically only used for testing.
|
|
1015
1119
|
*/
|
|
1016
|
-
constructor(initializers, synchronizers, _logger, _transitionConditions = {
|
|
1120
|
+
constructor(initializers, synchronizers, fdv1Synchronizers, _logger, _transitionConditions = {
|
|
1017
1121
|
[DataSourceState.Valid]: {
|
|
1018
1122
|
durationMS: DEFAULT_RECOVERY_TIME_MS,
|
|
1019
1123
|
transition: 'recover',
|
|
@@ -1049,8 +1153,9 @@ class CompositeDataSource {
|
|
|
1049
1153
|
this._initPhaseActive = initializers.length > 0; // init phase if we have initializers
|
|
1050
1154
|
this._initFactories = new DataSourceList(false, initializers);
|
|
1051
1155
|
this._syncFactories = new DataSourceList(true, synchronizers);
|
|
1156
|
+
this._fdv1Synchronizers = new DataSourceList(true, fdv1Synchronizers);
|
|
1052
1157
|
}
|
|
1053
|
-
async start(dataCallback, statusCallback) {
|
|
1158
|
+
async start(dataCallback, statusCallback, selectorGetter) {
|
|
1054
1159
|
if (!this._stopped) {
|
|
1055
1160
|
// don't allow multiple simultaneous runs
|
|
1056
1161
|
this._logger?.info('CompositeDataSource already running. Ignoring call to start.');
|
|
@@ -1087,12 +1192,18 @@ class CompositeDataSource {
|
|
|
1087
1192
|
// When we get a status update, we want to fallback if it is an error. We also want to schedule a transition for some
|
|
1088
1193
|
// time in the future if this status remains for some duration (ex: Recover to primary synchronizer after the secondary
|
|
1089
1194
|
// synchronizer has been Valid for some time). These scheduled transitions are configurable in the constructor.
|
|
1090
|
-
this._logger?.debug(`CompositeDataSource received state ${state} from underlying data source
|
|
1195
|
+
this._logger?.debug(`CompositeDataSource received state ${state} from underlying data source. Err is ${err}`);
|
|
1091
1196
|
if (err || state === DataSourceState.Closed) {
|
|
1092
1197
|
callbackHandler.disable();
|
|
1093
|
-
if (err
|
|
1198
|
+
if (err?.recoverable === false) {
|
|
1094
1199
|
// don't use this datasource's factory again
|
|
1200
|
+
this._logger?.debug(`Culling data source due to err ${err}`);
|
|
1095
1201
|
cullDSFactory?.();
|
|
1202
|
+
// this error indicates we should fallback to only using FDv1 synchronizers
|
|
1203
|
+
if (err instanceof LDFlagDeliveryFallbackError) {
|
|
1204
|
+
this._logger?.debug(`Falling back to FDv1`);
|
|
1205
|
+
this._syncFactories = this._fdv1Synchronizers;
|
|
1206
|
+
}
|
|
1096
1207
|
}
|
|
1097
1208
|
sanitizedStatusCallback(state, err);
|
|
1098
1209
|
this._consumeCancelToken(cancelScheduledTransition);
|
|
@@ -1119,7 +1230,7 @@ class CompositeDataSource {
|
|
|
1119
1230
|
}
|
|
1120
1231
|
}
|
|
1121
1232
|
});
|
|
1122
|
-
currentDS.start((basis, data) => callbackHandler.dataHandler(basis, data), (status, err) => callbackHandler.statusHandler(status, err));
|
|
1233
|
+
currentDS.start((basis, data) => callbackHandler.dataHandler(basis, data), (status, err) => callbackHandler.statusHandler(status, err), selectorGetter);
|
|
1123
1234
|
}
|
|
1124
1235
|
else {
|
|
1125
1236
|
// we don't have a data source to use!
|
|
@@ -1140,8 +1251,8 @@ class CompositeDataSource {
|
|
|
1140
1251
|
// stop the underlying datasource before transitioning to next state
|
|
1141
1252
|
currentDS?.stop();
|
|
1142
1253
|
if (transitionRequest.err && transitionRequest.transition !== 'stop') {
|
|
1143
|
-
// if the transition was due to an error, throttle the transition
|
|
1144
|
-
const delay = this._backoff.fail();
|
|
1254
|
+
// if the transition was due to an error we're not in the initializer phase, throttle the transition. Fallback between initializers is not throttled.
|
|
1255
|
+
const delay = this._initPhaseActive ? 0 : this._backoff.fail();
|
|
1145
1256
|
const { promise, cancel: cancelDelay } = this._cancellableDelay(delay);
|
|
1146
1257
|
this._cancelTokens.push(cancelDelay);
|
|
1147
1258
|
const delayedTransition = promise.then(() => {
|
|
@@ -1176,6 +1287,7 @@ class CompositeDataSource {
|
|
|
1176
1287
|
this._initPhaseActive = this._initFactories.length() > 0; // init phase if we have initializers;
|
|
1177
1288
|
this._initFactories.reset();
|
|
1178
1289
|
this._syncFactories.reset();
|
|
1290
|
+
this._fdv1Synchronizers.reset();
|
|
1179
1291
|
this._externalTransitionPromise = new Promise((tr) => {
|
|
1180
1292
|
this._externalTransitionResolve = tr;
|
|
1181
1293
|
});
|
|
@@ -1210,6 +1322,11 @@ class CompositeDataSource {
|
|
|
1210
1322
|
break;
|
|
1211
1323
|
case 'fallback':
|
|
1212
1324
|
default:
|
|
1325
|
+
// if asked to fallback after using all init factories, switch to sync factories
|
|
1326
|
+
if (this._initPhaseActive && this._initFactories.pos() >= this._initFactories.length()) {
|
|
1327
|
+
this._initPhaseActive = false;
|
|
1328
|
+
this._syncFactories.reset();
|
|
1329
|
+
}
|
|
1213
1330
|
if (this._initPhaseActive) {
|
|
1214
1331
|
isPrimary = this._initFactories.pos() === 0;
|
|
1215
1332
|
factory = this._initFactories.next();
|
|
@@ -1301,31 +1418,6 @@ var DataSourceErrorKind;
|
|
|
1301
1418
|
DataSourceErrorKind["InvalidData"] = "INVALID_DATA";
|
|
1302
1419
|
})(DataSourceErrorKind || (DataSourceErrorKind = {}));
|
|
1303
1420
|
|
|
1304
|
-
class LDFileDataSourceError extends Error {
|
|
1305
|
-
constructor(message) {
|
|
1306
|
-
super(message);
|
|
1307
|
-
this.name = 'LaunchDarklyFileDataSourceError';
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
class LDPollingError extends Error {
|
|
1311
|
-
constructor(kind, message, status, recoverable = true) {
|
|
1312
|
-
super(message);
|
|
1313
|
-
this.kind = kind;
|
|
1314
|
-
this.status = status;
|
|
1315
|
-
this.name = 'LaunchDarklyPollingError';
|
|
1316
|
-
this.recoverable = recoverable;
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
class LDStreamingError extends Error {
|
|
1320
|
-
constructor(kind, message, code, recoverable = true) {
|
|
1321
|
-
super(message);
|
|
1322
|
-
this.kind = kind;
|
|
1323
|
-
this.code = code;
|
|
1324
|
-
this.name = 'LaunchDarklyStreamingError';
|
|
1325
|
-
this.recoverable = recoverable;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
1421
|
/* eslint-disable import/prefer-default-export */
|
|
1330
1422
|
/**
|
|
1331
1423
|
* Enable / disable Auto environment attributes. When enabled, the SDK will automatically
|
|
@@ -1808,7 +1900,7 @@ class ServiceEndpoints {
|
|
|
1808
1900
|
}
|
|
1809
1901
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1810
1902
|
ServiceEndpoints.DEFAULT_EVENTS = 'https://events.launchdarkly.com';
|
|
1811
|
-
function getWithParams(uri, parameters) {
|
|
1903
|
+
function getWithParams(uri, parameters = []) {
|
|
1812
1904
|
if (parameters.length === 0) {
|
|
1813
1905
|
return uri;
|
|
1814
1906
|
}
|
|
@@ -1837,7 +1929,7 @@ function getStreamingUri(endpoints, path, parameters) {
|
|
|
1837
1929
|
* @param path The path to the resource, devoid of any query parameters or hrefs.
|
|
1838
1930
|
* @param parameters The query parameters. These query parameters must already have the appropriate encoding applied. This function WILL NOT apply it for you.
|
|
1839
1931
|
*/
|
|
1840
|
-
function getPollingUri(endpoints, path, parameters) {
|
|
1932
|
+
function getPollingUri(endpoints, path, parameters = []) {
|
|
1841
1933
|
const canonicalizedPath = canonicalizePath(path);
|
|
1842
1934
|
const combinedParameters = [...parameters];
|
|
1843
1935
|
if (endpoints.payloadFilterKey) {
|
|
@@ -1852,7 +1944,7 @@ function getPollingUri(endpoints, path, parameters) {
|
|
|
1852
1944
|
* @param path The path to the resource, devoid of any query parameters or hrefs.
|
|
1853
1945
|
* @param parameters The query parameters. These query parameters must already have the appropriate encoding applied. This function WILL NOT apply it for you.
|
|
1854
1946
|
*/
|
|
1855
|
-
function getEventsUri(endpoints, path, parameters) {
|
|
1947
|
+
function getEventsUri(endpoints, path, parameters = []) {
|
|
1856
1948
|
const canonicalizedPath = canonicalizePath(path);
|
|
1857
1949
|
return getWithParams(`${endpoints.events}/${canonicalizedPath}`, parameters);
|
|
1858
1950
|
}
|
|
@@ -2351,7 +2443,9 @@ function counterKey(event) {
|
|
|
2351
2443
|
* @internal
|
|
2352
2444
|
*/
|
|
2353
2445
|
class EventSummarizer {
|
|
2354
|
-
constructor() {
|
|
2446
|
+
constructor(_singleContext = false, _contextFilter) {
|
|
2447
|
+
this._singleContext = _singleContext;
|
|
2448
|
+
this._contextFilter = _contextFilter;
|
|
2355
2449
|
this._startDate = 0;
|
|
2356
2450
|
this._endDate = 0;
|
|
2357
2451
|
this._counters = {};
|
|
@@ -2359,6 +2453,9 @@ class EventSummarizer {
|
|
|
2359
2453
|
}
|
|
2360
2454
|
summarizeEvent(event) {
|
|
2361
2455
|
if (isFeature(event) && !event.excludeFromSummaries) {
|
|
2456
|
+
if (!this._context) {
|
|
2457
|
+
this._context = event.context;
|
|
2458
|
+
}
|
|
2362
2459
|
const countKey = counterKey(event);
|
|
2363
2460
|
const counter = this._counters[countKey];
|
|
2364
2461
|
let kinds = this._contextKinds[event.key];
|
|
@@ -2408,14 +2505,19 @@ class EventSummarizer {
|
|
|
2408
2505
|
flagSummary.counters.push(counterOut);
|
|
2409
2506
|
return acc;
|
|
2410
2507
|
}, {});
|
|
2411
|
-
|
|
2508
|
+
const event = {
|
|
2412
2509
|
startDate: this._startDate,
|
|
2413
2510
|
endDate: this._endDate,
|
|
2414
2511
|
features,
|
|
2415
2512
|
kind: 'summary',
|
|
2513
|
+
context: this._context !== undefined && this._singleContext
|
|
2514
|
+
? this._contextFilter?.filter(this._context)
|
|
2515
|
+
: undefined,
|
|
2416
2516
|
};
|
|
2517
|
+
this._clearSummary();
|
|
2518
|
+
return event;
|
|
2417
2519
|
}
|
|
2418
|
-
|
|
2520
|
+
_clearSummary() {
|
|
2419
2521
|
this._startDate = 0;
|
|
2420
2522
|
this._endDate = 0;
|
|
2421
2523
|
this._counters = {};
|
|
@@ -2430,6 +2532,38 @@ class LDInvalidSDKKeyError extends Error {
|
|
|
2430
2532
|
}
|
|
2431
2533
|
}
|
|
2432
2534
|
|
|
2535
|
+
class MultiEventSummarizer {
|
|
2536
|
+
constructor(_contextFilter, _logger) {
|
|
2537
|
+
this._contextFilter = _contextFilter;
|
|
2538
|
+
this._logger = _logger;
|
|
2539
|
+
this._summarizers = {};
|
|
2540
|
+
}
|
|
2541
|
+
summarizeEvent(event) {
|
|
2542
|
+
if (isFeature(event)) {
|
|
2543
|
+
const key = event.context.canonicalUnfilteredJson();
|
|
2544
|
+
if (!key) {
|
|
2545
|
+
if (event.context.valid) {
|
|
2546
|
+
// The context appeared valid, but it could not be hashed.
|
|
2547
|
+
// This is likely because of a cycle in the data.
|
|
2548
|
+
this._logger?.error('Unable to serialize context, likely the context contains a cycle.');
|
|
2549
|
+
}
|
|
2550
|
+
return;
|
|
2551
|
+
}
|
|
2552
|
+
let summarizer = this._summarizers[key];
|
|
2553
|
+
if (!summarizer) {
|
|
2554
|
+
this._summarizers[key] = new EventSummarizer(true, this._contextFilter);
|
|
2555
|
+
summarizer = this._summarizers[key];
|
|
2556
|
+
}
|
|
2557
|
+
summarizer.summarizeEvent(event);
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
getSummaries() {
|
|
2561
|
+
const summarizersToFlush = this._summarizers;
|
|
2562
|
+
this._summarizers = {};
|
|
2563
|
+
return Object.values(summarizersToFlush).map((summarizer) => summarizer.getSummary());
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2433
2567
|
/**
|
|
2434
2568
|
* The contents of this file are for event sampling. They are not used for
|
|
2435
2569
|
* any purpose requiring cryptographic security.
|
|
@@ -2450,12 +2584,14 @@ function shouldSample(ratio) {
|
|
|
2450
2584
|
return Math.floor(Math.random() * truncated) === 0;
|
|
2451
2585
|
}
|
|
2452
2586
|
|
|
2587
|
+
function isMultiEventSummarizer(summarizer) {
|
|
2588
|
+
return summarizer.getSummaries !== undefined;
|
|
2589
|
+
}
|
|
2453
2590
|
class EventProcessor {
|
|
2454
|
-
constructor(_config, clientContext, baseHeaders, _contextDeduplicator, _diagnosticsManager, start = true) {
|
|
2591
|
+
constructor(_config, clientContext, baseHeaders, _contextDeduplicator, _diagnosticsManager, start = true, summariesPerContext = false) {
|
|
2455
2592
|
this._config = _config;
|
|
2456
2593
|
this._contextDeduplicator = _contextDeduplicator;
|
|
2457
2594
|
this._diagnosticsManager = _diagnosticsManager;
|
|
2458
|
-
this._summarizer = new EventSummarizer();
|
|
2459
2595
|
this._queue = [];
|
|
2460
2596
|
this._lastKnownPastTime = 0;
|
|
2461
2597
|
this._droppedEvents = 0;
|
|
@@ -2468,6 +2604,12 @@ class EventProcessor {
|
|
|
2468
2604
|
this._logger = clientContext.basicConfiguration.logger;
|
|
2469
2605
|
this._eventSender = new EventSender(clientContext, baseHeaders);
|
|
2470
2606
|
this._contextFilter = new ContextFilter(_config.allAttributesPrivate, _config.privateAttributes.map((ref) => new AttributeReference(ref)));
|
|
2607
|
+
if (summariesPerContext) {
|
|
2608
|
+
this._summarizer = new MultiEventSummarizer(this._contextFilter, this._logger);
|
|
2609
|
+
}
|
|
2610
|
+
else {
|
|
2611
|
+
this._summarizer = new EventSummarizer();
|
|
2612
|
+
}
|
|
2471
2613
|
if (start) {
|
|
2472
2614
|
this.start();
|
|
2473
2615
|
}
|
|
@@ -2519,10 +2661,19 @@ class EventProcessor {
|
|
|
2519
2661
|
}
|
|
2520
2662
|
const eventsToFlush = this._queue;
|
|
2521
2663
|
this._queue = [];
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2664
|
+
if (isMultiEventSummarizer(this._summarizer)) {
|
|
2665
|
+
const summaries = this._summarizer.getSummaries();
|
|
2666
|
+
summaries.forEach((summary) => {
|
|
2667
|
+
if (Object.keys(summary.features).length) {
|
|
2668
|
+
eventsToFlush.push(summary);
|
|
2669
|
+
}
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
else {
|
|
2673
|
+
const summary = this._summarizer.getSummary();
|
|
2674
|
+
if (Object.keys(summary.features).length) {
|
|
2675
|
+
eventsToFlush.push(summary);
|
|
2676
|
+
}
|
|
2526
2677
|
}
|
|
2527
2678
|
if (!eventsToFlush.length) {
|
|
2528
2679
|
return;
|
|
@@ -2833,7 +2984,7 @@ class PayloadProcessor {
|
|
|
2833
2984
|
}
|
|
2834
2985
|
// at the time of writing this, it was agreed upon that SDKs could assume exactly 1 element in this list. In the future, a negotiation of protocol version will be required to remove this assumption.
|
|
2835
2986
|
const payload = data.payloads[0];
|
|
2836
|
-
switch (payload?.
|
|
2987
|
+
switch (payload?.intentCode) {
|
|
2837
2988
|
case 'xfer-full':
|
|
2838
2989
|
this._tempBasis = true;
|
|
2839
2990
|
break;
|
|
@@ -2846,6 +2997,7 @@ class PayloadProcessor {
|
|
|
2846
2997
|
break;
|
|
2847
2998
|
default:
|
|
2848
2999
|
// unrecognized intent code, return
|
|
3000
|
+
this._logger?.warn(`Unable to process intent code '${payload?.intentCode}'.`);
|
|
2849
3001
|
return;
|
|
2850
3002
|
}
|
|
2851
3003
|
this._tempId = payload?.id;
|
|
@@ -3113,6 +3265,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3113
3265
|
NullEventProcessor: NullEventProcessor,
|
|
3114
3266
|
PayloadProcessor: PayloadProcessor,
|
|
3115
3267
|
PayloadStreamReader: PayloadStreamReader,
|
|
3268
|
+
canonicalize: canonicalize,
|
|
3116
3269
|
initMetadataFromHeaders: initMetadataFromHeaders,
|
|
3117
3270
|
isLegacyUser: isLegacyUser,
|
|
3118
3271
|
isMultiKind: isMultiKind,
|
|
@@ -3123,5 +3276,5 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3123
3276
|
shouldSample: shouldSample
|
|
3124
3277
|
});
|
|
3125
3278
|
|
|
3126
|
-
export { ApplicationTags, AttributeReference, AutoEnvAttributes, BasicLogger, ClientContext, CompositeDataSource, Context, ContextFilter, DataSourceErrorKind, DateValidator, DefaultBackoff, FactoryOrInstance, Function, KindValidator, LDClientError, LDFileDataSourceError, LDPollingError, LDStreamingError, LDTimeoutError, LDUnexpectedResponseError, NullableBoolean, NumberWithMinimum, OptionMessages, SafeLogger, ServiceEndpoints, StringMatchingRegex, Type, TypeArray, TypeValidators, base64UrlEncode, cancelableTimedPromise, clone, createSafeLogger, debounce, deepCompact, defaultHeaders, fastDeepEqual, getEventsUri, getPollingUri, getStreamingUri, httpErrorMessage, index as internal, isHttpLocallyRecoverable, isHttpRecoverable, noop, secondsToMillis, shouldRetry, sleep, index$1 as subsystem, timedPromise };
|
|
3279
|
+
export { ApplicationTags, AttributeReference, AutoEnvAttributes, BasicLogger, ClientContext, CompositeDataSource, Context, ContextFilter, DataSourceErrorKind, DateValidator, DefaultBackoff, FactoryOrInstance, Function, KindValidator, LDClientError, LDFileDataSourceError, LDFlagDeliveryFallbackError, LDPollingError, LDStreamingError, LDTimeoutError, LDUnexpectedResponseError, NullableBoolean, NumberWithMinimum, OptionMessages, SafeLogger, ServiceEndpoints, StringMatchingRegex, Type, TypeArray, TypeValidators, base64UrlEncode, cancelableTimedPromise, clone, createSafeLogger, debounce, deepCompact, defaultHeaders, fastDeepEqual, getEventsUri, getPollingUri, getStreamingUri, httpErrorMessage, index as internal, isHttpLocallyRecoverable, isHttpRecoverable, noop, secondsToMillis, shouldRetry, sleep, index$1 as subsystem, timedPromise };
|
|
3127
3280
|
//# sourceMappingURL=index.mjs.map
|