@depup/launchdarkly-node-server-sdk 7.0.4-depup.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 (107) hide show
  1. package/.babelrc +16 -0
  2. package/.circleci/config.yml +89 -0
  3. package/.eslintignore +5 -0
  4. package/.eslintrc.yaml +114 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. package/.github/pull_request_template.md +21 -0
  9. package/.github/workflows/stale.yml +8 -0
  10. package/.hound.yml +33 -0
  11. package/.ldrelease/config.yml +28 -0
  12. package/.prettierrc +6 -0
  13. package/CHANGELOG.md +603 -0
  14. package/CODEOWNERS +2 -0
  15. package/CONTRIBUTING.md +55 -0
  16. package/LICENSE.txt +13 -0
  17. package/README.md +36 -0
  18. package/SECURITY.md +5 -0
  19. package/attribute_reference.js +217 -0
  20. package/big_segments.js +117 -0
  21. package/caching_store_wrapper.js +240 -0
  22. package/changes.json +30 -0
  23. package/configuration.js +235 -0
  24. package/context.js +98 -0
  25. package/context_filter.js +137 -0
  26. package/contract-tests/README.md +7 -0
  27. package/contract-tests/index.js +109 -0
  28. package/contract-tests/log.js +23 -0
  29. package/contract-tests/package.json +15 -0
  30. package/contract-tests/sdkClientEntity.js +110 -0
  31. package/contract-tests/testharness-suppressions.txt +2 -0
  32. package/diagnostic_events.js +151 -0
  33. package/docs/typedoc.js +10 -0
  34. package/errors.js +26 -0
  35. package/evaluator.js +822 -0
  36. package/event_factory.js +121 -0
  37. package/event_processor.js +320 -0
  38. package/event_summarizer.js +101 -0
  39. package/feature_store.js +120 -0
  40. package/feature_store_event_wrapper.js +258 -0
  41. package/file_data_source.js +192 -0
  42. package/flags_state.js +46 -0
  43. package/index.d.ts +2426 -0
  44. package/index.js +452 -0
  45. package/integrations.js +7 -0
  46. package/interfaces.js +2 -0
  47. package/loggers.js +125 -0
  48. package/messages.js +31 -0
  49. package/operators.js +106 -0
  50. package/package.json +105 -0
  51. package/polling.js +70 -0
  52. package/requestor.js +62 -0
  53. package/scripts/better-audit.sh +76 -0
  54. package/sharedtest/big_segment_store_tests.js +86 -0
  55. package/sharedtest/feature_store_tests.js +177 -0
  56. package/sharedtest/persistent_feature_store_tests.js +183 -0
  57. package/sharedtest/store_tests.js +7 -0
  58. package/streaming.js +179 -0
  59. package/test/LDClient-big-segments-test.js +92 -0
  60. package/test/LDClient-end-to-end-test.js +218 -0
  61. package/test/LDClient-evaluation-all-flags-test.js +226 -0
  62. package/test/LDClient-evaluation-test.js +204 -0
  63. package/test/LDClient-events-test.js +502 -0
  64. package/test/LDClient-listeners-test.js +180 -0
  65. package/test/LDClient-test.js +96 -0
  66. package/test/LDClient-tls-test.js +110 -0
  67. package/test/attribute_reference-test.js +494 -0
  68. package/test/big_segments-test.js +182 -0
  69. package/test/caching_store_wrapper-test.js +434 -0
  70. package/test/configuration-test.js +249 -0
  71. package/test/context-test.js +93 -0
  72. package/test/context_filter-test.js +424 -0
  73. package/test/diagnostic_events-test.js +152 -0
  74. package/test/evaluator-big-segments-test.js +301 -0
  75. package/test/evaluator-bucketing-test.js +333 -0
  76. package/test/evaluator-clause-test.js +277 -0
  77. package/test/evaluator-flag-test.js +452 -0
  78. package/test/evaluator-pre-conditions-test.js +105 -0
  79. package/test/evaluator-rule-test.js +131 -0
  80. package/test/evaluator-segment-match-test.js +310 -0
  81. package/test/evaluator_helpers.js +106 -0
  82. package/test/event_processor-test.js +680 -0
  83. package/test/event_summarizer-test.js +146 -0
  84. package/test/feature_store-test.js +42 -0
  85. package/test/feature_store_event_wrapper-test.js +182 -0
  86. package/test/feature_store_test_base.js +60 -0
  87. package/test/file_data_source-test.js +255 -0
  88. package/test/loggers-test.js +126 -0
  89. package/test/operators-test.js +102 -0
  90. package/test/polling-test.js +158 -0
  91. package/test/requestor-test.js +60 -0
  92. package/test/store_tests_big_segments-test.js +61 -0
  93. package/test/streaming-test.js +323 -0
  94. package/test/stubs.js +107 -0
  95. package/test/test_data-test.js +341 -0
  96. package/test/update_queue-test.js +61 -0
  97. package/test-types.ts +210 -0
  98. package/test_data.js +323 -0
  99. package/tsconfig.json +14 -0
  100. package/update_queue.js +28 -0
  101. package/utils/__tests__/httpUtils-test.js +39 -0
  102. package/utils/__tests__/wrapPromiseCallback-test.js +33 -0
  103. package/utils/asyncUtils.js +32 -0
  104. package/utils/httpUtils.js +105 -0
  105. package/utils/stringifyAttrs.js +14 -0
  106. package/utils/wrapPromiseCallback.js +36 -0
  107. package/versioned_data_kind.js +34 -0
@@ -0,0 +1,249 @@
1
+ var configuration = require('../configuration');
2
+
3
+ describe('configuration', function() {
4
+ const defaults = configuration.defaults();
5
+
6
+ function emptyConfigWithMockLogger() {
7
+ const logger = {
8
+ error: jest.fn(),
9
+ warn: jest.fn(),
10
+ info: jest.fn(),
11
+ debug: jest.fn(),
12
+ };
13
+ return { logger };
14
+ }
15
+
16
+ function expectDefault(name) {
17
+ const configIn = emptyConfigWithMockLogger();
18
+ const config = configuration.validate(configIn);
19
+ expect(config[name]).toBe(defaults[name]);
20
+ expect(configIn.logger.warn).not.toHaveBeenCalled();
21
+ }
22
+
23
+ // Even if checkDeprecated is not currently used in this file, please do not
24
+ // remove it, since we may deprecate an option in the future and should use this
25
+ // logic if we do.
26
+ function checkDeprecated(oldName, newName, value) {
27
+ it(`allows "${oldName}" as a deprecated equivalent to "${newName}"`, function() {
28
+ const configIn = emptyConfigWithMockLogger();
29
+ configIn[oldName] = value;
30
+ const config1 = configuration.validate(configIn);
31
+ expect(config1[newName]).toEqual(value);
32
+ expect(config1[oldName]).toBeUndefined();
33
+ expect(configIn.logger.warn).toHaveBeenCalledTimes(1);
34
+ });
35
+ }
36
+
37
+ function checkBooleanProperty(name) {
38
+ it(`enforces boolean type and default for "${name}"`, () => {
39
+ expectDefault(name);
40
+
41
+ const configIn1 = emptyConfigWithMockLogger();
42
+ configIn1[name] = true;
43
+ const config1 = configuration.validate(configIn1);
44
+ expect(config1[name]).toBe(true);
45
+ expect(configIn1.logger.warn).not.toHaveBeenCalled();
46
+
47
+ const configIn2 = emptyConfigWithMockLogger();
48
+ configIn2[name] = false;
49
+ const config2 = configuration.validate(configIn2);
50
+ expect(config2[name]).toBe(false);
51
+ expect(configIn2.logger.warn).not.toHaveBeenCalled();
52
+
53
+ const configIn3 = emptyConfigWithMockLogger();
54
+ configIn3[name] = 'abc';
55
+ const config3 = configuration.validate(configIn3);
56
+ expect(config3[name]).toBe(true);
57
+ expect(configIn3.logger.warn).toHaveBeenCalledTimes(1);
58
+
59
+ const configIn4 = emptyConfigWithMockLogger();
60
+ configIn4[name] = 0;
61
+ const config4 = configuration.validate(configIn4);
62
+ expect(config4[name]).toBe(false);
63
+ expect(configIn4.logger.warn).toHaveBeenCalledTimes(1);
64
+ });
65
+ }
66
+
67
+ checkBooleanProperty('stream');
68
+ checkBooleanProperty('sendEvents');
69
+ checkBooleanProperty('offline');
70
+ checkBooleanProperty('useLdd');
71
+ checkBooleanProperty('allAttributesPrivate');
72
+ checkBooleanProperty('diagnosticOptOut');
73
+
74
+ function checkNumericProperty(name, validValue) {
75
+ it(`enforces numeric type and default for "${name}"`, () => {
76
+ expectDefault(name);
77
+
78
+ const configIn1 = emptyConfigWithMockLogger();
79
+ configIn1[name] = validValue;
80
+ const config1 = configuration.validate(configIn1);
81
+ expect(config1[name]).toBe(validValue);
82
+ expect(configIn1.logger.warn).not.toHaveBeenCalled();
83
+
84
+ const configIn2 = emptyConfigWithMockLogger();
85
+ configIn2[name] = 'no';
86
+ const config2 = configuration.validate(configIn2);
87
+ expect(config2[name]).toBe(defaults[name]);
88
+ expect(configIn2.logger.warn).toHaveBeenCalledTimes(1);
89
+ });
90
+ }
91
+
92
+ checkNumericProperty('timeout', 10);
93
+ checkNumericProperty('capacity', 500);
94
+ checkNumericProperty('flushInterval', 45);
95
+ checkNumericProperty('pollInterval', 45);
96
+ checkNumericProperty('contextKeysCapacity', 500);
97
+ checkNumericProperty('contextKeysFlushInterval', 45);
98
+ checkNumericProperty('diagnosticRecordingInterval', 110);
99
+
100
+ function checkNumericRange(name, minimum, maximum) {
101
+ if (minimum !== undefined) {
102
+ it(`enforces minimum for "${name}"`, () => {
103
+ const configIn = emptyConfigWithMockLogger();
104
+ configIn[name] = minimum - 1;
105
+ const config = configuration.validate(configIn);
106
+ expect(config[name]).toBe(minimum);
107
+ expect(configIn.logger.warn).toHaveBeenCalledTimes(1);
108
+ });
109
+ }
110
+ if (maximum !== undefined) {
111
+ it(`enforces maximum for "${name}"`, () => {
112
+ const configIn = emptyConfigWithMockLogger();
113
+ configIn[name] = maximum + 1;
114
+ const config = configuration.validate(configIn);
115
+ expect(config[name]).toBe(maximum);
116
+ expect(configIn.logger.warn).toHaveBeenCalledTimes(1);
117
+ });
118
+ }
119
+ }
120
+
121
+ checkNumericRange('pollInterval', 30);
122
+ checkNumericRange('diagnosticRecordingInterval', 60);
123
+
124
+ function checkUriProperty(name) {
125
+ expectDefault(name);
126
+
127
+ const configIn1 = emptyConfigWithMockLogger();
128
+ configIn1[name] = 'http://test.com/';
129
+ const config1 = configuration.validate(configIn1);
130
+ expect(config1[name]).toEqual('http://test.com'); // trailing slash is removed
131
+ expect(configIn1.logger.warn).not.toHaveBeenCalled();
132
+
133
+ const configIn2 = emptyConfigWithMockLogger();
134
+ configIn2[name] = 3;
135
+ const config2 = configuration.validate(configIn2);
136
+ expect(config2[name]).toEqual(defaults[name]);
137
+ expect(configIn2.logger.warn).toHaveBeenCalledTimes(1);
138
+ }
139
+
140
+ checkUriProperty('baseUri');
141
+ checkUriProperty('streamUri');
142
+ checkUriProperty('eventsUri');
143
+
144
+ it('enforces array value for privateAttributes', () => {
145
+ const configIn0 = emptyConfigWithMockLogger();
146
+ const config0 = configuration.validate(configIn0);
147
+ expect(config0.privateAttributes).toEqual([]);
148
+ expect(configIn0.logger.warn).not.toHaveBeenCalled();
149
+
150
+ const configIn1 = emptyConfigWithMockLogger();
151
+ configIn1.privateAttributes = [ 'a' ];
152
+ const config1 = configuration.validate(configIn1);
153
+ expect(config1.privateAttributes).toEqual([ 'a' ]);
154
+ expect(configIn1.logger.warn).not.toHaveBeenCalled();
155
+
156
+ const configIn2 = emptyConfigWithMockLogger();
157
+ configIn2.privateAttributes = 'no';
158
+ const config2 = configuration.validate(configIn2);
159
+ expect(config2.privateAttributes).toEqual([]);
160
+ expect(configIn2.logger.warn).toHaveBeenCalledTimes(1);
161
+ });
162
+
163
+ it('should not share the default featureStore across different config instances', () => {
164
+ var config1 = configuration.validate({});
165
+ var config2 = configuration.validate({});
166
+ expect(config1.featureStore).not.toEqual(config2.featureStore);
167
+ });
168
+
169
+ it('complains if you set an unknown property', () => {
170
+ const configIn = emptyConfigWithMockLogger();
171
+ configIn.unsupportedThing = true;
172
+ configuration.validate(configIn);
173
+ expect(configIn.logger.warn).toHaveBeenCalledTimes(1);
174
+ });
175
+
176
+ it('throws an error if you pass in a logger with missing methods', () => {
177
+ const methods = ['error', 'warn', 'info', 'debug'];
178
+
179
+ methods.forEach(method => {
180
+ const configIn = emptyConfigWithMockLogger();
181
+ delete configIn.logger[method];
182
+ expect(() => configuration.validate(configIn)).toThrow(/Provided logger instance must support .* method/);
183
+ });
184
+ });
185
+
186
+ it('handles a valid application id', () => {
187
+ const configIn = emptyConfigWithMockLogger();
188
+ configIn.application = {id: 'test-application'};
189
+ expect(configuration.validate(configIn).application.id).toEqual('test-application');
190
+ });
191
+
192
+ it('logs a warning with an invalid application id', () => {
193
+ const configIn = emptyConfigWithMockLogger();
194
+ configIn.application = {id: 'test #$#$#'};
195
+ expect(configuration.validate(configIn).application.id).toBeUndefined();
196
+ expect(configIn.logger.warn).toHaveBeenCalledTimes(1);
197
+ });
198
+
199
+ it('logs a warning when a tag value is too long', async () => {
200
+ const configIn = emptyConfigWithMockLogger();
201
+ configIn.application = { id: 'a'.repeat(65), version: 'b'.repeat(64) };
202
+ expect(configuration.validate(configIn).application.id).toBeUndefined();
203
+ expect(configIn.logger.warn).toHaveBeenCalledTimes(1);
204
+ });
205
+
206
+ it('handles a valid application version', () => {
207
+ const configIn = emptyConfigWithMockLogger();
208
+ configIn.application = {version: 'test-version'};
209
+ expect(configuration.validate(configIn).application.version).toEqual('test-version');
210
+ });
211
+
212
+ it('logs a warning with an invalid application version', () => {
213
+ const configIn = emptyConfigWithMockLogger();
214
+ configIn.application = {version: 'test #$#$#'};
215
+ expect(configuration.validate(configIn).application.version).toBeUndefined();
216
+ expect(configIn.logger.warn).toHaveBeenCalledTimes(1);
217
+ });
218
+
219
+ it('includes application id and version in tags when present', () => {
220
+ expect(configuration.getTags({application: {id: 'test-id', version: 'test-version'}}))
221
+ .toEqual({'application-id': ['test-id'], 'application-version': ['test-version']});
222
+ });
223
+
224
+ it('handles the conversion of renamed options', () => {
225
+ const configIn = emptyConfigWithMockLogger();
226
+ configIn.userKeysCapacity = 1234;
227
+ configIn.userKeysFlushInterval = 5678;
228
+ const updatedConfig = configuration.validate(configIn);
229
+ expect(updatedConfig.contextKeysCapacity).toEqual(1234);
230
+ expect(updatedConfig.contextKeysFlushInterval).toEqual(5678);
231
+ expect(updatedConfig.userKeysCapacity).toBeUndefined();
232
+ expect(updatedConfig.userKeysFlushInterval).toBeUndefined();
233
+ });
234
+
235
+ it('does not override keys with renamed keys', () => {
236
+ const configIn = emptyConfigWithMockLogger();
237
+ configIn.contextKeysCapacity = 9999;
238
+ configIn.contextKeysFlushInterval = 8888;
239
+
240
+ configIn.userKeysCapacity = 1234;
241
+ configIn.userKeysFlushInterval = 5678;
242
+
243
+ const updatedConfig = configuration.validate(configIn);
244
+ expect(updatedConfig.contextKeysCapacity).toEqual(9999);
245
+ expect(updatedConfig.contextKeysFlushInterval).toEqual(8888);
246
+ expect(updatedConfig.userKeysCapacity).toBeUndefined();
247
+ expect(updatedConfig.userKeysFlushInterval).toBeUndefined();
248
+ });
249
+ });
@@ -0,0 +1,93 @@
1
+ const { checkContext, getContextKinds, getCanonicalKey } = require('../context');
2
+
3
+ describe.each([
4
+ { key: 'test' },
5
+ { kind: 'user', key: 'test' },
6
+ { kind: 'multi', user: { key: 'test' } }])
7
+ ('given a context which contains a single kind', (context) => {
8
+ it('should get the context kind', () => {
9
+ expect(getContextKinds(context)).toEqual(['user']);
10
+ });
11
+
12
+ it('should be valid', () => {
13
+ expect(checkContext(context, false)).toBeTruthy();
14
+ });
15
+ });
16
+
17
+ describe('given a valid multi-kind context', () => {
18
+ const context = {
19
+ kind: 'multi',
20
+ user: {
21
+ key: 'user'
22
+ },
23
+ org: {
24
+ key: 'org'
25
+ }
26
+ };
27
+
28
+ it('should get a list of the kinds', () => {
29
+ expect(getContextKinds(context).sort()).toEqual(['org', 'user']);
30
+ });
31
+
32
+ it('should be valid', () => {
33
+ expect(checkContext(context, false)).toBeTruthy();
34
+ });
35
+ });
36
+
37
+ // A sample of invalid characters.
38
+ const invalidSampleChars = [...`#$%&'()*+,/:;<=>?@[\\]^\`{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²
39
+ ³´µ¶·¸¹º»¼½¾¿À汉字`];
40
+ const badKinds = invalidSampleChars.map(char => ({ kind: char, key: 'test' }));
41
+
42
+ describe.each([
43
+ {}, // An empty object is not a valid context.
44
+ { key: '' }, // If allowLegacyKey is not true, then this should be invalid.
45
+ { kind: 'kind', key: 'kind' }, // The kind cannot be kind.
46
+ { kind: 'user' }, // The context needs to have a key.
47
+ { kind: 'org', key: '' }, // For a non-legacy context the key cannot be empty.
48
+ { kind: ' ', key: 'test' }, // Kind cannot be whitespace only.
49
+ { kind: 'cat dog', key: 'test' }, // Kind cannot contain whitespace
50
+ { kind: '~!@#$%^&*()_+', key: 'test' }, // Special characters are not valid.
51
+ ...badKinds,
52
+ ])('given invalid contexts', (context) => {
53
+ it('should not be valid', () => {
54
+ expect(checkContext(context, false)).toBeFalsy();
55
+ });
56
+ });
57
+
58
+ const validChars = ['0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.'];
59
+ const goodKinds = validChars.map(char => ([{ kind: char, key: 'test' }, false]));
60
+
61
+ describe.each([
62
+ [{ key: '' }, true], // Allow a legacy context with an empty key.
63
+ ...goodKinds
64
+ ])('given valid contexts', (context, allowLegacyKey) => {
65
+ it('should be valid and can get context kinds', () => {
66
+ expect(checkContext(context, allowLegacyKey)).toBeTruthy();
67
+ expect(getContextKinds(context)).toEqual([context.kind || 'user'])
68
+ });
69
+ });
70
+
71
+ describe('when determining canonical keys', () => {
72
+ it.each([
73
+ [{ key: 'test' }, 'test'],
74
+ [{ kind: 'user', key: 'test' }, 'test'],
75
+ [{ kind: 'org', key: 'orgtest' }, 'org:orgtest'],
76
+ [{ kind: 'multi', user: { key: 'usertest' } }, 'user:usertest'],
77
+ [{ kind: 'multi', user: { key: 'usertest' }, org: { key: 'orgtest' } }, 'org:orgtest:user:usertest'],
78
+ [{ kind: 'multi', user: { key: 'user:test' }, org: { key: 'org:test' } }, 'org:org%3Atest:user:user%3Atest'],
79
+ [{ kind: 'multi', user: { key: 'user:test' }, org: { key: 'org:test' } }, 'org:org%3Atest:user:user%3Atest'],
80
+ [{ kind: 'multi', user: { key: 'user%test' }, org: { key: 'org%test' } }, 'org:org%25test:user:user%25test'],
81
+ [
82
+ { kind: 'multi', user: { key: 'user%:test' }, org: { key: 'org%:test' } },
83
+ 'org:org%25%3Atest:user:user%25%3Atest',
84
+ ],
85
+ ])('produces a canonical key for valid contexts', (context, canonicalKey) => {
86
+ expect(getCanonicalKey(context)).toEqual(canonicalKey);
87
+ });
88
+
89
+ it('does not break with an null/undefined context', () => {
90
+ expect(getCanonicalKey(undefined)).toBeUndefined();
91
+ expect(getCanonicalKey(null)).toBeUndefined();
92
+ });
93
+ });