@configdirector/react-native-sdk 1.6.1 → 1.8.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/README.md +14 -0
- package/dist/configdirector-react-native-testing.d.mts +7 -15
- package/dist/configdirector-react-native-testing.mjs +8 -104
- package/dist/configdirector-react-native.d.mts +18 -6
- package/dist/configdirector-react-native.mjs +120 -16
- package/dist/{installedTestClient-CTPtIG6o.mjs → installedTestClient-CKjaEilj.mjs} +430 -91
- package/dist/{types-fcPMBgiX.d.mts → types-DoORnGTD.d.mts} +104 -48
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -10,6 +10,16 @@ React Native SDK for [ConfigDirector](https://www.configdirector.com), remote co
|
|
|
10
10
|
npm install --save @configdirector/react-native-sdk
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
Install `@react-native-async-storage/async-storage` as well, so the SDK can keep the same client
|
|
14
|
+
session for six hours across app starts. It is an optional peer dependency and works in Expo Go.
|
|
15
|
+
Without it, every app start begins a new client session and the SDK logs a warning.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install --save @react-native-async-storage/async-storage
|
|
19
|
+
# or in an Expo project
|
|
20
|
+
npx expo install @react-native-async-storage/async-storage
|
|
21
|
+
```
|
|
22
|
+
|
|
13
23
|
## Retrieve a value
|
|
14
24
|
|
|
15
25
|
```tsx
|
|
@@ -74,6 +84,10 @@ package's files are `.mjs` files, which the `jest-expo` and `@react-native/jest-
|
|
|
74
84
|
transform, so a Jest setup must let `transformIgnorePatterns` transform both packages and add a
|
|
75
85
|
`transform` entry for `.mjs` files; the documentation shows the configuration for both presets.
|
|
76
86
|
`jest-expo` requires `@react-native/jest-preset` as a peer dependency, so install it in either case.
|
|
87
|
+
The test client never touches AsyncStorage. A test that builds a real client instead, through
|
|
88
|
+
`createClient` or a provider given an `sdkKey`, logs the warning that AsyncStorage is missing and
|
|
89
|
+
starts a new client session under either preset; add `@babel/plugin-transform-dynamic-import` to the
|
|
90
|
+
Babel test environment for that client to use the AsyncStorage Jest mock instead.
|
|
77
91
|
|
|
78
92
|
## Documentation
|
|
79
93
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as NativeValues, d as ConfigDirectorLogger, l as ConfigDirectorContext, n as ConfigDirectorClient, o as NativeJsonValue, s as NativeValue } from "./types-DoORnGTD.mjs";
|
|
2
2
|
|
|
3
3
|
//#region ../shared/src/errors.d.ts
|
|
4
4
|
declare class ConfigDirectorConnectionError extends Error {
|
|
@@ -11,15 +11,6 @@ declare class ConfigDirectorValidationError extends Error {
|
|
|
11
11
|
constructor(message: string);
|
|
12
12
|
}
|
|
13
13
|
//#endregion
|
|
14
|
-
//#region ../shared/src/testing/values.d.ts
|
|
15
|
-
type TestJsonValue = boolean | number | string | null | TestJsonValue[] | {
|
|
16
|
-
[key: string]: TestJsonValue;
|
|
17
|
-
};
|
|
18
|
-
type TestValue = boolean | number | string | TestJsonValue[] | {
|
|
19
|
-
[key: string]: TestJsonValue;
|
|
20
|
-
};
|
|
21
|
-
type TestValues = Record<string, TestValue>;
|
|
22
|
-
//#endregion
|
|
23
14
|
//#region ../js-client-core/src/testing/TestClient.d.ts
|
|
24
15
|
/**
|
|
25
16
|
* Options for {@link createTestClient}. Every option is optional.
|
|
@@ -30,7 +21,7 @@ type TestClientOptions = {
|
|
|
30
21
|
* config type follows from each value: a boolean, an integral number (`integer`), any other
|
|
31
22
|
* number (`float`), a string, or a plain object or array (`json`).
|
|
32
23
|
*/
|
|
33
|
-
values?:
|
|
24
|
+
values?: NativeValues;
|
|
34
25
|
/**
|
|
35
26
|
* The connection timeout of the SDK client, in milliseconds. It bounds how long a held
|
|
36
27
|
* `initialize` or `updateContext` waits. Defaults to the SDK's production timeout.
|
|
@@ -59,7 +50,7 @@ interface TestClient {
|
|
|
59
50
|
* Stores `value` under `key` and, when the client is connected, delivers it as an update, so
|
|
60
51
|
* reads, watchers, and `configsUpdated` handlers see it.
|
|
61
52
|
*/
|
|
62
|
-
setValue(key: string, value:
|
|
53
|
+
setValue(key: string, value: NativeValue): void;
|
|
63
54
|
/**
|
|
64
55
|
* Removes `key` and, when the client is connected, delivers a full update without it, so reads
|
|
65
56
|
* fall back to the in-code default value and watchers of `key` receive that default.
|
|
@@ -69,7 +60,7 @@ interface TestClient {
|
|
|
69
60
|
* Replaces every stored value with `values`, disarms any armed hold or failure, and, when the
|
|
70
61
|
* client is connected, delivers the new values as a full update.
|
|
71
62
|
*/
|
|
72
|
-
replaceValues(values:
|
|
63
|
+
replaceValues(values: NativeValues): void;
|
|
73
64
|
/**
|
|
74
65
|
* Makes the next `initialize` wait until {@link completeInitialization} or
|
|
75
66
|
* {@link failInitialization} is called, or until the client's timeout elapses.
|
|
@@ -102,7 +93,8 @@ interface TestClient {
|
|
|
102
93
|
failContextUpdate(): void;
|
|
103
94
|
/**
|
|
104
95
|
* The context of every `initialize` and `updateContext` call, in call order. `initialize` without
|
|
105
|
-
* a context records an empty context
|
|
96
|
+
* a context records an empty context, and so does a call whose context has an invalid kind, since
|
|
97
|
+
* the client connects without it. Resumes are not recorded.
|
|
106
98
|
*/
|
|
107
99
|
readonly contextUpdates: ConfigDirectorContext[];
|
|
108
100
|
}
|
|
@@ -154,4 +146,4 @@ declare const installTestClient: (testClient: TestClient) => (() => void);
|
|
|
154
146
|
*/
|
|
155
147
|
declare const createTestClient: (options?: TestClientOptions) => TestClient;
|
|
156
148
|
//#endregion
|
|
157
|
-
export { ConfigDirectorConnectionError, ConfigDirectorValidationError, type TestClient, type TestClientOptions, type TestJsonValue, type TestValue, type TestValues, createTestClient, installTestClient };
|
|
149
|
+
export { ConfigDirectorConnectionError, ConfigDirectorValidationError, type TestClient, type TestClientOptions, type NativeJsonValue as TestJsonValue, type NativeValue as TestValue, type NativeValues as TestValues, createTestClient, installTestClient };
|
|
@@ -1,98 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region ../shared/src/testing/values.ts
|
|
3
|
-
const encodeTestValue$1 = (key, value) => {
|
|
4
|
-
if (key.trim().length == 0) throw new ConfigDirectorValidationError("Invalid config key. The key of a test value cannot be empty.");
|
|
5
|
-
return encode(key, value);
|
|
6
|
-
};
|
|
7
|
-
const encode = (key, value) => {
|
|
8
|
-
switch (typeof value) {
|
|
9
|
-
case "boolean": return {
|
|
10
|
-
type: "boolean",
|
|
11
|
-
value: value ? "true" : "false"
|
|
12
|
-
};
|
|
13
|
-
case "number": return encodeNumber(key, value);
|
|
14
|
-
case "string": return {
|
|
15
|
-
type: "string",
|
|
16
|
-
value
|
|
17
|
-
};
|
|
18
|
-
case "object": if (value !== null) {
|
|
19
|
-
assertJsonContents(key, value, "");
|
|
20
|
-
return {
|
|
21
|
-
type: "json",
|
|
22
|
-
value: JSON.stringify(value)
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
throw new ConfigDirectorValidationError(`Invalid test value for '${key}': ${describe(value)}. Use a boolean, a number, a string, a plain object, or an array.`);
|
|
27
|
-
};
|
|
28
|
-
const encodeNumber = (key, value) => {
|
|
29
|
-
if (!Number.isFinite(value)) throw new ConfigDirectorValidationError(`Invalid test value for '${key}': ${describe(value)} is not a finite number.`);
|
|
30
|
-
if (Number.isInteger(value)) {
|
|
31
|
-
if (!Number.isSafeInteger(value)) throw new ConfigDirectorValidationError(`Invalid test value for '${key}': ${value} is not a safe integer, so the SDK could not read it back exactly.`);
|
|
32
|
-
return {
|
|
33
|
-
type: "integer",
|
|
34
|
-
value: String(value)
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
type: "float",
|
|
39
|
-
value: toPlainDecimal(value)
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
const toPlainDecimal = (value) => {
|
|
43
|
-
const text = String(value);
|
|
44
|
-
const exponentIndex = text.indexOf("e-");
|
|
45
|
-
if (exponentIndex < 0) return text;
|
|
46
|
-
const sign = text.startsWith("-") ? "-" : "";
|
|
47
|
-
const [integerDigits = "", fractionDigits = ""] = text.slice(sign.length, exponentIndex).split(".");
|
|
48
|
-
const leadingZeros = Number(text.slice(exponentIndex + 2)) - integerDigits.length;
|
|
49
|
-
return `${sign}0.${"0".repeat(leadingZeros)}${integerDigits}${fractionDigits}`;
|
|
50
|
-
};
|
|
51
|
-
const assertJsonContents = (key, value, path) => {
|
|
52
|
-
if (value === null || typeof value === "boolean" || typeof value === "string") return;
|
|
53
|
-
if (typeof value === "number") {
|
|
54
|
-
if (!Number.isFinite(value)) throw invalidJsonContents(key, path, `${describe(value)} is not a finite number`);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (Array.isArray(value)) {
|
|
58
|
-
value.forEach((item, index) => assertJsonContents(key, item, `${path}[${index}]`));
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (isPlainObject(value)) {
|
|
62
|
-
for (const [property, item] of Object.entries(value)) assertJsonContents(key, item, path ? `${path}.${property}` : property);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
throw invalidJsonContents(key, path, `${describe(value)} cannot be encoded as JSON`);
|
|
66
|
-
};
|
|
67
|
-
const invalidJsonContents = (key, path, problem) => new ConfigDirectorValidationError(`Invalid test value for '${key}'${path ? ` at '${path}'` : ""}: ${problem}. JSON contents can hold booleans, finite numbers, strings, null, arrays, and plain objects.`);
|
|
68
|
-
const isPlainObject = (value) => {
|
|
69
|
-
if (typeof value !== "object" || value === null) return false;
|
|
70
|
-
const prototype = Object.getPrototypeOf(value);
|
|
71
|
-
return prototype === Object.prototype || prototype === null;
|
|
72
|
-
};
|
|
73
|
-
const describe = (value) => {
|
|
74
|
-
if (value === null) return "null";
|
|
75
|
-
if (typeof value === "function") return "a function";
|
|
76
|
-
if (typeof value === "object") {
|
|
77
|
-
var _value$constructor$na, _value$constructor;
|
|
78
|
-
return `a ${(_value$constructor$na = (_value$constructor = value.constructor) === null || _value$constructor === void 0 ? void 0 : _value$constructor.name) !== null && _value$constructor$na !== void 0 ? _value$constructor$na : "object"} instance`;
|
|
79
|
-
}
|
|
80
|
-
if (typeof value === "symbol") return "a symbol";
|
|
81
|
-
return String(value);
|
|
82
|
-
};
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region ../js-client-core/src/testing/values.ts
|
|
85
|
-
const encodeTestValue = (key, value, revision) => {
|
|
86
|
-
const encoded = encodeTestValue$1(key, value);
|
|
87
|
-
return {
|
|
88
|
-
id: `test-config:${key}`,
|
|
89
|
-
key,
|
|
90
|
-
type: encoded.type,
|
|
91
|
-
value: encoded.value,
|
|
92
|
-
valueId: `test-value:${key}:${revision}`
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
//#endregion
|
|
1
|
+
import { a as discardingTelemetryClient, d as Emitter, f as _defineProperty, l as ConfigDirectorConnectionError, o as createDefaultLogger, r as DefaultConfigDirectorClient, s as nativeValueConfigState, t as INSTALLED_TEST_CLIENT_SLOT, u as ConfigDirectorValidationError } from "./installedTestClient-CKjaEilj.mjs";
|
|
96
2
|
//#region ../js-client-core/src/testing/InMemoryConnection.ts
|
|
97
3
|
const TEST_ENVIRONMENT = {
|
|
98
4
|
environmentId: "test-environment",
|
|
@@ -129,7 +35,7 @@ var InMemoryConnection = class {
|
|
|
129
35
|
return [...this.recordedContexts];
|
|
130
36
|
}
|
|
131
37
|
setValue(key, value) {
|
|
132
|
-
const configState =
|
|
38
|
+
const configState = this.encode(key, value);
|
|
133
39
|
this.configs.set(key, configState);
|
|
134
40
|
this.deliverIfConnected({
|
|
135
41
|
...TEST_ENVIRONMENT,
|
|
@@ -278,18 +184,16 @@ var InMemoryConnection = class {
|
|
|
278
184
|
}
|
|
279
185
|
encodeAll(values) {
|
|
280
186
|
const encoded = /* @__PURE__ */ new Map();
|
|
281
|
-
for (const [key, value] of Object.entries(values)) encoded.set(key,
|
|
187
|
+
for (const [key, value] of Object.entries(values)) encoded.set(key, this.encode(key, value));
|
|
282
188
|
return encoded;
|
|
283
189
|
}
|
|
190
|
+
encode(key, value) {
|
|
191
|
+
return nativeValueConfigState(`test-config:${key}`, key, value, `test-value:${key}:${++this.revision}`);
|
|
192
|
+
}
|
|
284
193
|
};
|
|
285
194
|
//#endregion
|
|
286
195
|
//#region ../js-client-core/src/testing/TestClient.ts
|
|
287
196
|
const TEST_CLIENT_SDK_KEY = "test-client";
|
|
288
|
-
const noopTelemetryClient = {
|
|
289
|
-
updateContext: async () => {},
|
|
290
|
-
evaluatedConfig: () => {},
|
|
291
|
-
close: async () => {}
|
|
292
|
-
};
|
|
293
197
|
var InMemoryTestClient = class {
|
|
294
198
|
constructor(client, connection) {
|
|
295
199
|
this.client = client;
|
|
@@ -330,7 +234,7 @@ const createTestClient$1 = (sdkOptions, options) => {
|
|
|
330
234
|
var _options$logger, _options$values;
|
|
331
235
|
const logger = (_options$logger = options === null || options === void 0 ? void 0 : options.logger) !== null && _options$logger !== void 0 ? _options$logger : createDefaultLogger();
|
|
332
236
|
const connection = new InMemoryConnection(logger, (_options$values = options === null || options === void 0 ? void 0 : options.values) !== null && _options$values !== void 0 ? _options$values : {});
|
|
333
|
-
return new InMemoryTestClient(new DefaultConfigDirectorClient(
|
|
237
|
+
return new InMemoryTestClient(new DefaultConfigDirectorClient(() => discardingTelemetryClient, TEST_CLIENT_SDK_KEY, sdkOptions, {
|
|
334
238
|
logger,
|
|
335
239
|
connection: { timeout: options === null || options === void 0 ? void 0 : options.timeout }
|
|
336
240
|
}, { transport: connection }), connection);
|
|
@@ -391,7 +295,7 @@ const installTestClient = (testClient) => {
|
|
|
391
295
|
const createTestClient = (options) => {
|
|
392
296
|
return createTestClient$1({
|
|
393
297
|
sdkName: "react-native-sdk",
|
|
394
|
-
sdkVersion: "1.
|
|
298
|
+
sdkVersion: "1.8.0"
|
|
395
299
|
}, options);
|
|
396
300
|
};
|
|
397
301
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as OfflineValues, d as ConfigDirectorLogger, f as ConfigDirectorLoggingLevel, i as OfflineValue, l as ConfigDirectorContext, m as ConnectionMode, n as ConfigDirectorClient, p as ConfigValueType, r as ConfigDirectorClientOptions, t as ClientHooks, u as ConfigDirectorLogMessageDecorator } from "./types-DoORnGTD.mjs";
|
|
2
2
|
import { Component, PropsWithChildren } from "react";
|
|
3
3
|
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
@@ -39,19 +39,31 @@ type NetInfoState = {
|
|
|
39
39
|
*/
|
|
40
40
|
type NetInfoSubscribe = (callback: (state: NetInfoState) => void) => () => void;
|
|
41
41
|
type ConfigDirectorProviderOptions = {
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Your ConfigDirector client SDK key. Required unless `mode` is `offline`.
|
|
44
|
+
*/
|
|
45
|
+
sdkKey?: string;
|
|
43
46
|
appName?: string;
|
|
44
47
|
appVersion?: string;
|
|
45
48
|
url?: string;
|
|
46
49
|
timeout?: number;
|
|
47
50
|
/**
|
|
48
|
-
* The connection mode, one of `streaming` or `
|
|
49
|
-
* open and receives config updates as they happen. In `polling` mode configs are fetched once
|
|
50
|
-
* initialization and then again on every `pollingInterval`.
|
|
51
|
+
* The connection mode, one of `streaming`, `polling` or `offline`. In `streaming` mode the connection
|
|
52
|
+
* stays open and receives config updates as they happen. In `polling` mode configs are fetched once
|
|
53
|
+
* during initialization and then again on every `pollingInterval`. In `offline` mode the client never
|
|
54
|
+
* connects to ConfigDirector and sends nothing: it serves `values` to every context, or the in-code
|
|
55
|
+
* default values when none are given, and needs no `sdkKey`.
|
|
51
56
|
*
|
|
52
57
|
* Defaults to `streaming`.
|
|
53
58
|
*/
|
|
54
59
|
mode?: ConnectionMode;
|
|
60
|
+
/**
|
|
61
|
+
* The values to serve in `offline` mode, keyed by config key. The config type of each value follows
|
|
62
|
+
* from the value: a boolean, an integral number (`integer`), any other number (`float`), a string, or
|
|
63
|
+
* a plain object or array (`json`). Can be given only in `offline` mode; otherwise the client fails
|
|
64
|
+
* to initialize.
|
|
65
|
+
*/
|
|
66
|
+
values?: OfflineValues;
|
|
55
67
|
/**
|
|
56
68
|
* The polling interval in seconds when `mode` is `polling`. Has no effect in `streaming` mode.
|
|
57
69
|
*
|
|
@@ -133,4 +145,4 @@ declare class ConfigDirectorReactContextError extends Error {
|
|
|
133
145
|
//#region src/logger.d.ts
|
|
134
146
|
declare const createConsoleLogger: (level?: ConfigDirectorLoggingLevel) => DefaultConsoleLogger;
|
|
135
147
|
//#endregion
|
|
136
|
-
export { type ClientStatus, type ConfigDirectorClient, type ConfigDirectorClientOptions, type ConfigDirectorContext, type ConfigDirectorLogger, type ConfigDirectorLoggingLevel, ConfigDirectorProvider, type ConfigDirectorProviderClientProps, type ConfigDirectorProviderOptions, type ConfigDirectorProviderProps, ConfigDirectorReactContextError, type ConnectionMode, type NetInfoSubscribe, createClient, createConsoleLogger, useClient, useConfigValue, useContext };
|
|
148
|
+
export { type ClientStatus, type ConfigDirectorClient, type ConfigDirectorClientOptions, type ConfigDirectorContext, type ConfigDirectorLogger, type ConfigDirectorLoggingLevel, ConfigDirectorProvider, type ConfigDirectorProviderClientProps, type ConfigDirectorProviderOptions, type ConfigDirectorProviderProps, ConfigDirectorReactContextError, type ConnectionMode, type NetInfoSubscribe, type OfflineValue, type OfflineValues, createClient, createConsoleLogger, useClient, useConfigValue, useContext };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as isFetchErrorFatal, f as _defineProperty, i as fetchWithTimeout, n as readInstalledTestClient, o as createDefaultLogger, r as DefaultConfigDirectorClient } from "./installedTestClient-CKjaEilj.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";
|
|
@@ -285,6 +285,63 @@ var EvaluatedConfigEventPreprocessor = class {
|
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
287
|
};
|
|
288
|
+
var InvalidContextSubmissions = class {
|
|
289
|
+
constructor() {
|
|
290
|
+
_defineProperty(this, "countsByKindSample", /* @__PURE__ */ new Map());
|
|
291
|
+
_defineProperty(this, "countBeyondSampleLimit", 0);
|
|
292
|
+
_defineProperty(this, "startTime", void 0);
|
|
293
|
+
}
|
|
294
|
+
record(kindSample) {
|
|
295
|
+
var _this$startTime;
|
|
296
|
+
(_this$startTime = this.startTime) !== null && _this$startTime !== void 0 || (this.startTime = /* @__PURE__ */ new Date());
|
|
297
|
+
return this.add(kindSample, 1);
|
|
298
|
+
}
|
|
299
|
+
absorb(entries) {
|
|
300
|
+
for (const entry of entries) {
|
|
301
|
+
if (this.startTime === void 0 || entry.startTime < this.startTime) this.startTime = entry.startTime;
|
|
302
|
+
this.add(entry.event.kindSample, entry.count);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
add(kindSample, count) {
|
|
306
|
+
if (kindSample !== void 0) {
|
|
307
|
+
const knownCount = this.countsByKindSample.get(kindSample);
|
|
308
|
+
if (knownCount !== void 0) {
|
|
309
|
+
this.countsByKindSample.set(kindSample, knownCount + count);
|
|
310
|
+
return "known-kind-sample";
|
|
311
|
+
}
|
|
312
|
+
if (this.countsByKindSample.size < 20) {
|
|
313
|
+
this.countsByKindSample.set(kindSample, count);
|
|
314
|
+
return "new-kind-sample";
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const firstBeyondSampleLimit = this.countBeyondSampleLimit === 0;
|
|
318
|
+
this.countBeyondSampleLimit += count;
|
|
319
|
+
return firstBeyondSampleLimit ? "first-beyond-sample-limit" : "beyond-sample-limit";
|
|
320
|
+
}
|
|
321
|
+
takeSnapshot() {
|
|
322
|
+
var _this$startTime2;
|
|
323
|
+
const endTime = /* @__PURE__ */ new Date();
|
|
324
|
+
const startTime = (_this$startTime2 = this.startTime) !== null && _this$startTime2 !== void 0 ? _this$startTime2 : endTime;
|
|
325
|
+
const entryOf = (count, kindSample) => ({
|
|
326
|
+
startTime,
|
|
327
|
+
endTime,
|
|
328
|
+
count,
|
|
329
|
+
event: {
|
|
330
|
+
reason: "invalid-context-kind",
|
|
331
|
+
kindSample
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
const sampledEntries = Array.from(this.countsByKindSample, ([kindSample, count]) => entryOf(count, kindSample));
|
|
335
|
+
const entries = this.countBeyondSampleLimit > 0 ? [...sampledEntries, entryOf(this.countBeyondSampleLimit)] : sampledEntries;
|
|
336
|
+
this.clear();
|
|
337
|
+
return entries;
|
|
338
|
+
}
|
|
339
|
+
clear() {
|
|
340
|
+
this.countsByKindSample.clear();
|
|
341
|
+
this.countBeyondSampleLimit = 0;
|
|
342
|
+
this.startTime = void 0;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
288
345
|
//#endregion
|
|
289
346
|
//#region ../js-client-core/src/telemetry/ClientTelemetryEventCollector.ts
|
|
290
347
|
var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
@@ -293,6 +350,7 @@ var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
293
350
|
_defineProperty(this, "evaluationEventSnapshotPreprocessor", void 0);
|
|
294
351
|
_defineProperty(this, "reporter", void 0);
|
|
295
352
|
_defineProperty(this, "_context", void 0);
|
|
353
|
+
_defineProperty(this, "invalidContextSubmissions", new InvalidContextSubmissions());
|
|
296
354
|
this.reporter = new ClientEventReporter(options);
|
|
297
355
|
this.evaluationEventSnapshotPreprocessor = new EvaluatedConfigEventPreprocessor(options.valueIdGenerator);
|
|
298
356
|
}
|
|
@@ -306,6 +364,12 @@ var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
306
364
|
if (!this.collectEvents) return;
|
|
307
365
|
this.evaluationEventQueue.push(event);
|
|
308
366
|
}
|
|
367
|
+
recordInvalidContext(kindSample) {
|
|
368
|
+
this.invalidContextSubmissions.record(kindSample);
|
|
369
|
+
}
|
|
370
|
+
absorbInvalidContextSubmissions(entries) {
|
|
371
|
+
this.invalidContextSubmissions.absorb(entries);
|
|
372
|
+
}
|
|
309
373
|
async forceFlush() {
|
|
310
374
|
this.cancelScheduledFlush();
|
|
311
375
|
await this.flushAndScheduleNext();
|
|
@@ -320,7 +384,10 @@ var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
320
384
|
return await ((_this$reporter = this.reporter) === null || _this$reporter === void 0 ? void 0 : _this$reporter.report({
|
|
321
385
|
context: this._context,
|
|
322
386
|
discreteEvents: {},
|
|
323
|
-
aggregatedEvents: {
|
|
387
|
+
aggregatedEvents: {
|
|
388
|
+
evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot),
|
|
389
|
+
invalidContext: this.invalidContextSubmissions.takeSnapshot()
|
|
390
|
+
},
|
|
324
391
|
droppedEvents: { evaluatedConfig: evaluationSnapshot.droppedCount }
|
|
325
392
|
}));
|
|
326
393
|
}
|
|
@@ -453,6 +520,36 @@ const resolveMetadata = (provided, logger) => {
|
|
|
453
520
|
return metadata;
|
|
454
521
|
};
|
|
455
522
|
//#endregion
|
|
523
|
+
//#region src/instanceIdStorage.ts
|
|
524
|
+
const lookUpAsyncStorage = async () => {
|
|
525
|
+
try {
|
|
526
|
+
const { default: storage } = await import("@react-native-async-storage/async-storage");
|
|
527
|
+
return {
|
|
528
|
+
outcome: "installed",
|
|
529
|
+
storage
|
|
530
|
+
};
|
|
531
|
+
} catch {
|
|
532
|
+
return { outcome: "not installed" };
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
const asyncStorageInstanceIdStorage = (logger) => {
|
|
536
|
+
const asyncStorageLookup = lookUpAsyncStorage().then((lookup) => {
|
|
537
|
+
if (lookup.outcome === "not installed") logger.warn("The ConfigDirector SDK could not find @react-native-async-storage/async-storage, so every app start counts as a new client session. Install it for the SDK to keep the client session for six hours across app starts.");
|
|
538
|
+
return lookup;
|
|
539
|
+
});
|
|
540
|
+
const installedAsyncStorage = async () => {
|
|
541
|
+
const lookup = await asyncStorageLookup;
|
|
542
|
+
switch (lookup.outcome) {
|
|
543
|
+
case "installed": return lookup.storage;
|
|
544
|
+
case "not installed": throw new Error("@react-native-async-storage/async-storage is not installed");
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
return {
|
|
548
|
+
getItem: async (key) => (await installedAsyncStorage()).getItem(key),
|
|
549
|
+
setItem: async (key, value) => (await installedAsyncStorage()).setItem(key, value)
|
|
550
|
+
};
|
|
551
|
+
};
|
|
552
|
+
//#endregion
|
|
456
553
|
//#region src/utf8Encoder.ts
|
|
457
554
|
/**
|
|
458
555
|
* Encodes a string to a UTF-8 byte sequence.
|
|
@@ -607,6 +704,9 @@ var ReactNativeTelemetryClient = class {
|
|
|
607
704
|
evaluatedConfig(event) {
|
|
608
705
|
this.collector.evaluatedConfig(this.sanitizeEvaluatedConfigEvent(event));
|
|
609
706
|
}
|
|
707
|
+
recordInvalidContext(kindSample) {
|
|
708
|
+
this.collector.recordInvalidContext(kindSample);
|
|
709
|
+
}
|
|
610
710
|
async close() {
|
|
611
711
|
await this.collector.close();
|
|
612
712
|
}
|
|
@@ -642,19 +742,20 @@ var MinimalUrl = class {
|
|
|
642
742
|
const urlFactory = (input, base) => new MinimalUrl(input, base);
|
|
643
743
|
//#endregion
|
|
644
744
|
//#region src/client.ts
|
|
645
|
-
const createClient = (clientSdkKey, clientOptions) =>
|
|
646
|
-
|
|
745
|
+
const createClient = (clientSdkKey, clientOptions) => createReactNativeClient(clientSdkKey, clientOptions);
|
|
746
|
+
const createReactNativeClient = (clientSdkKey, clientOptions) => {
|
|
747
|
+
var _clientOptions$logger;
|
|
647
748
|
const sdkIdentity = {
|
|
648
749
|
sdkName: "react-native-sdk",
|
|
649
|
-
sdkVersion: "1.
|
|
750
|
+
sdkVersion: "1.8.0"
|
|
650
751
|
};
|
|
651
752
|
const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
|
|
652
753
|
const metadata = resolveMetadata(clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata, logger);
|
|
653
|
-
return new DefaultConfigDirectorClient(new ReactNativeTelemetryClient({
|
|
654
|
-
sdkKey: clientSdkKey,
|
|
754
|
+
return new DefaultConfigDirectorClient((liveTelemetry) => new ReactNativeTelemetryClient({
|
|
755
|
+
sdkKey: liveTelemetry.clientSdkKey,
|
|
655
756
|
sdkIdentity,
|
|
656
757
|
metaContext: metadata,
|
|
657
|
-
baseUrl:
|
|
758
|
+
baseUrl: liveTelemetry.baseUrl,
|
|
658
759
|
logger,
|
|
659
760
|
urlFactory
|
|
660
761
|
}), clientSdkKey, sdkIdentity, {
|
|
@@ -663,19 +764,21 @@ const createClient = (clientSdkKey, clientOptions) => {
|
|
|
663
764
|
metadata
|
|
664
765
|
}, {
|
|
665
766
|
fetch: reactNativeStreamingFetch,
|
|
666
|
-
urlFactory
|
|
767
|
+
urlFactory,
|
|
768
|
+
instanceIdStorage: asyncStorageInstanceIdStorage(logger)
|
|
667
769
|
});
|
|
668
770
|
};
|
|
669
771
|
//#endregion
|
|
670
772
|
//#region src/clientSource.ts
|
|
671
773
|
const buildClient = (options) => {
|
|
672
774
|
var _options$logger;
|
|
673
|
-
return
|
|
775
|
+
return createReactNativeClient(options.sdkKey, {
|
|
674
776
|
connection: {
|
|
675
777
|
url: options.url,
|
|
676
778
|
timeout: options.timeout,
|
|
677
779
|
mode: options.mode,
|
|
678
|
-
pollingInterval: options.pollingInterval
|
|
780
|
+
pollingInterval: options.pollingInterval,
|
|
781
|
+
values: options.values
|
|
679
782
|
},
|
|
680
783
|
metadata: {
|
|
681
784
|
appName: options.appName,
|
|
@@ -703,6 +806,7 @@ const resolveClientSource = (props) => {
|
|
|
703
806
|
};
|
|
704
807
|
//#endregion
|
|
705
808
|
//#region src/provider.tsx
|
|
809
|
+
const settledStatusOf = (client) => (client === null || client === void 0 ? void 0 : client.isReady) ? "ready" : "default";
|
|
706
810
|
var ConfigDirectorProvider = class extends Component {
|
|
707
811
|
constructor(props) {
|
|
708
812
|
super(props);
|
|
@@ -770,15 +874,15 @@ var ConfigDirectorProvider = class extends Component {
|
|
|
770
874
|
break;
|
|
771
875
|
case "none": return;
|
|
772
876
|
}
|
|
773
|
-
|
|
877
|
+
this.setState({ status: settledStatusOf(client) });
|
|
774
878
|
}
|
|
775
879
|
async componentDidUpdate(prevProps) {
|
|
776
880
|
if (hasGivenClient(prevProps) && hasGivenClient(this.props) && prevProps.client !== this.props.client) createConsoleLogger("warn").warn("The client prop of ConfigDirectorProvider changed after it mounted and is ignored. Remount the provider, for example with a new key, to use another client.");
|
|
777
881
|
if (!deepEqual(prevProps.context, this.props.context)) {
|
|
778
|
-
var _this$state$client3, _this$props$context
|
|
882
|
+
var _this$state$client3, _this$props$context;
|
|
779
883
|
this.setState({ status: "loading" });
|
|
780
884
|
await ((_this$state$client3 = this.state.client) === null || _this$state$client3 === void 0 ? void 0 : _this$state$client3.updateContext((_this$props$context = this.props.context) !== null && _this$props$context !== void 0 ? _this$props$context : {}));
|
|
781
|
-
|
|
885
|
+
this.setState({ status: settledStatusOf(this.state.client) });
|
|
782
886
|
}
|
|
783
887
|
}
|
|
784
888
|
componentWillUnmount() {
|
|
@@ -790,8 +894,8 @@ var ConfigDirectorProvider = class extends Component {
|
|
|
790
894
|
(_this$handlers = this.handlers) === null || _this$handlers === void 0 || _this$handlers.removeAll();
|
|
791
895
|
this.handlers = void 0;
|
|
792
896
|
if (this.ownsClient) {
|
|
793
|
-
var _this$state$
|
|
794
|
-
(_this$state$
|
|
897
|
+
var _this$state$client4;
|
|
898
|
+
(_this$state$client4 = this.state.client) === null || _this$state$client4 === void 0 || _this$state$client4.dispose();
|
|
795
899
|
this.disposedOwnClient = true;
|
|
796
900
|
}
|
|
797
901
|
}
|