@splitsoftware/splitio-commons 1.11.0 → 1.12.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 (132) hide show
  1. package/CHANGES.txt +15 -9
  2. package/cjs/evaluator/index.js +19 -3
  3. package/cjs/logger/constants.js +6 -4
  4. package/cjs/logger/messages/warn.js +5 -3
  5. package/cjs/sdkClient/client.js +19 -16
  6. package/cjs/sdkClient/clientInputValidation.js +16 -16
  7. package/cjs/sdkFactory/index.js +1 -1
  8. package/cjs/sdkManager/index.js +14 -13
  9. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +3 -10
  10. package/cjs/storages/inMemory/SplitsCacheInMemory.js +2 -10
  11. package/cjs/storages/inRedis/RedisAdapter.js +32 -13
  12. package/cjs/storages/inRedis/SegmentsCacheInRedis.js +2 -2
  13. package/cjs/storages/inRedis/SplitsCacheInRedis.js +39 -22
  14. package/cjs/storages/inRedis/index.js +1 -1
  15. package/cjs/storages/pluggable/SplitsCachePluggable.js +28 -11
  16. package/cjs/storages/pluggable/index.js +1 -1
  17. package/cjs/utils/constants/index.js +16 -2
  18. package/cjs/utils/inputValidation/index.js +5 -5
  19. package/cjs/utils/inputValidation/{splitExistance.js → splitExistence.js} +3 -3
  20. package/cjs/utils/inputValidation/{trafficTypeExistance.js → trafficTypeExistence.js} +6 -6
  21. package/cjs/utils/lang/sets.js +11 -1
  22. package/cjs/utils/settingsValidation/index.js +1 -1
  23. package/cjs/utils/settingsValidation/splitFilters.js +25 -17
  24. package/esm/evaluator/index.js +20 -4
  25. package/esm/logger/constants.js +4 -2
  26. package/esm/logger/messages/warn.js +5 -3
  27. package/esm/sdkClient/client.js +20 -17
  28. package/esm/sdkClient/clientInputValidation.js +18 -18
  29. package/esm/sdkFactory/index.js +1 -1
  30. package/esm/sdkManager/index.js +11 -10
  31. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +4 -11
  32. package/esm/storages/inMemory/SplitsCacheInMemory.js +3 -11
  33. package/esm/storages/inRedis/RedisAdapter.js +32 -13
  34. package/esm/storages/inRedis/SegmentsCacheInRedis.js +2 -2
  35. package/esm/storages/inRedis/SplitsCacheInRedis.js +40 -23
  36. package/esm/storages/inRedis/index.js +1 -1
  37. package/esm/storages/pluggable/SplitsCachePluggable.js +29 -12
  38. package/esm/storages/pluggable/index.js +1 -1
  39. package/esm/utils/constants/index.js +14 -0
  40. package/esm/utils/inputValidation/index.js +2 -2
  41. package/esm/utils/inputValidation/{splitExistance.js → splitExistence.js} +1 -1
  42. package/esm/utils/inputValidation/{trafficTypeExistance.js → trafficTypeExistence.js} +4 -4
  43. package/esm/utils/lang/sets.js +9 -0
  44. package/esm/utils/settingsValidation/index.js +1 -1
  45. package/esm/utils/settingsValidation/splitFilters.js +17 -9
  46. package/package.json +1 -1
  47. package/src/evaluator/index.ts +24 -4
  48. package/src/logger/constants.ts +4 -2
  49. package/src/logger/messages/warn.ts +9 -7
  50. package/src/sdkClient/client.ts +18 -18
  51. package/src/sdkClient/clientInputValidation.ts +18 -18
  52. package/src/sdkFactory/index.ts +1 -1
  53. package/src/sdkFactory/types.ts +3 -7
  54. package/src/sdkManager/index.ts +14 -14
  55. package/src/storages/AbstractSplitsCacheAsync.ts +1 -1
  56. package/src/storages/AbstractSplitsCacheSync.ts +1 -1
  57. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +8 -15
  58. package/src/storages/inMemory/SplitsCacheInMemory.ts +6 -14
  59. package/src/storages/inRedis/EventsCacheInRedis.ts +3 -3
  60. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +3 -3
  61. package/src/storages/inRedis/ImpressionsCacheInRedis.ts +3 -3
  62. package/src/storages/inRedis/RedisAdapter.ts +38 -16
  63. package/src/storages/inRedis/SegmentsCacheInRedis.ts +5 -5
  64. package/src/storages/inRedis/SplitsCacheInRedis.ts +49 -28
  65. package/src/storages/inRedis/TelemetryCacheInRedis.ts +2 -2
  66. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +3 -3
  67. package/src/storages/inRedis/index.ts +1 -1
  68. package/src/storages/pluggable/SplitsCachePluggable.ts +35 -13
  69. package/src/storages/pluggable/index.ts +1 -1
  70. package/src/storages/types.ts +5 -5
  71. package/src/trackers/impressionObserver/utils.ts +1 -1
  72. package/src/types.ts +0 -2
  73. package/src/utils/constants/index.ts +16 -0
  74. package/src/utils/inputValidation/index.ts +2 -2
  75. package/src/utils/inputValidation/{splitExistance.ts → splitExistence.ts} +1 -1
  76. package/src/utils/inputValidation/{trafficTypeExistance.ts → trafficTypeExistence.ts} +4 -4
  77. package/src/utils/lang/sets.ts +9 -1
  78. package/src/utils/redis/RedisMock.ts +1 -3
  79. package/src/utils/settingsValidation/index.ts +1 -1
  80. package/src/utils/settingsValidation/splitFilters.ts +19 -11
  81. package/types/evaluator/index.d.ts +1 -1
  82. package/types/logger/constants.d.ts +4 -2
  83. package/types/sdkClient/identity.d.ts +6 -0
  84. package/types/sdkFactory/types.d.ts +3 -3
  85. package/types/sdkManager/index.d.ts +2 -3
  86. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  87. package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
  88. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
  89. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -1
  90. package/types/storages/inRedis/EventsCacheInRedis.d.ts +2 -2
  91. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +3 -2
  92. package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +2 -2
  93. package/types/storages/inRedis/RedisAdapter.d.ts +1 -1
  94. package/types/storages/inRedis/SegmentsCacheInRedis.d.ts +3 -3
  95. package/types/storages/inRedis/SplitsCacheInRedis.d.ts +10 -14
  96. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +2 -2
  97. package/types/storages/inRedis/{uniqueKeysCacheInRedis.d.ts → UniqueKeysCacheInRedis.d.ts} +3 -2
  98. package/types/storages/pluggable/SplitsCachePluggable.d.ts +10 -9
  99. package/types/storages/types.d.ts +5 -5
  100. package/types/trackers/impressionObserver/utils.d.ts +1 -1
  101. package/types/types.d.ts +0 -2
  102. package/types/utils/constants/index.d.ts +12 -0
  103. package/types/utils/inputValidation/index.d.ts +2 -2
  104. package/types/utils/inputValidation/sdkKey.d.ts +7 -0
  105. package/types/utils/inputValidation/splitExistence.d.ts +7 -0
  106. package/types/utils/inputValidation/trafficTypeExistence.d.ts +9 -0
  107. package/types/utils/lang/sets.d.ts +1 -0
  108. package/types/utils/settingsValidation/splitFilters.d.ts +3 -2
  109. package/types/myLogger.d.ts +0 -5
  110. package/types/sdkClient/types.d.ts +0 -18
  111. package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
  112. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
  113. package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
  114. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
  115. package/types/storages/metadataBuilder.d.ts +0 -3
  116. package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
  117. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
  118. package/types/sync/offline/updaters/splitChangesUpdater.d.ts +0 -0
  119. package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
  120. package/types/sync/submitters/impressionCountsSubmitterInRedis.d.ts +0 -5
  121. package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
  122. package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
  123. package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
  124. package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
  125. package/types/sync/submitters/uniqueKeysSubmitterInRedis.d.ts +0 -5
  126. package/types/sync/syncTaskComposite.d.ts +0 -5
  127. package/types/trackers/filter/bloomFilter.d.ts +0 -10
  128. package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
  129. package/types/trackers/filter/types.d.ts +0 -5
  130. package/types/utils/timeTracker/index.d.ts +0 -70
  131. /package/types/storages/inMemory/{uniqueKeysCacheInMemory.d.ts → UniqueKeysCacheInMemory.d.ts} +0 -0
  132. /package/types/storages/inMemory/{uniqueKeysCacheInMemoryCS.d.ts → UniqueKeysCacheInMemoryCS.d.ts} +0 -0
@@ -12,12 +12,12 @@ import {
12
12
  validateIfOperational
13
13
  } from '../utils/inputValidation';
14
14
  import { startsWith } from '../utils/lang';
15
- import { CONTROL, CONTROL_WITH_CONFIG } from '../utils/constants';
15
+ import { CONTROL, CONTROL_WITH_CONFIG, GET_TREATMENT, GET_TREATMENTS, GET_TREATMENTS_BY_FLAG_SET, GET_TREATMENTS_BY_FLAG_SETS, GET_TREATMENTS_WITH_CONFIG, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, GET_TREATMENT_WITH_CONFIG, TRACK_FN_LABEL } from '../utils/constants';
16
16
  import { IReadinessManager } from '../readiness/types';
17
17
  import { MaybeThenable } from '../dtos/types';
18
18
  import { ISettings, SplitIO } from '../types';
19
19
  import { isStorageSync } from '../trackers/impressionObserver/utils';
20
- import { flagSetsAreValid } from '../utils/settingsValidation/splitFilters';
20
+ import { validateFlagSets } from '../utils/settingsValidation/splitFilters';
21
21
 
22
22
  /**
23
23
  * Decorator that validates the input before actually executing the client methods.
@@ -32,7 +32,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
32
32
  * Avoid repeating this validations code
33
33
  */
34
34
  function validateEvaluationParams(maybeKey: SplitIO.SplitKey, maybeFeatureFlagNameOrNames: string | string[] | undefined, maybeAttributes: SplitIO.Attributes | undefined, methodName: string, maybeFlagSetNameOrNames?: string[]) {
35
- const multi = startsWith(methodName, 'getTreatments');
35
+ const multi = startsWith(methodName, GET_TREATMENTS);
36
36
  const key = validateKey(log, maybeKey, methodName);
37
37
  let splitOrSplits: string | string[] | false = false;
38
38
  let flagSetOrFlagSets: string[] = [];
@@ -42,7 +42,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
42
42
  const attributes = validateAttributes(log, maybeAttributes, methodName);
43
43
  const isNotDestroyed = validateIfNotDestroyed(log, readinessManager, methodName);
44
44
  if (maybeFlagSetNameOrNames) {
45
- flagSetOrFlagSets = flagSetsAreValid(log, methodName, maybeFlagSetNameOrNames, settings.sync.__splitFiltersValidation.groupedFilters.bySet);
45
+ flagSetOrFlagSets = validateFlagSets(log, methodName, maybeFlagSetNameOrNames, settings.sync.__splitFiltersValidation.groupedFilters.bySet);
46
46
  }
47
47
 
48
48
  validateIfOperational(log, readinessManager, methodName, splitOrSplits);
@@ -63,7 +63,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
63
63
  }
64
64
 
65
65
  function getTreatment(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
66
- const params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, 'getTreatment');
66
+ const params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, GET_TREATMENT);
67
67
 
68
68
  if (params.valid) {
69
69
  return client.getTreatment(params.key as SplitIO.SplitKey, params.splitOrSplits as string, params.attributes as SplitIO.Attributes | undefined);
@@ -73,7 +73,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
73
73
  }
74
74
 
75
75
  function getTreatmentWithConfig(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
76
- const params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, 'getTreatmentWithConfig');
76
+ const params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, GET_TREATMENT_WITH_CONFIG);
77
77
 
78
78
  if (params.valid) {
79
79
  return client.getTreatmentWithConfig(params.key as SplitIO.SplitKey, params.splitOrSplits as string, params.attributes as SplitIO.Attributes | undefined);
@@ -83,7 +83,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
83
83
  }
84
84
 
85
85
  function getTreatments(maybeKey: SplitIO.SplitKey, maybeFeatureFlagNames: string[], maybeAttributes?: SplitIO.Attributes) {
86
- const params = validateEvaluationParams(maybeKey, maybeFeatureFlagNames, maybeAttributes, 'getTreatments');
86
+ const params = validateEvaluationParams(maybeKey, maybeFeatureFlagNames, maybeAttributes, GET_TREATMENTS);
87
87
 
88
88
  if (params.valid) {
89
89
  return client.getTreatments(params.key as SplitIO.SplitKey, params.splitOrSplits as string[], params.attributes as SplitIO.Attributes | undefined);
@@ -96,7 +96,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
96
96
  }
97
97
 
98
98
  function getTreatmentsWithConfig(maybeKey: SplitIO.SplitKey, maybeFeatureFlagNames: string[], maybeAttributes?: SplitIO.Attributes) {
99
- const params = validateEvaluationParams(maybeKey, maybeFeatureFlagNames, maybeAttributes, 'getTreatmentsWithConfig');
99
+ const params = validateEvaluationParams(maybeKey, maybeFeatureFlagNames, maybeAttributes, GET_TREATMENTS_WITH_CONFIG);
100
100
 
101
101
  if (params.valid) {
102
102
  return client.getTreatmentsWithConfig(params.key as SplitIO.SplitKey, params.splitOrSplits as string[], params.attributes as SplitIO.Attributes | undefined);
@@ -109,7 +109,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
109
109
  }
110
110
 
111
111
  function getTreatmentsByFlagSets(maybeKey: SplitIO.SplitKey, maybeFlagSets: string[], maybeAttributes?: SplitIO.Attributes) {
112
- const params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, 'getTreatmentsByFlagSets', maybeFlagSets);
112
+ const params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, GET_TREATMENTS_BY_FLAG_SETS, maybeFlagSets);
113
113
 
114
114
  if (params.valid) {
115
115
  return client.getTreatmentsByFlagSets(params.key as SplitIO.SplitKey, params.flagSetOrFlagSets as string[], params.attributes as SplitIO.Attributes | undefined);
@@ -119,7 +119,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
119
119
  }
120
120
 
121
121
  function getTreatmentsWithConfigByFlagSets(maybeKey: SplitIO.SplitKey, maybeFlagSets: string[], maybeAttributes?: SplitIO.Attributes) {
122
- const params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, 'getTreatmentsWithConfigByFlagSets', maybeFlagSets);
122
+ const params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, maybeFlagSets);
123
123
 
124
124
  if (params.valid) {
125
125
  return client.getTreatmentsWithConfigByFlagSets(params.key as SplitIO.SplitKey, params.flagSetOrFlagSets as string[], params.attributes as SplitIO.Attributes | undefined);
@@ -129,7 +129,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
129
129
  }
130
130
 
131
131
  function getTreatmentsByFlagSet(maybeKey: SplitIO.SplitKey, maybeFlagSet: string, maybeAttributes?: SplitIO.Attributes) {
132
- const params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, 'getTreatmentsByFlagSet', [maybeFlagSet]);
132
+ const params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, GET_TREATMENTS_BY_FLAG_SET, [maybeFlagSet]);
133
133
 
134
134
  if (params.valid) {
135
135
  return client.getTreatmentsByFlagSet(params.key as SplitIO.SplitKey, params.flagSetOrFlagSets[0] as string, params.attributes as SplitIO.Attributes | undefined);
@@ -139,7 +139,7 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
139
139
  }
140
140
 
141
141
  function getTreatmentsWithConfigByFlagSet(maybeKey: SplitIO.SplitKey, maybeFlagSet: string, maybeAttributes?: SplitIO.Attributes) {
142
- const params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, 'getTreatmentsWithConfigByFlagSet', [maybeFlagSet]);
142
+ const params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, [maybeFlagSet]);
143
143
 
144
144
  if (params.valid) {
145
145
  return client.getTreatmentsWithConfigByFlagSet(params.key as SplitIO.SplitKey, params.flagSetOrFlagSets[0] as string, params.attributes as SplitIO.Attributes | undefined);
@@ -149,12 +149,12 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
149
149
  }
150
150
 
151
151
  function track(maybeKey: SplitIO.SplitKey, maybeTT: string, maybeEvent: string, maybeEventValue?: number, maybeProperties?: SplitIO.Properties) {
152
- const key = validateKey(log, maybeKey, 'track');
153
- const tt = validateTrafficType(log, maybeTT, 'track');
154
- const event = validateEvent(log, maybeEvent, 'track');
155
- const eventValue = validateEventValue(log, maybeEventValue, 'track');
156
- const { properties, size } = validateEventProperties(log, maybeProperties, 'track');
157
- const isNotDestroyed = validateIfNotDestroyed(log, readinessManager, 'track');
152
+ const key = validateKey(log, maybeKey, TRACK_FN_LABEL);
153
+ const tt = validateTrafficType(log, maybeTT, TRACK_FN_LABEL);
154
+ const event = validateEvent(log, maybeEvent, TRACK_FN_LABEL);
155
+ const eventValue = validateEventValue(log, maybeEventValue, TRACK_FN_LABEL);
156
+ const { properties, size } = validateEventProperties(log, maybeProperties, TRACK_FN_LABEL);
157
+ const isNotDestroyed = validateIfNotDestroyed(log, readinessManager, TRACK_FN_LABEL);
158
158
 
159
159
  if (isNotDestroyed && key && tt && event && eventValue !== false && properties !== false) { // @ts-expect-error
160
160
  return client.track(key, tt, event, eventValue, properties, size);
@@ -83,7 +83,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
83
83
 
84
84
  // SDK client and manager
85
85
  const clientMethod = sdkClientMethodFactory(ctx);
86
- const managerInstance = sdkManagerFactory(log, storage.splits, sdkReadinessManager);
86
+ const managerInstance = sdkManagerFactory(settings, storage.splits, sdkReadinessManager);
87
87
 
88
88
  syncManager && syncManager.start();
89
89
  signalListener && signalListener.start();
@@ -1,9 +1,9 @@
1
1
  import { IIntegrationManager, IIntegrationFactoryParams } from '../integrations/types';
2
2
  import { ISignalListener } from '../listeners/types';
3
- import { ILogger } from '../logger/types';
4
3
  import { IReadinessManager, ISdkReadinessManager } from '../readiness/types';
4
+ import type { sdkManagerFactory } from '../sdkManager';
5
5
  import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types';
6
- import { IStorageAsync, IStorageSync, ISplitsCacheSync, ISplitsCacheAsync, IStorageFactoryParams } from '../storages/types';
6
+ import { IStorageAsync, IStorageSync, IStorageFactoryParams } from '../storages/types';
7
7
  import { ISyncManager } from '../sync/types';
8
8
  import { IImpressionObserver } from '../trackers/impressionObserver/types';
9
9
  import { IImpressionsTracker, IEventTracker, ITelemetryTracker, IFilterAdapter, IUniqueKeysTracker } from '../trackers/types';
@@ -87,11 +87,7 @@ export interface ISdkFactoryParams {
87
87
  syncManagerFactory?: (params: ISdkFactoryContextSync) => ISyncManager,
88
88
 
89
89
  // Sdk manager factory
90
- sdkManagerFactory: (
91
- log: ILogger,
92
- splits: ISplitsCacheSync | ISplitsCacheAsync,
93
- sdkReadinessManager: ISdkReadinessManager
94
- ) => SplitIO.IManager | SplitIO.IAsyncManager,
90
+ sdkManagerFactory: typeof sdkManagerFactory,
95
91
 
96
92
  // Sdk client method factory (ISDK::client method).
97
93
  // It Allows to distinguish SDK clients with the client-side API (`ICsSDK`) or server-side API (`ISDK` or `IAsyncSDK`).
@@ -1,16 +1,13 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { thenable } from '../utils/promise/thenable';
3
3
  import { find } from '../utils/lang';
4
- import { validateSplit, validateSplitExistance, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
4
+ import { validateSplit, validateSplitExistence, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
5
5
  import { ISplitsCacheAsync, ISplitsCacheSync } from '../storages/types';
6
6
  import { ISdkReadinessManager } from '../readiness/types';
7
7
  import { ISplit } from '../dtos/types';
8
- import { SplitIO } from '../types';
9
- import { ILogger } from '../logger/types';
10
-
11
- const SPLIT_FN_LABEL = 'split';
12
- const SPLITS_FN_LABEL = 'splits';
13
- const NAMES_FN_LABEL = 'names';
8
+ import { ISettings, SplitIO } from '../types';
9
+ import { isStorageSync } from '../trackers/impressionObserver/utils';
10
+ import { SPLIT_FN_LABEL, SPLITS_FN_LABEL, NAMES_FN_LABEL } from '../utils/constants';
14
11
 
15
12
  function collectTreatments(splitObject: ISplit) {
16
13
  const conditions = splitObject.conditions;
@@ -49,11 +46,14 @@ function objectsToViews(splitObjects: ISplit[]) {
49
46
  }
50
47
 
51
48
  export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplitsCacheAsync>(
52
- log: ILogger,
49
+ settings: Pick<ISettings, 'log' | 'mode'>,
53
50
  splits: TSplitCache,
54
- { readinessManager, sdkStatus }: ISdkReadinessManager
51
+ { readinessManager, sdkStatus }: ISdkReadinessManager,
55
52
  ): TSplitCache extends ISplitsCacheAsync ? SplitIO.IAsyncManager : SplitIO.IManager {
56
53
 
54
+ const log = settings.log;
55
+ const isSync = isStorageSync(settings);
56
+
57
57
  return objectAssign(
58
58
  // Proto-linkage of the readiness Event Emitter
59
59
  Object.create(sdkStatus),
@@ -64,19 +64,19 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
64
64
  split(featureFlagName: string) {
65
65
  const splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
66
66
  if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
67
- return null;
67
+ return isSync ? null : Promise.resolve(null);
68
68
  }
69
69
 
70
70
  const split = splits.getSplit(splitName);
71
71
 
72
72
  if (thenable(split)) {
73
73
  return split.catch(() => null).then(result => { // handle possible rejections when using pluggable storage
74
- validateSplitExistance(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
74
+ validateSplitExistence(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
75
75
  return objectToView(result);
76
76
  });
77
77
  }
78
78
 
79
- validateSplitExistance(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
79
+ validateSplitExistence(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
80
80
 
81
81
  return objectToView(split);
82
82
  },
@@ -85,7 +85,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
85
85
  */
86
86
  splits() {
87
87
  if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
88
- return [];
88
+ return isSync ? [] : Promise.resolve([]);
89
89
  }
90
90
  const currentSplits = splits.getAll();
91
91
 
@@ -98,7 +98,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
98
98
  */
99
99
  names() {
100
100
  if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
101
- return [];
101
+ return isSync ? [] : Promise.resolve([]);
102
102
  }
103
103
  const splitNames = splits.getSplitNames();
104
104
 
@@ -18,7 +18,7 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
18
18
  abstract getChangeNumber(): Promise<number>
19
19
  abstract getAll(): Promise<ISplit[]>
20
20
  abstract getSplitNames(): Promise<string[]>
21
- abstract getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>>
21
+ abstract getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]>
22
22
  abstract trafficTypeExists(trafficType: string): Promise<boolean>
23
23
  abstract clear(): Promise<boolean | void>
24
24
 
@@ -79,7 +79,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
79
79
  return false;
80
80
  }
81
81
 
82
- abstract getNamesByFlagSets(flagSets: string[]): ISet<string>
82
+ abstract getNamesByFlagSets(flagSets: string[]): ISet<string>[]
83
83
 
84
84
  }
85
85
 
@@ -4,7 +4,7 @@ import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
4
4
  import { KeyBuilderCS } from '../KeyBuilderCS';
5
5
  import { ILogger } from '../../logger/types';
6
6
  import { LOG_PREFIX } from './constants';
7
- import { ISet, _Set, returnSetsUnion, setToArray } from '../../utils/lang/sets';
7
+ import { ISet, _Set, setToArray } from '../../utils/lang/sets';
8
8
 
9
9
  /**
10
10
  * ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
@@ -257,19 +257,13 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
257
257
  // if the filter didn't change, nothing is done
258
258
  }
259
259
 
260
- getNamesByFlagSets(flagSets: string[]): ISet<string>{
261
- let toReturn: ISet<string> = new _Set([]);
262
- flagSets.forEach(flagSet => {
260
+ getNamesByFlagSets(flagSets: string[]): ISet<string>[] {
261
+ return flagSets.map(flagSet => {
263
262
  const flagSetKey = this.keys.buildFlagSetKey(flagSet);
264
- let flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
263
+ const flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
265
264
 
266
- if (flagSetFromLocalStorage) {
267
- const flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
268
- toReturn = returnSetsUnion(toReturn, flagSetCache);
269
- }
265
+ return new _Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
270
266
  });
271
- return toReturn;
272
-
273
267
  }
274
268
 
275
269
  private addToFlagSets(featureFlag: ISplit) {
@@ -281,10 +275,9 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
281
275
 
282
276
  const flagSetKey = this.keys.buildFlagSetKey(featureFlagSet);
283
277
 
284
- let flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
285
- if (!flagSetFromLocalStorage) flagSetFromLocalStorage = '[]';
278
+ const flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
286
279
 
287
- const flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
280
+ const flagSetCache = new _Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
288
281
  flagSetCache.add(featureFlag.name);
289
282
 
290
283
  localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
@@ -302,7 +295,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
302
295
  private removeNames(flagSetName: string, featureFlagName: string) {
303
296
  const flagSetKey = this.keys.buildFlagSetKey(flagSetName);
304
297
 
305
- let flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
298
+ const flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
306
299
 
307
300
  if (!flagSetFromLocalStorage) return;
308
301
 
@@ -1,7 +1,7 @@
1
1
  import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
2
2
  import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
3
3
  import { isFiniteNumber } from '../../utils/lang';
4
- import { ISet, _Set, returnSetsUnion } from '../../utils/lang/sets';
4
+ import { ISet, _Set } from '../../utils/lang/sets';
5
5
 
6
6
  /**
7
7
  * Default ISplitsCacheSync implementation that stores split definitions in memory.
@@ -16,9 +16,9 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
16
16
  private splitsWithSegmentsCount: number = 0;
17
17
  private flagSetsCache: Record<string, ISet<string>> = {};
18
18
 
19
- constructor(splitFiltersValidation: ISplitFiltersValidation = { queryString: null, groupedFilters: { bySet: [], byName: [], byPrefix: [] }, validFilters: [] }) {
19
+ constructor(splitFiltersValidation?: ISplitFiltersValidation) {
20
20
  super();
21
- this.flagSetsFilter = splitFiltersValidation.groupedFilters.bySet;
21
+ this.flagSetsFilter = splitFiltersValidation ? splitFiltersValidation.groupedFilters.bySet : [];
22
22
  }
23
23
 
24
24
  clear() {
@@ -105,16 +105,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
105
105
  return this.getChangeNumber() === -1 || this.splitsWithSegmentsCount > 0;
106
106
  }
107
107
 
108
- getNamesByFlagSets(flagSets: string[]): ISet<string>{
109
- let toReturn: ISet<string> = new _Set([]);
110
- flagSets.forEach(flagSet => {
111
- const featureFlagNames = this.flagSetsCache[flagSet];
112
- if (featureFlagNames) {
113
- toReturn = returnSetsUnion(toReturn, featureFlagNames);
114
- }
115
- });
116
- return toReturn;
117
-
108
+ getNamesByFlagSets(flagSets: string[]): ISet<string>[] {
109
+ return flagSets.map(flagSet => this.flagSetsCache[flagSet] || new _Set());
118
110
  }
119
111
 
120
112
  private addToFlagSets(featureFlag: ISplit) {
@@ -129,7 +121,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
129
121
  });
130
122
  }
131
123
 
132
- private removeFromFlagSets(featureFlagName :string, flagSets: string[] | undefined) {
124
+ private removeFromFlagSets(featureFlagName: string, flagSets: string[] | undefined) {
133
125
  if (!flagSets) return;
134
126
  flagSets.forEach(flagSet => {
135
127
  this.removeNames(flagSet, featureFlagName);
@@ -1,19 +1,19 @@
1
1
  import { IEventsCacheAsync } from '../types';
2
2
  import { IMetadata } from '../../dtos/types';
3
- import { Redis } from 'ioredis';
4
3
  import { SplitIO } from '../../types';
5
4
  import { ILogger } from '../../logger/types';
6
5
  import { LOG_PREFIX } from './constants';
7
6
  import { StoredEventWithMetadata } from '../../sync/submitters/types';
7
+ import type { RedisAdapter } from './RedisAdapter';
8
8
 
9
9
  export class EventsCacheInRedis implements IEventsCacheAsync {
10
10
 
11
11
  private readonly log: ILogger;
12
12
  private readonly key: string;
13
- private readonly redis: Redis;
13
+ private readonly redis: RedisAdapter;
14
14
  private readonly metadata: IMetadata;
15
15
 
16
- constructor(log: ILogger, key: string, redis: Redis, metadata: IMetadata) {
16
+ constructor(log: ILogger, key: string, redis: RedisAdapter, metadata: IMetadata) {
17
17
  this.log = log;
18
18
  this.key = key;
19
19
  this.redis = redis;
@@ -1,19 +1,19 @@
1
- import { Redis } from 'ioredis';
2
1
  import { ILogger } from '../../logger/types';
3
2
  import { ImpressionCountsPayload } from '../../sync/submitters/types';
4
3
  import { forOwn } from '../../utils/lang';
5
4
  import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
6
5
  import { LOG_PREFIX, REFRESH_RATE, TTL_REFRESH } from './constants';
6
+ import type { RedisAdapter } from './RedisAdapter';
7
7
 
8
8
  export class ImpressionCountsCacheInRedis extends ImpressionCountsCacheInMemory {
9
9
 
10
10
  private readonly log: ILogger;
11
11
  private readonly key: string;
12
- private readonly redis: Redis;
12
+ private readonly redis: RedisAdapter;
13
13
  private readonly refreshRate: number;
14
14
  private intervalId: any;
15
15
 
16
- constructor(log: ILogger, key: string, redis: Redis, impressionCountsCacheSize?: number, refreshRate = REFRESH_RATE) {
16
+ constructor(log: ILogger, key: string, redis: RedisAdapter, impressionCountsCacheSize?: number, refreshRate = REFRESH_RATE) {
17
17
  super(impressionCountsCacheSize);
18
18
  this.log = log;
19
19
  this.key = key;
@@ -1,10 +1,10 @@
1
1
  import { IImpressionsCacheAsync } from '../types';
2
2
  import { IMetadata } from '../../dtos/types';
3
3
  import { ImpressionDTO } from '../../types';
4
- import { Redis } from 'ioredis';
5
4
  import { StoredImpressionWithMetadata } from '../../sync/submitters/types';
6
5
  import { ILogger } from '../../logger/types';
7
6
  import { impressionsToJSON } from '../utils';
7
+ import type { RedisAdapter } from './RedisAdapter';
8
8
 
9
9
  const IMPRESSIONS_TTL_REFRESH = 3600; // 1 hr
10
10
 
@@ -12,10 +12,10 @@ export class ImpressionsCacheInRedis implements IImpressionsCacheAsync {
12
12
 
13
13
  private readonly log: ILogger;
14
14
  private readonly key: string;
15
- private readonly redis: Redis;
15
+ private readonly redis: RedisAdapter;
16
16
  private readonly metadata: IMetadata;
17
17
 
18
- constructor(log: ILogger, key: string, redis: Redis, metadata: IMetadata) {
18
+ constructor(log: ILogger, key: string, redis: RedisAdapter, metadata: IMetadata) {
19
19
  this.log = log;
20
20
  this.key = key;
21
21
  this.redis = redis;
@@ -1,4 +1,4 @@
1
- import ioredis from 'ioredis';
1
+ import ioredis, { Pipeline } from 'ioredis';
2
2
  import { ILogger } from '../../logger/types';
3
3
  import { merge, isString } from '../../utils/lang';
4
4
  import { _Set, setToArray, ISet } from '../../utils/lang/sets';
@@ -8,7 +8,8 @@ import { timeout } from '../../utils/promise/timeout';
8
8
  const LOG_PREFIX = 'storage:redis-adapter: ';
9
9
 
10
10
  // If we ever decide to fully wrap every method, there's a Commander.getBuiltinCommands from ioredis.
11
- const METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'pipeline', 'expire', 'mget', 'lrange', 'ltrim', 'hset'];
11
+ const METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'expire', 'mget', 'lrange', 'ltrim', 'hset', 'hincrby', 'popNRaw'];
12
+ const METHODS_TO_PROMISE_WRAP_EXEC = ['pipeline'];
12
13
 
13
14
  // Not part of the settings since it'll vary on each storage. We should be removing storage specific logic from elsewhere.
14
15
  const DEFAULT_OPTIONS = {
@@ -38,7 +39,7 @@ export class RedisAdapter extends ioredis {
38
39
  private _notReadyCommandsQueue?: IRedisCommand[];
39
40
  private _runningCommands: ISet<Promise<any>>;
40
41
 
41
- constructor(log: ILogger, storageSettings: Record<string, any>) {
42
+ constructor(log: ILogger, storageSettings: Record<string, any> = {}) {
42
43
  const options = RedisAdapter._defineOptions(storageSettings);
43
44
  // Call the ioredis constructor
44
45
  super(...RedisAdapter._defineLibrarySettings(options));
@@ -56,6 +57,7 @@ export class RedisAdapter extends ioredis {
56
57
  this.once('ready', () => {
57
58
  const commandsCount = this._notReadyCommandsQueue ? this._notReadyCommandsQueue.length : 0;
58
59
  this.log.info(LOG_PREFIX + `Redis connection established. Queued commands: ${commandsCount}.`);
60
+
59
61
  this._notReadyCommandsQueue && this._notReadyCommandsQueue.forEach(queued => {
60
62
  this.log.info(LOG_PREFIX + `Executing queued ${queued.name} command.`);
61
63
  queued.command().then(queued.resolve).catch(queued.reject);
@@ -71,16 +73,16 @@ export class RedisAdapter extends ioredis {
71
73
  _setTimeoutWrappers() {
72
74
  const instance: Record<string, any> = this;
73
75
 
74
- METHODS_TO_PROMISE_WRAP.forEach(method => {
75
- const originalMethod = instance[method];
76
-
77
- instance[method] = function () {
76
+ const wrapCommand = (originalMethod: Function, methodName: string) => {
77
+ // The value of "this" in this function should be the instance actually executing the method. It might be the instance referred (the base one)
78
+ // or it can be the instance of a Pipeline object.
79
+ return function (this: RedisAdapter | Pipeline) {
78
80
  const params = arguments;
81
+ const caller = this;
79
82
 
80
83
  function commandWrapper() {
81
- instance.log.debug(LOG_PREFIX + `Executing ${method}.`);
82
- // Return original method
83
- const result = originalMethod.apply(instance, params);
84
+ instance.log.debug(`${LOG_PREFIX}Executing ${methodName}.`);
85
+ const result = originalMethod.apply(caller, params);
84
86
 
85
87
  if (thenable(result)) {
86
88
  // For handling pending commands on disconnect, add to the set and remove once finished.
@@ -93,7 +95,7 @@ export class RedisAdapter extends ioredis {
93
95
  result.then(cleanUpRunningCommandsCb, cleanUpRunningCommandsCb);
94
96
 
95
97
  return timeout(instance._options.operationTimeout, result).catch(err => {
96
- instance.log.error(LOG_PREFIX + `${method} operation threw an error or exceeded configured timeout of ${instance._options.operationTimeout}ms. Message: ${err}`);
98
+ instance.log.error(`${LOG_PREFIX}${methodName} operation threw an error or exceeded configured timeout of ${instance._options.operationTimeout}ms. Message: ${err}`);
97
99
  // Handling is not the adapter responsibility.
98
100
  throw err;
99
101
  });
@@ -103,18 +105,38 @@ export class RedisAdapter extends ioredis {
103
105
  }
104
106
 
105
107
  if (instance._notReadyCommandsQueue) {
106
- return new Promise((res, rej) => {
108
+ return new Promise((resolve, reject) => {
107
109
  instance._notReadyCommandsQueue.unshift({
108
- resolve: res,
109
- reject: rej,
110
+ resolve,
111
+ reject,
110
112
  command: commandWrapper,
111
- name: method.toUpperCase()
113
+ name: methodName.toUpperCase()
112
114
  });
113
115
  });
114
116
  } else {
115
117
  return commandWrapper();
116
118
  }
117
119
  };
120
+ };
121
+
122
+ // Wrap regular async methods to track timeouts and queue when Redis is not yet executing commands.
123
+ METHODS_TO_PROMISE_WRAP.forEach(methodName => {
124
+ const originalFn = instance[methodName];
125
+ instance[methodName] = wrapCommand(originalFn, methodName);
126
+ });
127
+
128
+ // Special handling for pipeline~like methods. We need to wrap the async trigger, which is exec, but return the Pipeline right away.
129
+ METHODS_TO_PROMISE_WRAP_EXEC.forEach(methodName => {
130
+ const originalFn = instance[methodName];
131
+ // "First level wrapper" to handle the sync execution and wrap async, queueing later if applicable.
132
+ instance[methodName] = function () {
133
+ const res = originalFn.apply(instance, arguments);
134
+ const originalExec = res.exec;
135
+
136
+ res.exec = wrapCommand(originalExec, methodName + '.exec').bind(res);
137
+
138
+ return res;
139
+ };
118
140
  });
119
141
  }
120
142
 
@@ -124,7 +146,7 @@ export class RedisAdapter extends ioredis {
124
146
 
125
147
  instance.disconnect = function disconnect(...params: []) {
126
148
 
127
- setTimeout(function deferedDisconnect() {
149
+ setTimeout(function deferredDisconnect() {
128
150
  if (instance._runningCommands.size > 0) {
129
151
  instance.log.info(LOG_PREFIX + `Attempting to disconnect but there are ${instance._runningCommands.size} commands still waiting for resolution. Defering disconnection until those finish.`);
130
152
 
@@ -1,17 +1,17 @@
1
- import { Redis } from 'ioredis';
2
1
  import { ILogger } from '../../logger/types';
3
2
  import { isNaNNumber } from '../../utils/lang';
4
3
  import { LOG_PREFIX } from '../inLocalStorage/constants';
5
4
  import { KeyBuilderSS } from '../KeyBuilderSS';
6
5
  import { ISegmentsCacheAsync } from '../types';
6
+ import type { RedisAdapter } from './RedisAdapter';
7
7
 
8
8
  export class SegmentsCacheInRedis implements ISegmentsCacheAsync {
9
9
 
10
10
  private readonly log: ILogger;
11
- private readonly redis: Redis;
11
+ private readonly redis: RedisAdapter;
12
12
  private readonly keys: KeyBuilderSS;
13
13
 
14
- constructor(log: ILogger, keys: KeyBuilderSS, redis: Redis) {
14
+ constructor(log: ILogger, keys: KeyBuilderSS, redis: RedisAdapter) {
15
15
  this.log = log;
16
16
  this.redis = redis;
17
17
  this.keys = keys;
@@ -72,8 +72,8 @@ export class SegmentsCacheInRedis implements ISegmentsCacheAsync {
72
72
  return this.redis.smembers(this.keys.buildRegisteredSegmentsKey());
73
73
  }
74
74
 
75
- // @TODO remove/review. It is not being used.
75
+ // @TODO remove or implement. It is not being used.
76
76
  clear() {
77
- return this.redis.flushdb().then(status => status === 'OK');
77
+ return Promise.resolve();
78
78
  }
79
79
  }