@configdirector/react-native-sdk 0.7.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.
- package/dist/configdirector-react-native.mjs +113 -41
- package/package.json +5 -2
|
@@ -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";
|
|
@@ -550,13 +551,15 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
550
551
|
const requestedType = getRequestedType(defaultValue);
|
|
551
552
|
if (!value) return {
|
|
552
553
|
parsedValue: defaultValue,
|
|
554
|
+
parsedValueId: void 0,
|
|
553
555
|
requestedType,
|
|
554
556
|
usedDefault: true,
|
|
555
557
|
reason: "value-missing"
|
|
556
558
|
};
|
|
557
|
-
if (configState.type === "json") return parseJson(value, defaultValue, requestedType);
|
|
559
|
+
if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
|
|
558
560
|
if (typeof defaultValue === "string") return {
|
|
559
561
|
parsedValue: value,
|
|
562
|
+
parsedValueId: configState.valueId,
|
|
560
563
|
requestedType,
|
|
561
564
|
usedDefault: false,
|
|
562
565
|
reason: "found-match"
|
|
@@ -566,6 +569,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
566
569
|
const hasBoolean = typeof boolValue === "boolean";
|
|
567
570
|
return {
|
|
568
571
|
parsedValue: hasBoolean ? boolValue : defaultValue,
|
|
572
|
+
parsedValueId: hasBoolean ? configState.valueId : void 0,
|
|
569
573
|
requestedType,
|
|
570
574
|
usedDefault: !hasBoolean,
|
|
571
575
|
reason: hasBoolean ? "found-match" : "invalid-boolean"
|
|
@@ -576,6 +580,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
576
580
|
const hasNumber = typeof numValue === "number";
|
|
577
581
|
return {
|
|
578
582
|
parsedValue: hasNumber ? numValue : defaultValue,
|
|
583
|
+
parsedValueId: hasNumber ? configState.valueId : void 0,
|
|
579
584
|
requestedType,
|
|
580
585
|
usedDefault: !hasNumber,
|
|
581
586
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
@@ -586,6 +591,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
586
591
|
const hasNumber = typeof numValue === "number";
|
|
587
592
|
return {
|
|
588
593
|
parsedValue: hasNumber ? numValue : defaultValue,
|
|
594
|
+
parsedValueId: hasNumber ? configState.valueId : void 0,
|
|
589
595
|
requestedType,
|
|
590
596
|
usedDefault: !hasNumber,
|
|
591
597
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
@@ -593,6 +599,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
593
599
|
}
|
|
594
600
|
return {
|
|
595
601
|
parsedValue: value,
|
|
602
|
+
parsedValueId: configState.valueId,
|
|
596
603
|
requestedType,
|
|
597
604
|
usedDefault: false,
|
|
598
605
|
reason: "found-match"
|
|
@@ -616,9 +623,10 @@ const parseConfigFloat = (value) => {
|
|
|
616
623
|
if (isNaN(num) || !isFinite(num)) return;
|
|
617
624
|
return num;
|
|
618
625
|
};
|
|
619
|
-
const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
626
|
+
const parseJson = (jsonString, defaultValue, requestedType, valueId) => {
|
|
620
627
|
if (typeof defaultValue === "string") return {
|
|
621
628
|
parsedValue: jsonString,
|
|
629
|
+
parsedValueId: valueId,
|
|
622
630
|
requestedType,
|
|
623
631
|
usedDefault: false,
|
|
624
632
|
reason: "found-match"
|
|
@@ -626,6 +634,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
|
626
634
|
try {
|
|
627
635
|
return {
|
|
628
636
|
parsedValue: JSON.parse(jsonString),
|
|
637
|
+
parsedValueId: valueId,
|
|
629
638
|
requestedType,
|
|
630
639
|
usedDefault: false,
|
|
631
640
|
reason: "found-match"
|
|
@@ -633,6 +642,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
|
633
642
|
} catch {
|
|
634
643
|
return {
|
|
635
644
|
parsedValue: defaultValue,
|
|
645
|
+
parsedValueId: void 0,
|
|
636
646
|
requestedType,
|
|
637
647
|
usedDefault: true,
|
|
638
648
|
reason: "invalid-json"
|
|
@@ -1029,6 +1039,7 @@ var DefaultConfigDirectorClient = class {
|
|
|
1029
1039
|
defaultValue,
|
|
1030
1040
|
requestedType: parseResult.requestedType,
|
|
1031
1041
|
evaluatedValue: parseResult.parsedValue,
|
|
1042
|
+
evaluatedValueId: parseResult.parsedValueId,
|
|
1032
1043
|
usedDefault: parseResult.usedDefault,
|
|
1033
1044
|
evaluationReason: parseResult.reason
|
|
1034
1045
|
});
|
|
@@ -1094,26 +1105,36 @@ var DefaultConfigDirectorClient = class {
|
|
|
1094
1105
|
//#endregion
|
|
1095
1106
|
//#region ../shared/src/telemetry/utils.ts
|
|
1096
1107
|
const CONFIG_VALUE_MAX_LENGTH = 500;
|
|
1097
|
-
const
|
|
1098
|
-
if (
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
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) };
|
|
1102
1113
|
}
|
|
1103
|
-
|
|
1104
|
-
else return { value: value.toString() };
|
|
1105
|
-
};
|
|
1106
|
-
const djb2Hash = (data) => {
|
|
1107
|
-
return djb2(new TextEncoder().encode(data)).toString(16).padStart(8, "0");
|
|
1114
|
+
return tv;
|
|
1108
1115
|
};
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
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 };
|
|
1117
1138
|
};
|
|
1118
1139
|
const isEventListEmpty = (eventList) => {
|
|
1119
1140
|
const keys = Object.keys(eventList);
|
|
@@ -1289,9 +1310,6 @@ var TelemetryEventCollector = class {
|
|
|
1289
1310
|
const initialDelay = (_options$initialFlush = options.initialFlushIntervalDelay) !== null && _options$initialFlush !== void 0 ? _options$initialFlush : 5e3;
|
|
1290
1311
|
this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), initialDelay);
|
|
1291
1312
|
}
|
|
1292
|
-
sanitizeValue(value, type) {
|
|
1293
|
-
return sanitizeValue(value, type);
|
|
1294
|
-
}
|
|
1295
1313
|
async flushAndScheduleNext() {
|
|
1296
1314
|
if ((await this.flush()).fatalError) {
|
|
1297
1315
|
this.collectEvents = false;
|
|
@@ -1299,20 +1317,6 @@ var TelemetryEventCollector = class {
|
|
|
1299
1317
|
this.logger.warn("[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.");
|
|
1300
1318
|
} else this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.flushIntervalDelay);
|
|
1301
1319
|
}
|
|
1302
|
-
async flush() {
|
|
1303
|
-
var _this$reporter;
|
|
1304
|
-
if (!this.reporter) return {
|
|
1305
|
-
success: false,
|
|
1306
|
-
fatalError: false
|
|
1307
|
-
};
|
|
1308
|
-
const evaluationSnapshot = this.evaluationEventQueue.takeSnapshot();
|
|
1309
|
-
return await ((_this$reporter = this.reporter) === null || _this$reporter === void 0 ? void 0 : _this$reporter.report({
|
|
1310
|
-
context: this._context,
|
|
1311
|
-
discreteEvents: {},
|
|
1312
|
-
aggregatedEvents: { evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot) },
|
|
1313
|
-
droppedEvents: { evaluatedConfig: evaluationSnapshot.droppedCount }
|
|
1314
|
-
}));
|
|
1315
|
-
}
|
|
1316
1320
|
cancelScheduledFlush() {
|
|
1317
1321
|
clearTimeout(this.flushTimeout);
|
|
1318
1322
|
}
|
|
@@ -1327,13 +1331,36 @@ var TelemetryEventCollector = class {
|
|
|
1327
1331
|
}
|
|
1328
1332
|
};
|
|
1329
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
|
|
1330
1355
|
//#region ../js-client-core/src/telemetry/ClientTelemetryEventCollector.ts
|
|
1331
1356
|
var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
1332
1357
|
constructor(options) {
|
|
1333
1358
|
super(options);
|
|
1359
|
+
_defineProperty(this, "evaluationEventSnapshotPreprocessor", void 0);
|
|
1334
1360
|
_defineProperty(this, "reporter", void 0);
|
|
1335
1361
|
_defineProperty(this, "_context", void 0);
|
|
1336
1362
|
this.reporter = new ClientEventReporter(options);
|
|
1363
|
+
this.evaluationEventSnapshotPreprocessor = new EvaluatedConfigEventPreprocessor(options.valueIdGenerator);
|
|
1337
1364
|
}
|
|
1338
1365
|
async updateContext(value) {
|
|
1339
1366
|
this.cancelScheduledFlush();
|
|
@@ -1349,6 +1376,20 @@ var ClientTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
1349
1376
|
this.cancelScheduledFlush();
|
|
1350
1377
|
await this.flushAndScheduleNext();
|
|
1351
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
|
+
}
|
|
1352
1393
|
};
|
|
1353
1394
|
//#endregion
|
|
1354
1395
|
//#region src/logger.ts
|
|
@@ -1475,6 +1516,29 @@ const parseHeaders = (raw) => {
|
|
|
1475
1516
|
return headers;
|
|
1476
1517
|
};
|
|
1477
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
|
|
1478
1542
|
//#region src/ReactNativeTelemetryClient.ts
|
|
1479
1543
|
var ReactNativeTelemetryClient = class {
|
|
1480
1544
|
constructor(sdkKey, baseUrl, logger, urlFactory) {
|
|
@@ -1483,7 +1547,8 @@ var ReactNativeTelemetryClient = class {
|
|
|
1483
1547
|
sdkKey,
|
|
1484
1548
|
logger,
|
|
1485
1549
|
baseUrl,
|
|
1486
|
-
urlFactory
|
|
1550
|
+
urlFactory,
|
|
1551
|
+
valueIdGenerator: async (v) => generateValueId(v)
|
|
1487
1552
|
});
|
|
1488
1553
|
}
|
|
1489
1554
|
async updateContext(value) {
|
|
@@ -1498,8 +1563,15 @@ var ReactNativeTelemetryClient = class {
|
|
|
1498
1563
|
sanitizeEvaluatedConfigEvent(event) {
|
|
1499
1564
|
return {
|
|
1500
1565
|
...event,
|
|
1501
|
-
defaultValue:
|
|
1502
|
-
|
|
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
|
+
})
|
|
1503
1575
|
};
|
|
1504
1576
|
}
|
|
1505
1577
|
};
|
|
@@ -1525,7 +1597,7 @@ const createClient = (clientSdkKey, clientOptions) => {
|
|
|
1525
1597
|
const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
|
|
1526
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, {
|
|
1527
1599
|
sdkName: "react-native-sdk",
|
|
1528
|
-
sdkVersion: "0.
|
|
1600
|
+
sdkVersion: "0.8.0"
|
|
1529
1601
|
}, {
|
|
1530
1602
|
...clientOptions,
|
|
1531
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
|
}
|