@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.
- package/.babelrc +16 -0
- package/.circleci/config.yml +89 -0
- package/.eslintignore +5 -0
- package/.eslintrc.yaml +114 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/pull_request_template.md +21 -0
- package/.github/workflows/stale.yml +8 -0
- package/.hound.yml +33 -0
- package/.ldrelease/config.yml +28 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +603 -0
- package/CODEOWNERS +2 -0
- package/CONTRIBUTING.md +55 -0
- package/LICENSE.txt +13 -0
- package/README.md +36 -0
- package/SECURITY.md +5 -0
- package/attribute_reference.js +217 -0
- package/big_segments.js +117 -0
- package/caching_store_wrapper.js +240 -0
- package/changes.json +30 -0
- package/configuration.js +235 -0
- package/context.js +98 -0
- package/context_filter.js +137 -0
- package/contract-tests/README.md +7 -0
- package/contract-tests/index.js +109 -0
- package/contract-tests/log.js +23 -0
- package/contract-tests/package.json +15 -0
- package/contract-tests/sdkClientEntity.js +110 -0
- package/contract-tests/testharness-suppressions.txt +2 -0
- package/diagnostic_events.js +151 -0
- package/docs/typedoc.js +10 -0
- package/errors.js +26 -0
- package/evaluator.js +822 -0
- package/event_factory.js +121 -0
- package/event_processor.js +320 -0
- package/event_summarizer.js +101 -0
- package/feature_store.js +120 -0
- package/feature_store_event_wrapper.js +258 -0
- package/file_data_source.js +192 -0
- package/flags_state.js +46 -0
- package/index.d.ts +2426 -0
- package/index.js +452 -0
- package/integrations.js +7 -0
- package/interfaces.js +2 -0
- package/loggers.js +125 -0
- package/messages.js +31 -0
- package/operators.js +106 -0
- package/package.json +105 -0
- package/polling.js +70 -0
- package/requestor.js +62 -0
- package/scripts/better-audit.sh +76 -0
- package/sharedtest/big_segment_store_tests.js +86 -0
- package/sharedtest/feature_store_tests.js +177 -0
- package/sharedtest/persistent_feature_store_tests.js +183 -0
- package/sharedtest/store_tests.js +7 -0
- package/streaming.js +179 -0
- package/test/LDClient-big-segments-test.js +92 -0
- package/test/LDClient-end-to-end-test.js +218 -0
- package/test/LDClient-evaluation-all-flags-test.js +226 -0
- package/test/LDClient-evaluation-test.js +204 -0
- package/test/LDClient-events-test.js +502 -0
- package/test/LDClient-listeners-test.js +180 -0
- package/test/LDClient-test.js +96 -0
- package/test/LDClient-tls-test.js +110 -0
- package/test/attribute_reference-test.js +494 -0
- package/test/big_segments-test.js +182 -0
- package/test/caching_store_wrapper-test.js +434 -0
- package/test/configuration-test.js +249 -0
- package/test/context-test.js +93 -0
- package/test/context_filter-test.js +424 -0
- package/test/diagnostic_events-test.js +152 -0
- package/test/evaluator-big-segments-test.js +301 -0
- package/test/evaluator-bucketing-test.js +333 -0
- package/test/evaluator-clause-test.js +277 -0
- package/test/evaluator-flag-test.js +452 -0
- package/test/evaluator-pre-conditions-test.js +105 -0
- package/test/evaluator-rule-test.js +131 -0
- package/test/evaluator-segment-match-test.js +310 -0
- package/test/evaluator_helpers.js +106 -0
- package/test/event_processor-test.js +680 -0
- package/test/event_summarizer-test.js +146 -0
- package/test/feature_store-test.js +42 -0
- package/test/feature_store_event_wrapper-test.js +182 -0
- package/test/feature_store_test_base.js +60 -0
- package/test/file_data_source-test.js +255 -0
- package/test/loggers-test.js +126 -0
- package/test/operators-test.js +102 -0
- package/test/polling-test.js +158 -0
- package/test/requestor-test.js +60 -0
- package/test/store_tests_big_segments-test.js +61 -0
- package/test/streaming-test.js +323 -0
- package/test/stubs.js +107 -0
- package/test/test_data-test.js +341 -0
- package/test/update_queue-test.js +61 -0
- package/test-types.ts +210 -0
- package/test_data.js +323 -0
- package/tsconfig.json +14 -0
- package/update_queue.js +28 -0
- package/utils/__tests__/httpUtils-test.js +39 -0
- package/utils/__tests__/wrapPromiseCallback-test.js +33 -0
- package/utils/asyncUtils.js +32 -0
- package/utils/httpUtils.js +105 -0
- package/utils/stringifyAttrs.js +14 -0
- package/utils/wrapPromiseCallback.js +36 -0
- package/versioned_data_kind.js +34 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const ld = require('launchdarkly-node-server-sdk');
|
|
2
|
+
|
|
3
|
+
const { Log, sdkLogger } = require('./log');
|
|
4
|
+
|
|
5
|
+
const badCommandError = new Error('unsupported command');
|
|
6
|
+
|
|
7
|
+
function makeSdkConfig(options, tag) {
|
|
8
|
+
const cf = {
|
|
9
|
+
logger: sdkLogger(tag),
|
|
10
|
+
};
|
|
11
|
+
const maybeTime = seconds => (seconds === undefined || seconds === null ? undefined : seconds / 1000);
|
|
12
|
+
if (options.streaming) {
|
|
13
|
+
cf.streamUri = options.streaming.baseUri;
|
|
14
|
+
cf.streamInitialReconnectDelay = maybeTime(options.streaming.initialRetryDelayMs);
|
|
15
|
+
}
|
|
16
|
+
if (options.events) {
|
|
17
|
+
cf.allAttributesPrivate = options.events.allAttributesPrivate;
|
|
18
|
+
cf.eventsUri = options.events.baseUri;
|
|
19
|
+
cf.capacity = options.events.capacity;
|
|
20
|
+
cf.diagnosticOptOut = !options.events.enableDiagnostics;
|
|
21
|
+
cf.flushInterval = maybeTime(options.events.flushIntervalMs);
|
|
22
|
+
cf.privateAttributes = options.events.globalPrivateAttributes;
|
|
23
|
+
}
|
|
24
|
+
if (options.tags) {
|
|
25
|
+
cf.application = {
|
|
26
|
+
id: options.tags.applicationId,
|
|
27
|
+
version: options.tags.applicationVersion,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return cf;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function newSdkClientEntity(options) {
|
|
34
|
+
const c = {};
|
|
35
|
+
const log = Log(options.tag);
|
|
36
|
+
|
|
37
|
+
log.info('Creating client with configuration: ' + JSON.stringify(options.configuration));
|
|
38
|
+
const timeout =
|
|
39
|
+
options.configuration.startWaitTimeMs !== null && options.configuration.startWaitTimeMs !== undefined
|
|
40
|
+
? options.configuration.startWaitTimeMs
|
|
41
|
+
: 5000;
|
|
42
|
+
const client = ld.init(
|
|
43
|
+
options.configuration.credential || 'unknown-sdk-key',
|
|
44
|
+
makeSdkConfig(options.configuration, options.tag)
|
|
45
|
+
);
|
|
46
|
+
try {
|
|
47
|
+
await Promise.race([client.waitForInitialization(), new Promise(resolve => setTimeout(resolve, timeout))]);
|
|
48
|
+
} catch (_) {
|
|
49
|
+
// if waitForInitialization() rejects, the client failed to initialize, see next line
|
|
50
|
+
}
|
|
51
|
+
if (!client.initialized() && !options.configuration.initCanFail) {
|
|
52
|
+
client.close();
|
|
53
|
+
throw new Error('client initialization failed');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
c.close = () => {
|
|
57
|
+
client.close();
|
|
58
|
+
log.info('Test ended');
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
c.doCommand = async params => {
|
|
62
|
+
log.info('Received command: ' + params.command);
|
|
63
|
+
switch (params.command) {
|
|
64
|
+
case 'evaluate': {
|
|
65
|
+
const pe = params.evaluate;
|
|
66
|
+
if (pe.detail) {
|
|
67
|
+
return await client.variationDetail(pe.flagKey, pe.context || pe.user, pe.defaultValue);
|
|
68
|
+
} else {
|
|
69
|
+
const value = await client.variation(pe.flagKey, pe.context || pe.user, pe.defaultValue);
|
|
70
|
+
return { value };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
case 'evaluateAll': {
|
|
75
|
+
const pea = params.evaluateAll;
|
|
76
|
+
const eao = {
|
|
77
|
+
clientSideOnly: pea.clientSideOnly,
|
|
78
|
+
detailsOnlyForTrackedFlags: pea.detailsOnlyForTrackedFlags,
|
|
79
|
+
withReasons: pea.withReasons,
|
|
80
|
+
};
|
|
81
|
+
return { state: await client.allFlagsState(pea.context || pea.user, eao) };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
case 'identifyEvent':
|
|
85
|
+
client.identify(params.identifyEvent.context || params.identifyEvent.user);
|
|
86
|
+
return undefined;
|
|
87
|
+
|
|
88
|
+
case 'customEvent': {
|
|
89
|
+
const pce = params.customEvent;
|
|
90
|
+
client.track(pce.eventKey, pce.context || pce.user, pce.data, pce.metricValue);
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
case 'flushEvents':
|
|
95
|
+
client.flush();
|
|
96
|
+
return undefined;
|
|
97
|
+
|
|
98
|
+
case 'getBigSegmentStoreStatus':
|
|
99
|
+
return undefined;
|
|
100
|
+
|
|
101
|
+
default:
|
|
102
|
+
throw badCommandError;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return c;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports.newSdkClientEntity = newSdkClientEntity;
|
|
110
|
+
module.exports.badCommandError = badCommandError;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const { v4: uuidv4 } = require('uuid');
|
|
3
|
+
const configuration = require('./configuration');
|
|
4
|
+
const packageJson = require('./package.json');
|
|
5
|
+
|
|
6
|
+
// An object that maintains information that will go into diagnostic events, and knows how to format
|
|
7
|
+
// those events. It is instantiated by the SDK client, and shared with the event processor.
|
|
8
|
+
function DiagnosticsManager(config, diagnosticId, startTime) {
|
|
9
|
+
let dataSinceDate = startTime;
|
|
10
|
+
let streamInits = [];
|
|
11
|
+
const acc = {};
|
|
12
|
+
|
|
13
|
+
// Creates the initial event that is sent by the event processor when the SDK starts up. This will not
|
|
14
|
+
// be repeated during the lifetime of the SDK client.
|
|
15
|
+
acc.createInitEvent = () => ({
|
|
16
|
+
kind: 'diagnostic-init',
|
|
17
|
+
id: diagnosticId,
|
|
18
|
+
creationDate: startTime,
|
|
19
|
+
sdk: makeSdkData(config),
|
|
20
|
+
configuration: makeConfigData(config),
|
|
21
|
+
platform: makePlatformData(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Records a stream connection attempt (called by the stream processor).
|
|
25
|
+
// timestamp: Time of the *beginning* of the connection attempt.
|
|
26
|
+
// failed: True if the connection failed, or we got a read timeout before receiving a "put".
|
|
27
|
+
// durationMillis: Elapsed time between starting timestamp and when we either gave up/lost the
|
|
28
|
+
// connection or received a successful "put".
|
|
29
|
+
acc.recordStreamInit = (timestamp, failed, durationMillis) => {
|
|
30
|
+
const item = { timestamp, failed, durationMillis };
|
|
31
|
+
streamInits.push(item);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Creates a periodic event containing time-dependent stats, and resets the state of the manager with
|
|
35
|
+
// regard to those stats.
|
|
36
|
+
// Note: the reason droppedEvents, deduplicatedUsers, and eventsInLastBatch are passed into this function,
|
|
37
|
+
// instead of being properties of the DiagnosticsManager, is that the event processor is the one who's
|
|
38
|
+
// calling this function and is also the one who's tracking those stats.
|
|
39
|
+
acc.createStatsEventAndReset = (droppedEvents, deduplicatedUsers, eventsInLastBatch) => {
|
|
40
|
+
const currentTime = new Date().getTime();
|
|
41
|
+
const ret = {
|
|
42
|
+
kind: 'diagnostic',
|
|
43
|
+
id: diagnosticId,
|
|
44
|
+
creationDate: currentTime,
|
|
45
|
+
dataSinceDate,
|
|
46
|
+
droppedEvents,
|
|
47
|
+
deduplicatedUsers,
|
|
48
|
+
eventsInLastBatch,
|
|
49
|
+
streamInits,
|
|
50
|
+
};
|
|
51
|
+
dataSinceDate = currentTime;
|
|
52
|
+
streamInits = [];
|
|
53
|
+
return ret;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return acc;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function DiagnosticId(sdkKey) {
|
|
60
|
+
const ret = {
|
|
61
|
+
diagnosticId: uuidv4(),
|
|
62
|
+
};
|
|
63
|
+
if (sdkKey) {
|
|
64
|
+
ret.sdkKeySuffix = sdkKey.length > 6 ? sdkKey.substring(sdkKey.length - 6) : sdkKey;
|
|
65
|
+
}
|
|
66
|
+
return ret;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function makeSdkData(config) {
|
|
70
|
+
const sdkData = {
|
|
71
|
+
name: 'node-server-sdk',
|
|
72
|
+
version: packageJson.version,
|
|
73
|
+
};
|
|
74
|
+
if (config.wrapperName) {
|
|
75
|
+
sdkData.wrapperName = config.wrapperName;
|
|
76
|
+
}
|
|
77
|
+
if (config.wrapperVersion) {
|
|
78
|
+
sdkData.wrapperVersion = config.wrapperVersion;
|
|
79
|
+
}
|
|
80
|
+
return sdkData;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function makeConfigData(config) {
|
|
84
|
+
const defaults = configuration.defaults();
|
|
85
|
+
const secondsToMillis = sec => Math.trunc(sec * 1000);
|
|
86
|
+
|
|
87
|
+
function getComponentDescription(component, defaultName) {
|
|
88
|
+
if (component) {
|
|
89
|
+
return component.description || 'custom';
|
|
90
|
+
}
|
|
91
|
+
return defaultName;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const configData = {
|
|
95
|
+
customBaseURI: config.baseUri !== defaults.baseUri,
|
|
96
|
+
customStreamURI: config.streamUri !== defaults.streamUri,
|
|
97
|
+
customEventsURI: config.eventsUri !== defaults.eventsUri,
|
|
98
|
+
eventsCapacity: config.capacity,
|
|
99
|
+
connectTimeoutMillis: secondsToMillis(config.timeout),
|
|
100
|
+
socketTimeoutMillis: secondsToMillis(config.timeout), // Node doesn't distinguish between these two kinds of timeouts
|
|
101
|
+
eventsFlushIntervalMillis: secondsToMillis(config.flushInterval),
|
|
102
|
+
pollingIntervalMillis: secondsToMillis(config.pollInterval),
|
|
103
|
+
// startWaitMillis: n/a (Node SDK does not have this feature)
|
|
104
|
+
// samplingInterval: n/a (Node SDK does not have this feature)
|
|
105
|
+
reconnectTimeMillis: secondsToMillis(config.streamInitialReconnectDelay),
|
|
106
|
+
streamingDisabled: !config.stream,
|
|
107
|
+
usingRelayDaemon: !!config.useLdd,
|
|
108
|
+
offline: !!config.offline,
|
|
109
|
+
allAttributesPrivate: !!config.allAttributesPrivate,
|
|
110
|
+
contextKeysCapacity: config.contextKeysCapacity,
|
|
111
|
+
contextKeysFlushIntervalMillis: secondsToMillis(config.contextKeysFlushInterval),
|
|
112
|
+
usingProxy: !!(config.proxyAgent || config.proxyHost),
|
|
113
|
+
usingProxyAuthenticator: !!config.proxyAuth,
|
|
114
|
+
diagnosticRecordingIntervalMillis: secondsToMillis(config.diagnosticRecordingInterval),
|
|
115
|
+
dataStoreType: getComponentDescription(config.featureStore, 'memory'),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return configData;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function makePlatformData() {
|
|
122
|
+
return {
|
|
123
|
+
name: 'Node',
|
|
124
|
+
osArch: os.arch(),
|
|
125
|
+
osName: normalizePlatformName(os.platform()),
|
|
126
|
+
osVersion: os.release(),
|
|
127
|
+
// Note that os.release() is not the same OS version string that would be reported by other languages.
|
|
128
|
+
// It's defined as being the value returned by "uname -r" (e.g. on Mac OS 10.14, this is "18.7.0"; on
|
|
129
|
+
// Ubuntu 16.04, it is "4.4.0-1095-aws"), or GetVersionExW in Windows.
|
|
130
|
+
nodeVersion: process.versions.node,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function normalizePlatformName(platformName) {
|
|
135
|
+
// The following logic is based on how Node.js reports the platform name
|
|
136
|
+
switch (platformName) {
|
|
137
|
+
case 'darwin':
|
|
138
|
+
return 'MacOS';
|
|
139
|
+
case 'win32':
|
|
140
|
+
return 'Windows';
|
|
141
|
+
case 'linux':
|
|
142
|
+
return 'Linux';
|
|
143
|
+
default:
|
|
144
|
+
return platformName;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
DiagnosticsManager,
|
|
150
|
+
DiagnosticId,
|
|
151
|
+
};
|
package/docs/typedoc.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
out: '/tmp/project-releaser/project/docs/build/html',
|
|
3
|
+
exclude: [
|
|
4
|
+
'**/node_modules/**',
|
|
5
|
+
'test-types.ts'
|
|
6
|
+
],
|
|
7
|
+
name: "LaunchDarkly Server-Side Node SDK (7.0.0)",
|
|
8
|
+
readme: 'none', // don't add a home page with a copy of README.md
|
|
9
|
+
entryPoints: "/tmp/project-releaser/project/index.d.ts"
|
|
10
|
+
};
|
package/errors.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function createCustomError(name) {
|
|
2
|
+
function CustomError(message, code) {
|
|
3
|
+
Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
|
|
4
|
+
this.message = message;
|
|
5
|
+
this.code = code;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
CustomError.prototype = new Error();
|
|
9
|
+
CustomError.prototype.name = name;
|
|
10
|
+
CustomError.prototype.constructor = CustomError;
|
|
11
|
+
|
|
12
|
+
return CustomError;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
exports.LDPollingError = createCustomError('LaunchDarklyPollingError');
|
|
16
|
+
exports.LDStreamingError = createCustomError('LaunchDarklyStreamingError');
|
|
17
|
+
exports.LDUnexpectedResponseError = createCustomError('LaunchDarklyUnexpectedResponseError');
|
|
18
|
+
exports.LDInvalidSDKKeyError = createCustomError('LaunchDarklyInvalidSDKKeyError');
|
|
19
|
+
exports.LDClientError = createCustomError('LaunchDarklyClientError');
|
|
20
|
+
|
|
21
|
+
exports.isHttpErrorRecoverable = function (status) {
|
|
22
|
+
if (status >= 400 && status < 500) {
|
|
23
|
+
return status === 400 || status === 408 || status === 429;
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
};
|