launchdarkly-js-sdk-common 4.3.2 → 5.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -1
- package/src/AnonymousContextProcessor.js +95 -0
- package/src/ContextFilter.js +147 -0
- package/src/EventProcessor.js +30 -13
- package/src/EventSender.js +1 -1
- package/src/Identity.js +10 -11
- package/src/PersistentFlagStore.js +3 -3
- package/src/Requestor.js +4 -4
- package/src/Stream.js +5 -5
- package/src/__tests__/ContextFilter-test.js +470 -0
- package/src/__tests__/EventProcessor-test.js +50 -121
- package/src/__tests__/LDClient-events-test.js +9 -152
- package/src/__tests__/LDClient-inspectors-test.js +1 -1
- package/src/__tests__/LDClient-test.js +18 -15
- package/src/__tests__/TransientContextProcessor-test.js +115 -0
- package/src/__tests__/attributeReference-test.js +400 -0
- package/src/__tests__/configuration-test.js +20 -21
- package/src/__tests__/context-test.js +93 -0
- package/src/__tests__/diagnosticEvents-test.js +0 -4
- package/src/__tests__/utils-test.js +3 -9
- package/src/attributeReference.js +143 -0
- package/src/configuration.js +4 -8
- package/src/context.js +96 -0
- package/src/diagnosticEvents.js +0 -2
- package/src/index.js +76 -89
- package/src/messages.js +8 -8
- package/src/utils.js +18 -15
- package/test-types.ts +3 -7
- package/typings.d.ts +140 -76
- package/src/UserFilter.js +0 -75
- package/src/UserValidator.js +0 -56
- package/src/__tests__/UserFilter-test.js +0 -93
- package/src/__tests__/UserValidator-test.js +0 -57
package/src/index.js
CHANGED
|
@@ -7,13 +7,14 @@ const PersistentStorage = require('./PersistentStorage');
|
|
|
7
7
|
const Stream = require('./Stream');
|
|
8
8
|
const Requestor = require('./Requestor');
|
|
9
9
|
const Identity = require('./Identity');
|
|
10
|
-
const
|
|
10
|
+
const AnonymousContextProcessor = require('./AnonymousContextProcessor');
|
|
11
11
|
const configuration = require('./configuration');
|
|
12
12
|
const diagnostics = require('./diagnosticEvents');
|
|
13
13
|
const { commonBasicLogger } = require('./loggers');
|
|
14
14
|
const utils = require('./utils');
|
|
15
15
|
const errors = require('./errors');
|
|
16
16
|
const messages = require('./messages');
|
|
17
|
+
const { checkContext } = require('./context');
|
|
17
18
|
const { InspectorTypes, InspectorManager } = require('./InspectorManager');
|
|
18
19
|
|
|
19
20
|
const changeEvent = 'change';
|
|
@@ -28,7 +29,7 @@ const internalChangeEvent = 'internal-change';
|
|
|
28
29
|
//
|
|
29
30
|
// For definitions of the API in the platform object, see stubPlatform.js in the test code.
|
|
30
31
|
|
|
31
|
-
function initialize(env,
|
|
32
|
+
function initialize(env, context, specifiedOptions, platform, extraOptionDefs) {
|
|
32
33
|
const logger = createLogger();
|
|
33
34
|
const emitter = EventEmitter(logger);
|
|
34
35
|
const initializationStateTracker = InitializationStateTracker(emitter);
|
|
@@ -77,19 +78,19 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
77
78
|
// The "stateProvider" object is used in the Electron SDK, to allow one client instance to take partial
|
|
78
79
|
// control of another. If present, it has the following contract:
|
|
79
80
|
// - getInitialState() returns the initial client state if it is already available. The state is an
|
|
80
|
-
// object whose properties are "environment", "
|
|
81
|
+
// object whose properties are "environment", "context", and "flags".
|
|
81
82
|
// - on("init", listener) triggers an event when the initial client state becomes available, passing
|
|
82
83
|
// the state object to the listener.
|
|
83
|
-
// - on("update", listener) triggers an event when flag values change and/or the current
|
|
84
|
-
// The parameter is an object that *may* contain "
|
|
84
|
+
// - on("update", listener) triggers an event when flag values change and/or the current context changes.
|
|
85
|
+
// The parameter is an object that *may* contain "context" and/or "flags".
|
|
85
86
|
// - enqueueEvent(event) accepts an analytics event object and returns true if the stateProvider will
|
|
86
87
|
// be responsible for delivering it, or false if we still should deliver it ourselves.
|
|
87
88
|
const stateProvider = options.stateProvider;
|
|
88
89
|
|
|
89
90
|
const ident = Identity(null, onIdentifyChange);
|
|
90
|
-
const
|
|
91
|
+
const anonymousContextProcessor = new AnonymousContextProcessor(persistentStorage);
|
|
91
92
|
const persistentFlagStore = persistentStorage.isEnabled()
|
|
92
|
-
?
|
|
93
|
+
? PersistentFlagStore(persistentStorage, environment, hash, ident, logger)
|
|
93
94
|
: null;
|
|
94
95
|
|
|
95
96
|
function createLogger() {
|
|
@@ -134,22 +135,22 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
134
135
|
|
|
135
136
|
function enqueueEvent(event) {
|
|
136
137
|
if (!environment) {
|
|
137
|
-
// We're in paired mode and haven't been initialized with an environment or
|
|
138
|
+
// We're in paired mode and haven't been initialized with an environment or context yet
|
|
138
139
|
return;
|
|
139
140
|
}
|
|
140
141
|
if (stateProvider && stateProvider.enqueueEvent && stateProvider.enqueueEvent(event)) {
|
|
141
142
|
return; // it'll be handled elsewhere
|
|
142
143
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
return;
|
|
144
|
+
|
|
145
|
+
if (!event.context) {
|
|
146
|
+
if (firstEvent) {
|
|
147
|
+
logger.warn(messages.eventWithoutContext());
|
|
148
|
+
firstEvent = false;
|
|
150
149
|
}
|
|
151
|
-
|
|
150
|
+
return;
|
|
152
151
|
}
|
|
152
|
+
firstEvent = false;
|
|
153
|
+
|
|
153
154
|
if (shouldEnqueueEvent()) {
|
|
154
155
|
logger.debug(messages.debugEnqueueingEvent(event.kind));
|
|
155
156
|
events.enqueue(event);
|
|
@@ -158,13 +159,13 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
158
159
|
|
|
159
160
|
function notifyInspectionFlagUsed(key, detail) {
|
|
160
161
|
if (inspectorManager.hasListeners(InspectorTypes.flagUsed)) {
|
|
161
|
-
inspectorManager.onFlagUsed(key, detail, ident.
|
|
162
|
+
inspectorManager.onFlagUsed(key, detail, ident.getContext());
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
function notifyInspectionIdentityChanged() {
|
|
166
167
|
if (inspectorManager.hasListeners(InspectorTypes.clientIdentityChanged)) {
|
|
167
|
-
inspectorManager.onIdentityChanged(ident.
|
|
168
|
+
inspectorManager.onIdentityChanged(ident.getContext());
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
|
|
@@ -188,46 +189,39 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
188
189
|
}
|
|
189
190
|
}
|
|
190
191
|
|
|
191
|
-
function onIdentifyChange(user
|
|
192
|
+
function onIdentifyChange(user) {
|
|
192
193
|
sendIdentifyEvent(user);
|
|
193
|
-
if (!options.autoAliasingOptOut && previousUser && previousUser.anonymous && user && !user.anonymous) {
|
|
194
|
-
alias(user, previousUser);
|
|
195
|
-
}
|
|
196
194
|
notifyInspectionIdentityChanged();
|
|
197
195
|
}
|
|
198
196
|
|
|
199
|
-
function sendIdentifyEvent(
|
|
197
|
+
function sendIdentifyEvent(context) {
|
|
200
198
|
if (stateProvider) {
|
|
201
199
|
// In paired mode, the other client is responsible for sending identify events
|
|
202
200
|
return;
|
|
203
201
|
}
|
|
204
|
-
if (
|
|
202
|
+
if (context) {
|
|
205
203
|
enqueueEvent({
|
|
206
204
|
kind: 'identify',
|
|
207
|
-
|
|
208
|
-
user: user,
|
|
205
|
+
context,
|
|
209
206
|
creationDate: new Date().getTime(),
|
|
210
207
|
});
|
|
211
208
|
}
|
|
212
209
|
}
|
|
213
210
|
|
|
214
211
|
function sendFlagEvent(key, detail, defaultValue, includeReason) {
|
|
215
|
-
const
|
|
212
|
+
const context = ident.getContext();
|
|
216
213
|
const now = new Date();
|
|
217
214
|
const value = detail ? detail.value : null;
|
|
218
215
|
|
|
219
216
|
const event = {
|
|
220
217
|
kind: 'feature',
|
|
221
218
|
key: key,
|
|
222
|
-
|
|
219
|
+
context,
|
|
223
220
|
value: value,
|
|
224
221
|
variation: detail ? detail.variationIndex : null,
|
|
225
222
|
default: defaultValue,
|
|
226
223
|
creationDate: now.getTime(),
|
|
227
224
|
};
|
|
228
|
-
if (user && user.anonymous) {
|
|
229
|
-
event.contextKind = userContextKind(user);
|
|
230
|
-
}
|
|
231
225
|
const flag = flags[key];
|
|
232
226
|
if (flag) {
|
|
233
227
|
event.version = flag.flagVersion ? flag.flagVersion : flag.version;
|
|
@@ -241,26 +235,37 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
241
235
|
enqueueEvent(event);
|
|
242
236
|
}
|
|
243
237
|
|
|
244
|
-
function
|
|
238
|
+
function verifyContext(context) {
|
|
239
|
+
// The context will already have been processed to have a string key, so we
|
|
240
|
+
// do not need to allow for legacy keys in the check.
|
|
241
|
+
if (checkContext(context, false)) {
|
|
242
|
+
return Promise.resolve(context);
|
|
243
|
+
} else {
|
|
244
|
+
return Promise.reject(new errors.LDInvalidUserError(messages.invalidContext()));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function identify(context, newHash, onDone) {
|
|
245
249
|
if (closed) {
|
|
246
250
|
return utils.wrapPromiseCallback(Promise.resolve({}), onDone);
|
|
247
251
|
}
|
|
248
252
|
if (stateProvider) {
|
|
249
|
-
// We're being controlled by another client instance, so only that instance is allowed to change the
|
|
253
|
+
// We're being controlled by another client instance, so only that instance is allowed to change the context
|
|
250
254
|
logger.warn(messages.identifyDisabled());
|
|
251
255
|
return utils.wrapPromiseCallback(Promise.resolve(utils.transformVersionedValuesToValues(flags)), onDone);
|
|
252
256
|
}
|
|
253
257
|
const clearFirst = useLocalStorage && persistentFlagStore ? persistentFlagStore.clearFlags() : Promise.resolve();
|
|
254
258
|
return utils.wrapPromiseCallback(
|
|
255
259
|
clearFirst
|
|
256
|
-
.then(() =>
|
|
257
|
-
.then(
|
|
260
|
+
.then(() => anonymousContextProcessor.processContext(context))
|
|
261
|
+
.then(verifyContext)
|
|
262
|
+
.then(validatedContext =>
|
|
258
263
|
requestor
|
|
259
|
-
.fetchFlagSettings(
|
|
264
|
+
.fetchFlagSettings(validatedContext, newHash)
|
|
260
265
|
// the following then() is nested within this one so we can use realUser from the previous closure
|
|
261
266
|
.then(requestedFlags => {
|
|
262
267
|
const flagValueMap = utils.transformVersionedValuesToValues(requestedFlags);
|
|
263
|
-
ident.
|
|
268
|
+
ident.setContext(validatedContext);
|
|
264
269
|
hash = newHash;
|
|
265
270
|
if (requestedFlags) {
|
|
266
271
|
return replaceAllFlags(requestedFlags).then(() => flagValueMap);
|
|
@@ -283,8 +288,8 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
283
288
|
);
|
|
284
289
|
}
|
|
285
290
|
|
|
286
|
-
function
|
|
287
|
-
return ident.
|
|
291
|
+
function getContext() {
|
|
292
|
+
return ident.getContext();
|
|
288
293
|
}
|
|
289
294
|
|
|
290
295
|
function flush(onDone) {
|
|
@@ -355,26 +360,6 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
355
360
|
return user.anonymous ? 'anonymousUser' : 'user';
|
|
356
361
|
}
|
|
357
362
|
|
|
358
|
-
function alias(user, previousUser) {
|
|
359
|
-
if (stateProvider) {
|
|
360
|
-
// In paired mode, the other client is responsible for sending alias events
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (!user || !previousUser) {
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
enqueueEvent({
|
|
369
|
-
kind: 'alias',
|
|
370
|
-
key: user.key,
|
|
371
|
-
contextKind: userContextKind(user),
|
|
372
|
-
previousKey: previousUser.key,
|
|
373
|
-
previousContextKind: userContextKind(previousUser),
|
|
374
|
-
creationDate: new Date().getTime(),
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
|
|
378
363
|
function track(key, data, metricValue) {
|
|
379
364
|
if (typeof key !== 'string') {
|
|
380
365
|
emitter.maybeReportError(new errors.LDInvalidEventKeyError(messages.unknownCustomEventKey(key)));
|
|
@@ -390,16 +375,16 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
390
375
|
logger.warn(messages.unknownCustomEventKey(key));
|
|
391
376
|
}
|
|
392
377
|
|
|
393
|
-
const
|
|
378
|
+
const context = ident.getContext();
|
|
394
379
|
const e = {
|
|
395
380
|
kind: 'custom',
|
|
396
381
|
key: key,
|
|
397
|
-
|
|
382
|
+
context,
|
|
398
383
|
url: platform.getCurrentUrl(),
|
|
399
384
|
creationDate: new Date().getTime(),
|
|
400
385
|
};
|
|
401
|
-
if (
|
|
402
|
-
e.contextKind = userContextKind(
|
|
386
|
+
if (context && context.anonymous) {
|
|
387
|
+
e.contextKind = userContextKind(context);
|
|
403
388
|
}
|
|
404
389
|
// Note, check specifically for null/undefined because it is legal to set these fields to a falsey value.
|
|
405
390
|
if (data !== null && data !== undefined) {
|
|
@@ -413,7 +398,7 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
413
398
|
|
|
414
399
|
function connectStream() {
|
|
415
400
|
streamActive = true;
|
|
416
|
-
if (!ident.
|
|
401
|
+
if (!ident.getContext()) {
|
|
417
402
|
return;
|
|
418
403
|
}
|
|
419
404
|
const tryParseData = jsonData => {
|
|
@@ -424,16 +409,16 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
424
409
|
return undefined;
|
|
425
410
|
}
|
|
426
411
|
};
|
|
427
|
-
stream.connect(ident.
|
|
412
|
+
stream.connect(ident.getContext(), hash, {
|
|
428
413
|
ping: function() {
|
|
429
414
|
logger.debug(messages.debugStreamPing());
|
|
430
|
-
const
|
|
415
|
+
const contextAtTimeOfPingEvent = ident.getContext();
|
|
431
416
|
requestor
|
|
432
|
-
.fetchFlagSettings(
|
|
417
|
+
.fetchFlagSettings(contextAtTimeOfPingEvent, hash)
|
|
433
418
|
.then(requestedFlags => {
|
|
434
|
-
// Check whether the current
|
|
419
|
+
// Check whether the current context is still the same - we don't want to overwrite the flags if
|
|
435
420
|
// the application has called identify() while this request was in progress
|
|
436
|
-
if (utils.deepEquals(
|
|
421
|
+
if (utils.deepEquals(contextAtTimeOfPingEvent, ident.getContext())) {
|
|
437
422
|
replaceAllFlags(requestedFlags || {});
|
|
438
423
|
}
|
|
439
424
|
})
|
|
@@ -663,17 +648,20 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
663
648
|
if (!env) {
|
|
664
649
|
return Promise.reject(new errors.LDInvalidEnvironmentIdError(messages.environmentNotSpecified()));
|
|
665
650
|
}
|
|
666
|
-
return
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
651
|
+
return anonymousContextProcessor
|
|
652
|
+
.processContext(context)
|
|
653
|
+
.then(verifyContext)
|
|
654
|
+
.then(validatedContext => {
|
|
655
|
+
ident.setContext(validatedContext);
|
|
656
|
+
if (typeof options.bootstrap === 'object') {
|
|
657
|
+
// flags have already been set earlier
|
|
658
|
+
return signalSuccessfulInit();
|
|
659
|
+
} else if (useLocalStorage) {
|
|
660
|
+
return finishInitWithLocalStorage();
|
|
661
|
+
} else {
|
|
662
|
+
return finishInitWithPolling();
|
|
663
|
+
}
|
|
664
|
+
});
|
|
677
665
|
}
|
|
678
666
|
|
|
679
667
|
function finishInitWithLocalStorage() {
|
|
@@ -681,7 +669,7 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
681
669
|
if (storedFlags === null || storedFlags === undefined) {
|
|
682
670
|
flags = {};
|
|
683
671
|
return requestor
|
|
684
|
-
.fetchFlagSettings(ident.
|
|
672
|
+
.fetchFlagSettings(ident.getContext(), hash)
|
|
685
673
|
.then(requestedFlags => replaceAllFlags(requestedFlags || {}))
|
|
686
674
|
.then(signalSuccessfulInit)
|
|
687
675
|
.catch(err => {
|
|
@@ -696,7 +684,7 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
696
684
|
utils.onNextTick(signalSuccessfulInit);
|
|
697
685
|
|
|
698
686
|
return requestor
|
|
699
|
-
.fetchFlagSettings(ident.
|
|
687
|
+
.fetchFlagSettings(ident.getContext(), hash)
|
|
700
688
|
.then(requestedFlags => replaceAllFlags(requestedFlags))
|
|
701
689
|
.catch(err => emitter.maybeReportError(err));
|
|
702
690
|
}
|
|
@@ -705,7 +693,7 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
705
693
|
|
|
706
694
|
function finishInitWithPolling() {
|
|
707
695
|
return requestor
|
|
708
|
-
.fetchFlagSettings(ident.
|
|
696
|
+
.fetchFlagSettings(ident.getContext(), hash)
|
|
709
697
|
.then(requestedFlags => {
|
|
710
698
|
flags = requestedFlags || {};
|
|
711
699
|
|
|
@@ -721,14 +709,14 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
721
709
|
|
|
722
710
|
function initFromStateProvider(state) {
|
|
723
711
|
environment = state.environment;
|
|
724
|
-
ident.
|
|
712
|
+
ident.setContext(state.context);
|
|
725
713
|
flags = { ...state.flags };
|
|
726
714
|
utils.onNextTick(signalSuccessfulInit);
|
|
727
715
|
}
|
|
728
716
|
|
|
729
717
|
function updateFromStateProvider(state) {
|
|
730
|
-
if (state.
|
|
731
|
-
ident.
|
|
718
|
+
if (state.context) {
|
|
719
|
+
ident.setContext(state.context);
|
|
732
720
|
}
|
|
733
721
|
if (state.flags) {
|
|
734
722
|
replaceAllFlags(state.flags); // don't wait for this Promise to be resolved
|
|
@@ -788,11 +776,10 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
788
776
|
waitForInitialization: () => initializationStateTracker.getInitializationPromise(),
|
|
789
777
|
waitUntilReady: () => initializationStateTracker.getReadyPromise(),
|
|
790
778
|
identify: identify,
|
|
791
|
-
|
|
779
|
+
getContext: getContext,
|
|
792
780
|
variation: variation,
|
|
793
781
|
variationDetail: variationDetail,
|
|
794
782
|
track: track,
|
|
795
|
-
alias: alias,
|
|
796
783
|
on: on,
|
|
797
784
|
off: off,
|
|
798
785
|
setStreaming: setStreaming,
|
|
@@ -805,7 +792,7 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
805
792
|
client: client, // The client object containing all public methods.
|
|
806
793
|
options: options, // The validated configuration object, including all defaults.
|
|
807
794
|
emitter: emitter, // The event emitter which can be used to log errors or trigger events.
|
|
808
|
-
ident: ident, // The Identity object that manages the current
|
|
795
|
+
ident: ident, // The Identity object that manages the current context.
|
|
809
796
|
logger: logger, // The logging abstraction.
|
|
810
797
|
requestor: requestor, // The Requestor object.
|
|
811
798
|
start: start, // Starts the client once the environment is ready.
|
package/src/messages.js
CHANGED
|
@@ -25,7 +25,7 @@ const eventCapacityExceeded = function() {
|
|
|
25
25
|
return 'Exceeded event queue capacity. Increase capacity to avoid dropping events.';
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const eventWithoutContext = function() {
|
|
29
29
|
return 'Be sure to call `identify` in the LaunchDarkly client: https://docs.launchdarkly.com/sdk/features/identify#javascript';
|
|
30
30
|
};
|
|
31
31
|
|
|
@@ -60,12 +60,12 @@ const errorFetchingFlags = function(err) {
|
|
|
60
60
|
return 'Error fetching flag settings: ' + errorString(err);
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
const
|
|
64
|
-
return 'No
|
|
63
|
+
const contextNotSpecified = function() {
|
|
64
|
+
return 'No context specified.' + docLink;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const
|
|
68
|
-
return 'Invalid
|
|
67
|
+
const invalidContext = function() {
|
|
68
|
+
return 'Invalid context specified.' + docLink;
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
const invalidData = function() {
|
|
@@ -210,7 +210,7 @@ module.exports = {
|
|
|
210
210
|
environmentNotSpecified,
|
|
211
211
|
errorFetchingFlags,
|
|
212
212
|
eventCapacityExceeded,
|
|
213
|
-
|
|
213
|
+
eventWithoutContext,
|
|
214
214
|
httpErrorMessage,
|
|
215
215
|
httpUnavailable,
|
|
216
216
|
identifyDisabled,
|
|
@@ -219,8 +219,8 @@ module.exports = {
|
|
|
219
219
|
invalidData,
|
|
220
220
|
invalidInspector,
|
|
221
221
|
invalidKey,
|
|
222
|
+
invalidContext,
|
|
222
223
|
invalidTagValue,
|
|
223
|
-
invalidUser,
|
|
224
224
|
localStorageUnavailable,
|
|
225
225
|
networkError,
|
|
226
226
|
optionBelowMinimum,
|
|
@@ -230,8 +230,8 @@ module.exports = {
|
|
|
230
230
|
tagValueTooLong,
|
|
231
231
|
unknownCustomEventKey,
|
|
232
232
|
unknownOption,
|
|
233
|
+
contextNotSpecified,
|
|
233
234
|
unrecoverableStreamError,
|
|
234
|
-
userNotSpecified,
|
|
235
235
|
wrongOptionType,
|
|
236
236
|
wrongOptionTypeBoolean,
|
|
237
237
|
};
|
package/src/utils.js
CHANGED
|
@@ -120,7 +120,7 @@ function transformVersionedValuesToValues(flagsState) {
|
|
|
120
120
|
* @param {Array[Object}]} events queue of events to divide
|
|
121
121
|
* @returns Array[Array[Object]]
|
|
122
122
|
*/
|
|
123
|
-
function
|
|
123
|
+
function chunkEventsForUrl(maxLength, events) {
|
|
124
124
|
const allEvents = events.slice(0);
|
|
125
125
|
const allChunks = [];
|
|
126
126
|
let remainingSpace = maxLength;
|
|
@@ -165,34 +165,37 @@ function objectHasOwnProperty(object, name) {
|
|
|
165
165
|
return Object.prototype.hasOwnProperty.call(object, name);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
function
|
|
169
|
-
if (!
|
|
170
|
-
return
|
|
168
|
+
function sanitizeContext(context) {
|
|
169
|
+
if (!context) {
|
|
170
|
+
return context;
|
|
171
171
|
}
|
|
172
|
-
let
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
172
|
+
let newContext;
|
|
173
|
+
// Only stringify user attributes for legacy users.
|
|
174
|
+
if (context.kind === null || context.kind === undefined) {
|
|
175
|
+
userAttrsToStringify.forEach(attr => {
|
|
176
|
+
const value = context[attr];
|
|
177
|
+
if (value !== undefined && typeof value !== 'string') {
|
|
178
|
+
newContext = newContext || { ...context };
|
|
179
|
+
newContext[attr] = String(value);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
180
182
|
}
|
|
181
|
-
|
|
183
|
+
|
|
184
|
+
return newContext || context;
|
|
182
185
|
}
|
|
183
186
|
|
|
184
187
|
module.exports = {
|
|
185
188
|
appendUrlPath,
|
|
186
189
|
base64URLEncode,
|
|
187
190
|
btoa,
|
|
188
|
-
|
|
191
|
+
chunkEventsForUrl,
|
|
189
192
|
clone,
|
|
190
193
|
deepEquals,
|
|
191
194
|
extend,
|
|
192
195
|
getLDUserAgentString,
|
|
193
196
|
objectHasOwnProperty,
|
|
194
197
|
onNextTick,
|
|
195
|
-
|
|
198
|
+
sanitizeContext,
|
|
196
199
|
transformValuesToVersionedValues,
|
|
197
200
|
transformVersionedValuesToValues,
|
|
198
201
|
wrapPromiseCallback,
|
package/test-types.ts
CHANGED
|
@@ -7,7 +7,7 @@ import * as ld from 'launchdarkly-js-sdk-common';
|
|
|
7
7
|
var userWithKeyOnly: ld.LDUser = { key: 'user' };
|
|
8
8
|
var anonUserWithNoKey: ld.LDUser = { anonymous: true };
|
|
9
9
|
var anonUserWithKey: ld.LDUser = { key: 'anon-user', anonymous: true };
|
|
10
|
-
var user: ld.
|
|
10
|
+
var user: ld.LDContext = {
|
|
11
11
|
key: 'user',
|
|
12
12
|
secondary: 'otherkey',
|
|
13
13
|
name: 'name',
|
|
@@ -41,9 +41,7 @@ var allBaseOptions: ld.LDOptionsBase = {
|
|
|
41
41
|
evaluationReasons: true,
|
|
42
42
|
sendEvents: true,
|
|
43
43
|
allAttributesPrivate: true,
|
|
44
|
-
|
|
45
|
-
inlineUsersInEvents: true,
|
|
46
|
-
allowFrequentDuplicateEvents: true,
|
|
44
|
+
privateAttributes: [ 'x' ],
|
|
47
45
|
sendEventsOnlyForVariation: true,
|
|
48
46
|
flushInterval: 1,
|
|
49
47
|
streamReconnectDelay: 1,
|
|
@@ -63,9 +61,7 @@ client.identify(user).then(() => {});
|
|
|
63
61
|
client.identify(user, undefined, () => {});
|
|
64
62
|
client.identify(user, 'hash').then(() => {});
|
|
65
63
|
|
|
66
|
-
client.
|
|
67
|
-
|
|
68
|
-
var user: ld.LDUser = client.getUser();
|
|
64
|
+
var user: ld.LDContext = client.getContext();
|
|
69
65
|
|
|
70
66
|
client.flush(() => {});
|
|
71
67
|
client.flush().then(() => {});
|