launchdarkly-js-sdk-common 3.5.0 → 4.0.3

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 (68) hide show
  1. package/.circleci/config.yml +22 -0
  2. package/.eslintignore +4 -0
  3. package/.eslintrc.yaml +103 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. package/.github/pull_request_template.md +21 -0
  7. package/.ldrelease/config.yml +24 -0
  8. package/.prettierignore +1 -0
  9. package/.prettierrc +5 -0
  10. package/CHANGELOG.md +17 -0
  11. package/CONTRIBUTING.md +45 -0
  12. package/babel.config.js +18 -0
  13. package/docs/typedoc.js +11 -0
  14. package/jest.config.js +12 -0
  15. package/package.json +4 -32
  16. package/scripts/better-audit.sh +76 -0
  17. package/src/EventEmitter.js +60 -0
  18. package/src/EventProcessor.js +175 -0
  19. package/src/EventSender.js +87 -0
  20. package/src/EventSummarizer.js +84 -0
  21. package/src/Identity.js +26 -0
  22. package/src/InitializationState.js +83 -0
  23. package/src/PersistentFlagStore.js +50 -0
  24. package/src/PersistentStorage.js +81 -0
  25. package/src/Requestor.js +111 -0
  26. package/src/Stream.js +154 -0
  27. package/src/UserFilter.js +75 -0
  28. package/src/UserValidator.js +56 -0
  29. package/src/__tests__/.eslintrc.yaml +6 -0
  30. package/src/__tests__/EventProcessor-test.js +559 -0
  31. package/src/__tests__/EventSender-test.js +252 -0
  32. package/src/__tests__/EventSource-mock.js +61 -0
  33. package/src/__tests__/EventSummarizer-test.js +103 -0
  34. package/src/__tests__/LDClient-events-test.js +757 -0
  35. package/src/__tests__/LDClient-localstorage-test.js +179 -0
  36. package/src/__tests__/LDClient-streaming-test.js +716 -0
  37. package/src/__tests__/LDClient-test.js +753 -0
  38. package/src/__tests__/PersistentFlagStore-test.js +111 -0
  39. package/src/__tests__/Requestor-test.js +362 -0
  40. package/src/__tests__/Stream-test.js +299 -0
  41. package/src/__tests__/UserFilter-test.js +93 -0
  42. package/src/__tests__/UserValidator-test.js +57 -0
  43. package/src/__tests__/configuration-test.js +217 -0
  44. package/src/__tests__/diagnosticEvents-test.js +449 -0
  45. package/src/__tests__/loggers-test.js +149 -0
  46. package/src/__tests__/mockHttp.js +122 -0
  47. package/src/__tests__/promiseCoalescer-test.js +128 -0
  48. package/src/__tests__/stubPlatform.js +148 -0
  49. package/src/__tests__/testUtils.js +77 -0
  50. package/src/__tests__/utils-test.js +148 -0
  51. package/src/configuration.js +151 -0
  52. package/src/diagnosticEvents.js +269 -0
  53. package/src/errors.js +39 -0
  54. package/src/index.js +788 -0
  55. package/src/jest.setup.js +1 -0
  56. package/src/loggers.js +93 -0
  57. package/src/messages.js +222 -0
  58. package/src/promiseCoalescer.js +52 -0
  59. package/src/utils.js +214 -0
  60. package/test-types.ts +94 -0
  61. package/tsconfig.json +13 -0
  62. package/typings.d.ts +4 -43
  63. package/dist/ldclient-common.cjs.js +0 -2
  64. package/dist/ldclient-common.cjs.js.map +0 -1
  65. package/dist/ldclient-common.es.js +0 -2
  66. package/dist/ldclient-common.es.js.map +0 -1
  67. package/dist/ldclient-common.min.js +0 -2
  68. package/dist/ldclient-common.min.js.map +0 -1
@@ -0,0 +1,179 @@
1
+ import * as messages from '../messages';
2
+ import * as utils from '../utils';
3
+
4
+ import { sleepAsync, withCloseable } from 'launchdarkly-js-test-helpers';
5
+
6
+ import { respond, respondJson } from './mockHttp';
7
+ import * as stubPlatform from './stubPlatform';
8
+
9
+ // These tests cover the "bootstrap: 'localstorage'" mode. The actual implementation of local storage
10
+ // is provided by the platform-specific SDKs; we use a mock implementation here.
11
+
12
+ describe('LDClient local storage', () => {
13
+ const envName = 'UNKNOWN_ENVIRONMENT_ID';
14
+ const user = { key: 'user' };
15
+ const lsKey = 'ld:' + envName + ':' + utils.btoa(JSON.stringify(user));
16
+ let platform;
17
+
18
+ beforeEach(() => {
19
+ platform = stubPlatform.defaults();
20
+ });
21
+
22
+ async function withServer(asyncCallback) {
23
+ const server = platform.testing.http.newServer();
24
+ server.byDefault(respondJson({}));
25
+ return await withCloseable(server, asyncCallback);
26
+ }
27
+
28
+ async function withClient(user, extraConfig, asyncCallback) {
29
+ // We specify bootstrap: 'localstorage' for all tests in this file
30
+ const config = { baseUrl: 'shouldnt-use-this', bootstrap: 'localstorage', sendEvents: false, ...extraConfig };
31
+ const client = platform.testing.makeClient(envName, user, config);
32
+ return await withCloseable(client, asyncCallback);
33
+ }
34
+
35
+ describe('bootstrapping from local storage', () => {
36
+ it('does not try to use local storage if the platform says it is unavailable', async () => {
37
+ platform.localStorage = null;
38
+
39
+ await withServer(async server => {
40
+ await withClient(user, { baseUrl: server.url }, async client => {
41
+ await client.waitForInitialization();
42
+
43
+ // should see a flag request to the server right away, as if bootstrap was not specified
44
+ expect(server.requests.length()).toEqual(1);
45
+
46
+ expect(platform.testing.logger.output.warn).toEqual([messages.localStorageUnavailable()]);
47
+ });
48
+ });
49
+ });
50
+
51
+ it('uses cached flags if available and requests flags from server after ready', async () => {
52
+ const json = '{"flag-key": 1}';
53
+ platform.testing.setLocalStorageImmediately(lsKey, json);
54
+
55
+ await withServer(async server => {
56
+ // This no-op request handler means that the flags request will simply hang with no
57
+ // response, so we can be sure that we're seeing only the initial flags from local storage.
58
+ server.byDefault(() => {});
59
+
60
+ await withClient(user, { baseUrl: server.url }, async client => {
61
+ await client.waitForInitialization();
62
+
63
+ expect(client.variation('flag-key')).toEqual(1);
64
+
65
+ await sleepAsync(0); // allow any pending async tasks to complete
66
+
67
+ expect(server.requests.length()).toEqual(1);
68
+ });
69
+ });
70
+ });
71
+
72
+ it('starts with empty flags and requests them from server if there are no cached flags', async () => {
73
+ const flags = { 'flag-key': { value: 1 } };
74
+
75
+ await withServer(async server => {
76
+ server.byDefault(respondJson(flags));
77
+ await withClient(user, { baseUrl: server.url }, async client => {
78
+ // don't wait for ready event - verifying that variation() doesn't throw an error if called before ready
79
+ expect(client.variation('flag-key', 0)).toEqual(0);
80
+
81
+ // verify that the flags get requested from LD
82
+ await client.waitForInitialization();
83
+ expect(client.variation('flag-key')).toEqual(1);
84
+ });
85
+ });
86
+ });
87
+
88
+ it('should handle localStorage.get returning an error', async () => {
89
+ const myError = new Error('deliberate error');
90
+ platform.localStorage.get = () => Promise.reject(myError);
91
+ const flags = { 'enable-foo': { value: true } };
92
+
93
+ await withServer(async server => {
94
+ server.byDefault(respondJson(flags));
95
+ await withClient(user, { baseUrl: server.url }, async client => {
96
+ await client.waitForInitialization();
97
+ expect(platform.testing.logger.output.warn).toEqual([messages.localStorageUnavailable(myError)]);
98
+ });
99
+ });
100
+ });
101
+
102
+ it('should handle localStorage.set returning an error', async () => {
103
+ const myError = new Error('deliberate error');
104
+ platform.localStorage.set = () => Promise.reject(myError);
105
+ const flags = { 'enable-foo': { value: true } };
106
+
107
+ await withServer(async server => {
108
+ server.byDefault(respondJson(flags));
109
+ await withClient(user, { baseUrl: server.url }, async client => {
110
+ await client.waitForInitialization();
111
+
112
+ await sleepAsync(0); // allow any pending async tasks to complete
113
+
114
+ expect(platform.testing.logger.output.warn).toEqual([messages.localStorageUnavailable(myError)]);
115
+ });
116
+ });
117
+ });
118
+
119
+ it('should not update cached settings if there was an error fetching flags', async () => {
120
+ const json = '{"enable-foo": true}';
121
+ platform.testing.setLocalStorageImmediately(lsKey, json);
122
+
123
+ await withServer(async server => {
124
+ server.byDefault(respond(503));
125
+ await withClient(user, { baseUrl: server.url }, async client => {
126
+ await client.waitForInitialization();
127
+
128
+ await sleepAsync(0); // allow any pending async tasks to complete
129
+
130
+ const value = platform.testing.getLocalStorageImmediately(lsKey);
131
+ expect(value).toEqual(json);
132
+ });
133
+ });
134
+ });
135
+
136
+ it('should use hash as localStorage key when secure mode is enabled', async () => {
137
+ const hash = 'totallyLegitHash';
138
+ const lsKeyHash = 'ld:UNKNOWN_ENVIRONMENT_ID:' + hash;
139
+ const flags = { 'enable-foo': { value: true } };
140
+
141
+ await withServer(async server => {
142
+ server.byDefault(respondJson(flags));
143
+ await withClient(user, { baseUrl: server.url, hash }, async client => {
144
+ await client.waitForInitialization();
145
+ const value = platform.testing.getLocalStorageImmediately(lsKeyHash);
146
+ expect(JSON.parse(value)).toEqual({
147
+ $schema: 1,
148
+ 'enable-foo': { value: true },
149
+ });
150
+ });
151
+ });
152
+ });
153
+
154
+ it('should clear localStorage when user context is changed', async () => {
155
+ const lsKey2 = 'ld:UNKNOWN_ENVIRONMENT_ID:' + utils.btoa('{"key":"user2"}');
156
+ const flags = { 'enable-foo': { value: true } };
157
+ const user2 = { key: 'user2' };
158
+
159
+ await withServer(async server => {
160
+ server.byDefault(respondJson(flags));
161
+ await withClient(user, { baseUrl: server.url }, async client => {
162
+ await client.waitForInitialization();
163
+
164
+ await sleepAsync(0); // allow any pending async tasks to complete
165
+
166
+ await client.identify(user2);
167
+
168
+ const value1 = platform.testing.getLocalStorageImmediately(lsKey);
169
+ expect(value1).not.toEqual(expect.anything());
170
+ const value2 = platform.testing.getLocalStorageImmediately(lsKey2);
171
+ expect(JSON.parse(value2)).toEqual({
172
+ $schema: 1,
173
+ 'enable-foo': { value: true },
174
+ });
175
+ });
176
+ });
177
+ });
178
+ });
179
+ });