@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.
@@ -1,4 +1,4 @@
1
- import { i as ConfigDirectorContext, n as ConfigDirectorClient, o as ConfigDirectorLogger } from "./types-P1hoU2Ru.mjs";
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?: TestValues;
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: TestValue): void;
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: TestValues): void;
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 { c as ConfigDirectorConnectionError, d as _defineProperty, l as ConfigDirectorValidationError, o as createDefaultLogger, r as DefaultConfigDirectorClient, t as INSTALLED_TEST_CLIENT_SLOT, u as Emitter } from "./installedTestClient-LWv5ji8J.mjs";
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 = encodeTestValue(key, value, ++this.revision);
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, encodeTestValue(key, value, ++this.revision));
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(noopTelemetryClient, TEST_CLIENT_SDK_KEY, sdkOptions, {
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.7.0"
298
+ sdkVersion: "1.8.0"
396
299
  }, options);
397
300
  };
398
301
  //#endregion
@@ -1,4 +1,4 @@
1
- import { a as ConfigDirectorLogMessageDecorator, c as ConfigValueType, i as ConfigDirectorContext, l as ConnectionMode, n as ConfigDirectorClient, o as ConfigDirectorLogger, r as ConfigDirectorClientOptions, s as ConfigDirectorLoggingLevel, t as ClientHooks } from "./types-P1hoU2Ru.mjs";
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
- sdkKey: string;
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 `polling`. In `streaming` mode the connection stays
49
- * open and receives config updates as they happen. In `polling` mode configs are fetched once during
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 { a as CLIENT_BASE_URL, d as _defineProperty, i as fetchWithTimeout, n as readInstalledTestClient, o as createDefaultLogger, r as DefaultConfigDirectorClient, s as isFetchErrorFatal } from "./installedTestClient-LWv5ji8J.mjs";
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
- var _clientOptions$logger, _clientOptions$connec;
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.7.0"
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: (clientOptions === null || clientOptions === void 0 || (_clientOptions$connec = clientOptions.connection) === null || _clientOptions$connec === void 0 ? void 0 : _clientOptions$connec.url) ? urlFactory(clientOptions.connection.url) : CLIENT_BASE_URL,
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 createClient(options.sdkKey, {
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
- if (!client.isReady) this.setState({ status: "default" });
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, _this$state$client4;
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
- if (!((_this$state$client4 = this.state.client) === null || _this$state$client4 === void 0 ? void 0 : _this$state$client4.isReady)) this.setState({ status: "default" });
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$client5;
895
- (_this$state$client5 = this.state.client) === null || _this$state$client5 === void 0 || _this$state$client5.dispose();
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
- constructor(telemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
1133
- var _clientOptions$logger, _clientOptions$connec, _clientOptions$connec2, _internalClientOption, _this$parseUrl, _clientOptions$connec3, _clientOptions$connec4, _clientOptions$connec5, _internalClientOption2, _clientOptions$hooks, _clientOptions$hooks2, _clientOptions$hooks3, _clientOptions$hooks4, _clientOptions$hooks5;
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, "connectionMode", void 0);
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
- const baseUrl = (_this$parseUrl = this.parseUrl(clientOptions === null || clientOptions === void 0 || (_clientOptions$connec3 = clientOptions.connection) === null || _clientOptions$connec3 === void 0 ? void 0 : _clientOptions$connec3.url, urlFactory)) !== null && _this$parseUrl !== void 0 ? _this$parseUrl : CLIENT_BASE_URL;
1156
- this.connectionMode = (_clientOptions$connec4 = clientOptions === null || clientOptions === void 0 || (_clientOptions$connec5 = clientOptions.connection) === null || _clientOptions$connec5 === void 0 ? void 0 : _clientOptions$connec5.mode) !== null && _clientOptions$connec4 !== void 0 ? _clientOptions$connec4 : "streaming";
1157
- this.telemetryClient = telemetryClient;
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
- resolvePollingIntervalForMode(configuredSeconds) {
1197
- if (this.connectionMode !== "polling") return;
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
- buildTransport(clientSdkKey, sdkOptions, baseUrl, urlFactory, clientOptions, internalClientOptions) {
1206
- var _internalClientOption3, _clientOptions$connec6;
1207
- return new (this.getTransportConstructor(this.connectionMode))({
1208
- clientSession: this.clientSession,
1209
- clientSdkKey,
1210
- baseUrl,
1211
- resolveUrl: urlFactory,
1212
- metaContext: {
1213
- ...clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata,
1214
- sdkName: sdkOptions.sdkName,
1215
- sdkVersion: sdkOptions.sdkVersion,
1216
- userAgent: readUserAgent(),
1217
- host: readHost()
1218
- },
1219
- logger: this.logger,
1220
- fetch: internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.fetch,
1221
- connectionRetryDelay: (_internalClientOption3 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.connectionRetryDelay) !== null && _internalClientOption3 !== void 0 ? _internalClientOption3 : ((attempt) => {
1222
- return Math.pow(2, Math.min(attempt, MAX_EXPONENTIAL_DELAY)) * 1e3;
1223
- }),
1224
- pollingInterval: this.resolvePollingIntervalForMode(clientOptions === null || clientOptions === void 0 || (_clientOptions$connec6 = clientOptions.connection) === null || _clientOptions$connec6 === void 0 ? void 0 : _clientOptions$connec6.pollingInterval)
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(connectionMode) {
1228
- switch (connectionMode) {
1439
+ getTransportConstructor(mode) {
1440
+ switch (mode) {
1229
1441
  case "polling": return PollingTransport;
1230
- default: return StreamingTransport;
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
- await this.submitContext(context, "context update");
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.contextToConnectWith(context, submission), submission);
1464
+ await this.connectToTransport(this.admittedContextFor(context, submission), submission);
1242
1465
  }
1243
- contextToConnectWith(suppliedContext, submission) {
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; connecting as if no context was supplied. A context kind has 1 to 64 characters, using only lowercase letters, digits, '-' and '_'.`);
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. The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`);
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.logger.debug("[ConfigDirectorClient] pauseNetwork() called, pausing transport connection");
1420
- clearTimeout(this.timeoutTimer);
1421
- this.timeoutTimer = void 0;
1422
- this.transport.close();
1423
- this.ready = false;
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
- await this.connectToTransport(this.currentContext, "network resume");
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 { CLIENT_BASE_URL as a, ConfigDirectorConnectionError as c, _defineProperty as d, fetchWithTimeout as i, ConfigDirectorValidationError as l, readInstalledTestClient as n, createDefaultLogger as o, DefaultConfigDirectorClient as r, isFetchErrorFatal as s, INSTALLED_TEST_CLIENT_SLOT as t, Emitter as u };
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 { ConfigDirectorLogMessageDecorator as a, ConfigValueType as c, ConfigDirectorContext as i, ConnectionMode as l, ConfigDirectorClient as n, ConfigDirectorLogger as o, ConfigDirectorClientOptions as r, ConfigDirectorLoggingLevel as s, ClientHooks as t };
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.7.0",
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",