@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.
Files changed (74) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/AttributeReference.d.ts +1 -0
  3. package/dist/cjs/AttributeReference.d.ts.map +1 -1
  4. package/dist/cjs/Context.d.ts +9 -0
  5. package/dist/cjs/Context.d.ts.map +1 -1
  6. package/dist/cjs/api/platform/Requests.d.ts +1 -0
  7. package/dist/cjs/api/platform/Requests.d.ts.map +1 -1
  8. package/dist/cjs/api/subsystem/DataSystem/CallbackHandler.d.ts.map +1 -1
  9. package/dist/cjs/api/subsystem/DataSystem/DataSource.d.ts +18 -2
  10. package/dist/cjs/api/subsystem/DataSystem/DataSource.d.ts.map +1 -1
  11. package/dist/cjs/datasource/CompositeDataSource.d.ts +7 -2
  12. package/dist/cjs/datasource/CompositeDataSource.d.ts.map +1 -1
  13. package/dist/cjs/datasource/errors.d.ts +9 -0
  14. package/dist/cjs/datasource/errors.d.ts.map +1 -1
  15. package/dist/cjs/datasource/index.d.ts +2 -2
  16. package/dist/cjs/datasource/index.d.ts.map +1 -1
  17. package/dist/cjs/index.cjs +201 -47
  18. package/dist/cjs/index.cjs.map +1 -1
  19. package/dist/cjs/index.d.ts +2 -2
  20. package/dist/cjs/index.d.ts.map +1 -1
  21. package/dist/cjs/internal/events/EventProcessor.d.ts +1 -1
  22. package/dist/cjs/internal/events/EventProcessor.d.ts.map +1 -1
  23. package/dist/cjs/internal/events/LDEventSummarizer.d.ts +32 -0
  24. package/dist/cjs/internal/events/LDEventSummarizer.d.ts.map +1 -0
  25. package/dist/cjs/internal/events/MultiEventSummarizer.d.ts +13 -0
  26. package/dist/cjs/internal/events/MultiEventSummarizer.d.ts.map +1 -0
  27. package/dist/cjs/internal/fdv2/payloadProcessor.d.ts.map +1 -1
  28. package/dist/cjs/internal/fdv2/proto.d.ts +1 -1
  29. package/dist/cjs/internal/fdv2/proto.d.ts.map +1 -1
  30. package/dist/cjs/internal/index.d.ts +1 -0
  31. package/dist/cjs/internal/index.d.ts.map +1 -1
  32. package/dist/cjs/internal/json/canonicalize.d.ts +10 -0
  33. package/dist/cjs/internal/json/canonicalize.d.ts.map +1 -0
  34. package/dist/cjs/internal/json/index.d.ts +2 -0
  35. package/dist/cjs/internal/json/index.d.ts.map +1 -0
  36. package/dist/cjs/options/ServiceEndpoints.d.ts +2 -2
  37. package/dist/cjs/options/ServiceEndpoints.d.ts.map +1 -1
  38. package/dist/esm/AttributeReference.d.ts +1 -0
  39. package/dist/esm/AttributeReference.d.ts.map +1 -1
  40. package/dist/esm/Context.d.ts +9 -0
  41. package/dist/esm/Context.d.ts.map +1 -1
  42. package/dist/esm/api/platform/Requests.d.ts +1 -0
  43. package/dist/esm/api/platform/Requests.d.ts.map +1 -1
  44. package/dist/esm/api/subsystem/DataSystem/CallbackHandler.d.ts.map +1 -1
  45. package/dist/esm/api/subsystem/DataSystem/DataSource.d.ts +18 -2
  46. package/dist/esm/api/subsystem/DataSystem/DataSource.d.ts.map +1 -1
  47. package/dist/esm/datasource/CompositeDataSource.d.ts +7 -2
  48. package/dist/esm/datasource/CompositeDataSource.d.ts.map +1 -1
  49. package/dist/esm/datasource/errors.d.ts +9 -0
  50. package/dist/esm/datasource/errors.d.ts.map +1 -1
  51. package/dist/esm/datasource/index.d.ts +2 -2
  52. package/dist/esm/datasource/index.d.ts.map +1 -1
  53. package/dist/esm/index.d.ts +2 -2
  54. package/dist/esm/index.d.ts.map +1 -1
  55. package/dist/esm/index.mjs +201 -48
  56. package/dist/esm/index.mjs.map +1 -1
  57. package/dist/esm/internal/events/EventProcessor.d.ts +1 -1
  58. package/dist/esm/internal/events/EventProcessor.d.ts.map +1 -1
  59. package/dist/esm/internal/events/LDEventSummarizer.d.ts +32 -0
  60. package/dist/esm/internal/events/LDEventSummarizer.d.ts.map +1 -0
  61. package/dist/esm/internal/events/MultiEventSummarizer.d.ts +13 -0
  62. package/dist/esm/internal/events/MultiEventSummarizer.d.ts.map +1 -0
  63. package/dist/esm/internal/fdv2/payloadProcessor.d.ts.map +1 -1
  64. package/dist/esm/internal/fdv2/proto.d.ts +1 -1
  65. package/dist/esm/internal/fdv2/proto.d.ts.map +1 -1
  66. package/dist/esm/internal/index.d.ts +1 -0
  67. package/dist/esm/internal/index.d.ts.map +1 -1
  68. package/dist/esm/internal/json/canonicalize.d.ts +10 -0
  69. package/dist/esm/internal/json/canonicalize.d.ts.map +1 -0
  70. package/dist/esm/internal/json/index.d.ts +2 -0
  71. package/dist/esm/internal/json/index.d.ts.map +1 -0
  72. package/dist/esm/options/ServiceEndpoints.d.ts +2 -2
  73. package/dist/esm/options/ServiceEndpoints.d.ts.map +1 -1
  74. package/package.json +1 -1
@@ -117,6 +117,9 @@ class AttributeReference {
117
117
  return (this.depth === other.depth &&
118
118
  this._components.every((value, index) => value === other.getComponent(index)));
119
119
  }
120
+ get components() {
121
+ return [...this._components];
122
+ }
120
123
  }
121
124
  /**
122
125
  * For use as invalid references when deserializing Flag/Segment data.
@@ -316,6 +319,41 @@ function isLegacyUser(context) {
316
319
  return !('kind' in context) || context.kind === null || context.kind === undefined;
317
320
  }
318
321
 
322
+ /**
323
+ * Given some object to serialize product a canonicalized JSON string.
324
+ * https://www.rfc-editor.org/rfc/rfc8785.html
325
+ *
326
+ * We do not support custom toJSON methods on objects. Objects should be limited to basic types.
327
+ *
328
+ * @param object The object to serialize.
329
+ */
330
+ function canonicalize(object, visited = []) {
331
+ // For JavaScript the default JSON serialization will produce canonicalized output for basic types.
332
+ if (object === null || typeof object !== 'object') {
333
+ return JSON.stringify(object);
334
+ }
335
+ if (visited.includes(object)) {
336
+ throw new Error('Cycle detected');
337
+ }
338
+ if (Array.isArray(object)) {
339
+ const values = object
340
+ .map((item) => canonicalize(item, [...visited, object]))
341
+ .map((item) => (item === undefined ? 'null' : item));
342
+ return `[${values.join(',')}]`;
343
+ }
344
+ const values = Object.keys(object)
345
+ .sort()
346
+ .map((key) => {
347
+ const value = canonicalize(object[key], [...visited, object]);
348
+ if (value !== undefined) {
349
+ return `${JSON.stringify(key)}:${value}`;
350
+ }
351
+ return undefined;
352
+ })
353
+ .filter((item) => item !== undefined);
354
+ return `{${values.join(',')}}`;
355
+ }
356
+
319
357
  // The general strategy for the context is to transform the passed in context
320
358
  // as little as possible. We do convert the legacy users to a single kind
321
359
  // context, but we do not translate all passed contexts into a rigid structure.
@@ -690,6 +728,28 @@ class Context {
690
728
  get legacy() {
691
729
  return this._wasLegacy;
692
730
  }
731
+ /**
732
+ * Get the serialized canonical JSON for this context. This is not filtered for use in events.
733
+ *
734
+ * This method will cache the result.
735
+ *
736
+ * @returns The serialized canonical JSON or undefined if it cannot be serialized.
737
+ */
738
+ canonicalUnfilteredJson() {
739
+ if (!this.valid) {
740
+ return undefined;
741
+ }
742
+ if (this._cachedCanonicalJson) {
743
+ return this._cachedCanonicalJson;
744
+ }
745
+ try {
746
+ this._cachedCanonicalJson = canonicalize(Context.toLDContext(this));
747
+ }
748
+ catch {
749
+ // Indicated by undefined being returned.
750
+ }
751
+ return this._cachedCanonicalJson;
752
+ }
693
753
  }
694
754
  Context.UserKind = DEFAULT_KIND;
695
755
 
@@ -891,8 +951,6 @@ class CallbackHandler {
891
951
  if (this._disabled) {
892
952
  return;
893
953
  }
894
- // TODO: SDK-1044 track selector for future synchronizer to use
895
- // report data up
896
954
  this._dataCallback(basis, data);
897
955
  }
898
956
  async statusHandler(status, err) {
@@ -904,6 +962,11 @@ class CallbackHandler {
904
962
  }
905
963
 
906
964
  // TODO: refactor client-sdk to use this enum
965
+ /**
966
+ * @experimental
967
+ * This feature is not stable and not subject to any backwards compatibility guarantees or semantic
968
+ * versioning. It is not suitable for production usage.
969
+ */
907
970
  var DataSourceState;
908
971
  (function (DataSourceState) {
909
972
  // Positive confirmation of connection/data receipt
@@ -1004,6 +1067,43 @@ class DataSourceList {
1004
1067
  }
1005
1068
  }
1006
1069
 
1070
+ class LDFileDataSourceError extends Error {
1071
+ constructor(message) {
1072
+ super(message);
1073
+ this.name = 'LaunchDarklyFileDataSourceError';
1074
+ }
1075
+ }
1076
+ class LDPollingError extends Error {
1077
+ constructor(kind, message, status, recoverable = true) {
1078
+ super(message);
1079
+ this.kind = kind;
1080
+ this.status = status;
1081
+ this.name = 'LaunchDarklyPollingError';
1082
+ this.recoverable = recoverable;
1083
+ }
1084
+ }
1085
+ class LDStreamingError extends Error {
1086
+ constructor(kind, message, code, recoverable = true) {
1087
+ super(message);
1088
+ this.kind = kind;
1089
+ this.code = code;
1090
+ this.name = 'LaunchDarklyStreamingError';
1091
+ this.recoverable = recoverable;
1092
+ }
1093
+ }
1094
+ /**
1095
+ * This is a short term error and will be removed once FDv2 adoption is sufficient.
1096
+ */
1097
+ class LDFlagDeliveryFallbackError extends Error {
1098
+ constructor(kind, message, code) {
1099
+ super(message);
1100
+ this.kind = kind;
1101
+ this.code = code;
1102
+ this.name = 'LDFlagDeliveryFallbackError';
1103
+ this.recoverable = false;
1104
+ }
1105
+ }
1106
+
1007
1107
  const DEFAULT_FALLBACK_TIME_MS = 2 * 60 * 1000;
1008
1108
  const DEFAULT_RECOVERY_TIME_MS = 5 * 60 * 1000;
1009
1109
  /**
@@ -1014,8 +1114,12 @@ class CompositeDataSource {
1014
1114
  /**
1015
1115
  * @param initializers factories to create {@link DataSystemInitializer}s, in priority order.
1016
1116
  * @param synchronizers factories to create {@link DataSystemSynchronizer}s, in priority order.
1117
+ * @param fdv1Synchronizers factories to fallback to if we need to fallback to FDv1.
1118
+ * @param _logger for logging
1119
+ * @param _transitionConditions to control automated transition between datasources. Typically only used for testing.
1120
+ * @param _backoff to control delay between transitions. Typically only used for testing.
1017
1121
  */
1018
- constructor(initializers, synchronizers, _logger, _transitionConditions = {
1122
+ constructor(initializers, synchronizers, fdv1Synchronizers, _logger, _transitionConditions = {
1019
1123
  [DataSourceState.Valid]: {
1020
1124
  durationMS: DEFAULT_RECOVERY_TIME_MS,
1021
1125
  transition: 'recover',
@@ -1051,8 +1155,9 @@ class CompositeDataSource {
1051
1155
  this._initPhaseActive = initializers.length > 0; // init phase if we have initializers
1052
1156
  this._initFactories = new DataSourceList(false, initializers);
1053
1157
  this._syncFactories = new DataSourceList(true, synchronizers);
1158
+ this._fdv1Synchronizers = new DataSourceList(true, fdv1Synchronizers);
1054
1159
  }
1055
- async start(dataCallback, statusCallback) {
1160
+ async start(dataCallback, statusCallback, selectorGetter) {
1056
1161
  if (!this._stopped) {
1057
1162
  // don't allow multiple simultaneous runs
1058
1163
  this._logger?.info('CompositeDataSource already running. Ignoring call to start.');
@@ -1089,12 +1194,18 @@ class CompositeDataSource {
1089
1194
  // When we get a status update, we want to fallback if it is an error. We also want to schedule a transition for some
1090
1195
  // time in the future if this status remains for some duration (ex: Recover to primary synchronizer after the secondary
1091
1196
  // synchronizer has been Valid for some time). These scheduled transitions are configurable in the constructor.
1092
- this._logger?.debug(`CompositeDataSource received state ${state} from underlying data source.`);
1197
+ this._logger?.debug(`CompositeDataSource received state ${state} from underlying data source. Err is ${err}`);
1093
1198
  if (err || state === DataSourceState.Closed) {
1094
1199
  callbackHandler.disable();
1095
- if (err.recoverable === false) {
1200
+ if (err?.recoverable === false) {
1096
1201
  // don't use this datasource's factory again
1202
+ this._logger?.debug(`Culling data source due to err ${err}`);
1097
1203
  cullDSFactory?.();
1204
+ // this error indicates we should fallback to only using FDv1 synchronizers
1205
+ if (err instanceof LDFlagDeliveryFallbackError) {
1206
+ this._logger?.debug(`Falling back to FDv1`);
1207
+ this._syncFactories = this._fdv1Synchronizers;
1208
+ }
1098
1209
  }
1099
1210
  sanitizedStatusCallback(state, err);
1100
1211
  this._consumeCancelToken(cancelScheduledTransition);
@@ -1121,7 +1232,7 @@ class CompositeDataSource {
1121
1232
  }
1122
1233
  }
1123
1234
  });
1124
- currentDS.start((basis, data) => callbackHandler.dataHandler(basis, data), (status, err) => callbackHandler.statusHandler(status, err));
1235
+ currentDS.start((basis, data) => callbackHandler.dataHandler(basis, data), (status, err) => callbackHandler.statusHandler(status, err), selectorGetter);
1125
1236
  }
1126
1237
  else {
1127
1238
  // we don't have a data source to use!
@@ -1142,8 +1253,8 @@ class CompositeDataSource {
1142
1253
  // stop the underlying datasource before transitioning to next state
1143
1254
  currentDS?.stop();
1144
1255
  if (transitionRequest.err && transitionRequest.transition !== 'stop') {
1145
- // if the transition was due to an error, throttle the transition
1146
- const delay = this._backoff.fail();
1256
+ // if the transition was due to an error we're not in the initializer phase, throttle the transition. Fallback between initializers is not throttled.
1257
+ const delay = this._initPhaseActive ? 0 : this._backoff.fail();
1147
1258
  const { promise, cancel: cancelDelay } = this._cancellableDelay(delay);
1148
1259
  this._cancelTokens.push(cancelDelay);
1149
1260
  const delayedTransition = promise.then(() => {
@@ -1178,6 +1289,7 @@ class CompositeDataSource {
1178
1289
  this._initPhaseActive = this._initFactories.length() > 0; // init phase if we have initializers;
1179
1290
  this._initFactories.reset();
1180
1291
  this._syncFactories.reset();
1292
+ this._fdv1Synchronizers.reset();
1181
1293
  this._externalTransitionPromise = new Promise((tr) => {
1182
1294
  this._externalTransitionResolve = tr;
1183
1295
  });
@@ -1212,6 +1324,11 @@ class CompositeDataSource {
1212
1324
  break;
1213
1325
  case 'fallback':
1214
1326
  default:
1327
+ // if asked to fallback after using all init factories, switch to sync factories
1328
+ if (this._initPhaseActive && this._initFactories.pos() >= this._initFactories.length()) {
1329
+ this._initPhaseActive = false;
1330
+ this._syncFactories.reset();
1331
+ }
1215
1332
  if (this._initPhaseActive) {
1216
1333
  isPrimary = this._initFactories.pos() === 0;
1217
1334
  factory = this._initFactories.next();
@@ -1303,31 +1420,6 @@ exports.DataSourceErrorKind = void 0;
1303
1420
  DataSourceErrorKind["InvalidData"] = "INVALID_DATA";
1304
1421
  })(exports.DataSourceErrorKind || (exports.DataSourceErrorKind = {}));
1305
1422
 
1306
- class LDFileDataSourceError extends Error {
1307
- constructor(message) {
1308
- super(message);
1309
- this.name = 'LaunchDarklyFileDataSourceError';
1310
- }
1311
- }
1312
- class LDPollingError extends Error {
1313
- constructor(kind, message, status, recoverable = true) {
1314
- super(message);
1315
- this.kind = kind;
1316
- this.status = status;
1317
- this.name = 'LaunchDarklyPollingError';
1318
- this.recoverable = recoverable;
1319
- }
1320
- }
1321
- class LDStreamingError extends Error {
1322
- constructor(kind, message, code, recoverable = true) {
1323
- super(message);
1324
- this.kind = kind;
1325
- this.code = code;
1326
- this.name = 'LaunchDarklyStreamingError';
1327
- this.recoverable = recoverable;
1328
- }
1329
- }
1330
-
1331
1423
  /* eslint-disable import/prefer-default-export */
1332
1424
  /**
1333
1425
  * Enable / disable Auto environment attributes. When enabled, the SDK will automatically
@@ -1810,7 +1902,7 @@ class ServiceEndpoints {
1810
1902
  }
1811
1903
  // eslint-disable-next-line @typescript-eslint/naming-convention
1812
1904
  ServiceEndpoints.DEFAULT_EVENTS = 'https://events.launchdarkly.com';
1813
- function getWithParams(uri, parameters) {
1905
+ function getWithParams(uri, parameters = []) {
1814
1906
  if (parameters.length === 0) {
1815
1907
  return uri;
1816
1908
  }
@@ -1839,7 +1931,7 @@ function getStreamingUri(endpoints, path, parameters) {
1839
1931
  * @param path The path to the resource, devoid of any query parameters or hrefs.
1840
1932
  * @param parameters The query parameters. These query parameters must already have the appropriate encoding applied. This function WILL NOT apply it for you.
1841
1933
  */
1842
- function getPollingUri(endpoints, path, parameters) {
1934
+ function getPollingUri(endpoints, path, parameters = []) {
1843
1935
  const canonicalizedPath = canonicalizePath(path);
1844
1936
  const combinedParameters = [...parameters];
1845
1937
  if (endpoints.payloadFilterKey) {
@@ -1854,7 +1946,7 @@ function getPollingUri(endpoints, path, parameters) {
1854
1946
  * @param path The path to the resource, devoid of any query parameters or hrefs.
1855
1947
  * @param parameters The query parameters. These query parameters must already have the appropriate encoding applied. This function WILL NOT apply it for you.
1856
1948
  */
1857
- function getEventsUri(endpoints, path, parameters) {
1949
+ function getEventsUri(endpoints, path, parameters = []) {
1858
1950
  const canonicalizedPath = canonicalizePath(path);
1859
1951
  return getWithParams(`${endpoints.events}/${canonicalizedPath}`, parameters);
1860
1952
  }
@@ -2353,7 +2445,9 @@ function counterKey(event) {
2353
2445
  * @internal
2354
2446
  */
2355
2447
  class EventSummarizer {
2356
- constructor() {
2448
+ constructor(_singleContext = false, _contextFilter) {
2449
+ this._singleContext = _singleContext;
2450
+ this._contextFilter = _contextFilter;
2357
2451
  this._startDate = 0;
2358
2452
  this._endDate = 0;
2359
2453
  this._counters = {};
@@ -2361,6 +2455,9 @@ class EventSummarizer {
2361
2455
  }
2362
2456
  summarizeEvent(event) {
2363
2457
  if (isFeature(event) && !event.excludeFromSummaries) {
2458
+ if (!this._context) {
2459
+ this._context = event.context;
2460
+ }
2364
2461
  const countKey = counterKey(event);
2365
2462
  const counter = this._counters[countKey];
2366
2463
  let kinds = this._contextKinds[event.key];
@@ -2410,14 +2507,19 @@ class EventSummarizer {
2410
2507
  flagSummary.counters.push(counterOut);
2411
2508
  return acc;
2412
2509
  }, {});
2413
- return {
2510
+ const event = {
2414
2511
  startDate: this._startDate,
2415
2512
  endDate: this._endDate,
2416
2513
  features,
2417
2514
  kind: 'summary',
2515
+ context: this._context !== undefined && this._singleContext
2516
+ ? this._contextFilter?.filter(this._context)
2517
+ : undefined,
2418
2518
  };
2519
+ this._clearSummary();
2520
+ return event;
2419
2521
  }
2420
- clearSummary() {
2522
+ _clearSummary() {
2421
2523
  this._startDate = 0;
2422
2524
  this._endDate = 0;
2423
2525
  this._counters = {};
@@ -2432,6 +2534,38 @@ class LDInvalidSDKKeyError extends Error {
2432
2534
  }
2433
2535
  }
2434
2536
 
2537
+ class MultiEventSummarizer {
2538
+ constructor(_contextFilter, _logger) {
2539
+ this._contextFilter = _contextFilter;
2540
+ this._logger = _logger;
2541
+ this._summarizers = {};
2542
+ }
2543
+ summarizeEvent(event) {
2544
+ if (isFeature(event)) {
2545
+ const key = event.context.canonicalUnfilteredJson();
2546
+ if (!key) {
2547
+ if (event.context.valid) {
2548
+ // The context appeared valid, but it could not be hashed.
2549
+ // This is likely because of a cycle in the data.
2550
+ this._logger?.error('Unable to serialize context, likely the context contains a cycle.');
2551
+ }
2552
+ return;
2553
+ }
2554
+ let summarizer = this._summarizers[key];
2555
+ if (!summarizer) {
2556
+ this._summarizers[key] = new EventSummarizer(true, this._contextFilter);
2557
+ summarizer = this._summarizers[key];
2558
+ }
2559
+ summarizer.summarizeEvent(event);
2560
+ }
2561
+ }
2562
+ getSummaries() {
2563
+ const summarizersToFlush = this._summarizers;
2564
+ this._summarizers = {};
2565
+ return Object.values(summarizersToFlush).map((summarizer) => summarizer.getSummary());
2566
+ }
2567
+ }
2568
+
2435
2569
  /**
2436
2570
  * The contents of this file are for event sampling. They are not used for
2437
2571
  * any purpose requiring cryptographic security.
@@ -2452,12 +2586,14 @@ function shouldSample(ratio) {
2452
2586
  return Math.floor(Math.random() * truncated) === 0;
2453
2587
  }
2454
2588
 
2589
+ function isMultiEventSummarizer(summarizer) {
2590
+ return summarizer.getSummaries !== undefined;
2591
+ }
2455
2592
  class EventProcessor {
2456
- constructor(_config, clientContext, baseHeaders, _contextDeduplicator, _diagnosticsManager, start = true) {
2593
+ constructor(_config, clientContext, baseHeaders, _contextDeduplicator, _diagnosticsManager, start = true, summariesPerContext = false) {
2457
2594
  this._config = _config;
2458
2595
  this._contextDeduplicator = _contextDeduplicator;
2459
2596
  this._diagnosticsManager = _diagnosticsManager;
2460
- this._summarizer = new EventSummarizer();
2461
2597
  this._queue = [];
2462
2598
  this._lastKnownPastTime = 0;
2463
2599
  this._droppedEvents = 0;
@@ -2470,6 +2606,12 @@ class EventProcessor {
2470
2606
  this._logger = clientContext.basicConfiguration.logger;
2471
2607
  this._eventSender = new EventSender(clientContext, baseHeaders);
2472
2608
  this._contextFilter = new ContextFilter(_config.allAttributesPrivate, _config.privateAttributes.map((ref) => new AttributeReference(ref)));
2609
+ if (summariesPerContext) {
2610
+ this._summarizer = new MultiEventSummarizer(this._contextFilter, this._logger);
2611
+ }
2612
+ else {
2613
+ this._summarizer = new EventSummarizer();
2614
+ }
2473
2615
  if (start) {
2474
2616
  this.start();
2475
2617
  }
@@ -2521,10 +2663,19 @@ class EventProcessor {
2521
2663
  }
2522
2664
  const eventsToFlush = this._queue;
2523
2665
  this._queue = [];
2524
- const summary = this._summarizer.getSummary();
2525
- this._summarizer.clearSummary();
2526
- if (Object.keys(summary.features).length) {
2527
- eventsToFlush.push(summary);
2666
+ if (isMultiEventSummarizer(this._summarizer)) {
2667
+ const summaries = this._summarizer.getSummaries();
2668
+ summaries.forEach((summary) => {
2669
+ if (Object.keys(summary.features).length) {
2670
+ eventsToFlush.push(summary);
2671
+ }
2672
+ });
2673
+ }
2674
+ else {
2675
+ const summary = this._summarizer.getSummary();
2676
+ if (Object.keys(summary.features).length) {
2677
+ eventsToFlush.push(summary);
2678
+ }
2528
2679
  }
2529
2680
  if (!eventsToFlush.length) {
2530
2681
  return;
@@ -2835,7 +2986,7 @@ class PayloadProcessor {
2835
2986
  }
2836
2987
  // 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.
2837
2988
  const payload = data.payloads[0];
2838
- switch (payload?.code) {
2989
+ switch (payload?.intentCode) {
2839
2990
  case 'xfer-full':
2840
2991
  this._tempBasis = true;
2841
2992
  break;
@@ -2848,6 +2999,7 @@ class PayloadProcessor {
2848
2999
  break;
2849
3000
  default:
2850
3001
  // unrecognized intent code, return
3002
+ this._logger?.warn(`Unable to process intent code '${payload?.intentCode}'.`);
2851
3003
  return;
2852
3004
  }
2853
3005
  this._tempId = payload?.id;
@@ -3115,6 +3267,7 @@ var index = /*#__PURE__*/Object.freeze({
3115
3267
  NullEventProcessor: NullEventProcessor,
3116
3268
  PayloadProcessor: PayloadProcessor,
3117
3269
  PayloadStreamReader: PayloadStreamReader,
3270
+ canonicalize: canonicalize,
3118
3271
  initMetadataFromHeaders: initMetadataFromHeaders,
3119
3272
  isLegacyUser: isLegacyUser,
3120
3273
  isMultiKind: isMultiKind,
@@ -3139,6 +3292,7 @@ exports.Function = Function;
3139
3292
  exports.KindValidator = KindValidator;
3140
3293
  exports.LDClientError = LDClientError;
3141
3294
  exports.LDFileDataSourceError = LDFileDataSourceError;
3295
+ exports.LDFlagDeliveryFallbackError = LDFlagDeliveryFallbackError;
3142
3296
  exports.LDPollingError = LDPollingError;
3143
3297
  exports.LDStreamingError = LDStreamingError;
3144
3298
  exports.LDTimeoutError = LDTimeoutError;