@configdirector/react-native-sdk 1.10.1 → 1.11.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/dist/configdirector-react-native-testing.d.mts +12 -3
- package/dist/configdirector-react-native-testing.mjs +33 -5
- package/dist/configdirector-react-native.d.mts +2 -2
- package/dist/configdirector-react-native.mjs +56 -5
- package/dist/{installedTestClient-CM98r0qu.mjs → installedTestClient-BopKe_F5.mjs} +136 -3
- package/dist/{types-DmbDeZqL.d.mts → types-BCVhD8m2.d.mts} +68 -6
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as NativeJsonValue, d as ConfigDirectorContext, i as ConfigDirectorClient, l as NativeValue, p as ConfigDirectorLogger, u as NativeValues } from "./types-
|
|
1
|
+
import { S as TrackedEventDetails, b as TrackedEvent, c as NativeJsonValue, d as ConfigDirectorContext, i as ConfigDirectorClient, l as NativeValue, p as ConfigDirectorLogger, u as NativeValues, x as TrackedEventDetailValue } from "./types-BCVhD8m2.mjs";
|
|
2
2
|
|
|
3
3
|
//#region ../shared/src/errors.d.ts
|
|
4
4
|
declare class ConfigDirectorConnectionError extends Error {
|
|
@@ -28,7 +28,9 @@ type TestClientOptions = {
|
|
|
28
28
|
*/
|
|
29
29
|
timeout?: number;
|
|
30
30
|
/**
|
|
31
|
-
* The logger the SDK client uses. Defaults to the SDK's console logger at `warn` level.
|
|
31
|
+
* The logger the SDK client uses. Defaults to the SDK's console logger at `warn` level. Since the
|
|
32
|
+
* test client sends no telemetry, every tracked event that is not recorded as it was tracked logs
|
|
33
|
+
* a warning, rather than each cause once per telemetry batch.
|
|
32
34
|
*/
|
|
33
35
|
logger?: ConfigDirectorLogger;
|
|
34
36
|
};
|
|
@@ -97,6 +99,13 @@ interface TestClient {
|
|
|
97
99
|
* the client connects without it. Resumes are not recorded.
|
|
98
100
|
*/
|
|
99
101
|
readonly contextUpdates: ConfigDirectorContext[];
|
|
102
|
+
/**
|
|
103
|
+
* Every event the code under test tracked through the client, in call order, with the context
|
|
104
|
+
* the client had at that moment, as the SDK would report it: an event whose name does not have
|
|
105
|
+
* 1 to 150 characters is not recorded, and a value or details the SDK would leave out are missing
|
|
106
|
+
* from the record. The limit of 1,000 events per telemetry batch does not apply.
|
|
107
|
+
*/
|
|
108
|
+
readonly trackedEvents: TrackedEvent[];
|
|
100
109
|
}
|
|
101
110
|
//#endregion
|
|
102
111
|
//#region ../js-client-core/src/testing/installTestClient.d.ts
|
|
@@ -146,4 +155,4 @@ declare const installTestClient: (testClient: TestClient) => (() => void);
|
|
|
146
155
|
*/
|
|
147
156
|
declare const createTestClient: (options?: TestClientOptions) => TestClient;
|
|
148
157
|
//#endregion
|
|
149
|
-
export { ConfigDirectorConnectionError, ConfigDirectorValidationError, type TestClient, type TestClientOptions, type NativeJsonValue as TestJsonValue, type NativeValue as TestValue, type NativeValues as TestValues, createTestClient, installTestClient };
|
|
158
|
+
export { ConfigDirectorConnectionError, ConfigDirectorValidationError, type TestClient, type TestClientOptions, type NativeJsonValue as TestJsonValue, type NativeValue as TestValue, type NativeValues as TestValues, type TrackedEvent, type TrackedEventDetailValue, type TrackedEventDetails, createTestClient, installTestClient };
|
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
import { c as
|
|
1
|
+
import { _ as Emitter, c as trackedEventWarningMessages, d as discardingTelemetryClient, f as createDefaultLogger, g as ConfigDirectorValidationError, h as ConfigDirectorConnectionError, p as nativeValueConfigState, r as DefaultConfigDirectorClient, t as INSTALLED_TEST_CLIENT_SLOT, v as _defineProperty } from "./installedTestClient-BopKe_F5.mjs";
|
|
2
|
+
//#region ../shared/src/telemetry/RecordedTrackedEvents.ts
|
|
3
|
+
const recordedEventOf = (entry, context) => ({
|
|
4
|
+
name: entry.name,
|
|
5
|
+
...context === void 0 ? {} : { context },
|
|
6
|
+
...entry.value === void 0 ? {} : { value: entry.value },
|
|
7
|
+
...entry.details === void 0 ? {} : { details: entry.details }
|
|
8
|
+
});
|
|
9
|
+
var RecordedTrackedEvents = class {
|
|
10
|
+
constructor(logger) {
|
|
11
|
+
this.logger = logger;
|
|
12
|
+
_defineProperty(this, "events", []);
|
|
13
|
+
}
|
|
14
|
+
record(admission, context) {
|
|
15
|
+
admission.warnings.forEach((warning) => this.logger.warn(trackedEventWarningMessages[warning]));
|
|
16
|
+
switch (admission.outcome) {
|
|
17
|
+
case "dropped": return;
|
|
18
|
+
case "admitted":
|
|
19
|
+
this.events.push(recordedEventOf(admission.entry, context));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
2
25
|
//#region ../js-client-core/src/testing/InMemoryConnection.ts
|
|
3
26
|
const TEST_ENVIRONMENT = {
|
|
4
27
|
environmentId: "test-environment",
|
|
@@ -195,9 +218,10 @@ var InMemoryConnection = class {
|
|
|
195
218
|
//#region ../js-client-core/src/testing/TestClient.ts
|
|
196
219
|
const TEST_CLIENT_SDK_KEY = "test-client";
|
|
197
220
|
var InMemoryTestClient = class {
|
|
198
|
-
constructor(client, connection) {
|
|
221
|
+
constructor(client, connection, trackedEvents) {
|
|
199
222
|
this.client = client;
|
|
200
223
|
this.connection = connection;
|
|
224
|
+
this.trackedEvents = trackedEvents;
|
|
201
225
|
}
|
|
202
226
|
setValue(key, value) {
|
|
203
227
|
this.connection.setValue(key, value);
|
|
@@ -234,10 +258,14 @@ const createTestClient$1 = (sdkOptions, options) => {
|
|
|
234
258
|
var _options$logger, _options$values;
|
|
235
259
|
const logger = (_options$logger = options === null || options === void 0 ? void 0 : options.logger) !== null && _options$logger !== void 0 ? _options$logger : createDefaultLogger();
|
|
236
260
|
const connection = new InMemoryConnection(logger, (_options$values = options === null || options === void 0 ? void 0 : options.values) !== null && _options$values !== void 0 ? _options$values : {});
|
|
237
|
-
|
|
261
|
+
const recordedTrackedEvents = new RecordedTrackedEvents(logger);
|
|
262
|
+
return new InMemoryTestClient(new DefaultConfigDirectorClient(() => ({
|
|
263
|
+
...discardingTelemetryClient,
|
|
264
|
+
trackedEvent: (admission, context) => recordedTrackedEvents.record(admission, context)
|
|
265
|
+
}), TEST_CLIENT_SDK_KEY, sdkOptions, {
|
|
238
266
|
logger,
|
|
239
267
|
connection: { timeout: options === null || options === void 0 ? void 0 : options.timeout }
|
|
240
|
-
}, { transport: connection }), connection);
|
|
268
|
+
}, { transport: connection }), connection, recordedTrackedEvents.events);
|
|
241
269
|
};
|
|
242
270
|
//#endregion
|
|
243
271
|
//#region ../js-client-core/src/testing/installTestClient.ts
|
|
@@ -295,7 +323,7 @@ const installTestClient = (testClient) => {
|
|
|
295
323
|
const createTestClient = (options) => {
|
|
296
324
|
return createTestClient$1({
|
|
297
325
|
sdkName: "react-native-sdk",
|
|
298
|
-
sdkVersion: "1.
|
|
326
|
+
sdkVersion: "1.11.0"
|
|
299
327
|
}, options);
|
|
300
328
|
};
|
|
301
329
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as ConnectionMode, a as ConfigDirectorClientOptions, d as ConfigDirectorContext, f as ConfigDirectorLogMessageDecorator, g as ConfigValueType, h as ConfigEvaluation, i as ConfigDirectorClient, m as ConfigDirectorLoggingLevel, n as CachingProvider, o as OfflineValue, p as ConfigDirectorLogger, r as ClientHooks, s as OfflineValues, t as CachingOptions, v as EvaluationReason } from "./types-
|
|
1
|
+
import { C as Variation, S as TrackedEventDetails, _ as ConnectionMode, a as ConfigDirectorClientOptions, d as ConfigDirectorContext, f as ConfigDirectorLogMessageDecorator, g as ConfigValueType, h as ConfigEvaluation, i as ConfigDirectorClient, m as ConfigDirectorLoggingLevel, n as CachingProvider, o as OfflineValue, p as ConfigDirectorLogger, r as ClientHooks, s as OfflineValues, t as CachingOptions, v as EvaluationReason, x as TrackedEventDetailValue, y as TrackOptions } from "./types-BCVhD8m2.mjs";
|
|
2
2
|
import { Component, PropsWithChildren } from "react";
|
|
3
3
|
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
@@ -151,4 +151,4 @@ declare class ConfigDirectorReactContextError extends Error {
|
|
|
151
151
|
//#region src/logger.d.ts
|
|
152
152
|
declare const createConsoleLogger: (level?: ConfigDirectorLoggingLevel) => DefaultConsoleLogger;
|
|
153
153
|
//#endregion
|
|
154
|
-
export { type CachingOptions, type CachingProvider, type ClientStatus, type ConfigDirectorClient, type ConfigDirectorClientOptions, type ConfigDirectorContext, type ConfigDirectorLogger, type ConfigDirectorLoggingLevel, ConfigDirectorProvider, type ConfigDirectorProviderClientProps, type ConfigDirectorProviderOptions, type ConfigDirectorProviderProps, ConfigDirectorReactContextError, type ConfigEvaluation, type ConnectionMode, type EvaluationReason, type NetInfoSubscribe, type OfflineValue, type OfflineValues, createClient, createConsoleLogger, useClient, useConfigValue, useContext };
|
|
154
|
+
export { type CachingOptions, type CachingProvider, type ClientStatus, type ConfigDirectorClient, type ConfigDirectorClientOptions, type ConfigDirectorContext, type ConfigDirectorLogger, type ConfigDirectorLoggingLevel, ConfigDirectorProvider, type ConfigDirectorProviderClientProps, type ConfigDirectorProviderOptions, type ConfigDirectorProviderProps, ConfigDirectorReactContextError, type ConfigEvaluation, type ConnectionMode, type EvaluationReason, type NetInfoSubscribe, type OfflineValue, type OfflineValues, type TrackOptions, type TrackedEventDetailValue, type TrackedEventDetails, type Variation, createClient, createConsoleLogger, useClient, useConfigValue, useContext };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as DEFAULT_CACHING_SIZE_LIMIT_BYTES,
|
|
1
|
+
import { a as DEFAULT_CACHING_SIZE_LIMIT_BYTES, f as createDefaultLogger, i as CACHED_VALUES_STORAGE_KEY_PREFIX, l as utf8ByteLength, m as isFetchErrorFatal, n as readInstalledTestClient, r as DefaultConfigDirectorClient, s as logBatchWarnings, u as fetchWithTimeout, v as _defineProperty } from "./installedTestClient-BopKe_F5.mjs";
|
|
2
2
|
import React, { Component, useContext as useContext$1 } from "react";
|
|
3
3
|
import { AppState, NativeModules } from "react-native";
|
|
4
4
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
@@ -58,6 +58,7 @@ const isDroppedEventsEmpty = (droppedEvents) => {
|
|
|
58
58
|
};
|
|
59
59
|
//#endregion
|
|
60
60
|
//#region ../js-client-core/src/telemetry/ClientEventReporter.ts
|
|
61
|
+
const LARGEST_BODY_SENT_WITH_KEEPALIVE_BYTES = 64 * 1024 / 2;
|
|
61
62
|
var ClientEventReporter = class {
|
|
62
63
|
constructor(options) {
|
|
63
64
|
_defineProperty(this, "sdkKey", void 0);
|
|
@@ -107,10 +108,11 @@ var ClientEventReporter = class {
|
|
|
107
108
|
}
|
|
108
109
|
async sendReport(eventReport) {
|
|
109
110
|
try {
|
|
111
|
+
const body = JSON.stringify(eventReport);
|
|
110
112
|
const response = await fetchWithTimeout(5e3, this.url, {
|
|
111
113
|
method: "POST",
|
|
112
|
-
body
|
|
113
|
-
keepalive:
|
|
114
|
+
body,
|
|
115
|
+
keepalive: utf8ByteLength(body) <= LARGEST_BODY_SENT_WITH_KEEPALIVE_BYTES
|
|
114
116
|
}, this.logger);
|
|
115
117
|
const isFatalError = this.isStatusFatal(response.status);
|
|
116
118
|
if (isFatalError) this.logger.warn(`[EventReporter] Received a fatal status response (${response.status}) from the telemetry endpoint. No more telemetry data will be sent.`);
|
|
@@ -209,6 +211,43 @@ var EventQueue = class {
|
|
|
209
211
|
}
|
|
210
212
|
};
|
|
211
213
|
//#endregion
|
|
214
|
+
//#region ../shared/src/telemetry/TrackedEventQueue.ts
|
|
215
|
+
var TrackedEventQueue = class {
|
|
216
|
+
constructor() {
|
|
217
|
+
_defineProperty(this, "entries", []);
|
|
218
|
+
_defineProperty(this, "warningsRaisedThisBatch", /* @__PURE__ */ new Set());
|
|
219
|
+
}
|
|
220
|
+
add(admission) {
|
|
221
|
+
return this.warningsOfKeeping(admission).filter((warning) => this.isFirstThisBatch(warning));
|
|
222
|
+
}
|
|
223
|
+
absorb(entries) {
|
|
224
|
+
this.entries.push(...entries);
|
|
225
|
+
}
|
|
226
|
+
takeBatch() {
|
|
227
|
+
const batch = this.entries;
|
|
228
|
+
this.clear();
|
|
229
|
+
return batch;
|
|
230
|
+
}
|
|
231
|
+
clear() {
|
|
232
|
+
this.entries = [];
|
|
233
|
+
this.warningsRaisedThisBatch.clear();
|
|
234
|
+
}
|
|
235
|
+
warningsOfKeeping(admission) {
|
|
236
|
+
switch (admission.outcome) {
|
|
237
|
+
case "dropped": return admission.warnings;
|
|
238
|
+
case "admitted":
|
|
239
|
+
if (this.entries.length >= 1e3) return ["batch-already-full"];
|
|
240
|
+
this.entries.push(admission.entry);
|
|
241
|
+
return admission.warnings;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
isFirstThisBatch(warning) {
|
|
245
|
+
if (this.warningsRaisedThisBatch.has(warning)) return false;
|
|
246
|
+
this.warningsRaisedThisBatch.add(warning);
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
//#endregion
|
|
212
251
|
//#region ../shared/src/telemetry/TelemetryEventCollector.ts
|
|
213
252
|
var TelemetryEventCollector = class {
|
|
214
253
|
constructor(options) {
|
|
@@ -216,6 +255,7 @@ var TelemetryEventCollector = class {
|
|
|
216
255
|
_defineProperty(this, "logger", void 0);
|
|
217
256
|
_defineProperty(this, "evaluationEventQueue", void 0);
|
|
218
257
|
_defineProperty(this, "aggregator", new EventAggregator());
|
|
258
|
+
_defineProperty(this, "trackedEventQueue", new TrackedEventQueue());
|
|
219
259
|
_defineProperty(this, "flushIntervalDelay", void 0);
|
|
220
260
|
_defineProperty(this, "flushTimeout", void 0);
|
|
221
261
|
_defineProperty(this, "collectEvents", true);
|
|
@@ -231,6 +271,10 @@ var TelemetryEventCollector = class {
|
|
|
231
271
|
this.started = true;
|
|
232
272
|
this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.initialFlushIntervalDelay);
|
|
233
273
|
}
|
|
274
|
+
trackedEvent(admission) {
|
|
275
|
+
if (!this.collectEvents) return;
|
|
276
|
+
logBatchWarnings(this.logger, this.trackedEventQueue.add(admission));
|
|
277
|
+
}
|
|
234
278
|
async flushAndScheduleNext() {
|
|
235
279
|
if (!this.collectEvents) return;
|
|
236
280
|
let response;
|
|
@@ -264,6 +308,7 @@ var TelemetryEventCollector = class {
|
|
|
264
308
|
this.logger.warn("[TelemetryEventCollector] Error flushing telemetry events during close: ", error);
|
|
265
309
|
}
|
|
266
310
|
this.evaluationEventQueue.clear();
|
|
311
|
+
this.trackedEventQueue.clear();
|
|
267
312
|
}
|
|
268
313
|
dispose() {
|
|
269
314
|
this.close();
|
|
@@ -375,6 +420,9 @@ var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
375
420
|
absorbInvalidContextSubmissions(entries) {
|
|
376
421
|
this.invalidContextSubmissions.absorb(entries);
|
|
377
422
|
}
|
|
423
|
+
absorbTrackedEventEntries(entries) {
|
|
424
|
+
this.trackedEventQueue.absorb(entries);
|
|
425
|
+
}
|
|
378
426
|
async forceFlush() {
|
|
379
427
|
this.cancelScheduledFlush();
|
|
380
428
|
await this.flushAndScheduleNext();
|
|
@@ -388,7 +436,7 @@ var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
388
436
|
const evaluationSnapshot = await this.evaluationEventSnapshotPreprocessor.process(this.evaluationEventQueue.takeSnapshot());
|
|
389
437
|
return await ((_this$reporter = this.reporter) === null || _this$reporter === void 0 ? void 0 : _this$reporter.report({
|
|
390
438
|
context: this._context,
|
|
391
|
-
discreteEvents: {},
|
|
439
|
+
discreteEvents: { trackedEvents: this.trackedEventQueue.takeBatch() },
|
|
392
440
|
aggregatedEvents: {
|
|
393
441
|
evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot),
|
|
394
442
|
invalidContext: this.invalidContextSubmissions.takeSnapshot()
|
|
@@ -734,6 +782,9 @@ var ReactNativeTelemetryClient = class {
|
|
|
734
782
|
recordInvalidContext(kindSample) {
|
|
735
783
|
this.collector.recordInvalidContext(kindSample);
|
|
736
784
|
}
|
|
785
|
+
trackedEvent(admission) {
|
|
786
|
+
this.collector.trackedEvent(admission);
|
|
787
|
+
}
|
|
737
788
|
async close() {
|
|
738
789
|
await this.collector.close();
|
|
739
790
|
}
|
|
@@ -777,7 +828,7 @@ const createReactNativeClient = (clientSdkKey, clientOptions) => {
|
|
|
777
828
|
var _clientOptions$logger;
|
|
778
829
|
const sdkIdentity = {
|
|
779
830
|
sdkName: "react-native-sdk",
|
|
780
|
-
sdkVersion: "1.
|
|
831
|
+
sdkVersion: "1.11.0"
|
|
781
832
|
};
|
|
782
833
|
const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
|
|
783
834
|
const metadata = resolveMetadata(clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata, logger);
|
|
@@ -724,6 +724,27 @@ const offlineSourceOf = (connection) => {
|
|
|
724
724
|
};
|
|
725
725
|
//#endregion
|
|
726
726
|
//#region ../shared/src/value-parser.ts
|
|
727
|
+
const NO_VARIATION_OR_ROLLOUT_SHARE = {
|
|
728
|
+
variation: void 0,
|
|
729
|
+
ruleId: void 0,
|
|
730
|
+
shareId: void 0
|
|
731
|
+
};
|
|
732
|
+
const variationOf = (configState) => {
|
|
733
|
+
const variation = configState.variation;
|
|
734
|
+
if (variation == null) return;
|
|
735
|
+
return variation.name ? {
|
|
736
|
+
id: variation.id,
|
|
737
|
+
name: variation.name
|
|
738
|
+
} : { id: variation.id };
|
|
739
|
+
};
|
|
740
|
+
const variationAndRolloutShareOf = (configState) => {
|
|
741
|
+
var _configState$ruleId, _configState$shareId;
|
|
742
|
+
return {
|
|
743
|
+
variation: variationOf(configState),
|
|
744
|
+
ruleId: (_configState$ruleId = configState.ruleId) !== null && _configState$ruleId !== void 0 ? _configState$ruleId : void 0,
|
|
745
|
+
shareId: (_configState$shareId = configState.shareId) !== null && _configState$shareId !== void 0 ? _configState$shareId : void 0
|
|
746
|
+
};
|
|
747
|
+
};
|
|
727
748
|
const getRequestedType = (defaultValue) => {
|
|
728
749
|
const baseType = typeof defaultValue;
|
|
729
750
|
if (baseType === "object") try {
|
|
@@ -749,6 +770,13 @@ const configTypesNotReadableAsString = new Set([
|
|
|
749
770
|
"float"
|
|
750
771
|
]);
|
|
751
772
|
const parseConfigValue = (configState, defaultValue) => {
|
|
773
|
+
const valueAndReason = parseValueAndReason(configState, defaultValue);
|
|
774
|
+
return {
|
|
775
|
+
...valueAndReason,
|
|
776
|
+
...valueAndReason.usedDefault ? NO_VARIATION_OR_ROLLOUT_SHARE : variationAndRolloutShareOf(configState)
|
|
777
|
+
};
|
|
778
|
+
};
|
|
779
|
+
const parseValueAndReason = (configState, defaultValue) => {
|
|
752
780
|
const value = configState.value;
|
|
753
781
|
const requestedType = getRequestedType(defaultValue);
|
|
754
782
|
if (value == null || value === "") return {
|
|
@@ -954,6 +982,7 @@ const discardingTelemetryClient = {
|
|
|
954
982
|
updateContext: async () => {},
|
|
955
983
|
evaluatedConfig: () => {},
|
|
956
984
|
recordInvalidContext: () => {},
|
|
985
|
+
trackedEvent: () => {},
|
|
957
986
|
close: async () => {}
|
|
958
987
|
};
|
|
959
988
|
//#endregion
|
|
@@ -1338,6 +1367,93 @@ const utf8ByteLength = (text) => {
|
|
|
1338
1367
|
return bytes;
|
|
1339
1368
|
};
|
|
1340
1369
|
//#endregion
|
|
1370
|
+
//#region ../shared/src/telemetry/reported-context.ts
|
|
1371
|
+
const USER_CONTEXT_KIND = "user";
|
|
1372
|
+
const reportedContextOf = (context) => {
|
|
1373
|
+
var _context$kind;
|
|
1374
|
+
const contextIdentifier = (context === null || context === void 0 ? void 0 : context.anonymous) ? void 0 : context === null || context === void 0 ? void 0 : context.id;
|
|
1375
|
+
if (contextIdentifier === void 0) return {};
|
|
1376
|
+
return {
|
|
1377
|
+
contextKind: (_context$kind = context === null || context === void 0 ? void 0 : context.kind) !== null && _context$kind !== void 0 ? _context$kind : USER_CONTEXT_KIND,
|
|
1378
|
+
contextIdentifier
|
|
1379
|
+
};
|
|
1380
|
+
};
|
|
1381
|
+
//#endregion
|
|
1382
|
+
//#region ../shared/src/telemetry/tracked-events.ts
|
|
1383
|
+
const TRACKED_EVENTS_PER_BATCH = 1e3;
|
|
1384
|
+
const NAME_MAXIMUM_CHARACTERS = 150;
|
|
1385
|
+
const NAME_MAXIMUM_UTF16_UNITS = NAME_MAXIMUM_CHARACTERS * 2;
|
|
1386
|
+
const DETAILS_MAXIMUM_TOP_LEVEL_KEYS = 20;
|
|
1387
|
+
const DETAILS_MAXIMUM_JSON_BYTES = 1024;
|
|
1388
|
+
const trackedEventWarningMessages = {
|
|
1389
|
+
"name-outside-the-character-limit": `[ConfigDirectorClient] A tracked event was not reported because its name does not have 1 to ${NAME_MAXIMUM_CHARACTERS} characters.`,
|
|
1390
|
+
"value-not-a-finite-number": "[ConfigDirectorClient] A tracked event was reported without its value because the value is not a finite number.",
|
|
1391
|
+
"details-not-reportable": `[ConfigDirectorClient] A tracked event was reported without its details because they are not an object, have more than ${DETAILS_MAXIMUM_TOP_LEVEL_KEYS} top-level keys, are larger than ${DETAILS_MAXIMUM_JSON_BYTES} bytes as JSON, or cannot be written as JSON.`,
|
|
1392
|
+
"batch-already-full": `[ConfigDirectorClient] A tracked event was not reported because ${TRACKED_EVENTS_PER_BATCH} tracked events are already waiting for the next telemetry batch.`
|
|
1393
|
+
};
|
|
1394
|
+
const logBatchWarnings = (logger, warnings) => {
|
|
1395
|
+
for (const warning of warnings) logger.warn(`${trackedEventWarningMessages[warning]} Further tracked events like it are not logged until the next telemetry batch is sent.`);
|
|
1396
|
+
};
|
|
1397
|
+
const isGiven = (option) => option !== void 0 && option !== null;
|
|
1398
|
+
const isNonArrayObject = (candidate) => typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
|
|
1399
|
+
const isWithinTheCharacterLimit = (name) => typeof name === "string" && name.length >= 1 && name.length <= NAME_MAXIMUM_UTF16_UNITS && Array.from(name).length <= NAME_MAXIMUM_CHARACTERS;
|
|
1400
|
+
const reportableValueOf = (value) => {
|
|
1401
|
+
if (!isGiven(value)) return { status: "not-given" };
|
|
1402
|
+
if (typeof value === "number" && Number.isFinite(value)) return {
|
|
1403
|
+
status: "reportable",
|
|
1404
|
+
fields: { value }
|
|
1405
|
+
};
|
|
1406
|
+
return {
|
|
1407
|
+
status: "left-out",
|
|
1408
|
+
warning: "value-not-a-finite-number"
|
|
1409
|
+
};
|
|
1410
|
+
};
|
|
1411
|
+
const jsonCopyWithinTheLimits = (details) => {
|
|
1412
|
+
if (!isNonArrayObject(details)) return;
|
|
1413
|
+
try {
|
|
1414
|
+
const json = JSON.stringify(details);
|
|
1415
|
+
if (typeof json !== "string" || utf8ByteLength(json) > DETAILS_MAXIMUM_JSON_BYTES) return;
|
|
1416
|
+
const copy = JSON.parse(json);
|
|
1417
|
+
return isNonArrayObject(copy) && Object.keys(copy).length <= DETAILS_MAXIMUM_TOP_LEVEL_KEYS ? copy : void 0;
|
|
1418
|
+
} catch {
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
const reportableDetailsOf = (details) => {
|
|
1423
|
+
if (!isGiven(details)) return { status: "not-given" };
|
|
1424
|
+
const jsonCopy = jsonCopyWithinTheLimits(details);
|
|
1425
|
+
if (jsonCopy === void 0) return {
|
|
1426
|
+
status: "left-out",
|
|
1427
|
+
warning: "details-not-reportable"
|
|
1428
|
+
};
|
|
1429
|
+
return {
|
|
1430
|
+
status: "reportable",
|
|
1431
|
+
fields: { details: jsonCopy }
|
|
1432
|
+
};
|
|
1433
|
+
};
|
|
1434
|
+
const fieldsOf = (option) => option.status === "reportable" ? option.fields : void 0;
|
|
1435
|
+
const warningsOf = (...options) => options.flatMap((option) => option.status === "left-out" ? [option.warning] : []);
|
|
1436
|
+
const admitTrackedEvent = (call) => {
|
|
1437
|
+
var _call$options, _call$options2;
|
|
1438
|
+
if (!isWithinTheCharacterLimit(call.name)) return {
|
|
1439
|
+
outcome: "dropped",
|
|
1440
|
+
warnings: ["name-outside-the-character-limit"]
|
|
1441
|
+
};
|
|
1442
|
+
const value = reportableValueOf((_call$options = call.options) === null || _call$options === void 0 ? void 0 : _call$options.value);
|
|
1443
|
+
const details = reportableDetailsOf((_call$options2 = call.options) === null || _call$options2 === void 0 ? void 0 : _call$options2.details);
|
|
1444
|
+
return {
|
|
1445
|
+
outcome: "admitted",
|
|
1446
|
+
entry: {
|
|
1447
|
+
name: call.name,
|
|
1448
|
+
...reportedContextOf(call.context),
|
|
1449
|
+
...fieldsOf(value),
|
|
1450
|
+
...fieldsOf(details),
|
|
1451
|
+
happenedAt: call.happenedAt.toISOString()
|
|
1452
|
+
},
|
|
1453
|
+
warnings: warningsOf(value, details)
|
|
1454
|
+
};
|
|
1455
|
+
};
|
|
1456
|
+
//#endregion
|
|
1341
1457
|
//#region ../js-client-core/src/cached-values.ts
|
|
1342
1458
|
const ENTRY_FORMAT_VERSION = 2;
|
|
1343
1459
|
const FIRST_ENTRY_FORMAT_VERSION = 1;
|
|
@@ -1836,7 +1952,7 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1836
1952
|
return this.evaluationOf(configKey, configState, defaultValue);
|
|
1837
1953
|
}
|
|
1838
1954
|
evaluationOf(configKey, configState, defaultValue) {
|
|
1839
|
-
var _this$currentContext2, _parseResult$parsedVa;
|
|
1955
|
+
var _this$currentContext2, _parseResult$variatio, _parseResult$parsedVa;
|
|
1840
1956
|
if (!configState) {
|
|
1841
1957
|
var _this$currentContext;
|
|
1842
1958
|
this.logger.debug(`[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`);
|
|
@@ -1871,12 +1987,16 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1871
1987
|
evaluatedValue: parseResult.parsedValue,
|
|
1872
1988
|
evaluatedValueId: parseResult.parsedValueId,
|
|
1873
1989
|
usedDefault: parseResult.usedDefault,
|
|
1874
|
-
evaluationReason: reason
|
|
1990
|
+
evaluationReason: reason,
|
|
1991
|
+
ruleId: parseResult.ruleId,
|
|
1992
|
+
shareId: parseResult.shareId,
|
|
1993
|
+
variationId: (_parseResult$variatio = parseResult.variation) === null || _parseResult$variatio === void 0 ? void 0 : _parseResult$variatio.id
|
|
1875
1994
|
});
|
|
1876
1995
|
const evaluation = {
|
|
1877
1996
|
key: configKey,
|
|
1878
1997
|
value: parseResult.parsedValue,
|
|
1879
1998
|
valueId: (_parseResult$parsedVa = parseResult.parsedValueId) !== null && _parseResult$parsedVa !== void 0 ? _parseResult$parsedVa : void 0,
|
|
1999
|
+
variation: parseResult.variation,
|
|
1880
2000
|
isDefaultValue: parseResult.usedDefault,
|
|
1881
2001
|
reason,
|
|
1882
2002
|
context: this.context
|
|
@@ -1919,6 +2039,19 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1919
2039
|
unwatchAll() {
|
|
1920
2040
|
this.handlersMap.clear();
|
|
1921
2041
|
}
|
|
2042
|
+
track(eventName, options) {
|
|
2043
|
+
try {
|
|
2044
|
+
const context = this.currentContext;
|
|
2045
|
+
this.telemetryClient.trackedEvent(admitTrackedEvent({
|
|
2046
|
+
name: eventName,
|
|
2047
|
+
context,
|
|
2048
|
+
options,
|
|
2049
|
+
happenedAt: /* @__PURE__ */ new Date()
|
|
2050
|
+
}), context);
|
|
2051
|
+
} catch (error) {
|
|
2052
|
+
this.logger.warn("[ConfigDirectorClient] A tracked event was not reported because it could not be read: ", error);
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
1922
2055
|
pauseNetwork() {
|
|
1923
2056
|
switch (this.connectionPlan.kind) {
|
|
1924
2057
|
case "stay-offline": return;
|
|
@@ -1959,4 +2092,4 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
|
1959
2092
|
const INSTALLED_TEST_CLIENT_SLOT = Symbol.for("@configdirector/installed-test-client");
|
|
1960
2093
|
const readInstalledTestClient = () => globalThis[INSTALLED_TEST_CLIENT_SLOT];
|
|
1961
2094
|
//#endregion
|
|
1962
|
-
export { DEFAULT_CACHING_SIZE_LIMIT_BYTES as a,
|
|
2095
|
+
export { Emitter as _, DEFAULT_CACHING_SIZE_LIMIT_BYTES as a, trackedEventWarningMessages as c, discardingTelemetryClient as d, createDefaultLogger as f, ConfigDirectorValidationError as g, ConfigDirectorConnectionError as h, CACHED_VALUES_STORAGE_KEY_PREFIX as i, utf8ByteLength as l, isFetchErrorFatal as m, readInstalledTestClient as n, TRACKED_EVENTS_PER_BATCH as o, nativeValueConfigState as p, DefaultConfigDirectorClient as r, logBatchWarnings as s, INSTALLED_TEST_CLIENT_SLOT as t, fetchWithTimeout as u, _defineProperty as v };
|
|
@@ -25,24 +25,72 @@ interface ConfigDirectorLogMessageDecorator {
|
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* Why an evaluation returned the value it did. `found-match` returns a value from ConfigDirector,
|
|
28
|
-
* and `cached` a value kept on the device while a client SDK waits for live values.
|
|
28
|
+
* and `cached` a value kept on the device while a client SDK waits for live values.
|
|
29
|
+
* `rollout-skipped-no-identifier` returns the default targeting rule value: a server SDK skipped a
|
|
30
|
+
* percentage rollout because the context has no `id` to place it in a share. `value-missing`
|
|
29
31
|
* returns the in-code default value because the config has no value. Every other reason returns the
|
|
30
32
|
* in-code default value because of an error: the config does not exist (`config-state-missing`), no
|
|
31
33
|
* values have arrived yet (`client-not-ready`), or the value cannot be read as the type of the in-code
|
|
32
34
|
* default value.
|
|
33
35
|
*/
|
|
34
|
-
type EvaluationReason = "found-match" | "config-state-missing" | "client-not-ready" | "type-mismatch" | "value-missing" | "invalid-number" | "invalid-json" | "invalid-boolean" | "cached";
|
|
36
|
+
type EvaluationReason = "found-match" | "rollout-skipped-no-identifier" | "config-state-missing" | "client-not-ready" | "type-mismatch" | "value-missing" | "invalid-number" | "invalid-json" | "invalid-boolean" | "cached";
|
|
37
|
+
/**
|
|
38
|
+
* One of the values a config can serve, such as the control or a treatment of an experiment.
|
|
39
|
+
*/
|
|
40
|
+
type Variation = {
|
|
41
|
+
/** The ID ConfigDirector assigned to the variation, which stays the same when it is renamed. */id: string; /** The variation's name, when it has one. */
|
|
42
|
+
name?: string;
|
|
43
|
+
};
|
|
35
44
|
/**
|
|
36
45
|
* One evaluation of a config: the value it returned, why, and the context it was evaluated for.
|
|
37
46
|
*/
|
|
38
47
|
type ConfigEvaluation<T extends ConfigValueType = ConfigValueType> = {
|
|
39
48
|
/** The config key that was evaluated. */key: string; /** The value returned, which is the in-code default value when {@link isDefaultValue} is `true`. */
|
|
40
49
|
value: T; /** The ID ConfigDirector assigned to the value returned, when ConfigDirector sent one with it. */
|
|
41
|
-
valueId?: string;
|
|
50
|
+
valueId?: string;
|
|
51
|
+
/**
|
|
52
|
+
* The variation that was served. It is `undefined` when the config has no variations or the
|
|
53
|
+
* in-code default value is returned.
|
|
54
|
+
*/
|
|
55
|
+
variation?: Variation; /** Whether the evaluation returned the in-code default value. */
|
|
42
56
|
isDefaultValue: boolean; /** Why the evaluation returned this value. */
|
|
43
57
|
reason: EvaluationReason; /** The context the config was evaluated for, if any. */
|
|
44
58
|
context?: ConfigDirectorContext;
|
|
45
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* A value in the details of a tracked event: a string, a number, a boolean, or an array or object
|
|
62
|
+
* of such values.
|
|
63
|
+
*/
|
|
64
|
+
type TrackedEventDetailValue = string | number | boolean | TrackedEventDetailValue[] | {
|
|
65
|
+
[key: string]: TrackedEventDetailValue;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Details of a tracked event, such as the plan a customer chose. Details with more than 20
|
|
69
|
+
* top-level keys, or larger than 1 KB as JSON, are left out and the event is still reported.
|
|
70
|
+
*/
|
|
71
|
+
type TrackedEventDetails = {
|
|
72
|
+
[key: string]: TrackedEventDetailValue;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* What a tracked event carries besides its name.
|
|
76
|
+
*/
|
|
77
|
+
type TrackOptions = {
|
|
78
|
+
/**
|
|
79
|
+
* A number the event measures, such as an amount of money, a count or a duration. A value that
|
|
80
|
+
* is not a finite number is left out and the event is still reported.
|
|
81
|
+
*/
|
|
82
|
+
value?: number; /** Details of the event. */
|
|
83
|
+
details?: TrackedEventDetails;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* An event the code under test tracked, as a test client recorded it.
|
|
87
|
+
*/
|
|
88
|
+
type TrackedEvent = {
|
|
89
|
+
/** The name the event was tracked with. */name: string; /** The context the event was tracked for, if any. */
|
|
90
|
+
context?: ConfigDirectorContext; /** The value the event was tracked with, when it is a finite number. */
|
|
91
|
+
value?: number; /** The details the event was tracked with, when they are within the limits. */
|
|
92
|
+
details?: TrackedEventDetails;
|
|
93
|
+
};
|
|
46
94
|
/**
|
|
47
95
|
* The context to be sent to ConfigDirector: the user or other entity, such as a device or an
|
|
48
96
|
* organization, that configs are evaluated for. This context will be used for targeting rules
|
|
@@ -363,8 +411,8 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
|
|
|
363
411
|
getValue<T extends ConfigValueType>(configKey: string, defaultValue: T): T;
|
|
364
412
|
/**
|
|
365
413
|
* Evaluates a config as {@link getValue} does and returns the evaluation: the value, the reason it
|
|
366
|
-
* was served
|
|
367
|
-
* telemetry.
|
|
414
|
+
* was served, its value ID and its variation. Like every read, it fires `configEvaluated` and is
|
|
415
|
+
* counted in telemetry.
|
|
368
416
|
*
|
|
369
417
|
* @returns The evaluation, whose `value` is what {@link getValue} returns
|
|
370
418
|
* @param configKey The config key to evaluate
|
|
@@ -396,6 +444,20 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
|
|
|
396
444
|
* Removes all subscribers from all config keys
|
|
397
445
|
*/
|
|
398
446
|
unwatchAll(): void;
|
|
447
|
+
/**
|
|
448
|
+
* Reports to ConfigDirector that something happened for the current context, such as a completed
|
|
449
|
+
* checkout, so it can be counted. The event is sent with the next telemetry batch. `track` never
|
|
450
|
+
* throws, and does nothing in `offline` mode.
|
|
451
|
+
*
|
|
452
|
+
* An event counts for an experiment only when the current context has an `id`, the one the
|
|
453
|
+
* experiment's config was evaluated for. Before the client has a context, or for a context
|
|
454
|
+
* without an `id`, the event is still reported.
|
|
455
|
+
*
|
|
456
|
+
* @param eventName The name of the event, 1 to 150 characters. An event with any other name is
|
|
457
|
+
* not reported and a warning is logged.
|
|
458
|
+
* @param options The event's {@link TrackOptions.value} and {@link TrackOptions.details} (optional)
|
|
459
|
+
*/
|
|
460
|
+
track(eventName: string, options?: TrackOptions): void;
|
|
399
461
|
/**
|
|
400
462
|
* Pauses the network connection without clearing event handlers, watch handlers, or config
|
|
401
463
|
* state. Intended for mobile environments where the OS may kill background connections (e.g.
|
|
@@ -418,4 +480,4 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
|
|
|
418
480
|
dispose(): void;
|
|
419
481
|
}
|
|
420
482
|
//#endregion
|
|
421
|
-
export { ConnectionMode as _, ConfigDirectorClientOptions as a, NativeJsonValue as c, ConfigDirectorContext as d, ConfigDirectorLogMessageDecorator as f, ConfigValueType as g, ConfigEvaluation as h, ConfigDirectorClient as i, NativeValue as l, ConfigDirectorLoggingLevel as m, CachingProvider as n, OfflineValue as o, ConfigDirectorLogger as p, ClientHooks as r, OfflineValues as s, CachingOptions as t, NativeValues as u, EvaluationReason as v };
|
|
483
|
+
export { Variation as C, TrackedEventDetails as S, ConnectionMode as _, ConfigDirectorClientOptions as a, TrackedEvent as b, NativeJsonValue as c, ConfigDirectorContext as d, ConfigDirectorLogMessageDecorator as f, ConfigValueType as g, ConfigEvaluation as h, ConfigDirectorClient as i, NativeValue as l, ConfigDirectorLoggingLevel as m, CachingProvider as n, OfflineValue as o, ConfigDirectorLogger as p, ClientHooks as r, OfflineValues as s, CachingOptions as t, NativeValues as u, EvaluationReason as v, TrackedEventDetailValue as x, TrackOptions as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configdirector/react-native-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "React Native client SDK for ConfigDirector. ConfigDirector is a remote configuration and feature flag service.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ConfigDirector",
|