@configdirector/react-native-sdk 1.9.1 → 1.10.1

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 { c as NativeJsonValue, d as ConfigDirectorContext, i as ConfigDirectorClient, l as NativeValue, p as ConfigDirectorLogger, u as NativeValues } from "./types-BBQmjjf5.mjs";
1
+ import { c as NativeJsonValue, d as ConfigDirectorContext, i as ConfigDirectorClient, l as NativeValue, p as ConfigDirectorLogger, u as NativeValues } from "./types-DmbDeZqL.mjs";
2
2
 
3
3
  //#region ../shared/src/errors.d.ts
4
4
  declare class ConfigDirectorConnectionError extends Error {
@@ -1,4 +1,4 @@
1
- import { c as createDefaultLogger, d as ConfigDirectorConnectionError, f as ConfigDirectorValidationError, l as nativeValueConfigState, m as _defineProperty, p as Emitter, r as DefaultConfigDirectorClient, s as discardingTelemetryClient, t as INSTALLED_TEST_CLIENT_SLOT } from "./installedTestClient-CNYgzQuj.mjs";
1
+ import { c as createDefaultLogger, d as ConfigDirectorConnectionError, f as ConfigDirectorValidationError, l as nativeValueConfigState, m as _defineProperty, p as Emitter, r as DefaultConfigDirectorClient, s as discardingTelemetryClient, t as INSTALLED_TEST_CLIENT_SLOT } from "./installedTestClient-CM98r0qu.mjs";
2
2
  //#region ../js-client-core/src/testing/InMemoryConnection.ts
3
3
  const TEST_ENVIRONMENT = {
4
4
  environmentId: "test-environment",
@@ -295,7 +295,7 @@ const installTestClient = (testClient) => {
295
295
  const createTestClient = (options) => {
296
296
  return createTestClient$1({
297
297
  sdkName: "react-native-sdk",
298
- sdkVersion: "1.9.1"
298
+ sdkVersion: "1.10.1"
299
299
  }, options);
300
300
  };
301
301
  //#endregion
@@ -1,4 +1,4 @@
1
- import { a as ConfigDirectorClientOptions, d as ConfigDirectorContext, f as ConfigDirectorLogMessageDecorator, g as ConnectionMode, h as ConfigValueType, i as ConfigDirectorClient, m as ConfigDirectorLoggingLevel, n as CachingProvider, o as OfflineValue, p as ConfigDirectorLogger, r as ClientHooks, s as OfflineValues, t as CachingOptions } from "./types-BBQmjjf5.mjs";
1
+ import { _ as ConnectionMode, a as ConfigDirectorClientOptions, d as ConfigDirectorContext, f as ConfigDirectorLogMessageDecorator, g as ConfigValueType, h as ConfigEvaluation, i as ConfigDirectorClient, m as ConfigDirectorLoggingLevel, n as CachingProvider, o as OfflineValue, p as ConfigDirectorLogger, r as ClientHooks, s as OfflineValues, t as CachingOptions, v as EvaluationReason } from "./types-DmbDeZqL.mjs";
2
2
  import { Component, PropsWithChildren } from "react";
3
3
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
4
4
 
@@ -151,4 +151,4 @@ declare class ConfigDirectorReactContextError extends Error {
151
151
  //#region src/logger.d.ts
152
152
  declare const createConsoleLogger: (level?: ConfigDirectorLoggingLevel) => DefaultConsoleLogger;
153
153
  //#endregion
154
- export { type CachingOptions, type CachingProvider, type ClientStatus, type ConfigDirectorClient, type ConfigDirectorClientOptions, type ConfigDirectorContext, type ConfigDirectorLogger, type ConfigDirectorLoggingLevel, ConfigDirectorProvider, type ConfigDirectorProviderClientProps, type ConfigDirectorProviderOptions, type ConfigDirectorProviderProps, ConfigDirectorReactContextError, type ConnectionMode, type NetInfoSubscribe, type OfflineValue, type OfflineValues, createClient, createConsoleLogger, useClient, useConfigValue, useContext };
154
+ export { type CachingOptions, type CachingProvider, type ClientStatus, type ConfigDirectorClient, type ConfigDirectorClientOptions, type ConfigDirectorContext, type ConfigDirectorLogger, type ConfigDirectorLoggingLevel, ConfigDirectorProvider, type ConfigDirectorProviderClientProps, type ConfigDirectorProviderOptions, type ConfigDirectorProviderProps, ConfigDirectorReactContextError, type ConfigEvaluation, type ConnectionMode, type EvaluationReason, type NetInfoSubscribe, type OfflineValue, type OfflineValues, createClient, createConsoleLogger, useClient, useConfigValue, useContext };
@@ -1,4 +1,4 @@
1
- import { a as DEFAULT_CACHING_SIZE_LIMIT_BYTES, c as createDefaultLogger, i as CACHED_VALUES_STORAGE_KEY_PREFIX, m as _defineProperty, n as readInstalledTestClient, o as fetchWithTimeout, r as DefaultConfigDirectorClient, u as isFetchErrorFatal } from "./installedTestClient-CNYgzQuj.mjs";
1
+ import { a as DEFAULT_CACHING_SIZE_LIMIT_BYTES, c as createDefaultLogger, i as CACHED_VALUES_STORAGE_KEY_PREFIX, m as _defineProperty, n as readInstalledTestClient, o as fetchWithTimeout, r as DefaultConfigDirectorClient, u as isFetchErrorFatal } from "./installedTestClient-CM98r0qu.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";
@@ -542,6 +542,41 @@ const findAsyncStorage = (logger) => importAsyncStorage().then((lookup) => {
542
542
  return lookup;
543
543
  });
544
544
  //#endregion
545
+ //#region src/cachingProvider.ts
546
+ const asyncStorageCachingProvider = (asyncStorage) => (clientSdkKeyDigest) => {
547
+ const storageKey = clientSdkKeyDigest.then((digest) => CACHED_VALUES_STORAGE_KEY_PREFIX + digest);
548
+ return {
549
+ read: async () => {
550
+ const lookup = await asyncStorage;
551
+ return lookup.outcome === "installed" ? lookup.storage.getItem(await storageKey) : void 0;
552
+ },
553
+ write: async (value) => {
554
+ const lookup = await asyncStorage;
555
+ if (lookup.outcome === "installed") await lookup.storage.setItem(await storageKey, value);
556
+ },
557
+ remove: async () => {
558
+ const lookup = await asyncStorage;
559
+ if (lookup.outcome === "installed") await lookup.storage.removeItem(await storageKey);
560
+ },
561
+ sizeLimit: () => DEFAULT_CACHING_SIZE_LIMIT_BYTES
562
+ };
563
+ };
564
+ //#endregion
565
+ //#region src/instanceIdStorage.ts
566
+ const asyncStorageInstanceIdStorage = (asyncStorage) => {
567
+ const installedAsyncStorage = async () => {
568
+ const lookup = await asyncStorage;
569
+ switch (lookup.outcome) {
570
+ case "installed": return lookup.storage;
571
+ case "not installed": throw new Error("@react-native-async-storage/async-storage is not installed");
572
+ }
573
+ };
574
+ return {
575
+ getItem: async (key) => (await installedAsyncStorage()).getItem(key),
576
+ setItem: async (key, value) => (await installedAsyncStorage()).setItem(key, value)
577
+ };
578
+ };
579
+ //#endregion
545
580
  //#region src/utf8Encoder.ts
546
581
  /**
547
582
  * Encodes a string to a UTF-8 byte sequence.
@@ -582,42 +617,6 @@ const encodeManually = (str) => {
582
617
  return new Uint8Array(bytes);
583
618
  };
584
619
  //#endregion
585
- //#region src/cachingProvider.ts
586
- const storageKeyFor = (clientSdkKey) => CACHED_VALUES_STORAGE_KEY_PREFIX + bytesToHex(sha256(encodeUtf8(clientSdkKey)));
587
- const asyncStorageCachingProvider = (asyncStorage) => (clientSdkKey) => {
588
- const storageKey = storageKeyFor(clientSdkKey);
589
- return {
590
- read: async () => {
591
- const lookup = await asyncStorage;
592
- return lookup.outcome === "installed" ? lookup.storage.getItem(storageKey) : void 0;
593
- },
594
- write: async (value) => {
595
- const lookup = await asyncStorage;
596
- if (lookup.outcome === "installed") await lookup.storage.setItem(storageKey, value);
597
- },
598
- remove: async () => {
599
- const lookup = await asyncStorage;
600
- if (lookup.outcome === "installed") await lookup.storage.removeItem(storageKey);
601
- },
602
- sizeLimit: () => DEFAULT_CACHING_SIZE_LIMIT_BYTES
603
- };
604
- };
605
- //#endregion
606
- //#region src/instanceIdStorage.ts
607
- const asyncStorageInstanceIdStorage = (asyncStorage) => {
608
- const installedAsyncStorage = async () => {
609
- const lookup = await asyncStorage;
610
- switch (lookup.outcome) {
611
- case "installed": return lookup.storage;
612
- case "not installed": throw new Error("@react-native-async-storage/async-storage is not installed");
613
- }
614
- };
615
- return {
616
- getItem: async (key) => (await installedAsyncStorage()).getItem(key),
617
- setItem: async (key, value) => (await installedAsyncStorage()).setItem(key, value)
618
- };
619
- };
620
- //#endregion
621
620
  //#region src/reactNativeStreamingFetch.ts
622
621
  const abortError = (message) => {
623
622
  const err = new Error(message);
@@ -754,6 +753,9 @@ var ReactNativeTelemetryClient = class {
754
753
  }
755
754
  };
756
755
  //#endregion
756
+ //#region src/sha256HexDigest.ts
757
+ const sha256HexDigest = (text) => Promise.resolve(bytesToHex(sha256(encodeUtf8(text))));
758
+ //#endregion
757
759
  //#region src/url.ts
758
760
  var MinimalUrl = class {
759
761
  constructor(input, base) {
@@ -775,7 +777,7 @@ const createReactNativeClient = (clientSdkKey, clientOptions) => {
775
777
  var _clientOptions$logger;
776
778
  const sdkIdentity = {
777
779
  sdkName: "react-native-sdk",
778
- sdkVersion: "1.9.1"
780
+ sdkVersion: "1.10.1"
779
781
  };
780
782
  const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
781
783
  const metadata = resolveMetadata(clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata, logger);
@@ -796,6 +798,7 @@ const createReactNativeClient = (clientSdkKey, clientOptions) => {
796
798
  fetch: reactNativeStreamingFetch,
797
799
  urlFactory,
798
800
  instanceIdStorage: asyncStorageInstanceIdStorage(asyncStorage),
801
+ sha256HexDigest,
799
802
  platformCachingProvider: asyncStorageCachingProvider(asyncStorage)
800
803
  });
801
804
  };
@@ -875,9 +875,12 @@ const parseJson = (jsonString, defaultValue, requestedType, valueId) => {
875
875
  };
876
876
  const isJsonTypeCompatible = (parsed, defaultValue) => {
877
877
  if (parsed === null) return false;
878
- const defaultType = typeof defaultValue;
879
- if (defaultType === "bigint") return typeof parsed === "number";
880
- return typeof parsed === defaultType;
878
+ return jsonShapeOf(parsed) === jsonShapeOf(defaultValue);
879
+ };
880
+ const jsonShapeOf = (value) => {
881
+ if (Array.isArray(value)) return "array";
882
+ const nativeType = typeof value;
883
+ return nativeType === "bigint" ? "number" : nativeType;
881
884
  };
882
885
  //#endregion
883
886
  //#region ../shared/src/logger.ts
@@ -1210,8 +1213,8 @@ var PollingTransport = class extends AbstractPollingTransport {
1210
1213
  this.pollingIntervalSeconds = (_options$pollingInter = options.pollingInterval) !== null && _options$pollingInter !== void 0 ? _options$pollingInter : 60;
1211
1214
  }
1212
1215
  async connect(context, timeout) {
1213
- const generation = ++this.connectionGeneration;
1214
- this.clearPollingInterval();
1216
+ this.endCurrentConnection();
1217
+ const generation = this.connectionGeneration;
1215
1218
  const clientSession = await this.options.clientSession;
1216
1219
  if (generation !== this.connectionGeneration) return this;
1217
1220
  try {
@@ -1226,9 +1229,14 @@ var PollingTransport = class extends AbstractPollingTransport {
1226
1229
  return this;
1227
1230
  }
1228
1231
  close() {
1229
- this.connectionGeneration++;
1232
+ this.endCurrentConnection();
1230
1233
  super.close();
1231
1234
  }
1235
+ endCurrentConnection() {
1236
+ this.connectionGeneration++;
1237
+ this.clearPollingInterval();
1238
+ this.lastUpdateTimestamp = void 0;
1239
+ }
1232
1240
  async fetchConfigs(context, timeout, clientSession) {
1233
1241
  if (this.fatalError) {
1234
1242
  this.logger.warn("[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.");
@@ -1331,11 +1339,12 @@ const utf8ByteLength = (text) => {
1331
1339
  };
1332
1340
  //#endregion
1333
1341
  //#region ../js-client-core/src/cached-values.ts
1334
- const ENTRY_FORMAT_VERSION = 1;
1342
+ const ENTRY_FORMAT_VERSION = 2;
1343
+ const FIRST_ENTRY_FORMAT_VERSION = 1;
1335
1344
  const DEFAULT_CONTEXT_KIND = "user";
1336
1345
  const CACHED_VALUES_STORAGE_KEY_PREFIX = "configdirector-sdk:cached-values:";
1337
1346
  const DEFAULT_CACHING_SIZE_LIMIT_BYTES = 256 * 1024;
1338
- const planCaching = (connectionPlan, caching, platformProvider) => {
1347
+ const planCaching = (connectionPlan, caching, sha256HexDigest, platformCachingProvider) => {
1339
1348
  var _caching$provider;
1340
1349
  if (connectionPlan.kind === "stay-offline") return {
1341
1350
  kind: "no cache",
@@ -1345,7 +1354,12 @@ const planCaching = (connectionPlan, caching, platformProvider) => {
1345
1354
  kind: "no cache",
1346
1355
  reason: "turned off"
1347
1356
  };
1348
- const provider = (_caching$provider = caching === null || caching === void 0 ? void 0 : caching.provider) !== null && _caching$provider !== void 0 ? _caching$provider : platformProvider === null || platformProvider === void 0 ? void 0 : platformProvider(connectionPlan.clientSdkKey);
1357
+ if (!sha256HexDigest) return {
1358
+ kind: "no cache",
1359
+ reason: "no SHA-256 available"
1360
+ };
1361
+ const clientSdkKeyDigest = sha256HexDigest(connectionPlan.clientSdkKey);
1362
+ const provider = (_caching$provider = caching === null || caching === void 0 ? void 0 : caching.provider) !== null && _caching$provider !== void 0 ? _caching$provider : platformCachingProvider === null || platformCachingProvider === void 0 ? void 0 : platformCachingProvider(clientSdkKeyDigest);
1349
1363
  if (!provider) return {
1350
1364
  kind: "no cache",
1351
1365
  reason: "no provider available"
@@ -1353,7 +1367,7 @@ const planCaching = (connectionPlan, caching, platformProvider) => {
1353
1367
  return {
1354
1368
  kind: "cache",
1355
1369
  provider,
1356
- clientSdkKey: connectionPlan.clientSdkKey
1370
+ clientSdkKeyDigest
1357
1371
  };
1358
1372
  };
1359
1373
  const contextTagFor = (context, instanceId) => {
@@ -1389,15 +1403,30 @@ const isConfigStateMap = (candidate) => {
1389
1403
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return false;
1390
1404
  return Object.values(candidate).every((config) => config && typeof config === "object" && typeof config.id === "string" && typeof config.key === "string");
1391
1405
  };
1392
- const isEntryFor = (clientSdkKey, candidate) => {
1406
+ const isEntryFor = (clientSdkKeyDigest, candidate) => {
1393
1407
  if (!candidate || typeof candidate !== "object") return false;
1394
1408
  const entry = candidate;
1395
- return entry.formatVersion === ENTRY_FORMAT_VERSION && entry.clientSdkKey === clientSdkKey && isContextTag(entry.contextTag) && typeof entry.environmentId === "string" && typeof entry.projectId === "string" && isConfigStateMap(entry.configs) && typeof entry.receivedAt === "number";
1409
+ return entry.formatVersion === ENTRY_FORMAT_VERSION && entry.clientSdkKeyDigest === clientSdkKeyDigest && isContextTag(entry.contextTag) && typeof entry.environmentId === "string" && typeof entry.projectId === "string" && isConfigStateMap(entry.configs) && typeof entry.receivedAt === "number";
1410
+ };
1411
+ const hasFirstFormatVersion = (candidate) => !!candidate && typeof candidate === "object" && candidate.formatVersion === FIRST_ENTRY_FORMAT_VERSION;
1412
+ const parseEntry = (raw, clientSdkKeyDigest) => {
1413
+ let candidate;
1414
+ try {
1415
+ candidate = JSON.parse(raw);
1416
+ } catch {
1417
+ return { outcome: "unreadable" };
1418
+ }
1419
+ if (isEntryFor(clientSdkKeyDigest, candidate)) return {
1420
+ outcome: "stored",
1421
+ cachedValues: candidate
1422
+ };
1423
+ if (hasFirstFormatVersion(candidate)) return { outcome: "first format" };
1424
+ return { outcome: "unreadable" };
1396
1425
  };
1397
1426
  var CachedValuesStore = class {
1398
- constructor(provider, clientSdkKey, logger) {
1427
+ constructor(provider, clientSdkKeyDigest, logger) {
1399
1428
  this.provider = provider;
1400
- this.clientSdkKey = clientSdkKey;
1429
+ this.clientSdkKeyDigest = clientSdkKeyDigest;
1401
1430
  this.logger = logger;
1402
1431
  _defineProperty(this, "warned", /* @__PURE__ */ new Set());
1403
1432
  _defineProperty(this, "pendingRead", void 0);
@@ -1422,23 +1451,16 @@ var CachedValuesStore = class {
1422
1451
  return { outcome: "failed" };
1423
1452
  }
1424
1453
  if (raw == null || raw === "") return { outcome: "nothing stored" };
1425
- const entry = this.parseEntry(raw);
1426
- if (!entry) {
1427
- this.warnOnce("unreadable", "The values kept on the device could not be read and were removed, starting without them");
1428
- await this.removeEntry();
1429
- return { outcome: "unreadable" };
1430
- }
1431
- return {
1432
- outcome: "stored",
1433
- cachedValues: entry
1434
- };
1435
- }
1436
- parseEntry(raw) {
1437
- try {
1438
- const candidate = JSON.parse(raw);
1439
- return isEntryFor(this.clientSdkKey, candidate) ? candidate : void 0;
1440
- } catch {
1441
- return;
1454
+ const entryParseOutcome = parseEntry(raw, await this.clientSdkKeyDigest);
1455
+ switch (entryParseOutcome.outcome) {
1456
+ case "stored": return entryParseOutcome;
1457
+ case "first format":
1458
+ await this.removeEntry();
1459
+ return entryParseOutcome;
1460
+ case "unreadable":
1461
+ this.warnOnce("unreadable", "The values kept on the device could not be read and were removed, starting without them");
1462
+ await this.removeEntry();
1463
+ return entryParseOutcome;
1442
1464
  }
1443
1465
  }
1444
1466
  async writeQueued() {
@@ -1457,7 +1479,7 @@ var CachedValuesStore = class {
1457
1479
  async writeEntry(cachedValues) {
1458
1480
  const entry = {
1459
1481
  formatVersion: ENTRY_FORMAT_VERSION,
1460
- clientSdkKey: this.clientSdkKey,
1482
+ clientSdkKeyDigest: await this.clientSdkKeyDigest,
1461
1483
  ...cachedValues
1462
1484
  };
1463
1485
  const serialized = JSON.stringify(entry);
@@ -1522,7 +1544,7 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
1522
1544
  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;
1523
1545
  const urlFactory = (_internalClientOption = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.urlFactory) !== null && _internalClientOption !== void 0 ? _internalClientOption : defaultUrlFactory;
1524
1546
  this.connectionPlan = planConnection(clientSdkKey, clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.connection, urlFactory);
1525
- this.cachedValuesStore = this.buildCachedValuesStore(planCaching(this.connectionPlan, clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.caching, internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.platformCachingProvider));
1547
+ this.cachedValuesStore = this.buildCachedValuesStore(planCaching(this.connectionPlan, clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.caching, internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.sha256HexDigest, internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.platformCachingProvider));
1526
1548
  this.telemetryClient = this.buildTelemetryClient(liveTelemetryClient, clientOptions);
1527
1549
  this.transport = (_internalClientOption2 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.transport) !== null && _internalClientOption2 !== void 0 ? _internalClientOption2 : this.buildTransport(sdkOptions, clientOptions, urlFactory, internalClientOptions);
1528
1550
  this.transport.on("configSetReceived", (configSet) => {
@@ -1566,7 +1588,7 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
1566
1588
  this.logger.debug(`[ConfigDirectorClient] No values are kept on the device: ${cachingPlan.reason}`);
1567
1589
  return;
1568
1590
  case "cache": {
1569
- const store = new CachedValuesStore(cachingPlan.provider, cachingPlan.clientSdkKey, this.logger);
1591
+ const store = new CachedValuesStore(cachingPlan.provider, cachingPlan.clientSdkKeyDigest, this.logger);
1570
1592
  store.read();
1571
1593
  return store;
1572
1594
  }
@@ -1582,6 +1604,7 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
1582
1604
  const read = await store.read();
1583
1605
  switch (read.outcome) {
1584
1606
  case "nothing stored":
1607
+ case "first format":
1585
1608
  case "unreadable":
1586
1609
  case "failed": return;
1587
1610
  case "stored": break;
@@ -1779,7 +1802,7 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
1779
1802
  var _this$configSet, _this$handlersMap$get;
1780
1803
  const configState = (_this$configSet = this.configSet) === null || _this$configSet === void 0 ? void 0 : _this$configSet.configs[configKey];
1781
1804
  (_this$handlersMap$get = this.handlersMap.get(configKey)) === null || _this$handlersMap$get === void 0 || _this$handlersMap$get.forEach((h) => {
1782
- const value = this.getValueFromConfigState(configKey, configState, h.defaultValue);
1805
+ const value = this.evaluationOf(configKey, configState, h.defaultValue).value;
1783
1806
  h.handler(value);
1784
1807
  });
1785
1808
  }
@@ -1804,12 +1827,15 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
1804
1827
  } else handlers.splice(0);
1805
1828
  }
1806
1829
  getValue(configKey, defaultValue) {
1830
+ return this.evaluate(configKey, defaultValue).value;
1831
+ }
1832
+ evaluate(configKey, defaultValue) {
1807
1833
  var _this$configSet2;
1808
1834
  this.validateDefaultValue(defaultValue);
1809
1835
  const configState = (_this$configSet2 = this.configSet) === null || _this$configSet2 === void 0 ? void 0 : _this$configSet2.configs[configKey];
1810
- return this.getValueFromConfigState(configKey, configState, defaultValue);
1836
+ return this.evaluationOf(configKey, configState, defaultValue);
1811
1837
  }
1812
- getValueFromConfigState(configKey, configState, defaultValue) {
1838
+ evaluationOf(configKey, configState, defaultValue) {
1813
1839
  var _this$currentContext2, _parseResult$parsedVa;
1814
1840
  if (!configState) {
1815
1841
  var _this$currentContext;
@@ -1824,14 +1850,15 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
1824
1850
  usedDefault: true,
1825
1851
  evaluationReason: reason
1826
1852
  });
1827
- this.dispatchEvaluationEvent({
1853
+ const evaluation = {
1828
1854
  key: configKey,
1829
1855
  value: defaultValue,
1830
1856
  isDefaultValue: true,
1831
1857
  reason,
1832
1858
  context: this.context
1833
- });
1834
- return defaultValue;
1859
+ };
1860
+ this.dispatchEvaluationEvent(evaluation);
1861
+ return evaluation;
1835
1862
  }
1836
1863
  const parseResult = parseConfigValue(configState, defaultValue);
1837
1864
  const reason = this.servedReason(parseResult.reason);
@@ -1846,16 +1873,17 @@ var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
1846
1873
  usedDefault: parseResult.usedDefault,
1847
1874
  evaluationReason: reason
1848
1875
  });
1849
- this.dispatchEvaluationEvent({
1876
+ const evaluation = {
1850
1877
  key: configKey,
1851
1878
  value: parseResult.parsedValue,
1852
1879
  valueId: (_parseResult$parsedVa = parseResult.parsedValueId) !== null && _parseResult$parsedVa !== void 0 ? _parseResult$parsedVa : void 0,
1853
1880
  isDefaultValue: parseResult.usedDefault,
1854
1881
  reason,
1855
1882
  context: this.context
1856
- });
1883
+ };
1884
+ this.dispatchEvaluationEvent(evaluation);
1857
1885
  this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);
1858
- return parseResult.parsedValue;
1886
+ return evaluation;
1859
1887
  }
1860
1888
  dispatchEvaluationEvent(event) {
1861
1889
  setTimeout(() => this.emit("configEvaluated", { evaluation: event }), 0);
@@ -23,13 +23,24 @@ interface ConfigDirectorLogger {
23
23
  interface ConfigDirectorLogMessageDecorator {
24
24
  decorateMessage(message: string): string;
25
25
  }
26
+ /**
27
+ * Why an evaluation returned the value it did. `found-match` returns a value from ConfigDirector,
28
+ * and `cached` a value kept on the device while a client SDK waits for live values. `value-missing`
29
+ * returns the in-code default value because the config has no value. Every other reason returns the
30
+ * in-code default value because of an error: the config does not exist (`config-state-missing`), no
31
+ * values have arrived yet (`client-not-ready`), or the value cannot be read as the type of the in-code
32
+ * default value.
33
+ */
26
34
  type EvaluationReason = "found-match" | "config-state-missing" | "client-not-ready" | "type-mismatch" | "value-missing" | "invalid-number" | "invalid-json" | "invalid-boolean" | "cached";
27
- type ConfigEvaluation = {
28
- key: string;
29
- value: ConfigValueType;
30
- valueId?: string;
31
- isDefaultValue: boolean;
32
- reason: EvaluationReason;
35
+ /**
36
+ * One evaluation of a config: the value it returned, why, and the context it was evaluated for.
37
+ */
38
+ type ConfigEvaluation<T extends ConfigValueType = ConfigValueType> = {
39
+ /** The config key that was evaluated. */key: string; /** The value returned, which is the in-code default value when {@link isDefaultValue} is `true`. */
40
+ value: T; /** The ID ConfigDirector assigned to the value returned, when ConfigDirector sent one with it. */
41
+ valueId?: string; /** Whether the evaluation returned the in-code default value. */
42
+ isDefaultValue: boolean; /** Why the evaluation returned this value. */
43
+ reason: EvaluationReason; /** The context the config was evaluated for, if any. */
33
44
  context?: ConfigDirectorContext;
34
45
  };
35
46
  /**
@@ -350,6 +361,16 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
350
361
  * is called before initialization is done.
351
362
  */
352
363
  getValue<T extends ConfigValueType>(configKey: string, defaultValue: T): T;
364
+ /**
365
+ * Evaluates a config as {@link getValue} does and returns the evaluation: the value, the reason it
366
+ * was served and its value ID. Like every read, it fires `configEvaluated` and is counted in
367
+ * telemetry.
368
+ *
369
+ * @returns The evaluation, whose `value` is what {@link getValue} returns
370
+ * @param configKey The config key to evaluate
371
+ * @param defaultValue The in-code default value, returned when no value from ConfigDirector can be served
372
+ */
373
+ evaluate<T extends ConfigValueType>(configKey: string, defaultValue: T): ConfigEvaluation<T>;
353
374
  /**
354
375
  * Watches for changes to a a config evaluation value. Whenever the config value changes, the
355
376
  * provided callback function will be called with the new value. Changes can happen due to updates
@@ -397,4 +418,4 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
397
418
  dispose(): void;
398
419
  }
399
420
  //#endregion
400
- export { ConfigDirectorClientOptions as a, NativeJsonValue as c, ConfigDirectorContext as d, ConfigDirectorLogMessageDecorator as f, ConnectionMode as g, ConfigValueType as h, ConfigDirectorClient as i, NativeValue as l, ConfigDirectorLoggingLevel as m, CachingProvider as n, OfflineValue as o, ConfigDirectorLogger as p, ClientHooks as r, OfflineValues as s, CachingOptions as t, NativeValues as u };
421
+ export { ConnectionMode as _, ConfigDirectorClientOptions as a, NativeJsonValue as c, ConfigDirectorContext as d, ConfigDirectorLogMessageDecorator as f, ConfigValueType as g, ConfigEvaluation as h, ConfigDirectorClient as i, NativeValue as l, ConfigDirectorLoggingLevel as m, CachingProvider as n, OfflineValue as o, ConfigDirectorLogger as p, ClientHooks as r, OfflineValues as s, CachingOptions as t, NativeValues as u, EvaluationReason as v };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configdirector/react-native-sdk",
3
- "version": "1.9.1",
3
+ "version": "1.10.1",
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",