@configdirector/react-native-sdk 0.6.0 → 0.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.
|
@@ -52,6 +52,14 @@ type ConfigDirectorContext = {
|
|
|
52
52
|
traits?: {
|
|
53
53
|
[key: string]: unknown;
|
|
54
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Whether or not to treat the context as anonymous during evaluation. When `true`, the
|
|
57
|
+
* context values will be used for targeting rules evaluation but the context will not be
|
|
58
|
+
* persisted and will not appear in the dashboard.
|
|
59
|
+
*
|
|
60
|
+
* @default false
|
|
61
|
+
*/
|
|
62
|
+
anonymous?: boolean;
|
|
55
63
|
};
|
|
56
64
|
/**
|
|
57
65
|
* Metadata about your application. It is recommended you include these values when configuring
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
1
2
|
import React, { Component, useContext as useContext$1 } from "react";
|
|
2
3
|
import { AppState } from "react-native";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -453,7 +454,8 @@ var StreamingTransport = class {
|
|
|
453
454
|
body: JSON.stringify({
|
|
454
455
|
givenContext: context,
|
|
455
456
|
metaContext: this.options.metaContext,
|
|
456
|
-
clientSdkKey: this.options.clientSdkKey
|
|
457
|
+
clientSdkKey: this.options.clientSdkKey,
|
|
458
|
+
instanceId: this.options.instanceId
|
|
457
459
|
}),
|
|
458
460
|
onMessage: ({ data }) => {
|
|
459
461
|
this.dispatchMessage(data);
|
|
@@ -549,13 +551,15 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
549
551
|
const requestedType = getRequestedType(defaultValue);
|
|
550
552
|
if (!value) return {
|
|
551
553
|
parsedValue: defaultValue,
|
|
554
|
+
parsedValueId: void 0,
|
|
552
555
|
requestedType,
|
|
553
556
|
usedDefault: true,
|
|
554
557
|
reason: "value-missing"
|
|
555
558
|
};
|
|
556
|
-
if (configState.type === "json") return parseJson(value, defaultValue, requestedType);
|
|
559
|
+
if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
|
|
557
560
|
if (typeof defaultValue === "string") return {
|
|
558
561
|
parsedValue: value,
|
|
562
|
+
parsedValueId: configState.valueId,
|
|
559
563
|
requestedType,
|
|
560
564
|
usedDefault: false,
|
|
561
565
|
reason: "found-match"
|
|
@@ -565,6 +569,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
565
569
|
const hasBoolean = typeof boolValue === "boolean";
|
|
566
570
|
return {
|
|
567
571
|
parsedValue: hasBoolean ? boolValue : defaultValue,
|
|
572
|
+
parsedValueId: hasBoolean ? configState.valueId : void 0,
|
|
568
573
|
requestedType,
|
|
569
574
|
usedDefault: !hasBoolean,
|
|
570
575
|
reason: hasBoolean ? "found-match" : "invalid-boolean"
|
|
@@ -575,6 +580,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
575
580
|
const hasNumber = typeof numValue === "number";
|
|
576
581
|
return {
|
|
577
582
|
parsedValue: hasNumber ? numValue : defaultValue,
|
|
583
|
+
parsedValueId: hasNumber ? configState.valueId : void 0,
|
|
578
584
|
requestedType,
|
|
579
585
|
usedDefault: !hasNumber,
|
|
580
586
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
@@ -585,6 +591,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
585
591
|
const hasNumber = typeof numValue === "number";
|
|
586
592
|
return {
|
|
587
593
|
parsedValue: hasNumber ? numValue : defaultValue,
|
|
594
|
+
parsedValueId: hasNumber ? configState.valueId : void 0,
|
|
588
595
|
requestedType,
|
|
589
596
|
usedDefault: !hasNumber,
|
|
590
597
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
@@ -592,6 +599,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
592
599
|
}
|
|
593
600
|
return {
|
|
594
601
|
parsedValue: value,
|
|
602
|
+
parsedValueId: configState.valueId,
|
|
595
603
|
requestedType,
|
|
596
604
|
usedDefault: false,
|
|
597
605
|
reason: "found-match"
|
|
@@ -615,9 +623,10 @@ const parseConfigFloat = (value) => {
|
|
|
615
623
|
if (isNaN(num) || !isFinite(num)) return;
|
|
616
624
|
return num;
|
|
617
625
|
};
|
|
618
|
-
const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
626
|
+
const parseJson = (jsonString, defaultValue, requestedType, valueId) => {
|
|
619
627
|
if (typeof defaultValue === "string") return {
|
|
620
628
|
parsedValue: jsonString,
|
|
629
|
+
parsedValueId: valueId,
|
|
621
630
|
requestedType,
|
|
622
631
|
usedDefault: false,
|
|
623
632
|
reason: "found-match"
|
|
@@ -625,6 +634,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
|
625
634
|
try {
|
|
626
635
|
return {
|
|
627
636
|
parsedValue: JSON.parse(jsonString),
|
|
637
|
+
parsedValueId: valueId,
|
|
628
638
|
requestedType,
|
|
629
639
|
usedDefault: false,
|
|
630
640
|
reason: "found-match"
|
|
@@ -632,6 +642,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
|
632
642
|
} catch {
|
|
633
643
|
return {
|
|
634
644
|
parsedValue: defaultValue,
|
|
645
|
+
parsedValueId: void 0,
|
|
635
646
|
requestedType,
|
|
636
647
|
usedDefault: true,
|
|
637
648
|
reason: "invalid-json"
|
|
@@ -819,6 +830,7 @@ var PollingTransport = class extends AbstractPollingTransport {
|
|
|
819
830
|
givenContext: context,
|
|
820
831
|
metaContext: this.options.metaContext,
|
|
821
832
|
clientSdkKey: this.options.clientSdkKey,
|
|
833
|
+
instanceId: this.options.instanceId,
|
|
822
834
|
lastUpdateTimestamp: this.lastUpdateTimestamp
|
|
823
835
|
})
|
|
824
836
|
}, this.logger);
|
|
@@ -876,6 +888,8 @@ var DefaultConfigDirectorClient = class {
|
|
|
876
888
|
_defineProperty(this, "readyResolve", void 0);
|
|
877
889
|
_defineProperty(this, "currentContext", void 0);
|
|
878
890
|
_defineProperty(this, "connectionMode", void 0);
|
|
891
|
+
_defineProperty(this, "instanceId", void 0);
|
|
892
|
+
this.instanceId = crypto.randomUUID();
|
|
879
893
|
this.logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createDefaultLogger();
|
|
880
894
|
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;
|
|
881
895
|
const urlFactory = (_internalClientOption = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.urlFactory) !== null && _internalClientOption !== void 0 ? _internalClientOption : defaultUrlFactory;
|
|
@@ -884,6 +898,7 @@ var DefaultConfigDirectorClient = class {
|
|
|
884
898
|
const transportConstructor = this.getTransportConstructor(this.connectionMode);
|
|
885
899
|
this.telemetryClient = telemetryClient;
|
|
886
900
|
this.transport = new transportConstructor({
|
|
901
|
+
instanceId: this.instanceId,
|
|
887
902
|
clientSdkKey,
|
|
888
903
|
baseUrl,
|
|
889
904
|
resolveUrl: urlFactory,
|
|
@@ -1024,6 +1039,7 @@ var DefaultConfigDirectorClient = class {
|
|
|
1024
1039
|
defaultValue,
|
|
1025
1040
|
requestedType: parseResult.requestedType,
|
|
1026
1041
|
evaluatedValue: parseResult.parsedValue,
|
|
1042
|
+
evaluatedValueId: parseResult.parsedValueId,
|
|
1027
1043
|
usedDefault: parseResult.usedDefault,
|
|
1028
1044
|
evaluationReason: parseResult.reason
|
|
1029
1045
|
});
|
|
@@ -1089,26 +1105,36 @@ var DefaultConfigDirectorClient = class {
|
|
|
1089
1105
|
//#endregion
|
|
1090
1106
|
//#region ../shared/src/telemetry/utils.ts
|
|
1091
1107
|
const CONFIG_VALUE_MAX_LENGTH = 500;
|
|
1092
|
-
const
|
|
1093
|
-
if (
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
return {
|
|
1108
|
+
const compactTelemetryValue = async (tv, valueIdGenerator) => {
|
|
1109
|
+
if (tv.valueId) return { valueId: tv.valueId };
|
|
1110
|
+
if (tv.value) {
|
|
1111
|
+
if (tv.value.length <= CONFIG_VALUE_MAX_LENGTH && tv.type != "json") return { value: tv.value };
|
|
1112
|
+
return { valueId: await valueIdGenerator(tv.value) };
|
|
1097
1113
|
}
|
|
1098
|
-
|
|
1099
|
-
else return { value: value.toString() };
|
|
1114
|
+
return tv;
|
|
1100
1115
|
};
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1116
|
+
const mapToTelemetryValue = (ev) => {
|
|
1117
|
+
var _ev$value;
|
|
1118
|
+
if (ev.type === "json" || ev.type == null && typeof ev.value === "object") {
|
|
1119
|
+
if (ev.valueId) return {
|
|
1120
|
+
valueId: ev.valueId,
|
|
1121
|
+
type: "json"
|
|
1122
|
+
};
|
|
1123
|
+
try {
|
|
1124
|
+
return {
|
|
1125
|
+
value: JSON.stringify(ev.value),
|
|
1126
|
+
type: "json"
|
|
1127
|
+
};
|
|
1128
|
+
} catch {
|
|
1129
|
+
return {
|
|
1130
|
+
value: ev.value.toString(),
|
|
1131
|
+
type: "json"
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
const stringValue = (_ev$value = ev.value) === null || _ev$value === void 0 ? void 0 : _ev$value.toString();
|
|
1136
|
+
if (stringValue && stringValue.length <= CONFIG_VALUE_MAX_LENGTH) return { value: stringValue };
|
|
1137
|
+
return ev.valueId ? { valueId: ev.valueId } : { value: stringValue };
|
|
1112
1138
|
};
|
|
1113
1139
|
const isEventListEmpty = (eventList) => {
|
|
1114
1140
|
const keys = Object.keys(eventList);
|
|
@@ -1284,9 +1310,6 @@ var TelemetryEventCollector = class {
|
|
|
1284
1310
|
const initialDelay = (_options$initialFlush = options.initialFlushIntervalDelay) !== null && _options$initialFlush !== void 0 ? _options$initialFlush : 5e3;
|
|
1285
1311
|
this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), initialDelay);
|
|
1286
1312
|
}
|
|
1287
|
-
sanitizeValue(value, type) {
|
|
1288
|
-
return sanitizeValue(value, type);
|
|
1289
|
-
}
|
|
1290
1313
|
async flushAndScheduleNext() {
|
|
1291
1314
|
if ((await this.flush()).fatalError) {
|
|
1292
1315
|
this.collectEvents = false;
|
|
@@ -1294,20 +1317,6 @@ var TelemetryEventCollector = class {
|
|
|
1294
1317
|
this.logger.warn("[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.");
|
|
1295
1318
|
} else this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.flushIntervalDelay);
|
|
1296
1319
|
}
|
|
1297
|
-
async flush() {
|
|
1298
|
-
var _this$reporter;
|
|
1299
|
-
if (!this.reporter) return {
|
|
1300
|
-
success: false,
|
|
1301
|
-
fatalError: false
|
|
1302
|
-
};
|
|
1303
|
-
const evaluationSnapshot = this.evaluationEventQueue.takeSnapshot();
|
|
1304
|
-
return await ((_this$reporter = this.reporter) === null || _this$reporter === void 0 ? void 0 : _this$reporter.report({
|
|
1305
|
-
context: this._context,
|
|
1306
|
-
discreteEvents: {},
|
|
1307
|
-
aggregatedEvents: { evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot) },
|
|
1308
|
-
droppedEvents: { evaluatedConfig: evaluationSnapshot.droppedCount }
|
|
1309
|
-
}));
|
|
1310
|
-
}
|
|
1311
1320
|
cancelScheduledFlush() {
|
|
1312
1321
|
clearTimeout(this.flushTimeout);
|
|
1313
1322
|
}
|
|
@@ -1322,13 +1331,36 @@ var TelemetryEventCollector = class {
|
|
|
1322
1331
|
}
|
|
1323
1332
|
};
|
|
1324
1333
|
//#endregion
|
|
1334
|
+
//#region ../shared/src/telemetry/EvaluatedConfigEventPreprocessor.ts
|
|
1335
|
+
var EvaluatedConfigEventPreprocessor = class {
|
|
1336
|
+
constructor(valueIdGenerator) {
|
|
1337
|
+
this.valueIdGenerator = valueIdGenerator;
|
|
1338
|
+
this.valueIdGenerator = valueIdGenerator;
|
|
1339
|
+
}
|
|
1340
|
+
async process(snapshot) {
|
|
1341
|
+
const events = await Promise.all(snapshot.events.map(async (e) => {
|
|
1342
|
+
return {
|
|
1343
|
+
...e,
|
|
1344
|
+
defaultValue: await compactTelemetryValue(e.defaultValue, this.valueIdGenerator),
|
|
1345
|
+
evaluatedValue: await compactTelemetryValue(e.evaluatedValue, this.valueIdGenerator)
|
|
1346
|
+
};
|
|
1347
|
+
}));
|
|
1348
|
+
return {
|
|
1349
|
+
...snapshot,
|
|
1350
|
+
events
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
//#endregion
|
|
1325
1355
|
//#region ../js-client-core/src/telemetry/ClientTelemetryEventCollector.ts
|
|
1326
1356
|
var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
1327
1357
|
constructor(options) {
|
|
1328
1358
|
super(options);
|
|
1359
|
+
_defineProperty(this, "evaluationEventSnapshotPreprocessor", void 0);
|
|
1329
1360
|
_defineProperty(this, "reporter", void 0);
|
|
1330
1361
|
_defineProperty(this, "_context", void 0);
|
|
1331
1362
|
this.reporter = new ClientEventReporter(options);
|
|
1363
|
+
this.evaluationEventSnapshotPreprocessor = new EvaluatedConfigEventPreprocessor(options.valueIdGenerator);
|
|
1332
1364
|
}
|
|
1333
1365
|
async updateContext(value) {
|
|
1334
1366
|
this.cancelScheduledFlush();
|
|
@@ -1344,6 +1376,20 @@ var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
1344
1376
|
this.cancelScheduledFlush();
|
|
1345
1377
|
await this.flushAndScheduleNext();
|
|
1346
1378
|
}
|
|
1379
|
+
async flush() {
|
|
1380
|
+
var _this$reporter;
|
|
1381
|
+
if (!this.reporter) return {
|
|
1382
|
+
success: false,
|
|
1383
|
+
fatalError: false
|
|
1384
|
+
};
|
|
1385
|
+
const evaluationSnapshot = await this.evaluationEventSnapshotPreprocessor.process(this.evaluationEventQueue.takeSnapshot());
|
|
1386
|
+
return await ((_this$reporter = this.reporter) === null || _this$reporter === void 0 ? void 0 : _this$reporter.report({
|
|
1387
|
+
context: this._context,
|
|
1388
|
+
discreteEvents: {},
|
|
1389
|
+
aggregatedEvents: { evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot) },
|
|
1390
|
+
droppedEvents: { evaluatedConfig: evaluationSnapshot.droppedCount }
|
|
1391
|
+
}));
|
|
1392
|
+
}
|
|
1347
1393
|
};
|
|
1348
1394
|
//#endregion
|
|
1349
1395
|
//#region src/logger.ts
|
|
@@ -1470,6 +1516,29 @@ const parseHeaders = (raw) => {
|
|
|
1470
1516
|
return headers;
|
|
1471
1517
|
};
|
|
1472
1518
|
//#endregion
|
|
1519
|
+
//#region ../shared/src/base62-encoder.ts
|
|
1520
|
+
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
1521
|
+
const toBase62 = (buf, length) => {
|
|
1522
|
+
const ZERO = BigInt(0);
|
|
1523
|
+
const BASE = BigInt(62);
|
|
1524
|
+
const hex = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1525
|
+
let n = BigInt("0x" + hex);
|
|
1526
|
+
let result = "";
|
|
1527
|
+
while (n > ZERO) {
|
|
1528
|
+
result = BASE62[Number(n % BASE)] + result;
|
|
1529
|
+
n /= BASE;
|
|
1530
|
+
}
|
|
1531
|
+
return result.padStart(length, "0");
|
|
1532
|
+
};
|
|
1533
|
+
//#endregion
|
|
1534
|
+
//#region src/value-id-generator.ts
|
|
1535
|
+
const DIGEST_BYTES = 16;
|
|
1536
|
+
const BASE62_LENGTH = Math.ceil(DIGEST_BYTES * 8 / Math.log2(62));
|
|
1537
|
+
const generateValueId = (v) => {
|
|
1538
|
+
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, DIGEST_BYTES), BASE62_LENGTH);
|
|
1540
|
+
};
|
|
1541
|
+
//#endregion
|
|
1473
1542
|
//#region src/ReactNativeTelemetryClient.ts
|
|
1474
1543
|
var ReactNativeTelemetryClient = class {
|
|
1475
1544
|
constructor(sdkKey, baseUrl, logger, urlFactory) {
|
|
@@ -1478,7 +1547,8 @@ var ReactNativeTelemetryClient = class {
|
|
|
1478
1547
|
sdkKey,
|
|
1479
1548
|
logger,
|
|
1480
1549
|
baseUrl,
|
|
1481
|
-
urlFactory
|
|
1550
|
+
urlFactory,
|
|
1551
|
+
valueIdGenerator: async (v) => generateValueId(v)
|
|
1482
1552
|
});
|
|
1483
1553
|
}
|
|
1484
1554
|
async updateContext(value) {
|
|
@@ -1493,8 +1563,15 @@ var ReactNativeTelemetryClient = class {
|
|
|
1493
1563
|
sanitizeEvaluatedConfigEvent(event) {
|
|
1494
1564
|
return {
|
|
1495
1565
|
...event,
|
|
1496
|
-
defaultValue:
|
|
1497
|
-
|
|
1566
|
+
defaultValue: mapToTelemetryValue({
|
|
1567
|
+
value: event.defaultValue,
|
|
1568
|
+
type: event.type
|
|
1569
|
+
}),
|
|
1570
|
+
evaluatedValue: mapToTelemetryValue({
|
|
1571
|
+
value: event.evaluatedValue,
|
|
1572
|
+
valueId: event.evaluatedValueId,
|
|
1573
|
+
type: event.type
|
|
1574
|
+
})
|
|
1498
1575
|
};
|
|
1499
1576
|
}
|
|
1500
1577
|
};
|
|
@@ -1520,7 +1597,7 @@ const createClient = (clientSdkKey, clientOptions) => {
|
|
|
1520
1597
|
const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
|
|
1521
1598
|
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, {
|
|
1522
1599
|
sdkName: "react-native-sdk",
|
|
1523
|
-
sdkVersion: "0.
|
|
1600
|
+
sdkVersion: "0.8.0"
|
|
1524
1601
|
}, {
|
|
1525
1602
|
...clientOptions,
|
|
1526
1603
|
logger
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configdirector/react-native-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.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",
|
|
@@ -52,6 +52,9 @@
|
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"registry": "https://registry.npmjs.org/"
|
|
54
54
|
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@noble/hashes": "^2.2.0"
|
|
57
|
+
},
|
|
55
58
|
"devDependencies": {
|
|
56
59
|
"@babel/core": "^7.29.0",
|
|
57
60
|
"@eslint/js": "^10.0.1",
|
|
@@ -112,7 +115,7 @@
|
|
|
112
115
|
"^@eventsource/(.*)$": "<rootDir>/../eventsource/src/$1"
|
|
113
116
|
},
|
|
114
117
|
"transformIgnorePatterns": [
|
|
115
|
-
"node_modules/(?!((jest-)?react-native|@react-native(-community)
|
|
118
|
+
"node_modules/(?!((jest-)?react-native|@react-native(-community)?|@noble)/)"
|
|
116
119
|
]
|
|
117
120
|
}
|
|
118
121
|
}
|