@configdirector/react-native-sdk 0.8.0 → 0.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.
|
@@ -26,6 +26,15 @@ interface ConfigDirectorLogger {
|
|
|
26
26
|
interface ConfigDirectorLogMessageDecorator {
|
|
27
27
|
decorateMessage(message: string): string;
|
|
28
28
|
}
|
|
29
|
+
type EvaluationReason = "found-match" | "config-state-missing" | "client-not-ready" | "type-mismatch" | "value-missing" | "invalid-number" | "invalid-json" | "invalid-boolean";
|
|
30
|
+
type ConfigEvaluation = {
|
|
31
|
+
key: string;
|
|
32
|
+
value: ConfigValueType;
|
|
33
|
+
valueId?: string;
|
|
34
|
+
isDefaultValue: boolean;
|
|
35
|
+
reason: EvaluationReason;
|
|
36
|
+
context?: ConfigDirectorContext;
|
|
37
|
+
};
|
|
29
38
|
/**
|
|
30
39
|
* The user's context to be sent to ConfigDirector. This context will be used for targeting
|
|
31
40
|
* rules evaluation.
|
|
@@ -131,8 +140,31 @@ type ConfigDirectorClientOptions = {
|
|
|
131
140
|
* );
|
|
132
141
|
*/
|
|
133
142
|
logger?: ConfigDirectorLogger;
|
|
143
|
+
/**
|
|
144
|
+
* Handlers for hooks/events emitted by the client. Each hook accepts either a single event handler or
|
|
145
|
+
* an array of event handlers.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* import { createClient, createConsoleLogger } from "@configdirector/client-sdk";
|
|
149
|
+
* const client = createClient(
|
|
150
|
+
* "YOUR-SDK-KEY",
|
|
151
|
+
* {
|
|
152
|
+
* hooks: {
|
|
153
|
+
* configEvaluated: (event) => { console.log(event.evaluation); }
|
|
154
|
+
* },
|
|
155
|
+
* },
|
|
156
|
+
* );
|
|
157
|
+
*/
|
|
158
|
+
hooks?: ClientHooks;
|
|
159
|
+
};
|
|
160
|
+
type ClientHooks = {
|
|
161
|
+
clientReady?: HookHandler<"clientReady"> | HookHandler<"clientReady">[];
|
|
162
|
+
configsUpdated?: HookHandler<"configsUpdated"> | HookHandler<"configsUpdated">[];
|
|
163
|
+
contextUpdated?: HookHandler<"contextUpdated"> | HookHandler<"contextUpdated">[];
|
|
164
|
+
configEvaluated?: HookHandler<"configEvaluated"> | HookHandler<"configEvaluated">[];
|
|
134
165
|
};
|
|
135
166
|
type ClientConnectAction = "initialization" | "context update" | "network resume";
|
|
167
|
+
type HookHandler<TEvent extends keyof ClientEvents> = (payload: ClientEvents[TEvent]) => void;
|
|
136
168
|
type ClientEvents = {
|
|
137
169
|
configsUpdated: {
|
|
138
170
|
keys: string[];
|
|
@@ -143,6 +175,9 @@ type ClientEvents = {
|
|
|
143
175
|
contextUpdated: {
|
|
144
176
|
context: ConfigDirectorContext | undefined;
|
|
145
177
|
};
|
|
178
|
+
configEvaluated: {
|
|
179
|
+
evaluation: ConfigEvaluation;
|
|
180
|
+
};
|
|
146
181
|
};
|
|
147
182
|
type WatchHandler<T extends ConfigValueType> = (message: T) => void;
|
|
148
183
|
/**
|
|
@@ -297,6 +332,7 @@ type ConfigDirectorProviderOptions = {
|
|
|
297
332
|
timeout?: number;
|
|
298
333
|
context?: ConfigDirectorContext;
|
|
299
334
|
logger?: ConfigDirectorLogger;
|
|
335
|
+
hooks?: ClientHooks;
|
|
300
336
|
netInfoSubscribe?: NetInfoSubscribe;
|
|
301
337
|
};
|
|
302
338
|
//#endregion
|
|
@@ -564,7 +564,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
564
564
|
usedDefault: false,
|
|
565
565
|
reason: "found-match"
|
|
566
566
|
};
|
|
567
|
-
if (typeof defaultValue === "boolean" && configState.type === "boolean") {
|
|
567
|
+
if (typeof defaultValue === "boolean" && (configState.type === "boolean" || configState.type === "string")) {
|
|
568
568
|
const boolValue = parseConfigBoolean(value);
|
|
569
569
|
const hasBoolean = typeof boolValue === "boolean";
|
|
570
570
|
return {
|
|
@@ -586,7 +586,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
586
586
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
587
587
|
};
|
|
588
588
|
}
|
|
589
|
-
if (isNumericNativeType(typeof defaultValue) && (configState.type === "float" || configState.type === "enum")) {
|
|
589
|
+
if (isNumericNativeType(typeof defaultValue) && (configState.type === "float" || configState.type === "enum" || configState.type === "string")) {
|
|
590
590
|
const numValue = parseConfigFloat(value);
|
|
591
591
|
const hasNumber = typeof numValue === "number";
|
|
592
592
|
return {
|
|
@@ -873,7 +873,7 @@ const CLIENT_BASE_URL = "https://client-sdk-api.configdirector.com";
|
|
|
873
873
|
const MAX_EXPONENTIAL_DELAY = 9;
|
|
874
874
|
var DefaultConfigDirectorClient = class {
|
|
875
875
|
constructor(telemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
|
|
876
|
-
var _clientOptions$logger, _clientOptions$connec, _clientOptions$connec2, _internalClientOption, _this$parseUrl, _clientOptions$connec3, _clientOptions$connec4, _clientOptions$connec5, _navigator, _internalClientOption2, _clientOptions$connec6;
|
|
876
|
+
var _clientOptions$logger, _clientOptions$connec, _clientOptions$connec2, _internalClientOption, _this$parseUrl, _clientOptions$connec3, _clientOptions$connec4, _clientOptions$connec5, _navigator, _internalClientOption2, _clientOptions$connec6, _clientOptions$hooks, _clientOptions$hooks2, _clientOptions$hooks3, _clientOptions$hooks4;
|
|
877
877
|
_defineProperty(this, "logger", void 0);
|
|
878
878
|
_defineProperty(this, "telemetryClient", void 0);
|
|
879
879
|
_defineProperty(this, "configSet", void 0);
|
|
@@ -921,18 +921,27 @@ var DefaultConfigDirectorClient = class {
|
|
|
921
921
|
const configKeys = Object.keys(configSet.configs);
|
|
922
922
|
if (!this.configSet || configSet.kind == "full") {
|
|
923
923
|
this.configSet = configSet;
|
|
924
|
-
this.
|
|
924
|
+
this.emit("configsUpdated", { keys: configKeys });
|
|
925
925
|
this.updateWatchers(configSet.configs);
|
|
926
926
|
} else {
|
|
927
927
|
this.configSet.configs = {
|
|
928
928
|
...this.configSet.configs,
|
|
929
929
|
...configSet.configs
|
|
930
930
|
};
|
|
931
|
-
this.
|
|
931
|
+
this.emit("configsUpdated", { keys: configKeys });
|
|
932
932
|
this.updateWatchers(configSet.configs);
|
|
933
933
|
}
|
|
934
934
|
this.logger.debug("[ConfigDirectorClient] ConfigSet updated from server:", { keys: configKeys });
|
|
935
935
|
});
|
|
936
|
+
this.registerHandler("clientReady", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks = clientOptions.hooks) === null || _clientOptions$hooks === void 0 ? void 0 : _clientOptions$hooks.clientReady);
|
|
937
|
+
this.registerHandler("configEvaluated", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks2 = clientOptions.hooks) === null || _clientOptions$hooks2 === void 0 ? void 0 : _clientOptions$hooks2.configEvaluated);
|
|
938
|
+
this.registerHandler("configsUpdated", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks3 = clientOptions.hooks) === null || _clientOptions$hooks3 === void 0 ? void 0 : _clientOptions$hooks3.configsUpdated);
|
|
939
|
+
this.registerHandler("contextUpdated", clientOptions === null || clientOptions === void 0 || (_clientOptions$hooks4 = clientOptions.hooks) === null || _clientOptions$hooks4 === void 0 ? void 0 : _clientOptions$hooks4.contextUpdated);
|
|
940
|
+
}
|
|
941
|
+
registerHandler(event, handler) {
|
|
942
|
+
if (!handler) return;
|
|
943
|
+
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
944
|
+
for (const h of handlers) if (typeof h === "function") this.on(event, h);
|
|
936
945
|
}
|
|
937
946
|
getTransportConstructor(connectionMode) {
|
|
938
947
|
switch (connectionMode) {
|
|
@@ -959,14 +968,14 @@ var DefaultConfigDirectorClient = class {
|
|
|
959
968
|
}).then(() => {
|
|
960
969
|
this.ready = true;
|
|
961
970
|
this.initializing = false;
|
|
962
|
-
this.
|
|
971
|
+
this.emit("clientReady", { action: caller });
|
|
963
972
|
this.logger.debug("[ConfigDirectorClient] Received initial payload from the server, client is ready");
|
|
964
973
|
});
|
|
965
974
|
const startTime = (/* @__PURE__ */ new Date()).getTime();
|
|
966
975
|
await this.transport.connect(context !== null && context !== void 0 ? context : {}, this.timeout);
|
|
967
976
|
this.currentContext = context;
|
|
968
977
|
this.telemetryClient.updateContext(context);
|
|
969
|
-
this.
|
|
978
|
+
this.emit("contextUpdated", { context });
|
|
970
979
|
const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - startTime;
|
|
971
980
|
const remainingTimeout = this.timeout - elapsedTime;
|
|
972
981
|
clearTimeout(this.timeoutTimer);
|
|
@@ -1016,10 +1025,11 @@ var DefaultConfigDirectorClient = class {
|
|
|
1016
1025
|
return this.getValueFromConfigState(configKey, configState, defaultValue);
|
|
1017
1026
|
}
|
|
1018
1027
|
getValueFromConfigState(configKey, configState, defaultValue) {
|
|
1019
|
-
var _this$currentContext2;
|
|
1028
|
+
var _this$currentContext2, _parseResult$parsedVa;
|
|
1020
1029
|
if (!configState) {
|
|
1021
1030
|
var _this$currentContext;
|
|
1022
1031
|
this.logger.debug(`[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`);
|
|
1032
|
+
const reason = this.isReady ? "config-state-missing" : "client-not-ready";
|
|
1023
1033
|
this.telemetryClient.evaluatedConfig({
|
|
1024
1034
|
contextId: (_this$currentContext = this.currentContext) === null || _this$currentContext === void 0 ? void 0 : _this$currentContext.id,
|
|
1025
1035
|
key: configKey,
|
|
@@ -1027,7 +1037,14 @@ var DefaultConfigDirectorClient = class {
|
|
|
1027
1037
|
requestedType: getRequestedType(defaultValue),
|
|
1028
1038
|
evaluatedValue: defaultValue,
|
|
1029
1039
|
usedDefault: true,
|
|
1030
|
-
evaluationReason:
|
|
1040
|
+
evaluationReason: reason
|
|
1041
|
+
});
|
|
1042
|
+
this.dispatchEvaluationEvent({
|
|
1043
|
+
key: configKey,
|
|
1044
|
+
value: defaultValue,
|
|
1045
|
+
isDefaultValue: true,
|
|
1046
|
+
reason,
|
|
1047
|
+
context: this.context
|
|
1031
1048
|
});
|
|
1032
1049
|
return defaultValue;
|
|
1033
1050
|
}
|
|
@@ -1043,9 +1060,27 @@ var DefaultConfigDirectorClient = class {
|
|
|
1043
1060
|
usedDefault: parseResult.usedDefault,
|
|
1044
1061
|
evaluationReason: parseResult.reason
|
|
1045
1062
|
});
|
|
1063
|
+
this.dispatchEvaluationEvent({
|
|
1064
|
+
key: configKey,
|
|
1065
|
+
value: parseResult.parsedValue,
|
|
1066
|
+
valueId: (_parseResult$parsedVa = parseResult.parsedValueId) !== null && _parseResult$parsedVa !== void 0 ? _parseResult$parsedVa : void 0,
|
|
1067
|
+
isDefaultValue: parseResult.usedDefault,
|
|
1068
|
+
reason: parseResult.reason,
|
|
1069
|
+
context: this.context
|
|
1070
|
+
});
|
|
1046
1071
|
this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);
|
|
1047
1072
|
return parseResult.parsedValue;
|
|
1048
1073
|
}
|
|
1074
|
+
dispatchEvaluationEvent(event) {
|
|
1075
|
+
setTimeout(() => this.emit("configEvaluated", { evaluation: event }), 0);
|
|
1076
|
+
}
|
|
1077
|
+
emit(name, payload) {
|
|
1078
|
+
try {
|
|
1079
|
+
this.eventEmitter.emit(name, payload);
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
this.logger.error(`[ConfigDirectorClient] Error executing event handlers for '${name}'`, error);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1049
1084
|
parseUrl(url, urlFactory) {
|
|
1050
1085
|
if (!url) return;
|
|
1051
1086
|
try {
|
|
@@ -1530,13 +1565,12 @@ const toBase62 = (buf, length) => {
|
|
|
1530
1565
|
}
|
|
1531
1566
|
return result.padStart(length, "0");
|
|
1532
1567
|
};
|
|
1568
|
+
const BASE62_LENGTH = Math.ceil(128 / Math.log2(62));
|
|
1533
1569
|
//#endregion
|
|
1534
1570
|
//#region src/value-id-generator.ts
|
|
1535
|
-
const DIGEST_BYTES = 16;
|
|
1536
|
-
const BASE62_LENGTH = Math.ceil(DIGEST_BYTES * 8 / Math.log2(62));
|
|
1537
1571
|
const generateValueId = (v) => {
|
|
1538
1572
|
var _v$toString;
|
|
1539
|
-
return toBase62(sha256(encodeUtf8((_v$toString = v === null || v === void 0 ? void 0 : v.toString()) !== null && _v$toString !== void 0 ? _v$toString : "")).subarray(0,
|
|
1573
|
+
return toBase62(sha256(encodeUtf8((_v$toString = v === null || v === void 0 ? void 0 : v.toString()) !== null && _v$toString !== void 0 ? _v$toString : "")).subarray(0, 16), BASE62_LENGTH);
|
|
1540
1574
|
};
|
|
1541
1575
|
//#endregion
|
|
1542
1576
|
//#region src/ReactNativeTelemetryClient.ts
|
|
@@ -1597,7 +1631,7 @@ const createClient = (clientSdkKey, clientOptions) => {
|
|
|
1597
1631
|
const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
|
|
1598
1632
|
return new DefaultConfigDirectorClient(new ReactNativeTelemetryClient(clientSdkKey, (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, logger, urlFactory), clientSdkKey, {
|
|
1599
1633
|
sdkName: "react-native-sdk",
|
|
1600
|
-
sdkVersion: "0.
|
|
1634
|
+
sdkVersion: "0.9.0"
|
|
1601
1635
|
}, {
|
|
1602
1636
|
...clientOptions,
|
|
1603
1637
|
logger
|
|
@@ -1647,7 +1681,8 @@ var ConfigDirectorProvider = class extends Component {
|
|
|
1647
1681
|
appName: props.appName,
|
|
1648
1682
|
appVersion: props.appVersion
|
|
1649
1683
|
},
|
|
1650
|
-
logger: (_props$logger = props.logger) !== null && _props$logger !== void 0 ? _props$logger : createConsoleLogger("warn")
|
|
1684
|
+
logger: (_props$logger = props.logger) !== null && _props$logger !== void 0 ? _props$logger : createConsoleLogger("warn"),
|
|
1685
|
+
hooks: props.hooks
|
|
1651
1686
|
}),
|
|
1652
1687
|
status: "loading"
|
|
1653
1688
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configdirector/react-native-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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",
|