@configdirector/react-native-sdk 1.7.0 → 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/dist/configdirector-react-native-testing.d.mts +5 -14
- package/dist/configdirector-react-native-testing.mjs +8 -105
- package/dist/configdirector-react-native.d.mts +18 -6
- package/dist/configdirector-react-native.mjs +17 -14
- package/dist/{installedTestClient-LWv5ji8J.mjs → installedTestClient-CKjaEilj.mjs} +293 -61
- package/dist/{types-P1hoU2Ru.d.mts → types-DoORnGTD.d.mts} +70 -35
- package/package.json +1 -1
|
@@ -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.
|
|
@@ -155,4 +146,4 @@ declare const installTestClient: (testClient: TestClient) => (() => void);
|
|
|
155
146
|
*/
|
|
156
147
|
declare const createTestClient: (options?: TestClientOptions) => TestClient;
|
|
157
148
|
//#endregion
|
|
158
|
-
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,19 +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
|
-
recordInvalidContext: () => {},
|
|
292
|
-
close: async () => {}
|
|
293
|
-
};
|
|
294
197
|
var InMemoryTestClient = class {
|
|
295
198
|
constructor(client, connection) {
|
|
296
199
|
this.client = client;
|
|
@@ -331,7 +234,7 @@ const createTestClient$1 = (sdkOptions, options) => {
|
|
|
331
234
|
var _options$logger, _options$values;
|
|
332
235
|
const logger = (_options$logger = options === null || options === void 0 ? void 0 : options.logger) !== null && _options$logger !== void 0 ? _options$logger : createDefaultLogger();
|
|
333
236
|
const connection = new InMemoryConnection(logger, (_options$values = options === null || options === void 0 ? void 0 : options.values) !== null && _options$values !== void 0 ? _options$values : {});
|
|
334
|
-
return new InMemoryTestClient(new DefaultConfigDirectorClient(
|
|
237
|
+
return new InMemoryTestClient(new DefaultConfigDirectorClient(() => discardingTelemetryClient, TEST_CLIENT_SDK_KEY, sdkOptions, {
|
|
335
238
|
logger,
|
|
336
239
|
connection: { timeout: options === null || options === void 0 ? void 0 : options.timeout }
|
|
337
240
|
}, { transport: connection }), connection);
|
|
@@ -392,7 +295,7 @@ const installTestClient = (testClient) => {
|
|
|
392
295
|
const createTestClient = (options) => {
|
|
393
296
|
return createTestClient$1({
|
|
394
297
|
sdkName: "react-native-sdk",
|
|
395
|
-
sdkVersion: "1.
|
|
298
|
+
sdkVersion: "1.8.0"
|
|
396
299
|
}, options);
|
|
397
300
|
};
|
|
398
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";
|
|
@@ -742,19 +742,20 @@ var MinimalUrl = class {
|
|
|
742
742
|
const urlFactory = (input, base) => new MinimalUrl(input, base);
|
|
743
743
|
//#endregion
|
|
744
744
|
//#region src/client.ts
|
|
745
|
-
const createClient = (clientSdkKey, clientOptions) =>
|
|
746
|
-
|
|
745
|
+
const createClient = (clientSdkKey, clientOptions) => createReactNativeClient(clientSdkKey, clientOptions);
|
|
746
|
+
const createReactNativeClient = (clientSdkKey, clientOptions) => {
|
|
747
|
+
var _clientOptions$logger;
|
|
747
748
|
const sdkIdentity = {
|
|
748
749
|
sdkName: "react-native-sdk",
|
|
749
|
-
sdkVersion: "1.
|
|
750
|
+
sdkVersion: "1.8.0"
|
|
750
751
|
};
|
|
751
752
|
const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
|
|
752
753
|
const metadata = resolveMetadata(clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata, logger);
|
|
753
|
-
return new DefaultConfigDirectorClient(new ReactNativeTelemetryClient({
|
|
754
|
-
sdkKey: clientSdkKey,
|
|
754
|
+
return new DefaultConfigDirectorClient((liveTelemetry) => new ReactNativeTelemetryClient({
|
|
755
|
+
sdkKey: liveTelemetry.clientSdkKey,
|
|
755
756
|
sdkIdentity,
|
|
756
757
|
metaContext: metadata,
|
|
757
|
-
baseUrl:
|
|
758
|
+
baseUrl: liveTelemetry.baseUrl,
|
|
758
759
|
logger,
|
|
759
760
|
urlFactory
|
|
760
761
|
}), clientSdkKey, sdkIdentity, {
|
|
@@ -771,12 +772,13 @@ const createClient = (clientSdkKey, clientOptions) => {
|
|
|
771
772
|
//#region src/clientSource.ts
|
|
772
773
|
const buildClient = (options) => {
|
|
773
774
|
var _options$logger;
|
|
774
|
-
return
|
|
775
|
+
return createReactNativeClient(options.sdkKey, {
|
|
775
776
|
connection: {
|
|
776
777
|
url: options.url,
|
|
777
778
|
timeout: options.timeout,
|
|
778
779
|
mode: options.mode,
|
|
779
|
-
pollingInterval: options.pollingInterval
|
|
780
|
+
pollingInterval: options.pollingInterval,
|
|
781
|
+
values: options.values
|
|
780
782
|
},
|
|
781
783
|
metadata: {
|
|
782
784
|
appName: options.appName,
|
|
@@ -804,6 +806,7 @@ const resolveClientSource = (props) => {
|
|
|
804
806
|
};
|
|
805
807
|
//#endregion
|
|
806
808
|
//#region src/provider.tsx
|
|
809
|
+
const settledStatusOf = (client) => (client === null || client === void 0 ? void 0 : client.isReady) ? "ready" : "default";
|
|
807
810
|
var ConfigDirectorProvider = class extends Component {
|
|
808
811
|
constructor(props) {
|
|
809
812
|
super(props);
|
|
@@ -871,15 +874,15 @@ var ConfigDirectorProvider = class extends Component {
|
|
|
871
874
|
break;
|
|
872
875
|
case "none": return;
|
|
873
876
|
}
|
|
874
|
-
|
|
877
|
+
this.setState({ status: settledStatusOf(client) });
|
|
875
878
|
}
|
|
876
879
|
async componentDidUpdate(prevProps) {
|
|
877
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.");
|
|
878
881
|
if (!deepEqual(prevProps.context, this.props.context)) {
|
|
879
|
-
var _this$state$client3, _this$props$context
|
|
882
|
+
var _this$state$client3, _this$props$context;
|
|
880
883
|
this.setState({ status: "loading" });
|
|
881
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 : {}));
|
|
882
|
-
|
|
885
|
+
this.setState({ status: settledStatusOf(this.state.client) });
|
|
883
886
|
}
|
|
884
887
|
}
|
|
885
888
|
componentWillUnmount() {
|
|
@@ -891,8 +894,8 @@ var ConfigDirectorProvider = class extends Component {
|
|
|
891
894
|
(_this$handlers = this.handlers) === null || _this$handlers === void 0 || _this$handlers.removeAll();
|
|
892
895
|
this.handlers = void 0;
|
|
893
896
|
if (this.ownsClient) {
|
|
894
|
-
var _this$state$
|
|
895
|
-
(_this$state$
|
|
897
|
+
var _this$state$client4;
|
|
898
|
+
(_this$state$client4 = this.state.client) === null || _this$state$client4 === void 0 || _this$state$client4.dispose();
|
|
896
899
|
this.disposedOwnClient = true;
|
|
897
900
|
}
|
|
898
901
|
}
|
|
@@ -89,6 +89,13 @@ var ConfigDirectorValidationError = class ConfigDirectorValidationError extends
|
|
|
89
89
|
Object.setPrototypeOf(this, ConfigDirectorValidationError.prototype);
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
|
+
var ConfigDirectorInitializationError = class ConfigDirectorInitializationError extends Error {
|
|
93
|
+
constructor(message) {
|
|
94
|
+
super(message);
|
|
95
|
+
_defineProperty(this, "name", "ConfigDirectorInitializationError");
|
|
96
|
+
Object.setPrototypeOf(this, ConfigDirectorInitializationError.prototype);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
92
99
|
//#endregion
|
|
93
100
|
//#region ../js-client-core/src/errors.ts
|
|
94
101
|
const isFetchErrorFatal = (fetchError) => {
|
|
@@ -535,6 +542,187 @@ var StreamingTransport = class {
|
|
|
535
542
|
}
|
|
536
543
|
};
|
|
537
544
|
//#endregion
|
|
545
|
+
//#region ../shared/src/native-values.ts
|
|
546
|
+
const encodeNativeValue = (key, value) => {
|
|
547
|
+
if (key.trim().length == 0) throw new ConfigDirectorValidationError("Invalid config key. The key of a value cannot be empty.");
|
|
548
|
+
return encode(key, value);
|
|
549
|
+
};
|
|
550
|
+
const encode = (key, value) => {
|
|
551
|
+
switch (typeof value) {
|
|
552
|
+
case "boolean": return {
|
|
553
|
+
type: "boolean",
|
|
554
|
+
value: value ? "true" : "false"
|
|
555
|
+
};
|
|
556
|
+
case "number": return encodeNumber(key, value);
|
|
557
|
+
case "string": return {
|
|
558
|
+
type: "string",
|
|
559
|
+
value
|
|
560
|
+
};
|
|
561
|
+
case "object": if (value !== null) {
|
|
562
|
+
assertJsonContents(key, value, "");
|
|
563
|
+
return {
|
|
564
|
+
type: "json",
|
|
565
|
+
value: JSON.stringify(value)
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
throw new ConfigDirectorValidationError(`Invalid value for '${key}': ${describe(value)}. Use a boolean, a number, a string, a plain object, or an array.`);
|
|
570
|
+
};
|
|
571
|
+
const encodeNumber = (key, value) => {
|
|
572
|
+
if (!Number.isFinite(value)) throw new ConfigDirectorValidationError(`Invalid value for '${key}': ${describe(value)} is not a finite number.`);
|
|
573
|
+
if (Number.isInteger(value)) {
|
|
574
|
+
if (!Number.isSafeInteger(value)) throw new ConfigDirectorValidationError(`Invalid value for '${key}': ${value} is not a safe integer, so the SDK could not read it back exactly.`);
|
|
575
|
+
return {
|
|
576
|
+
type: "integer",
|
|
577
|
+
value: String(value)
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
type: "float",
|
|
582
|
+
value: toPlainDecimal(value)
|
|
583
|
+
};
|
|
584
|
+
};
|
|
585
|
+
const toPlainDecimal = (value) => {
|
|
586
|
+
const text = String(value);
|
|
587
|
+
const exponentIndex = text.indexOf("e-");
|
|
588
|
+
if (exponentIndex < 0) return text;
|
|
589
|
+
const sign = text.startsWith("-") ? "-" : "";
|
|
590
|
+
const [integerDigits = "", fractionDigits = ""] = text.slice(sign.length, exponentIndex).split(".");
|
|
591
|
+
const leadingZeros = Number(text.slice(exponentIndex + 2)) - integerDigits.length;
|
|
592
|
+
return `${sign}0.${"0".repeat(leadingZeros)}${integerDigits}${fractionDigits}`;
|
|
593
|
+
};
|
|
594
|
+
const assertJsonContents = (key, value, path) => {
|
|
595
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") return;
|
|
596
|
+
if (typeof value === "number") {
|
|
597
|
+
if (!Number.isFinite(value)) throw invalidJsonContents(key, path, `${describe(value)} is not a finite number`);
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (Array.isArray(value)) {
|
|
601
|
+
value.forEach((item, index) => assertJsonContents(key, item, `${path}[${index}]`));
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (isPlainObject(value)) {
|
|
605
|
+
for (const [property, item] of Object.entries(value)) assertJsonContents(key, item, path ? `${path}.${property}` : property);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
throw invalidJsonContents(key, path, `${describe(value)} cannot be encoded as JSON`);
|
|
609
|
+
};
|
|
610
|
+
const invalidJsonContents = (key, path, problem) => new ConfigDirectorValidationError(`Invalid value for '${key}'${path ? ` at '${path}'` : ""}: ${problem}. JSON contents can hold booleans, finite numbers, strings, null, arrays, and plain objects.`);
|
|
611
|
+
const isPlainObject = (value) => {
|
|
612
|
+
if (typeof value !== "object" || value === null) return false;
|
|
613
|
+
const prototype = Object.getPrototypeOf(value);
|
|
614
|
+
return prototype === Object.prototype || prototype === null;
|
|
615
|
+
};
|
|
616
|
+
const describe = (value) => {
|
|
617
|
+
if (value === null) return "null";
|
|
618
|
+
if (typeof value === "function") return "a function";
|
|
619
|
+
if (typeof value === "object") {
|
|
620
|
+
var _value$constructor$na, _value$constructor;
|
|
621
|
+
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`;
|
|
622
|
+
}
|
|
623
|
+
if (typeof value === "symbol") return "a symbol";
|
|
624
|
+
return String(value);
|
|
625
|
+
};
|
|
626
|
+
//#endregion
|
|
627
|
+
//#region ../js-client-core/src/native-values.ts
|
|
628
|
+
const nativeValueConfigState = (id, key, value, valueId) => {
|
|
629
|
+
const encoded = encodeNativeValue(key, value);
|
|
630
|
+
return {
|
|
631
|
+
id,
|
|
632
|
+
key,
|
|
633
|
+
type: encoded.type,
|
|
634
|
+
value: encoded.value,
|
|
635
|
+
valueId
|
|
636
|
+
};
|
|
637
|
+
};
|
|
638
|
+
//#endregion
|
|
639
|
+
//#region ../js-client-core/src/OfflineTransport.ts
|
|
640
|
+
const OFFLINE_ENVIRONMENT = {
|
|
641
|
+
environmentId: "offline",
|
|
642
|
+
projectId: "offline"
|
|
643
|
+
};
|
|
644
|
+
var OfflineTransport = class {
|
|
645
|
+
constructor(source) {
|
|
646
|
+
this.source = source;
|
|
647
|
+
_defineProperty(this, "emitter", new Emitter());
|
|
648
|
+
_defineProperty(this, "on", (name, handler) => this.emitter.on(name, handler));
|
|
649
|
+
_defineProperty(this, "off", (name, handler) => this.emitter.off(name, handler));
|
|
650
|
+
}
|
|
651
|
+
async connect() {
|
|
652
|
+
const configs = loadOfflineConfigs(this.source);
|
|
653
|
+
this.emitter.emit("configSetReceived", {
|
|
654
|
+
...OFFLINE_ENVIRONMENT,
|
|
655
|
+
kind: "full",
|
|
656
|
+
configs
|
|
657
|
+
});
|
|
658
|
+
return this;
|
|
659
|
+
}
|
|
660
|
+
clear() {
|
|
661
|
+
this.emitter.clear();
|
|
662
|
+
}
|
|
663
|
+
close() {}
|
|
664
|
+
dispose() {
|
|
665
|
+
this.clear();
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
const loadOfflineConfigs = (source) => {
|
|
669
|
+
switch (source.kind) {
|
|
670
|
+
case "in-code-defaults": return {};
|
|
671
|
+
case "values": return encodeOfflineValues(source.values);
|
|
672
|
+
case "rejected": throw new ConfigDirectorInitializationError(source.message);
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
const encodeOfflineValues = (values) => {
|
|
676
|
+
try {
|
|
677
|
+
return Object.fromEntries(Object.entries(values).map(([key, value]) => [key, nativeValueConfigState(`offline:${key}`, key, value, void 0)]));
|
|
678
|
+
} catch (error) {
|
|
679
|
+
if (error instanceof ConfigDirectorValidationError) throw new ConfigDirectorInitializationError(`The offline values could not be loaded. ${error.message}`);
|
|
680
|
+
throw error;
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region ../shared/src/constants.ts
|
|
685
|
+
const CLIENT_BASE_URL = "https://client-sdk-api.configdirector.com";
|
|
686
|
+
//#endregion
|
|
687
|
+
//#region ../js-client-core/src/connection-plan.ts
|
|
688
|
+
const planConnection = (clientSdkKey, connection, urlFactory) => {
|
|
689
|
+
var _connection$mode;
|
|
690
|
+
if ((connection === null || connection === void 0 ? void 0 : connection.mode) === "offline") return {
|
|
691
|
+
kind: "stay-offline",
|
|
692
|
+
source: offlineSourceOf(connection)
|
|
693
|
+
};
|
|
694
|
+
const mode = (_connection$mode = connection === null || connection === void 0 ? void 0 : connection.mode) !== null && _connection$mode !== void 0 ? _connection$mode : "streaming";
|
|
695
|
+
if (!clientSdkKey || clientSdkKey.trim().length == 0) throw new ConfigDirectorValidationError("No client SDK key was provided, the client cannot be instantiated without a valid client SDK key");
|
|
696
|
+
if ((connection === null || connection === void 0 ? void 0 : connection.values) !== void 0) return {
|
|
697
|
+
kind: "stay-offline",
|
|
698
|
+
source: {
|
|
699
|
+
kind: "rejected",
|
|
700
|
+
message: `The connection option 'values' applies only when the connection mode is 'offline', but the mode is '${mode}'.`
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
return {
|
|
704
|
+
kind: "connect",
|
|
705
|
+
mode,
|
|
706
|
+
clientSdkKey,
|
|
707
|
+
baseUrl: baseUrlOf(connection === null || connection === void 0 ? void 0 : connection.url, urlFactory)
|
|
708
|
+
};
|
|
709
|
+
};
|
|
710
|
+
const baseUrlOf = (url, urlFactory) => {
|
|
711
|
+
if (!url) return CLIENT_BASE_URL;
|
|
712
|
+
try {
|
|
713
|
+
return urlFactory(url);
|
|
714
|
+
} catch (error) {
|
|
715
|
+
throw new ConfigDirectorValidationError(`Invalid base URL '${url}'. Parsing failed: ${error}`);
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
const offlineSourceOf = (connection) => {
|
|
719
|
+
if (connection.values !== void 0) return {
|
|
720
|
+
kind: "values",
|
|
721
|
+
values: connection.values
|
|
722
|
+
};
|
|
723
|
+
return { kind: "in-code-defaults" };
|
|
724
|
+
};
|
|
725
|
+
//#endregion
|
|
538
726
|
//#region ../shared/src/value-parser.ts
|
|
539
727
|
const getRequestedType = (defaultValue) => {
|
|
540
728
|
const baseType = typeof defaultValue;
|
|
@@ -758,12 +946,17 @@ const createDefaultLogger = (level, messageDecorator) => {
|
|
|
758
946
|
return new DefaultConsoleLogger(level !== null && level !== void 0 ? level : "warn", messageDecorator !== null && messageDecorator !== void 0 ? messageDecorator : new LogMessageDecorator());
|
|
759
947
|
};
|
|
760
948
|
//#endregion
|
|
949
|
+
//#region ../js-client-core/src/telemetry/discarding.ts
|
|
950
|
+
const discardingTelemetryClient = {
|
|
951
|
+
updateContext: async () => {},
|
|
952
|
+
evaluatedConfig: () => {},
|
|
953
|
+
recordInvalidContext: () => {},
|
|
954
|
+
close: async () => {}
|
|
955
|
+
};
|
|
956
|
+
//#endregion
|
|
761
957
|
//#region ../shared/src/url.ts
|
|
762
958
|
const defaultUrlFactory = (input, base) => new URL(input, base === null || base === void 0 ? void 0 : base.toString());
|
|
763
959
|
//#endregion
|
|
764
|
-
//#region ../shared/src/constants.ts
|
|
765
|
-
const CLIENT_BASE_URL = "https://client-sdk-api.configdirector.com";
|
|
766
|
-
//#endregion
|
|
767
960
|
//#region ../shared/src/transport/pollingInterval.ts
|
|
768
961
|
const resolvePollingInterval = (configuredSeconds, defaults) => {
|
|
769
962
|
if (configuredSeconds === void 0) return {
|
|
@@ -1128,9 +1321,13 @@ const admitContext = (context) => {
|
|
|
1128
1321
|
//#endregion
|
|
1129
1322
|
//#region ../js-client-core/src/DefaultConfigDirectorClient.ts
|
|
1130
1323
|
const MAX_EXPONENTIAL_DELAY = 9;
|
|
1131
|
-
var DefaultConfigDirectorClient = class {
|
|
1132
|
-
|
|
1133
|
-
|
|
1324
|
+
var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
1325
|
+
static from(liveTelemetryClient, clientSdkKeyOrOptions, sdkOptions, clientOptions) {
|
|
1326
|
+
if (typeof clientSdkKeyOrOptions === "object") return new DefaultConfigDirectorClient(liveTelemetryClient, void 0, sdkOptions, clientSdkKeyOrOptions);
|
|
1327
|
+
return new DefaultConfigDirectorClient(liveTelemetryClient, clientSdkKeyOrOptions, sdkOptions, clientOptions);
|
|
1328
|
+
}
|
|
1329
|
+
constructor(liveTelemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
|
|
1330
|
+
var _clientOptions$logger, _clientOptions$connec, _clientOptions$connec2, _internalClientOption, _internalClientOption2, _clientOptions$hooks, _clientOptions$hooks2, _clientOptions$hooks3, _clientOptions$hooks4, _clientOptions$hooks5;
|
|
1134
1331
|
_defineProperty(this, "logger", void 0);
|
|
1135
1332
|
_defineProperty(this, "telemetryClient", void 0);
|
|
1136
1333
|
_defineProperty(this, "configSet", void 0);
|
|
@@ -1145,17 +1342,15 @@ var DefaultConfigDirectorClient = class {
|
|
|
1145
1342
|
_defineProperty(this, "readyPromise", void 0);
|
|
1146
1343
|
_defineProperty(this, "readyResolve", void 0);
|
|
1147
1344
|
_defineProperty(this, "currentContext", void 0);
|
|
1148
|
-
_defineProperty(this, "
|
|
1345
|
+
_defineProperty(this, "connectionPlan", void 0);
|
|
1149
1346
|
_defineProperty(this, "clientSession", void 0);
|
|
1150
1347
|
this.logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createDefaultLogger();
|
|
1151
1348
|
this.clientSession = resolveClientSession(this.logger, internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.instanceIdStorage);
|
|
1152
1349
|
this.timeout = (_clientOptions$connec = clientOptions === null || clientOptions === void 0 || (_clientOptions$connec2 = clientOptions.connection) === null || _clientOptions$connec2 === void 0 ? void 0 : _clientOptions$connec2.timeout) !== null && _clientOptions$connec !== void 0 ? _clientOptions$connec : 3e3;
|
|
1153
|
-
this.validateSdkKeyPresence(clientSdkKey);
|
|
1154
1350
|
const urlFactory = (_internalClientOption = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.urlFactory) !== null && _internalClientOption !== void 0 ? _internalClientOption : defaultUrlFactory;
|
|
1155
|
-
|
|
1156
|
-
this.
|
|
1157
|
-
this.
|
|
1158
|
-
this.transport = (_internalClientOption2 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.transport) !== null && _internalClientOption2 !== void 0 ? _internalClientOption2 : this.buildTransport(clientSdkKey, sdkOptions, baseUrl, urlFactory, clientOptions, internalClientOptions);
|
|
1351
|
+
this.connectionPlan = planConnection(clientSdkKey, clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.connection, urlFactory);
|
|
1352
|
+
this.telemetryClient = this.buildTelemetryClient(liveTelemetryClient, clientOptions);
|
|
1353
|
+
this.transport = (_internalClientOption2 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.transport) !== null && _internalClientOption2 !== void 0 ? _internalClientOption2 : this.buildTransport(sdkOptions, clientOptions, urlFactory, internalClientOptions);
|
|
1159
1354
|
this.transport.on("configSetReceived", (configSet) => {
|
|
1160
1355
|
var _this$readyResolve;
|
|
1161
1356
|
this.hasReceivedConfigSet = true;
|
|
@@ -1193,8 +1388,8 @@ var DefaultConfigDirectorClient = class {
|
|
|
1193
1388
|
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
1194
1389
|
for (const h of handlers) if (typeof h === "function") this.on(event, h);
|
|
1195
1390
|
}
|
|
1196
|
-
|
|
1197
|
-
if (
|
|
1391
|
+
pollingIntervalFor(mode, configuredSeconds) {
|
|
1392
|
+
if (mode !== "polling") return;
|
|
1198
1393
|
const resolved = resolvePollingInterval(configuredSeconds, {
|
|
1199
1394
|
defaultSeconds: 60,
|
|
1200
1395
|
minimumSeconds: 30
|
|
@@ -1202,32 +1397,49 @@ var DefaultConfigDirectorClient = class {
|
|
|
1202
1397
|
if (resolved.warning) this.logger.warn(`[ConfigDirectorClient] ${resolved.warning}`);
|
|
1203
1398
|
return resolved.seconds;
|
|
1204
1399
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1400
|
+
buildTelemetryClient(liveTelemetryClient, clientOptions) {
|
|
1401
|
+
const plan = this.connectionPlan;
|
|
1402
|
+
switch (plan.kind) {
|
|
1403
|
+
case "stay-offline": return discardingTelemetryClient;
|
|
1404
|
+
case "connect": return liveTelemetryClient({
|
|
1405
|
+
clientSdkKey: plan.clientSdkKey,
|
|
1406
|
+
baseUrl: plan.baseUrl,
|
|
1407
|
+
logger: this.logger,
|
|
1408
|
+
metadata: clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
buildTransport(sdkOptions, clientOptions, urlFactory, internalClientOptions) {
|
|
1413
|
+
const plan = this.connectionPlan;
|
|
1414
|
+
switch (plan.kind) {
|
|
1415
|
+
case "stay-offline": return new OfflineTransport(plan.source);
|
|
1416
|
+
case "connect":
|
|
1417
|
+
var _internalClientOption3, _clientOptions$connec3;
|
|
1418
|
+
return new (this.getTransportConstructor(plan.mode))({
|
|
1419
|
+
clientSession: this.clientSession,
|
|
1420
|
+
clientSdkKey: plan.clientSdkKey,
|
|
1421
|
+
baseUrl: plan.baseUrl,
|
|
1422
|
+
resolveUrl: urlFactory,
|
|
1423
|
+
metaContext: {
|
|
1424
|
+
...clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata,
|
|
1425
|
+
sdkName: sdkOptions.sdkName,
|
|
1426
|
+
sdkVersion: sdkOptions.sdkVersion,
|
|
1427
|
+
userAgent: readUserAgent(),
|
|
1428
|
+
host: readHost()
|
|
1429
|
+
},
|
|
1430
|
+
logger: this.logger,
|
|
1431
|
+
fetch: internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.fetch,
|
|
1432
|
+
connectionRetryDelay: (_internalClientOption3 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.connectionRetryDelay) !== null && _internalClientOption3 !== void 0 ? _internalClientOption3 : ((attempt) => {
|
|
1433
|
+
return Math.pow(2, Math.min(attempt, MAX_EXPONENTIAL_DELAY)) * 1e3;
|
|
1434
|
+
}),
|
|
1435
|
+
pollingInterval: this.pollingIntervalFor(plan.mode, clientOptions === null || clientOptions === void 0 || (_clientOptions$connec3 = clientOptions.connection) === null || _clientOptions$connec3 === void 0 ? void 0 : _clientOptions$connec3.pollingInterval)
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1226
1438
|
}
|
|
1227
|
-
getTransportConstructor(
|
|
1228
|
-
switch (
|
|
1439
|
+
getTransportConstructor(mode) {
|
|
1440
|
+
switch (mode) {
|
|
1229
1441
|
case "polling": return PollingTransport;
|
|
1230
|
-
|
|
1442
|
+
case "streaming": return StreamingTransport;
|
|
1231
1443
|
}
|
|
1232
1444
|
}
|
|
1233
1445
|
async initialize(context) {
|
|
@@ -1235,17 +1447,28 @@ var DefaultConfigDirectorClient = class {
|
|
|
1235
1447
|
await this.submitContext(context, "initialization");
|
|
1236
1448
|
}
|
|
1237
1449
|
async updateContext(context) {
|
|
1238
|
-
|
|
1450
|
+
switch (this.connectionPlan.kind) {
|
|
1451
|
+
case "connect":
|
|
1452
|
+
await this.submitContext(context, "context update");
|
|
1453
|
+
return;
|
|
1454
|
+
case "stay-offline":
|
|
1455
|
+
this.storeContext(this.admittedContextFor(context, "context update"));
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
storeContext(context) {
|
|
1460
|
+
this.currentContext = context;
|
|
1461
|
+
this.emit("contextUpdated", { context });
|
|
1239
1462
|
}
|
|
1240
1463
|
async submitContext(context, submission) {
|
|
1241
|
-
await this.connectToTransport(this.
|
|
1464
|
+
await this.connectToTransport(this.admittedContextFor(context, submission), submission);
|
|
1242
1465
|
}
|
|
1243
|
-
|
|
1466
|
+
admittedContextFor(suppliedContext, submission) {
|
|
1244
1467
|
const admission = admitContext(suppliedContext);
|
|
1245
1468
|
switch (admission.outcome) {
|
|
1246
1469
|
case "admitted": return admission.context;
|
|
1247
1470
|
case "ignored":
|
|
1248
|
-
this.logger.error(`[ConfigDirectorClient] Ignored the context supplied for ${submission} because its kind '${admission.kindSample}' is invalid;
|
|
1471
|
+
this.logger.error(`[ConfigDirectorClient] Ignored the context supplied for ${submission} because its kind '${admission.kindSample}' is invalid; continuing as if no context was supplied. A context kind has 1 to 64 characters, using only lowercase letters, digits, '-' and '_'.`);
|
|
1249
1472
|
this.telemetryClient.recordInvalidContext(admission.kindSample);
|
|
1250
1473
|
return;
|
|
1251
1474
|
}
|
|
@@ -1277,11 +1500,21 @@ var DefaultConfigDirectorClient = class {
|
|
|
1277
1500
|
if (remainingTimeout > 0) await Promise.race([this.readyPromise, new Promise((resolve) => {
|
|
1278
1501
|
this.timeoutTimer = setTimeout(() => resolve(), remainingTimeout);
|
|
1279
1502
|
})]).finally(() => clearTimeout(this.timeoutTimer));
|
|
1280
|
-
if (!this.ready) this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${caller} after ${this.timeout}ms.
|
|
1503
|
+
if (!this.ready) this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${caller} after ${this.timeout}ms. ${this.timeoutWarningDetails()}`);
|
|
1281
1504
|
} catch (error) {
|
|
1505
|
+
if (error instanceof ConfigDirectorInitializationError) {
|
|
1506
|
+
this.initializing = false;
|
|
1507
|
+
throw error;
|
|
1508
|
+
}
|
|
1282
1509
|
this.logger.error(`[ConfigDirectorClient] An error occurred during ${caller}: `, error);
|
|
1283
1510
|
}
|
|
1284
1511
|
}
|
|
1512
|
+
timeoutWarningDetails() {
|
|
1513
|
+
switch (this.connectionPlan.kind) {
|
|
1514
|
+
case "connect": return "The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.";
|
|
1515
|
+
case "stay-offline": return "The offline values were not delivered, so configs return the default value.";
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1285
1518
|
findKeysRemovedBy(configSet) {
|
|
1286
1519
|
if (!this.configSet || configSet.kind != "full") return [];
|
|
1287
1520
|
return Object.keys(this.configSet.configs).filter((key) => !Object.prototype.hasOwnProperty.call(configSet.configs, key));
|
|
@@ -1380,17 +1613,6 @@ var DefaultConfigDirectorClient = class {
|
|
|
1380
1613
|
this.logger.error(`[ConfigDirectorClient] Error executing event handlers for '${name}'`, error);
|
|
1381
1614
|
}
|
|
1382
1615
|
}
|
|
1383
|
-
parseUrl(url, urlFactory) {
|
|
1384
|
-
if (!url) return;
|
|
1385
|
-
try {
|
|
1386
|
-
return urlFactory(url);
|
|
1387
|
-
} catch (error) {
|
|
1388
|
-
throw new ConfigDirectorValidationError(`Invalid base URL '${url}'. Parsing failed: ${error}`);
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
validateSdkKeyPresence(sdkKey) {
|
|
1392
|
-
if (!sdkKey || sdkKey.trim().length == 0) throw new ConfigDirectorValidationError("No client SDK key was provided, the client cannot be instantiated without a valid client SDK key");
|
|
1393
|
-
}
|
|
1394
1616
|
validateDefaultValue(defaultValue) {
|
|
1395
1617
|
if (defaultValue === void 0 || defaultValue === null) throw new ConfigDirectorValidationError("Invalid default value. The default value for a config must be defined and non-null.");
|
|
1396
1618
|
if (typeof defaultValue === "function") throw new ConfigDirectorValidationError("Invalid default value. The default value for a config cannot be a function.");
|
|
@@ -1416,14 +1638,24 @@ var DefaultConfigDirectorClient = class {
|
|
|
1416
1638
|
this.handlersMap.clear();
|
|
1417
1639
|
}
|
|
1418
1640
|
pauseNetwork() {
|
|
1419
|
-
this.
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1641
|
+
switch (this.connectionPlan.kind) {
|
|
1642
|
+
case "stay-offline": return;
|
|
1643
|
+
case "connect":
|
|
1644
|
+
this.logger.debug("[ConfigDirectorClient] pauseNetwork() called, pausing transport connection");
|
|
1645
|
+
clearTimeout(this.timeoutTimer);
|
|
1646
|
+
this.timeoutTimer = void 0;
|
|
1647
|
+
this.transport.close();
|
|
1648
|
+
this.ready = false;
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1424
1651
|
}
|
|
1425
1652
|
async resumeNetwork() {
|
|
1426
|
-
|
|
1653
|
+
switch (this.connectionPlan.kind) {
|
|
1654
|
+
case "stay-offline": return;
|
|
1655
|
+
case "connect":
|
|
1656
|
+
await this.connectToTransport(this.currentContext, "network resume");
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1427
1659
|
}
|
|
1428
1660
|
async close() {
|
|
1429
1661
|
var _this$readyResolve2;
|
|
@@ -1445,4 +1677,4 @@ var DefaultConfigDirectorClient = class {
|
|
|
1445
1677
|
const INSTALLED_TEST_CLIENT_SLOT = Symbol.for("@configdirector/installed-test-client");
|
|
1446
1678
|
const readInstalledTestClient = () => globalThis[INSTALLED_TEST_CLIENT_SLOT];
|
|
1447
1679
|
//#endregion
|
|
1448
|
-
export {
|
|
1680
|
+
export { discardingTelemetryClient as a, isFetchErrorFatal as c, Emitter as d, _defineProperty as f, fetchWithTimeout as i, ConfigDirectorConnectionError as l, readInstalledTestClient as n, createDefaultLogger as o, DefaultConfigDirectorClient as r, nativeValueConfigState as s, INSTALLED_TEST_CLIENT_SLOT as t, ConfigDirectorValidationError as u };
|
|
@@ -88,9 +88,76 @@ type ConfigDirectorMetaContext = {
|
|
|
88
88
|
appVersion?: string;
|
|
89
89
|
appName?: string;
|
|
90
90
|
};
|
|
91
|
-
type ConnectionMode = "streaming" | "polling";
|
|
91
|
+
type ConnectionMode = "streaming" | "polling" | "offline";
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region ../shared/src/native-values.d.ts
|
|
94
|
+
type NativeJsonValue = boolean | number | string | null | NativeJsonValue[] | {
|
|
95
|
+
[key: string]: NativeJsonValue;
|
|
96
|
+
};
|
|
97
|
+
type NativeValue = boolean | number | string | NativeJsonValue[] | {
|
|
98
|
+
[key: string]: NativeJsonValue;
|
|
99
|
+
};
|
|
100
|
+
type NativeValues = Record<string, NativeValue>;
|
|
92
101
|
//#endregion
|
|
93
102
|
//#region ../js-client-core/src/types.d.ts
|
|
103
|
+
/**
|
|
104
|
+
* One value an offline client serves for a config key. The config type follows from the value: a
|
|
105
|
+
* boolean, an integral number (`integer`), any other number (`float`), a string, or a plain object
|
|
106
|
+
* or array (`json`).
|
|
107
|
+
*/
|
|
108
|
+
type OfflineValue = NativeValue;
|
|
109
|
+
/**
|
|
110
|
+
* The values an offline client serves, keyed by config key. See {@link OfflineValue} for how each
|
|
111
|
+
* value's config type is decided. Every context receives the same value.
|
|
112
|
+
*/
|
|
113
|
+
type OfflineValues = NativeValues;
|
|
114
|
+
/**
|
|
115
|
+
* Connection options of the {@link ConfigDirectorClient}
|
|
116
|
+
*/
|
|
117
|
+
type ConnectionOptions = {
|
|
118
|
+
/**
|
|
119
|
+
* The connection mode to be used, one of `streaming` (default), `polling` or `offline`.
|
|
120
|
+
* If set to `streaming`, the connection will remain open and receive updates whenever
|
|
121
|
+
* config state is updated on the ConfigDirector dashboard.
|
|
122
|
+
* When set to `polling`, there will be an initial request to retrieve config state during
|
|
123
|
+
* initialization, and additional requests on a `pollingInterval`.
|
|
124
|
+
* When set to `offline`, the client never connects to ConfigDirector and sends nothing: it serves
|
|
125
|
+
* the values given in {@link values} to every context, or the in-code default values when none
|
|
126
|
+
* are given. No SDK key is needed.
|
|
127
|
+
*
|
|
128
|
+
* Defaults to `streaming`
|
|
129
|
+
*/
|
|
130
|
+
mode?: ConnectionMode;
|
|
131
|
+
/**
|
|
132
|
+
* The values to serve in `offline` mode, keyed by config key. A value such as `null` makes
|
|
133
|
+
* `initialize` reject with a `ConfigDirectorInitializationError`.
|
|
134
|
+
*
|
|
135
|
+
* Can be given only in `offline` mode; otherwise `initialize` rejects.
|
|
136
|
+
*/
|
|
137
|
+
values?: OfflineValues;
|
|
138
|
+
/**
|
|
139
|
+
* The polling interval in _seconds_ when the `mode` is set to `polling`. This option has no
|
|
140
|
+
* effect in the other modes.
|
|
141
|
+
*
|
|
142
|
+
* Defaults to 60 seconds. The minimum is 30 seconds. A value below the minimum is raised to the
|
|
143
|
+
* minimum and a warning is logged.
|
|
144
|
+
*/
|
|
145
|
+
pollingInterval?: number;
|
|
146
|
+
/**
|
|
147
|
+
* The timeout, in _milliseconds_, to be used in initialization and when updating the context.
|
|
148
|
+
* If streaming is enabled, the operation (initialization or context update) may still succeed
|
|
149
|
+
* after it times out if no unrecoverable errors are encountered (like an invalid SDK key).
|
|
150
|
+
* If streaming is disabled, if the operation times out, it will not be retried. This option has
|
|
151
|
+
* no effect in `offline` mode.
|
|
152
|
+
*/
|
|
153
|
+
timeout?: number;
|
|
154
|
+
/**
|
|
155
|
+
* The base URL to the ConfigDirector SDK server. To be used only when needing to route through a
|
|
156
|
+
* proxy to connect to the ConfigDirector SDK server. Please refer to the docs on how to configure
|
|
157
|
+
* a proxy for the client SDK. This option has no effect in `offline` mode.
|
|
158
|
+
*/
|
|
159
|
+
url?: string;
|
|
160
|
+
};
|
|
94
161
|
/**
|
|
95
162
|
* Configuration options for the {@link ConfigDirectorClient}
|
|
96
163
|
*/
|
|
@@ -102,39 +169,7 @@ type ConfigDirectorClientOptions = {
|
|
|
102
169
|
/**
|
|
103
170
|
* Connection options
|
|
104
171
|
*/
|
|
105
|
-
connection?:
|
|
106
|
-
/**
|
|
107
|
-
* The connection mode to be used, one of `streaming` (default) or `polling`.
|
|
108
|
-
* If set to `streaming`, the connection will remain open and receive updates whenever
|
|
109
|
-
* config state is updated on the ConfigDirector dashboard.
|
|
110
|
-
* When set to `polling`, there will be an initial request to retrieve config state during
|
|
111
|
-
* initialization, and additional requests on a `pollingInterval`.
|
|
112
|
-
*
|
|
113
|
-
* Defaults to `streaming`
|
|
114
|
-
*/
|
|
115
|
-
mode?: ConnectionMode;
|
|
116
|
-
/**
|
|
117
|
-
* The polling interval in _seconds_ when the `mode` is set to `polling`. This option has no
|
|
118
|
-
* effect when the `mode` is set to `streaming`.
|
|
119
|
-
*
|
|
120
|
-
* Defaults to 60 seconds. The minimum is 30 seconds. A value below the minimum is raised to the
|
|
121
|
-
* minimum and a warning is logged.
|
|
122
|
-
*/
|
|
123
|
-
pollingInterval?: number;
|
|
124
|
-
/**
|
|
125
|
-
* The timeout, in _milliseconds_, to be used in initialization and when updating the context.
|
|
126
|
-
* If streaming is enabled, the operation (initialization or context update) may still succeed
|
|
127
|
-
* after it times out if no unrecoverable errors are encountered (like an invalid SDK key).
|
|
128
|
-
* If streaming is disabled, if the operation times out, it will not be retried.
|
|
129
|
-
*/
|
|
130
|
-
timeout?: number;
|
|
131
|
-
/**
|
|
132
|
-
* The base URL to the ConfigDirector SDK server. To be used only when needing to route through a
|
|
133
|
-
* proxy to connect to the ConfigDirector SDK server. Please refer to the docs on how to configure
|
|
134
|
-
* a proxy for the client SDK.
|
|
135
|
-
*/
|
|
136
|
-
url?: string;
|
|
137
|
-
};
|
|
172
|
+
connection?: ConnectionOptions;
|
|
138
173
|
/**
|
|
139
174
|
* A logger that implements {@link ConfigDirectorLogger}. It defaults to the ConfigDirector console
|
|
140
175
|
* logger set to 'warn' level.
|
|
@@ -313,4 +348,4 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
|
|
|
313
348
|
dispose(): void;
|
|
314
349
|
}
|
|
315
350
|
//#endregion
|
|
316
|
-
export {
|
|
351
|
+
export { OfflineValues as a, NativeValues as c, ConfigDirectorLogger as d, ConfigDirectorLoggingLevel as f, OfflineValue as i, ConfigDirectorContext as l, ConnectionMode as m, ConfigDirectorClient as n, NativeJsonValue as o, ConfigValueType as p, ConfigDirectorClientOptions as r, NativeValue as s, ClientHooks as t, ConfigDirectorLogMessageDecorator as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configdirector/react-native-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.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",
|