@configdirector/react-native-sdk 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- 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 +24 -6
- package/dist/configdirector-react-native.mjs +65 -35
- package/dist/{installedTestClient-LWv5ji8J.mjs → installedTestClient-BluKT8Qj.mjs} +579 -94
- package/dist/{types-P1hoU2Ru.d.mts → types-BBQmjjf5.d.mts} +123 -39
- package/package.json +1 -1
|
@@ -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 {
|
|
@@ -789,10 +982,34 @@ const generateInstanceId = () => {
|
|
|
789
982
|
});
|
|
790
983
|
};
|
|
791
984
|
//#endregion
|
|
985
|
+
//#region ../js-client-core/src/browser-globals.ts
|
|
986
|
+
const read = (readValue) => {
|
|
987
|
+
try {
|
|
988
|
+
return readValue(globalThis);
|
|
989
|
+
} catch {
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
const readUserAgent = () => read((globals) => {
|
|
994
|
+
var _globals$navigator;
|
|
995
|
+
return (_globals$navigator = globals.navigator) === null || _globals$navigator === void 0 ? void 0 : _globals$navigator.userAgent;
|
|
996
|
+
});
|
|
997
|
+
const readHost = () => read((globals) => {
|
|
998
|
+
var _globals$location;
|
|
999
|
+
return (_globals$location = globals.location) === null || _globals$location === void 0 ? void 0 : _globals$location.host;
|
|
1000
|
+
});
|
|
1001
|
+
const localStorageIfAvailable = () => {
|
|
1002
|
+
try {
|
|
1003
|
+
var _localStorage;
|
|
1004
|
+
return (_localStorage = globalThis.localStorage) !== null && _localStorage !== void 0 ? _localStorage : void 0;
|
|
1005
|
+
} catch {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
//#endregion
|
|
792
1010
|
//#region ../js-client-core/src/client-session.ts
|
|
793
1011
|
const STORAGE_KEY = "configdirector-sdk:instance-id";
|
|
794
1012
|
const RECORD_VERSION = 1;
|
|
795
|
-
const MAX_AGE_MS = 360 * 60 * 1e3;
|
|
796
1013
|
const STORAGE_DEADLINE_MS = 1e3;
|
|
797
1014
|
const CHECKSUM_SALT = "configdirector.instanceId.v1";
|
|
798
1015
|
const UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
@@ -809,14 +1026,6 @@ const checksumFor = (id, createdAt) => {
|
|
|
809
1026
|
const low = fnv1a(payload, 2166136261);
|
|
810
1027
|
return fnv1a(`${low}|${payload}`, 16777619).toString(16).padStart(8, "0") + low.toString(16).padStart(8, "0");
|
|
811
1028
|
};
|
|
812
|
-
const localStorageIfAvailable = () => {
|
|
813
|
-
try {
|
|
814
|
-
var _localStorage;
|
|
815
|
-
return (_localStorage = globalThis.localStorage) !== null && _localStorage !== void 0 ? _localStorage : void 0;
|
|
816
|
-
} catch {
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
1029
|
const isUsableRecord = (record) => {
|
|
821
1030
|
if (!record || typeof record !== "object") return false;
|
|
822
1031
|
const { v, id, createdAt, checksum } = record;
|
|
@@ -845,11 +1054,6 @@ const readStoredInstanceId = async (storage, logger) => {
|
|
|
845
1054
|
logger.debug("[ConfigDirectorClient] The stored instance id failed validation, generating a new one");
|
|
846
1055
|
return { outcome: "nothing usable" };
|
|
847
1056
|
}
|
|
848
|
-
const age = Date.now() - record.createdAt;
|
|
849
|
-
if (age < 0 || age >= MAX_AGE_MS) {
|
|
850
|
-
logger.debug("[ConfigDirectorClient] The stored instance id has expired, generating a new one");
|
|
851
|
-
return { outcome: "nothing usable" };
|
|
852
|
-
}
|
|
853
1057
|
return {
|
|
854
1058
|
outcome: "stored",
|
|
855
1059
|
instanceId: record.id
|
|
@@ -1069,23 +1273,6 @@ var PollingTransport = class extends AbstractPollingTransport {
|
|
|
1069
1273
|
}
|
|
1070
1274
|
};
|
|
1071
1275
|
//#endregion
|
|
1072
|
-
//#region ../js-client-core/src/browser-globals.ts
|
|
1073
|
-
const read = (readValue) => {
|
|
1074
|
-
try {
|
|
1075
|
-
return readValue(globalThis);
|
|
1076
|
-
} catch {
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
};
|
|
1080
|
-
const readUserAgent = () => read((globals) => {
|
|
1081
|
-
var _globals$navigator;
|
|
1082
|
-
return (_globals$navigator = globals.navigator) === null || _globals$navigator === void 0 ? void 0 : _globals$navigator.userAgent;
|
|
1083
|
-
});
|
|
1084
|
-
const readHost = () => read((globals) => {
|
|
1085
|
-
var _globals$location;
|
|
1086
|
-
return (_globals$location = globals.location) === null || _globals$location === void 0 ? void 0 : _globals$location.host;
|
|
1087
|
-
});
|
|
1088
|
-
//#endregion
|
|
1089
1276
|
//#region ../shared/src/context-kind.ts
|
|
1090
1277
|
const CONTEXT_KIND_MAXIMUM_LENGTH = 64;
|
|
1091
1278
|
const KIND_SAMPLE_MAXIMUM_CHARACTERS = 128;
|
|
@@ -1126,11 +1313,190 @@ const admitContext = (context) => {
|
|
|
1126
1313
|
};
|
|
1127
1314
|
};
|
|
1128
1315
|
//#endregion
|
|
1316
|
+
//#region ../shared/src/utf8-byte-length.ts
|
|
1317
|
+
const isHighSurrogate = (codeUnit) => codeUnit >= 55296 && codeUnit <= 56319;
|
|
1318
|
+
const isLowSurrogate = (codeUnit) => codeUnit >= 56320 && codeUnit <= 57343;
|
|
1319
|
+
const utf8ByteLength = (text) => {
|
|
1320
|
+
let bytes = 0;
|
|
1321
|
+
for (let i = 0; i < text.length; i++) {
|
|
1322
|
+
const codeUnit = text.charCodeAt(i);
|
|
1323
|
+
if (codeUnit <= 127) bytes += 1;
|
|
1324
|
+
else if (codeUnit <= 2047) bytes += 2;
|
|
1325
|
+
else if (isHighSurrogate(codeUnit) && i + 1 < text.length && isLowSurrogate(text.charCodeAt(i + 1))) {
|
|
1326
|
+
bytes += 4;
|
|
1327
|
+
i++;
|
|
1328
|
+
} else bytes += 3;
|
|
1329
|
+
}
|
|
1330
|
+
return bytes;
|
|
1331
|
+
};
|
|
1332
|
+
//#endregion
|
|
1333
|
+
//#region ../js-client-core/src/cached-values.ts
|
|
1334
|
+
const ENTRY_FORMAT_VERSION = 1;
|
|
1335
|
+
const DEFAULT_CONTEXT_KIND = "user";
|
|
1336
|
+
const CACHED_VALUES_STORAGE_KEY_PREFIX = "configdirector-sdk:cached-values:";
|
|
1337
|
+
const DEFAULT_CACHING_SIZE_LIMIT_BYTES = 256 * 1024;
|
|
1338
|
+
const planCaching = (connectionPlan, caching, platformProvider) => {
|
|
1339
|
+
var _caching$provider;
|
|
1340
|
+
if (connectionPlan.kind === "stay-offline") return {
|
|
1341
|
+
kind: "no cache",
|
|
1342
|
+
reason: "offline mode"
|
|
1343
|
+
};
|
|
1344
|
+
if ((caching === null || caching === void 0 ? void 0 : caching.enabled) === false) return {
|
|
1345
|
+
kind: "no cache",
|
|
1346
|
+
reason: "turned off"
|
|
1347
|
+
};
|
|
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);
|
|
1349
|
+
if (!provider) return {
|
|
1350
|
+
kind: "no cache",
|
|
1351
|
+
reason: "no provider available"
|
|
1352
|
+
};
|
|
1353
|
+
return {
|
|
1354
|
+
kind: "cache",
|
|
1355
|
+
provider,
|
|
1356
|
+
clientSdkKey: connectionPlan.clientSdkKey
|
|
1357
|
+
};
|
|
1358
|
+
};
|
|
1359
|
+
const contextTagFor = (context, instanceId) => {
|
|
1360
|
+
var _context$kind;
|
|
1361
|
+
const contextKind = (_context$kind = context === null || context === void 0 ? void 0 : context.kind) !== null && _context$kind !== void 0 ? _context$kind : DEFAULT_CONTEXT_KIND;
|
|
1362
|
+
if ((context === null || context === void 0 ? void 0 : context.id) != null) return {
|
|
1363
|
+
contextKind,
|
|
1364
|
+
contextId: context.id
|
|
1365
|
+
};
|
|
1366
|
+
return {
|
|
1367
|
+
contextKind,
|
|
1368
|
+
anonymousInstanceId: instanceId
|
|
1369
|
+
};
|
|
1370
|
+
};
|
|
1371
|
+
const sameContextTag = (stored, launch) => {
|
|
1372
|
+
if (stored.contextKind !== launch.contextKind) return false;
|
|
1373
|
+
if ("contextId" in stored && "contextId" in launch) return stored.contextId === launch.contextId;
|
|
1374
|
+
if ("anonymousInstanceId" in stored && "anonymousInstanceId" in launch) return stored.anonymousInstanceId === launch.anonymousInstanceId;
|
|
1375
|
+
return false;
|
|
1376
|
+
};
|
|
1377
|
+
const decideCachedValuesAdoption = (cachedValues, launchContextTag, hasReceivedLiveValues) => {
|
|
1378
|
+
if (hasReceivedLiveValues) return "live values already arrived";
|
|
1379
|
+
if (!sameContextTag(cachedValues.contextTag, launchContextTag)) return "context mismatch";
|
|
1380
|
+
return "adopt";
|
|
1381
|
+
};
|
|
1382
|
+
const isContextTag = (candidate) => {
|
|
1383
|
+
if (!candidate || typeof candidate !== "object") return false;
|
|
1384
|
+
const { contextKind, contextId, anonymousInstanceId } = candidate;
|
|
1385
|
+
if (typeof contextKind !== "string") return false;
|
|
1386
|
+
return typeof contextId === "string" || typeof anonymousInstanceId === "string";
|
|
1387
|
+
};
|
|
1388
|
+
const isConfigStateMap = (candidate) => {
|
|
1389
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return false;
|
|
1390
|
+
return Object.values(candidate).every((config) => config && typeof config === "object" && typeof config.id === "string" && typeof config.key === "string");
|
|
1391
|
+
};
|
|
1392
|
+
const isEntryFor = (clientSdkKey, candidate) => {
|
|
1393
|
+
if (!candidate || typeof candidate !== "object") return false;
|
|
1394
|
+
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";
|
|
1396
|
+
};
|
|
1397
|
+
var CachedValuesStore = class {
|
|
1398
|
+
constructor(provider, clientSdkKey, logger) {
|
|
1399
|
+
this.provider = provider;
|
|
1400
|
+
this.clientSdkKey = clientSdkKey;
|
|
1401
|
+
this.logger = logger;
|
|
1402
|
+
_defineProperty(this, "warned", /* @__PURE__ */ new Set());
|
|
1403
|
+
_defineProperty(this, "pendingRead", void 0);
|
|
1404
|
+
_defineProperty(this, "queuedCachedValues", void 0);
|
|
1405
|
+
_defineProperty(this, "writing", false);
|
|
1406
|
+
}
|
|
1407
|
+
read() {
|
|
1408
|
+
var _this$pendingRead;
|
|
1409
|
+
(_this$pendingRead = this.pendingRead) !== null && _this$pendingRead !== void 0 || (this.pendingRead = this.readEntry());
|
|
1410
|
+
return this.pendingRead;
|
|
1411
|
+
}
|
|
1412
|
+
replace(cachedValues) {
|
|
1413
|
+
this.queuedCachedValues = cachedValues;
|
|
1414
|
+
this.writeQueued();
|
|
1415
|
+
}
|
|
1416
|
+
async readEntry() {
|
|
1417
|
+
let raw;
|
|
1418
|
+
try {
|
|
1419
|
+
raw = await this.provider.read();
|
|
1420
|
+
} catch (error) {
|
|
1421
|
+
this.warnOnce("read failed", "Could not read the values kept on the device, starting without them:", error);
|
|
1422
|
+
return { outcome: "failed" };
|
|
1423
|
+
}
|
|
1424
|
+
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;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
async writeQueued() {
|
|
1445
|
+
if (this.writing) return;
|
|
1446
|
+
this.writing = true;
|
|
1447
|
+
try {
|
|
1448
|
+
while (this.queuedCachedValues) {
|
|
1449
|
+
const cachedValues = this.queuedCachedValues;
|
|
1450
|
+
this.queuedCachedValues = void 0;
|
|
1451
|
+
await this.writeEntry(cachedValues);
|
|
1452
|
+
}
|
|
1453
|
+
} finally {
|
|
1454
|
+
this.writing = false;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
async writeEntry(cachedValues) {
|
|
1458
|
+
const entry = {
|
|
1459
|
+
formatVersion: ENTRY_FORMAT_VERSION,
|
|
1460
|
+
clientSdkKey: this.clientSdkKey,
|
|
1461
|
+
...cachedValues
|
|
1462
|
+
};
|
|
1463
|
+
const serialized = JSON.stringify(entry);
|
|
1464
|
+
const size = utf8ByteLength(serialized);
|
|
1465
|
+
const limit = this.provider.sizeLimit();
|
|
1466
|
+
if (size > limit) {
|
|
1467
|
+
this.warnOnce("over the size limit", `The values received (${size} bytes) exceed the caching provider's limit of ${limit} bytes, so no values are kept on the device`);
|
|
1468
|
+
await this.removeEntry();
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
try {
|
|
1472
|
+
await this.provider.write(serialized);
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
this.warnOnce("write failed", "Could not keep the values on the device:", error);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
async removeEntry() {
|
|
1478
|
+
try {
|
|
1479
|
+
await this.provider.remove();
|
|
1480
|
+
} catch (error) {
|
|
1481
|
+
this.warnOnce("remove failed", "Could not remove the values kept on the device:", error);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
warnOnce(warning, message, ...details) {
|
|
1485
|
+
if (this.warned.has(warning)) return;
|
|
1486
|
+
this.warned.add(warning);
|
|
1487
|
+
this.logger.warn(`[ConfigDirectorClient] ${message}`, ...details);
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
//#endregion
|
|
1129
1491
|
//#region ../js-client-core/src/DefaultConfigDirectorClient.ts
|
|
1130
1492
|
const MAX_EXPONENTIAL_DELAY = 9;
|
|
1131
|
-
var DefaultConfigDirectorClient = class {
|
|
1132
|
-
|
|
1133
|
-
|
|
1493
|
+
var DefaultConfigDirectorClient = class DefaultConfigDirectorClient {
|
|
1494
|
+
static from(liveTelemetryClient, clientSdkKeyOrOptions, sdkOptions, clientOptions, internalClientOptions) {
|
|
1495
|
+
if (typeof clientSdkKeyOrOptions === "object") return new DefaultConfigDirectorClient(liveTelemetryClient, void 0, sdkOptions, clientSdkKeyOrOptions, internalClientOptions);
|
|
1496
|
+
return new DefaultConfigDirectorClient(liveTelemetryClient, clientSdkKeyOrOptions, sdkOptions, clientOptions, internalClientOptions);
|
|
1497
|
+
}
|
|
1498
|
+
constructor(liveTelemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
|
|
1499
|
+
var _clientOptions$logger, _clientOptions$connec, _clientOptions$connec2, _internalClientOption, _internalClientOption2, _clientOptions$hooks, _clientOptions$hooks2, _clientOptions$hooks3, _clientOptions$hooks4, _clientOptions$hooks5, _clientOptions$hooks6;
|
|
1134
1500
|
_defineProperty(this, "logger", void 0);
|
|
1135
1501
|
_defineProperty(this, "telemetryClient", void 0);
|
|
1136
1502
|
_defineProperty(this, "configSet", void 0);
|
|
@@ -1145,20 +1511,24 @@ var DefaultConfigDirectorClient = class {
|
|
|
1145
1511
|
_defineProperty(this, "readyPromise", void 0);
|
|
1146
1512
|
_defineProperty(this, "readyResolve", void 0);
|
|
1147
1513
|
_defineProperty(this, "currentContext", void 0);
|
|
1148
|
-
_defineProperty(this, "
|
|
1514
|
+
_defineProperty(this, "submittedContext", void 0);
|
|
1515
|
+
_defineProperty(this, "connectionPlan", void 0);
|
|
1149
1516
|
_defineProperty(this, "clientSession", void 0);
|
|
1517
|
+
_defineProperty(this, "cachedValuesStore", void 0);
|
|
1518
|
+
_defineProperty(this, "servingCachedValues", false);
|
|
1519
|
+
_defineProperty(this, "cachedValuesAdoptionStarted", false);
|
|
1150
1520
|
this.logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createDefaultLogger();
|
|
1151
1521
|
this.clientSession = resolveClientSession(this.logger, internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.instanceIdStorage);
|
|
1152
1522
|
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
1523
|
const urlFactory = (_internalClientOption = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.urlFactory) !== null && _internalClientOption !== void 0 ? _internalClientOption : defaultUrlFactory;
|
|
1155
|
-
|
|
1156
|
-
this.
|
|
1157
|
-
this.telemetryClient =
|
|
1158
|
-
this.transport = (_internalClientOption2 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.transport) !== null && _internalClientOption2 !== void 0 ? _internalClientOption2 : this.buildTransport(
|
|
1524
|
+
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));
|
|
1526
|
+
this.telemetryClient = this.buildTelemetryClient(liveTelemetryClient, clientOptions);
|
|
1527
|
+
this.transport = (_internalClientOption2 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.transport) !== null && _internalClientOption2 !== void 0 ? _internalClientOption2 : this.buildTransport(sdkOptions, clientOptions, urlFactory, internalClientOptions);
|
|
1159
1528
|
this.transport.on("configSetReceived", (configSet) => {
|
|
1160
1529
|
var _this$readyResolve;
|
|
1161
1530
|
this.hasReceivedConfigSet = true;
|
|
1531
|
+
this.servingCachedValues = false;
|
|
1162
1532
|
(_this$readyResolve = this.readyResolve) === null || _this$readyResolve === void 0 || _this$readyResolve.call(this);
|
|
1163
1533
|
const removedKeys = this.findKeysRemovedBy(configSet);
|
|
1164
1534
|
if (!this.configSet || configSet.kind == "full") this.configSet = configSet;
|
|
@@ -1166,6 +1536,7 @@ var DefaultConfigDirectorClient = class {
|
|
|
1166
1536
|
...this.configSet.configs,
|
|
1167
1537
|
...configSet.configs
|
|
1168
1538
|
};
|
|
1539
|
+
this.keepValuesOnTheDevice(this.configSet);
|
|
1169
1540
|
const keys = Object.keys(configSet.configs);
|
|
1170
1541
|
this.emit("configsUpdated", {
|
|
1171
1542
|
keys,
|
|
@@ -1187,14 +1558,88 @@ var DefaultConfigDirectorClient = class {
|
|
|
1187
1558
|
this.registerHandler("configsUpdated", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks3 = clientOptions.hooks) === null || _clientOptions$hooks3 === void 0 ? void 0 : _clientOptions$hooks3.configsUpdated);
|
|
1188
1559
|
this.registerHandler("contextUpdated", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks4 = clientOptions.hooks) === null || _clientOptions$hooks4 === void 0 ? void 0 : _clientOptions$hooks4.contextUpdated);
|
|
1189
1560
|
this.registerHandler("connectionError", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks5 = clientOptions.hooks) === null || _clientOptions$hooks5 === void 0 ? void 0 : _clientOptions$hooks5.connectionError);
|
|
1561
|
+
this.registerHandler("cachedValuesLoaded", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks6 = clientOptions.hooks) === null || _clientOptions$hooks6 === void 0 ? void 0 : _clientOptions$hooks6.cachedValuesLoaded);
|
|
1562
|
+
}
|
|
1563
|
+
buildCachedValuesStore(cachingPlan) {
|
|
1564
|
+
switch (cachingPlan.kind) {
|
|
1565
|
+
case "no cache":
|
|
1566
|
+
this.logger.debug(`[ConfigDirectorClient] No values are kept on the device: ${cachingPlan.reason}`);
|
|
1567
|
+
return;
|
|
1568
|
+
case "cache": {
|
|
1569
|
+
const store = new CachedValuesStore(cachingPlan.provider, cachingPlan.clientSdkKey, this.logger);
|
|
1570
|
+
store.read();
|
|
1571
|
+
return store;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
startCachedValuesAdoption() {
|
|
1576
|
+
const store = this.cachedValuesStore;
|
|
1577
|
+
if (!store || this.cachedValuesAdoptionStarted) return;
|
|
1578
|
+
this.cachedValuesAdoptionStarted = true;
|
|
1579
|
+
this.adoptCachedValues(store);
|
|
1580
|
+
}
|
|
1581
|
+
async adoptCachedValues(store) {
|
|
1582
|
+
const read = await store.read();
|
|
1583
|
+
switch (read.outcome) {
|
|
1584
|
+
case "nothing stored":
|
|
1585
|
+
case "unreadable":
|
|
1586
|
+
case "failed": return;
|
|
1587
|
+
case "stored": break;
|
|
1588
|
+
}
|
|
1589
|
+
const { instanceId } = await this.clientSession;
|
|
1590
|
+
const launchContextTag = contextTagFor(this.submittedContext, instanceId);
|
|
1591
|
+
const adoption = decideCachedValuesAdoption(read.cachedValues, launchContextTag, this.hasReceivedConfigSet);
|
|
1592
|
+
switch (adoption) {
|
|
1593
|
+
case "adopt":
|
|
1594
|
+
this.serveCachedValues(read.cachedValues);
|
|
1595
|
+
return;
|
|
1596
|
+
case "live values already arrived":
|
|
1597
|
+
case "context mismatch":
|
|
1598
|
+
this.logger.debug(`[ConfigDirectorClient] The values kept on the device were not adopted: ${adoption}`);
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
serveCachedValues(cachedValues) {
|
|
1603
|
+
const { environmentId, projectId, configs } = cachedValues;
|
|
1604
|
+
this.configSet = {
|
|
1605
|
+
environmentId,
|
|
1606
|
+
projectId,
|
|
1607
|
+
configs,
|
|
1608
|
+
kind: "full"
|
|
1609
|
+
};
|
|
1610
|
+
this.servingCachedValues = true;
|
|
1611
|
+
const keys = Object.keys(configs);
|
|
1612
|
+
this.emit("cachedValuesLoaded", {});
|
|
1613
|
+
this.emit("configsUpdated", {
|
|
1614
|
+
keys,
|
|
1615
|
+
removedKeys: []
|
|
1616
|
+
});
|
|
1617
|
+
this.updateWatchers(keys);
|
|
1618
|
+
this.logger.debug("[ConfigDirectorClient] Serving the values kept on the device until live values arrive:", { keys });
|
|
1619
|
+
}
|
|
1620
|
+
keepValuesOnTheDevice(configSet) {
|
|
1621
|
+
const store = this.cachedValuesStore;
|
|
1622
|
+
if (!store) return;
|
|
1623
|
+
const context = this.submittedContext;
|
|
1624
|
+
const receivedAt = Date.now();
|
|
1625
|
+
this.clientSession.then(({ instanceId }) => store.replace({
|
|
1626
|
+
contextTag: contextTagFor(context, instanceId),
|
|
1627
|
+
environmentId: configSet.environmentId,
|
|
1628
|
+
projectId: configSet.projectId,
|
|
1629
|
+
configs: configSet.configs,
|
|
1630
|
+
receivedAt
|
|
1631
|
+
}));
|
|
1632
|
+
}
|
|
1633
|
+
servedReason(reason) {
|
|
1634
|
+
return this.servingCachedValues && reason === "found-match" ? "cached" : reason;
|
|
1190
1635
|
}
|
|
1191
1636
|
registerHandler(event, handler) {
|
|
1192
1637
|
if (!handler) return;
|
|
1193
1638
|
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
1194
1639
|
for (const h of handlers) if (typeof h === "function") this.on(event, h);
|
|
1195
1640
|
}
|
|
1196
|
-
|
|
1197
|
-
if (
|
|
1641
|
+
pollingIntervalFor(mode, configuredSeconds) {
|
|
1642
|
+
if (mode !== "polling") return;
|
|
1198
1643
|
const resolved = resolvePollingInterval(configuredSeconds, {
|
|
1199
1644
|
defaultSeconds: 60,
|
|
1200
1645
|
minimumSeconds: 30
|
|
@@ -1202,32 +1647,49 @@ var DefaultConfigDirectorClient = class {
|
|
|
1202
1647
|
if (resolved.warning) this.logger.warn(`[ConfigDirectorClient] ${resolved.warning}`);
|
|
1203
1648
|
return resolved.seconds;
|
|
1204
1649
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
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
|
-
});
|
|
1650
|
+
buildTelemetryClient(liveTelemetryClient, clientOptions) {
|
|
1651
|
+
const plan = this.connectionPlan;
|
|
1652
|
+
switch (plan.kind) {
|
|
1653
|
+
case "stay-offline": return discardingTelemetryClient;
|
|
1654
|
+
case "connect": return liveTelemetryClient({
|
|
1655
|
+
clientSdkKey: plan.clientSdkKey,
|
|
1656
|
+
baseUrl: plan.baseUrl,
|
|
1657
|
+
logger: this.logger,
|
|
1658
|
+
metadata: clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1226
1661
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1662
|
+
buildTransport(sdkOptions, clientOptions, urlFactory, internalClientOptions) {
|
|
1663
|
+
const plan = this.connectionPlan;
|
|
1664
|
+
switch (plan.kind) {
|
|
1665
|
+
case "stay-offline": return new OfflineTransport(plan.source);
|
|
1666
|
+
case "connect":
|
|
1667
|
+
var _internalClientOption3, _clientOptions$connec3;
|
|
1668
|
+
return new (this.getTransportConstructor(plan.mode))({
|
|
1669
|
+
clientSession: this.clientSession,
|
|
1670
|
+
clientSdkKey: plan.clientSdkKey,
|
|
1671
|
+
baseUrl: plan.baseUrl,
|
|
1672
|
+
resolveUrl: urlFactory,
|
|
1673
|
+
metaContext: {
|
|
1674
|
+
...clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.metadata,
|
|
1675
|
+
sdkName: sdkOptions.sdkName,
|
|
1676
|
+
sdkVersion: sdkOptions.sdkVersion,
|
|
1677
|
+
userAgent: readUserAgent(),
|
|
1678
|
+
host: readHost()
|
|
1679
|
+
},
|
|
1680
|
+
logger: this.logger,
|
|
1681
|
+
fetch: internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.fetch,
|
|
1682
|
+
connectionRetryDelay: (_internalClientOption3 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.connectionRetryDelay) !== null && _internalClientOption3 !== void 0 ? _internalClientOption3 : ((attempt) => {
|
|
1683
|
+
return Math.pow(2, Math.min(attempt, MAX_EXPONENTIAL_DELAY)) * 1e3;
|
|
1684
|
+
}),
|
|
1685
|
+
pollingInterval: this.pollingIntervalFor(plan.mode, clientOptions === null || clientOptions === void 0 || (_clientOptions$connec3 = clientOptions.connection) === null || _clientOptions$connec3 === void 0 ? void 0 : _clientOptions$connec3.pollingInterval)
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
getTransportConstructor(mode) {
|
|
1690
|
+
switch (mode) {
|
|
1229
1691
|
case "polling": return PollingTransport;
|
|
1230
|
-
|
|
1692
|
+
case "streaming": return StreamingTransport;
|
|
1231
1693
|
}
|
|
1232
1694
|
}
|
|
1233
1695
|
async initialize(context) {
|
|
@@ -1235,17 +1697,28 @@ var DefaultConfigDirectorClient = class {
|
|
|
1235
1697
|
await this.submitContext(context, "initialization");
|
|
1236
1698
|
}
|
|
1237
1699
|
async updateContext(context) {
|
|
1238
|
-
|
|
1700
|
+
switch (this.connectionPlan.kind) {
|
|
1701
|
+
case "connect":
|
|
1702
|
+
await this.submitContext(context, "context update");
|
|
1703
|
+
return;
|
|
1704
|
+
case "stay-offline":
|
|
1705
|
+
this.storeContext(this.admittedContextFor(context, "context update"));
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
storeContext(context) {
|
|
1710
|
+
this.currentContext = context;
|
|
1711
|
+
this.emit("contextUpdated", { context });
|
|
1239
1712
|
}
|
|
1240
1713
|
async submitContext(context, submission) {
|
|
1241
|
-
await this.connectToTransport(this.
|
|
1714
|
+
await this.connectToTransport(this.admittedContextFor(context, submission), submission);
|
|
1242
1715
|
}
|
|
1243
|
-
|
|
1716
|
+
admittedContextFor(suppliedContext, submission) {
|
|
1244
1717
|
const admission = admitContext(suppliedContext);
|
|
1245
1718
|
switch (admission.outcome) {
|
|
1246
1719
|
case "admitted": return admission.context;
|
|
1247
1720
|
case "ignored":
|
|
1248
|
-
this.logger.error(`[ConfigDirectorClient] Ignored the context supplied for ${submission} because its kind '${admission.kindSample}' is invalid;
|
|
1721
|
+
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
1722
|
this.telemetryClient.recordInvalidContext(admission.kindSample);
|
|
1250
1723
|
return;
|
|
1251
1724
|
}
|
|
@@ -1254,6 +1727,8 @@ var DefaultConfigDirectorClient = class {
|
|
|
1254
1727
|
return this.currentContext;
|
|
1255
1728
|
}
|
|
1256
1729
|
async connectToTransport(context, caller) {
|
|
1730
|
+
this.submittedContext = context;
|
|
1731
|
+
if (caller === "initialization") this.startCachedValuesAdoption();
|
|
1257
1732
|
try {
|
|
1258
1733
|
this.ready = false;
|
|
1259
1734
|
this.readyPromise = new Promise((resolve) => {
|
|
@@ -1277,11 +1752,21 @@ var DefaultConfigDirectorClient = class {
|
|
|
1277
1752
|
if (remainingTimeout > 0) await Promise.race([this.readyPromise, new Promise((resolve) => {
|
|
1278
1753
|
this.timeoutTimer = setTimeout(() => resolve(), remainingTimeout);
|
|
1279
1754
|
})]).finally(() => clearTimeout(this.timeoutTimer));
|
|
1280
|
-
if (!this.ready) this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${caller} after ${this.timeout}ms.
|
|
1755
|
+
if (!this.ready) this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${caller} after ${this.timeout}ms. ${this.timeoutWarningDetails()}`);
|
|
1281
1756
|
} catch (error) {
|
|
1757
|
+
if (error instanceof ConfigDirectorInitializationError) {
|
|
1758
|
+
this.initializing = false;
|
|
1759
|
+
throw error;
|
|
1760
|
+
}
|
|
1282
1761
|
this.logger.error(`[ConfigDirectorClient] An error occurred during ${caller}: `, error);
|
|
1283
1762
|
}
|
|
1284
1763
|
}
|
|
1764
|
+
timeoutWarningDetails() {
|
|
1765
|
+
switch (this.connectionPlan.kind) {
|
|
1766
|
+
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.";
|
|
1767
|
+
case "stay-offline": return "The offline values were not delivered, so configs return the default value.";
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1285
1770
|
findKeysRemovedBy(configSet) {
|
|
1286
1771
|
if (!this.configSet || configSet.kind != "full") return [];
|
|
1287
1772
|
return Object.keys(this.configSet.configs).filter((key) => !Object.prototype.hasOwnProperty.call(configSet.configs, key));
|
|
@@ -1348,6 +1833,7 @@ var DefaultConfigDirectorClient = class {
|
|
|
1348
1833
|
return defaultValue;
|
|
1349
1834
|
}
|
|
1350
1835
|
const parseResult = parseConfigValue(configState, defaultValue);
|
|
1836
|
+
const reason = this.servedReason(parseResult.reason);
|
|
1351
1837
|
this.telemetryClient.evaluatedConfig({
|
|
1352
1838
|
contextId: (_this$currentContext2 = this.currentContext) === null || _this$currentContext2 === void 0 ? void 0 : _this$currentContext2.id,
|
|
1353
1839
|
type: configState.type,
|
|
@@ -1357,14 +1843,14 @@ var DefaultConfigDirectorClient = class {
|
|
|
1357
1843
|
evaluatedValue: parseResult.parsedValue,
|
|
1358
1844
|
evaluatedValueId: parseResult.parsedValueId,
|
|
1359
1845
|
usedDefault: parseResult.usedDefault,
|
|
1360
|
-
evaluationReason:
|
|
1846
|
+
evaluationReason: reason
|
|
1361
1847
|
});
|
|
1362
1848
|
this.dispatchEvaluationEvent({
|
|
1363
1849
|
key: configKey,
|
|
1364
1850
|
value: parseResult.parsedValue,
|
|
1365
1851
|
valueId: (_parseResult$parsedVa = parseResult.parsedValueId) !== null && _parseResult$parsedVa !== void 0 ? _parseResult$parsedVa : void 0,
|
|
1366
1852
|
isDefaultValue: parseResult.usedDefault,
|
|
1367
|
-
reason
|
|
1853
|
+
reason,
|
|
1368
1854
|
context: this.context
|
|
1369
1855
|
});
|
|
1370
1856
|
this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);
|
|
@@ -1380,17 +1866,6 @@ var DefaultConfigDirectorClient = class {
|
|
|
1380
1866
|
this.logger.error(`[ConfigDirectorClient] Error executing event handlers for '${name}'`, error);
|
|
1381
1867
|
}
|
|
1382
1868
|
}
|
|
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
1869
|
validateDefaultValue(defaultValue) {
|
|
1395
1870
|
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
1871
|
if (typeof defaultValue === "function") throw new ConfigDirectorValidationError("Invalid default value. The default value for a config cannot be a function.");
|
|
@@ -1416,14 +1891,24 @@ var DefaultConfigDirectorClient = class {
|
|
|
1416
1891
|
this.handlersMap.clear();
|
|
1417
1892
|
}
|
|
1418
1893
|
pauseNetwork() {
|
|
1419
|
-
this.
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1894
|
+
switch (this.connectionPlan.kind) {
|
|
1895
|
+
case "stay-offline": return;
|
|
1896
|
+
case "connect":
|
|
1897
|
+
this.logger.debug("[ConfigDirectorClient] pauseNetwork() called, pausing transport connection");
|
|
1898
|
+
clearTimeout(this.timeoutTimer);
|
|
1899
|
+
this.timeoutTimer = void 0;
|
|
1900
|
+
this.transport.close();
|
|
1901
|
+
this.ready = false;
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1424
1904
|
}
|
|
1425
1905
|
async resumeNetwork() {
|
|
1426
|
-
|
|
1906
|
+
switch (this.connectionPlan.kind) {
|
|
1907
|
+
case "stay-offline": return;
|
|
1908
|
+
case "connect":
|
|
1909
|
+
await this.connectToTransport(this.currentContext, "network resume");
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1427
1912
|
}
|
|
1428
1913
|
async close() {
|
|
1429
1914
|
var _this$readyResolve2;
|
|
@@ -1445,4 +1930,4 @@ var DefaultConfigDirectorClient = class {
|
|
|
1445
1930
|
const INSTALLED_TEST_CLIENT_SLOT = Symbol.for("@configdirector/installed-test-client");
|
|
1446
1931
|
const readInstalledTestClient = () => globalThis[INSTALLED_TEST_CLIENT_SLOT];
|
|
1447
1932
|
//#endregion
|
|
1448
|
-
export {
|
|
1933
|
+
export { DEFAULT_CACHING_SIZE_LIMIT_BYTES as a, createDefaultLogger as c, ConfigDirectorConnectionError as d, ConfigDirectorValidationError as f, CACHED_VALUES_STORAGE_KEY_PREFIX as i, nativeValueConfigState as l, _defineProperty as m, readInstalledTestClient as n, fetchWithTimeout as o, Emitter as p, DefaultConfigDirectorClient as r, discardingTelemetryClient as s, INSTALLED_TEST_CLIENT_SLOT as t, isFetchErrorFatal as u };
|