@configdirector/nuxt-sdk 0.7.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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +9 -0
- package/dist/runtime/app/composables/useConfigDirectorClient.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClient.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClientHooks.d.mts +6 -0
- package/dist/runtime/app/composables/useConfigDirectorClientHooks.d.ts +6 -0
- package/dist/runtime/app/composables/useConfigDirectorClientHooks.mjs +22 -0
- package/dist/runtime/app/composables/useConfigDirectorContext.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorContext.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorValue.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorValue.d.ts +1 -1
- package/dist/runtime/nitro/plugin.d.mts +1 -1
- package/dist/runtime/nitro/plugin.d.ts +1 -1
- package/dist/runtime/nitro/plugin.mjs +126 -42
- package/dist/runtime/plugin.client.mjs +110 -32
- package/dist/runtime/server/composables/useConfigDirectorClient.d.mts +1 -1
- package/dist/runtime/server/composables/useConfigDirectorClient.d.ts +1 -1
- package/dist/runtime/server/composables/useConfigDirectorServerHooks.d.mts +6 -0
- package/dist/runtime/server/composables/useConfigDirectorServerHooks.d.ts +6 -0
- package/dist/runtime/server/composables/useConfigDirectorServerHooks.mjs +14 -0
- package/dist/runtime/{types-BkRZRTVv.d.mts → types-B4EK1B6g.d.mts} +12 -2
- package/dist/runtime/{types-f2z6DgES.d.mts → types-CHmsCChN.d.mts} +11 -1
- package/dist/runtime/{types-BDwzL0dp.d.mts → types-CzmBIfqo.d.mts} +11 -2
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -37,6 +37,10 @@ var module_default = defineNuxtModule({
|
|
|
37
37
|
name: "useConfigDirectorClient",
|
|
38
38
|
as: "useConfigDirectorClient",
|
|
39
39
|
from: resolver.resolve("./runtime/server/composables/useConfigDirectorClient")
|
|
40
|
+
}, {
|
|
41
|
+
name: "useConfigDirectorServerHooks",
|
|
42
|
+
as: "useConfigDirectorServerHooks",
|
|
43
|
+
from: resolver.resolve("./runtime/server/composables/useConfigDirectorServerHooks")
|
|
40
44
|
}]);
|
|
41
45
|
addPlugin(resolver.resolve("./runtime/plugin.server"));
|
|
42
46
|
addPlugin(resolver.resolve("./runtime/plugin.client"));
|
|
@@ -60,6 +64,11 @@ var module_default = defineNuxtModule({
|
|
|
60
64
|
name: "useConfigDirectorValue",
|
|
61
65
|
as: "useConfigDirectorValue",
|
|
62
66
|
from: resolver.resolve("./runtime/app/composables/useConfigDirectorValue")
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "useConfigDirectorClientHooks",
|
|
70
|
+
as: "useConfigDirectorClientHooks",
|
|
71
|
+
from: resolver.resolve("./runtime/app/composables/useConfigDirectorClientHooks")
|
|
63
72
|
}
|
|
64
73
|
]);
|
|
65
74
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { t as ClientHooks } from "../../types-B4EK1B6g.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime/app/composables/useConfigDirectorClientHooks.d.ts
|
|
4
|
+
declare const useConfigDirectorClientHooks: (hooks: ClientHooks) => void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { useConfigDirectorClientHooks };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { t as ClientHooks } from "../../types-B4EK1B6g.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime/app/composables/useConfigDirectorClientHooks.d.ts
|
|
4
|
+
declare const useConfigDirectorClientHooks: (hooks: ClientHooks) => void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { useConfigDirectorClientHooks };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useNuxtApp } from "#app";
|
|
2
|
+
import { getCurrentInstance, onUnmounted } from "vue";
|
|
3
|
+
//#region src/runtime/app/composables/useConfigDirectorClientHooks.ts
|
|
4
|
+
const useConfigDirectorClientHooks = (hooks) => {
|
|
5
|
+
const { $configDirectorClient } = useNuxtApp();
|
|
6
|
+
const client = $configDirectorClient;
|
|
7
|
+
const hookEntries = Object.entries(hooks);
|
|
8
|
+
for (const [event, handler] of hookEntries) {
|
|
9
|
+
if (!handler) continue;
|
|
10
|
+
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
11
|
+
for (const h of handlers) client.on(event, h);
|
|
12
|
+
}
|
|
13
|
+
if (getCurrentInstance()) onUnmounted(() => {
|
|
14
|
+
for (const [event, handler] of hookEntries) {
|
|
15
|
+
if (!handler) continue;
|
|
16
|
+
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
17
|
+
for (const h of handlers) client.off(event, h);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { useConfigDirectorClientHooks };
|
|
@@ -2,6 +2,7 @@ import EventEmitter, { EventEmitter as EventEmitter$1 } from "node:events";
|
|
|
2
2
|
import { findByPointer } from "@jsonjoy.com/json-pointer";
|
|
3
3
|
import * as semver from "semver";
|
|
4
4
|
import { rapidhash } from "rapidhash-js";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
5
6
|
import { defineNitroPlugin, useRuntimeConfig } from "nitropack/runtime";
|
|
6
7
|
import { consola } from "consola";
|
|
7
8
|
//#region ../shared/src/errors.ts
|
|
@@ -849,22 +850,25 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
849
850
|
const requestedType = getRequestedType(defaultValue);
|
|
850
851
|
if (!value) return {
|
|
851
852
|
parsedValue: defaultValue,
|
|
853
|
+
parsedValueId: void 0,
|
|
852
854
|
requestedType,
|
|
853
855
|
usedDefault: true,
|
|
854
856
|
reason: "value-missing"
|
|
855
857
|
};
|
|
856
|
-
if (configState.type === "json") return parseJson(value, defaultValue, requestedType);
|
|
858
|
+
if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
|
|
857
859
|
if (typeof defaultValue === "string") return {
|
|
858
860
|
parsedValue: value,
|
|
861
|
+
parsedValueId: configState.valueId,
|
|
859
862
|
requestedType,
|
|
860
863
|
usedDefault: false,
|
|
861
864
|
reason: "found-match"
|
|
862
865
|
};
|
|
863
|
-
if (typeof defaultValue === "boolean" && configState.type === "boolean") {
|
|
866
|
+
if (typeof defaultValue === "boolean" && (configState.type === "boolean" || configState.type === "string")) {
|
|
864
867
|
const boolValue = parseConfigBoolean(value);
|
|
865
868
|
const hasBoolean = typeof boolValue === "boolean";
|
|
866
869
|
return {
|
|
867
870
|
parsedValue: hasBoolean ? boolValue : defaultValue,
|
|
871
|
+
parsedValueId: hasBoolean ? configState.valueId : void 0,
|
|
868
872
|
requestedType,
|
|
869
873
|
usedDefault: !hasBoolean,
|
|
870
874
|
reason: hasBoolean ? "found-match" : "invalid-boolean"
|
|
@@ -875,16 +879,18 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
875
879
|
const hasNumber = typeof numValue === "number";
|
|
876
880
|
return {
|
|
877
881
|
parsedValue: hasNumber ? numValue : defaultValue,
|
|
882
|
+
parsedValueId: hasNumber ? configState.valueId : void 0,
|
|
878
883
|
requestedType,
|
|
879
884
|
usedDefault: !hasNumber,
|
|
880
885
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
881
886
|
};
|
|
882
887
|
}
|
|
883
|
-
if (isNumericNativeType(typeof defaultValue) && (configState.type === "float" || configState.type === "enum")) {
|
|
888
|
+
if (isNumericNativeType(typeof defaultValue) && (configState.type === "float" || configState.type === "enum" || configState.type === "string")) {
|
|
884
889
|
const numValue = parseConfigFloat(value);
|
|
885
890
|
const hasNumber = typeof numValue === "number";
|
|
886
891
|
return {
|
|
887
892
|
parsedValue: hasNumber ? numValue : defaultValue,
|
|
893
|
+
parsedValueId: hasNumber ? configState.valueId : void 0,
|
|
888
894
|
requestedType,
|
|
889
895
|
usedDefault: !hasNumber,
|
|
890
896
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
@@ -892,6 +898,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
892
898
|
}
|
|
893
899
|
return {
|
|
894
900
|
parsedValue: value,
|
|
901
|
+
parsedValueId: configState.valueId,
|
|
895
902
|
requestedType,
|
|
896
903
|
usedDefault: false,
|
|
897
904
|
reason: "found-match"
|
|
@@ -915,9 +922,10 @@ const parseConfigFloat = (value) => {
|
|
|
915
922
|
if (isNaN(num) || !isFinite(num)) return;
|
|
916
923
|
return num;
|
|
917
924
|
};
|
|
918
|
-
const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
925
|
+
const parseJson = (jsonString, defaultValue, requestedType, valueId) => {
|
|
919
926
|
if (typeof defaultValue === "string") return {
|
|
920
927
|
parsedValue: jsonString,
|
|
928
|
+
parsedValueId: valueId,
|
|
921
929
|
requestedType,
|
|
922
930
|
usedDefault: false,
|
|
923
931
|
reason: "found-match"
|
|
@@ -925,6 +933,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
|
925
933
|
try {
|
|
926
934
|
return {
|
|
927
935
|
parsedValue: JSON.parse(jsonString),
|
|
936
|
+
parsedValueId: valueId,
|
|
928
937
|
requestedType,
|
|
929
938
|
usedDefault: false,
|
|
930
939
|
reason: "found-match"
|
|
@@ -932,6 +941,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
|
932
941
|
} catch {
|
|
933
942
|
return {
|
|
934
943
|
parsedValue: defaultValue,
|
|
944
|
+
parsedValueId: void 0,
|
|
935
945
|
requestedType,
|
|
936
946
|
usedDefault: true,
|
|
937
947
|
reason: "invalid-json"
|
|
@@ -1007,25 +1017,35 @@ const defaultUrlFactory = (input, base) => new URL(input, base?.toString());
|
|
|
1007
1017
|
//#endregion
|
|
1008
1018
|
//#region ../shared/src/telemetry/utils.ts
|
|
1009
1019
|
const CONFIG_VALUE_MAX_LENGTH = 500;
|
|
1010
|
-
const
|
|
1011
|
-
if (
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
return {
|
|
1020
|
+
const compactTelemetryValue = async (tv, valueIdGenerator) => {
|
|
1021
|
+
if (tv.valueId) return { valueId: tv.valueId };
|
|
1022
|
+
if (tv.value) {
|
|
1023
|
+
if (tv.value.length <= CONFIG_VALUE_MAX_LENGTH && tv.type != "json") return { value: tv.value };
|
|
1024
|
+
return { valueId: await valueIdGenerator(tv.value) };
|
|
1015
1025
|
}
|
|
1016
|
-
|
|
1017
|
-
else return { value: value.toString() };
|
|
1018
|
-
};
|
|
1019
|
-
const djb2Hash = (data) => {
|
|
1020
|
-
return djb2(new TextEncoder().encode(data)).toString(16).padStart(8, "0");
|
|
1026
|
+
return tv;
|
|
1021
1027
|
};
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1028
|
+
const mapToTelemetryValue = (ev) => {
|
|
1029
|
+
if (ev.type === "json" || ev.type == null && typeof ev.value === "object") {
|
|
1030
|
+
if (ev.valueId) return {
|
|
1031
|
+
valueId: ev.valueId,
|
|
1032
|
+
type: "json"
|
|
1033
|
+
};
|
|
1034
|
+
try {
|
|
1035
|
+
return {
|
|
1036
|
+
value: JSON.stringify(ev.value),
|
|
1037
|
+
type: "json"
|
|
1038
|
+
};
|
|
1039
|
+
} catch {
|
|
1040
|
+
return {
|
|
1041
|
+
value: ev.value.toString(),
|
|
1042
|
+
type: "json"
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1027
1045
|
}
|
|
1028
|
-
|
|
1046
|
+
const stringValue = ev.value?.toString();
|
|
1047
|
+
if (stringValue && stringValue.length <= CONFIG_VALUE_MAX_LENGTH) return { value: stringValue };
|
|
1048
|
+
return ev.valueId ? { valueId: ev.valueId } : { value: stringValue };
|
|
1029
1049
|
};
|
|
1030
1050
|
const isEventListEmpty = (eventList) => {
|
|
1031
1051
|
const keys = Object.keys(eventList);
|
|
@@ -1189,9 +1209,6 @@ var TelemetryEventCollector = class {
|
|
|
1189
1209
|
const initialDelay = options.initialFlushIntervalDelay ?? 5e3;
|
|
1190
1210
|
this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), initialDelay);
|
|
1191
1211
|
}
|
|
1192
|
-
sanitizeValue(value, type) {
|
|
1193
|
-
return sanitizeValue(value, type);
|
|
1194
|
-
}
|
|
1195
1212
|
async flushAndScheduleNext() {
|
|
1196
1213
|
if ((await this.flush()).fatalError) {
|
|
1197
1214
|
this.collectEvents = false;
|
|
@@ -1199,19 +1216,6 @@ var TelemetryEventCollector = class {
|
|
|
1199
1216
|
this.logger.warn("[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.");
|
|
1200
1217
|
} else this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.flushIntervalDelay);
|
|
1201
1218
|
}
|
|
1202
|
-
async flush() {
|
|
1203
|
-
if (!this.reporter) return {
|
|
1204
|
-
success: false,
|
|
1205
|
-
fatalError: false
|
|
1206
|
-
};
|
|
1207
|
-
const evaluationSnapshot = this.evaluationEventQueue.takeSnapshot();
|
|
1208
|
-
return await this.reporter?.report({
|
|
1209
|
-
context: this._context,
|
|
1210
|
-
discreteEvents: {},
|
|
1211
|
-
aggregatedEvents: { evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot) },
|
|
1212
|
-
droppedEvents: { evaluatedConfig: evaluationSnapshot.droppedCount }
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
1219
|
cancelScheduledFlush() {
|
|
1216
1220
|
clearTimeout(this.flushTimeout);
|
|
1217
1221
|
}
|
|
@@ -1258,9 +1262,31 @@ var LimitedMap = class extends Map {
|
|
|
1258
1262
|
}
|
|
1259
1263
|
};
|
|
1260
1264
|
//#endregion
|
|
1265
|
+
//#region ../shared/src/telemetry/EvaluatedConfigEventPreprocessor.ts
|
|
1266
|
+
var EvaluatedConfigEventPreprocessor = class {
|
|
1267
|
+
constructor(valueIdGenerator) {
|
|
1268
|
+
this.valueIdGenerator = valueIdGenerator;
|
|
1269
|
+
this.valueIdGenerator = valueIdGenerator;
|
|
1270
|
+
}
|
|
1271
|
+
async process(snapshot) {
|
|
1272
|
+
const events = await Promise.all(snapshot.events.map(async (e) => {
|
|
1273
|
+
return {
|
|
1274
|
+
...e,
|
|
1275
|
+
defaultValue: await compactTelemetryValue(e.defaultValue, this.valueIdGenerator),
|
|
1276
|
+
evaluatedValue: await compactTelemetryValue(e.evaluatedValue, this.valueIdGenerator)
|
|
1277
|
+
};
|
|
1278
|
+
}));
|
|
1279
|
+
return {
|
|
1280
|
+
...snapshot,
|
|
1281
|
+
events
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
//#endregion
|
|
1261
1286
|
//#region ../js-server-sdk/src/telemetry/ServerTelemetryEventCollector.ts
|
|
1262
1287
|
const DEFAULT_CONTEXTS_LIMIT = 1e3;
|
|
1263
1288
|
var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
1289
|
+
evaluationEventSnapshotPreprocessor;
|
|
1264
1290
|
reporter;
|
|
1265
1291
|
_context;
|
|
1266
1292
|
contexts;
|
|
@@ -1268,6 +1294,7 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
1268
1294
|
super(options);
|
|
1269
1295
|
this.reporter = new ServerEventReporter(options);
|
|
1270
1296
|
this.contexts = new LimitedMap(options.contextLimit ?? DEFAULT_CONTEXTS_LIMIT);
|
|
1297
|
+
this.evaluationEventSnapshotPreprocessor = new EvaluatedConfigEventPreprocessor(options.valueIdGenerator);
|
|
1271
1298
|
}
|
|
1272
1299
|
evaluatedConfig(payload) {
|
|
1273
1300
|
if (!this.collectEvents) return;
|
|
@@ -1281,8 +1308,15 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
1281
1308
|
sanitizeEvaluatedConfigEvent(event) {
|
|
1282
1309
|
return {
|
|
1283
1310
|
...event,
|
|
1284
|
-
defaultValue:
|
|
1285
|
-
|
|
1311
|
+
defaultValue: mapToTelemetryValue({
|
|
1312
|
+
value: event.defaultValue,
|
|
1313
|
+
type: event.type
|
|
1314
|
+
}),
|
|
1315
|
+
evaluatedValue: mapToTelemetryValue({
|
|
1316
|
+
value: event.evaluatedValue,
|
|
1317
|
+
valueId: event.evaluatedValueId,
|
|
1318
|
+
type: event.type
|
|
1319
|
+
})
|
|
1286
1320
|
};
|
|
1287
1321
|
}
|
|
1288
1322
|
snapshotContexts() {
|
|
@@ -1299,7 +1333,7 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
1299
1333
|
success: false,
|
|
1300
1334
|
fatalError: false
|
|
1301
1335
|
};
|
|
1302
|
-
const evaluationSnapshot = this.evaluationEventQueue.takeSnapshot();
|
|
1336
|
+
const evaluationSnapshot = await this.evaluationEventSnapshotPreprocessor.process(this.evaluationEventQueue.takeSnapshot());
|
|
1303
1337
|
const contextsSnapshot = this.snapshotContexts();
|
|
1304
1338
|
return await this.reporter?.report({
|
|
1305
1339
|
context: this._context,
|
|
@@ -1313,6 +1347,27 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
1313
1347
|
}
|
|
1314
1348
|
};
|
|
1315
1349
|
//#endregion
|
|
1350
|
+
//#region ../shared/src/base62-encoder.ts
|
|
1351
|
+
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
1352
|
+
const toBase62 = (buf, length) => {
|
|
1353
|
+
const ZERO = BigInt(0);
|
|
1354
|
+
const BASE = BigInt(62);
|
|
1355
|
+
const hex = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1356
|
+
let n = BigInt("0x" + hex);
|
|
1357
|
+
let result = "";
|
|
1358
|
+
while (n > ZERO) {
|
|
1359
|
+
result = BASE62[Number(n % BASE)] + result;
|
|
1360
|
+
n /= BASE;
|
|
1361
|
+
}
|
|
1362
|
+
return result.padStart(length, "0");
|
|
1363
|
+
};
|
|
1364
|
+
const BASE62_LENGTH = Math.ceil(128 / Math.log2(62));
|
|
1365
|
+
//#endregion
|
|
1366
|
+
//#region ../js-server-sdk/src/telemetry/value-id-generator.ts
|
|
1367
|
+
const generateValueId = async (v) => {
|
|
1368
|
+
return toBase62(createHash("sha256").update(v?.toString() ?? "").digest().subarray(0, 16), BASE62_LENGTH);
|
|
1369
|
+
};
|
|
1370
|
+
//#endregion
|
|
1316
1371
|
//#region ../js-server-sdk/src/DefaultConfigDirectorClient.ts
|
|
1317
1372
|
const defaultBaseUrl = new URL("https://server-sdk-api.configdirector.com");
|
|
1318
1373
|
const DEFAULT_FLUSH_INTERVAL = 3e4;
|
|
@@ -1347,7 +1402,8 @@ var DefaultConfigDirectorClient = class {
|
|
|
1347
1402
|
flushIntervalDelay: clientOptions?.telemetry?.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
|
|
1348
1403
|
evaluationQueueLimit: Math.ceil(queueLimit * .7),
|
|
1349
1404
|
contextLimit: Math.floor(queueLimit * .3),
|
|
1350
|
-
urlFactory: defaultUrlFactory
|
|
1405
|
+
urlFactory: defaultUrlFactory,
|
|
1406
|
+
valueIdGenerator: generateValueId
|
|
1351
1407
|
});
|
|
1352
1408
|
this.transport = new transportConstructor({
|
|
1353
1409
|
serverSdkKey,
|
|
@@ -1377,6 +1433,14 @@ var DefaultConfigDirectorClient = class {
|
|
|
1377
1433
|
}
|
|
1378
1434
|
this.logger.debug(`[ConfigDirectorClient] ConfigSet updated from server with ${configKeys.length} key(s)`, { keys: configKeys });
|
|
1379
1435
|
});
|
|
1436
|
+
this.registerHandler("clientReady", clientOptions?.hooks?.clientReady);
|
|
1437
|
+
this.registerHandler("configEvaluated", clientOptions?.hooks?.configEvaluated);
|
|
1438
|
+
this.registerHandler("configsUpdated", clientOptions?.hooks?.configsUpdated);
|
|
1439
|
+
}
|
|
1440
|
+
registerHandler(event, handler) {
|
|
1441
|
+
if (!handler) return;
|
|
1442
|
+
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
1443
|
+
for (const h of handlers) if (typeof h === "function") this.on(event, h);
|
|
1380
1444
|
}
|
|
1381
1445
|
async initialize() {
|
|
1382
1446
|
try {
|
|
@@ -1460,14 +1524,24 @@ var DefaultConfigDirectorClient = class {
|
|
|
1460
1524
|
defaultValue,
|
|
1461
1525
|
requestedType: parseResult.requestedType,
|
|
1462
1526
|
evaluatedValue: parseResult.parsedValue,
|
|
1527
|
+
evaluatedValueId: parseResult.parsedValueId,
|
|
1463
1528
|
usedDefault: parseResult.usedDefault,
|
|
1464
1529
|
evaluationReason: parseResult.reason
|
|
1465
1530
|
}
|
|
1466
1531
|
});
|
|
1532
|
+
this.dispatchEvaluationEvent({
|
|
1533
|
+
key: configKey,
|
|
1534
|
+
value: parseResult.parsedValue,
|
|
1535
|
+
valueId: parseResult.parsedValueId ?? void 0,
|
|
1536
|
+
isDefaultValue: parseResult.usedDefault,
|
|
1537
|
+
reason: parseResult.reason,
|
|
1538
|
+
context
|
|
1539
|
+
});
|
|
1467
1540
|
this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);
|
|
1468
1541
|
return parseResult.parsedValue;
|
|
1469
1542
|
} else {
|
|
1470
1543
|
this.logger.debug(`[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`);
|
|
1544
|
+
const reason = this.isReady ? "config-state-missing" : "client-not-ready";
|
|
1471
1545
|
this.usageEventCollector.evaluatedConfig({
|
|
1472
1546
|
context,
|
|
1473
1547
|
evaluation: {
|
|
@@ -1476,12 +1550,22 @@ var DefaultConfigDirectorClient = class {
|
|
|
1476
1550
|
requestedType: getRequestedType(defaultValue),
|
|
1477
1551
|
evaluatedValue: defaultValue,
|
|
1478
1552
|
usedDefault: true,
|
|
1479
|
-
evaluationReason:
|
|
1553
|
+
evaluationReason: reason
|
|
1480
1554
|
}
|
|
1481
1555
|
});
|
|
1556
|
+
this.dispatchEvaluationEvent({
|
|
1557
|
+
key: configKey,
|
|
1558
|
+
value: defaultValue,
|
|
1559
|
+
isDefaultValue: true,
|
|
1560
|
+
reason,
|
|
1561
|
+
context
|
|
1562
|
+
});
|
|
1482
1563
|
return defaultValue;
|
|
1483
1564
|
}
|
|
1484
1565
|
}
|
|
1566
|
+
dispatchEvaluationEvent(event) {
|
|
1567
|
+
setTimeout(() => this.eventEmitter.emit("configEvaluated", { evaluation: event }), 0);
|
|
1568
|
+
}
|
|
1485
1569
|
parseUrl(url) {
|
|
1486
1570
|
if (!url) return;
|
|
1487
1571
|
try {
|
|
@@ -1552,7 +1636,7 @@ var plugin_default = defineNitroPlugin((nitroApp) => {
|
|
|
1552
1636
|
const { serverSdkKey, baseUrl } = runtimeConfig.configdirector;
|
|
1553
1637
|
const client = new DefaultConfigDirectorClient(serverSdkKey, {
|
|
1554
1638
|
sdkName: "nuxt-sdk",
|
|
1555
|
-
sdkVersion: "0.
|
|
1639
|
+
sdkVersion: "0.9.0"
|
|
1556
1640
|
}, {
|
|
1557
1641
|
logger,
|
|
1558
1642
|
...baseUrl ? { connection: { url: baseUrl } } : {}
|
|
@@ -502,22 +502,25 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
502
502
|
const requestedType = getRequestedType(defaultValue);
|
|
503
503
|
if (!value) return {
|
|
504
504
|
parsedValue: defaultValue,
|
|
505
|
+
parsedValueId: void 0,
|
|
505
506
|
requestedType,
|
|
506
507
|
usedDefault: true,
|
|
507
508
|
reason: "value-missing"
|
|
508
509
|
};
|
|
509
|
-
if (configState.type === "json") return parseJson(value, defaultValue, requestedType);
|
|
510
|
+
if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
|
|
510
511
|
if (typeof defaultValue === "string") return {
|
|
511
512
|
parsedValue: value,
|
|
513
|
+
parsedValueId: configState.valueId,
|
|
512
514
|
requestedType,
|
|
513
515
|
usedDefault: false,
|
|
514
516
|
reason: "found-match"
|
|
515
517
|
};
|
|
516
|
-
if (typeof defaultValue === "boolean" && configState.type === "boolean") {
|
|
518
|
+
if (typeof defaultValue === "boolean" && (configState.type === "boolean" || configState.type === "string")) {
|
|
517
519
|
const boolValue = parseConfigBoolean(value);
|
|
518
520
|
const hasBoolean = typeof boolValue === "boolean";
|
|
519
521
|
return {
|
|
520
522
|
parsedValue: hasBoolean ? boolValue : defaultValue,
|
|
523
|
+
parsedValueId: hasBoolean ? configState.valueId : void 0,
|
|
521
524
|
requestedType,
|
|
522
525
|
usedDefault: !hasBoolean,
|
|
523
526
|
reason: hasBoolean ? "found-match" : "invalid-boolean"
|
|
@@ -528,16 +531,18 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
528
531
|
const hasNumber = typeof numValue === "number";
|
|
529
532
|
return {
|
|
530
533
|
parsedValue: hasNumber ? numValue : defaultValue,
|
|
534
|
+
parsedValueId: hasNumber ? configState.valueId : void 0,
|
|
531
535
|
requestedType,
|
|
532
536
|
usedDefault: !hasNumber,
|
|
533
537
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
534
538
|
};
|
|
535
539
|
}
|
|
536
|
-
if (isNumericNativeType(typeof defaultValue) && (configState.type === "float" || configState.type === "enum")) {
|
|
540
|
+
if (isNumericNativeType(typeof defaultValue) && (configState.type === "float" || configState.type === "enum" || configState.type === "string")) {
|
|
537
541
|
const numValue = parseConfigFloat(value);
|
|
538
542
|
const hasNumber = typeof numValue === "number";
|
|
539
543
|
return {
|
|
540
544
|
parsedValue: hasNumber ? numValue : defaultValue,
|
|
545
|
+
parsedValueId: hasNumber ? configState.valueId : void 0,
|
|
541
546
|
requestedType,
|
|
542
547
|
usedDefault: !hasNumber,
|
|
543
548
|
reason: hasNumber ? "found-match" : "invalid-number"
|
|
@@ -545,6 +550,7 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
545
550
|
}
|
|
546
551
|
return {
|
|
547
552
|
parsedValue: value,
|
|
553
|
+
parsedValueId: configState.valueId,
|
|
548
554
|
requestedType,
|
|
549
555
|
usedDefault: false,
|
|
550
556
|
reason: "found-match"
|
|
@@ -568,9 +574,10 @@ const parseConfigFloat = (value) => {
|
|
|
568
574
|
if (isNaN(num) || !isFinite(num)) return;
|
|
569
575
|
return num;
|
|
570
576
|
};
|
|
571
|
-
const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
577
|
+
const parseJson = (jsonString, defaultValue, requestedType, valueId) => {
|
|
572
578
|
if (typeof defaultValue === "string") return {
|
|
573
579
|
parsedValue: jsonString,
|
|
580
|
+
parsedValueId: valueId,
|
|
574
581
|
requestedType,
|
|
575
582
|
usedDefault: false,
|
|
576
583
|
reason: "found-match"
|
|
@@ -578,6 +585,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
|
578
585
|
try {
|
|
579
586
|
return {
|
|
580
587
|
parsedValue: JSON.parse(jsonString),
|
|
588
|
+
parsedValueId: valueId,
|
|
581
589
|
requestedType,
|
|
582
590
|
usedDefault: false,
|
|
583
591
|
reason: "found-match"
|
|
@@ -585,6 +593,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
|
|
|
585
593
|
} catch {
|
|
586
594
|
return {
|
|
587
595
|
parsedValue: defaultValue,
|
|
596
|
+
parsedValueId: void 0,
|
|
588
597
|
requestedType,
|
|
589
598
|
usedDefault: true,
|
|
590
599
|
reason: "invalid-json"
|
|
@@ -859,18 +868,27 @@ var DefaultConfigDirectorClient = class {
|
|
|
859
868
|
const configKeys = Object.keys(configSet.configs);
|
|
860
869
|
if (!this.configSet || configSet.kind == "full") {
|
|
861
870
|
this.configSet = configSet;
|
|
862
|
-
this.
|
|
871
|
+
this.emit("configsUpdated", { keys: configKeys });
|
|
863
872
|
this.updateWatchers(configSet.configs);
|
|
864
873
|
} else {
|
|
865
874
|
this.configSet.configs = {
|
|
866
875
|
...this.configSet.configs,
|
|
867
876
|
...configSet.configs
|
|
868
877
|
};
|
|
869
|
-
this.
|
|
878
|
+
this.emit("configsUpdated", { keys: configKeys });
|
|
870
879
|
this.updateWatchers(configSet.configs);
|
|
871
880
|
}
|
|
872
881
|
this.logger.debug("[ConfigDirectorClient] ConfigSet updated from server:", { keys: configKeys });
|
|
873
882
|
});
|
|
883
|
+
this.registerHandler("clientReady", clientOptions?.hooks?.clientReady);
|
|
884
|
+
this.registerHandler("configEvaluated", clientOptions?.hooks?.configEvaluated);
|
|
885
|
+
this.registerHandler("configsUpdated", clientOptions?.hooks?.configsUpdated);
|
|
886
|
+
this.registerHandler("contextUpdated", clientOptions?.hooks?.contextUpdated);
|
|
887
|
+
}
|
|
888
|
+
registerHandler(event, handler) {
|
|
889
|
+
if (!handler) return;
|
|
890
|
+
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
891
|
+
for (const h of handlers) if (typeof h === "function") this.on(event, h);
|
|
874
892
|
}
|
|
875
893
|
getTransportConstructor(connectionMode) {
|
|
876
894
|
switch (connectionMode) {
|
|
@@ -897,14 +915,14 @@ var DefaultConfigDirectorClient = class {
|
|
|
897
915
|
}).then(() => {
|
|
898
916
|
this.ready = true;
|
|
899
917
|
this.initializing = false;
|
|
900
|
-
this.
|
|
918
|
+
this.emit("clientReady", { action: caller });
|
|
901
919
|
this.logger.debug("[ConfigDirectorClient] Received initial payload from the server, client is ready");
|
|
902
920
|
});
|
|
903
921
|
const startTime = (/* @__PURE__ */ new Date()).getTime();
|
|
904
922
|
await this.transport.connect(context ?? {}, this.timeout);
|
|
905
923
|
this.currentContext = context;
|
|
906
924
|
this.telemetryClient.updateContext(context);
|
|
907
|
-
this.
|
|
925
|
+
this.emit("contextUpdated", { context });
|
|
908
926
|
const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - startTime;
|
|
909
927
|
const remainingTimeout = this.timeout - elapsedTime;
|
|
910
928
|
clearTimeout(this.timeoutTimer);
|
|
@@ -954,6 +972,7 @@ var DefaultConfigDirectorClient = class {
|
|
|
954
972
|
getValueFromConfigState(configKey, configState, defaultValue) {
|
|
955
973
|
if (!configState) {
|
|
956
974
|
this.logger.debug(`[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`);
|
|
975
|
+
const reason = this.isReady ? "config-state-missing" : "client-not-ready";
|
|
957
976
|
this.telemetryClient.evaluatedConfig({
|
|
958
977
|
contextId: this.currentContext?.id,
|
|
959
978
|
key: configKey,
|
|
@@ -961,7 +980,14 @@ var DefaultConfigDirectorClient = class {
|
|
|
961
980
|
requestedType: getRequestedType(defaultValue),
|
|
962
981
|
evaluatedValue: defaultValue,
|
|
963
982
|
usedDefault: true,
|
|
964
|
-
evaluationReason:
|
|
983
|
+
evaluationReason: reason
|
|
984
|
+
});
|
|
985
|
+
this.dispatchEvaluationEvent({
|
|
986
|
+
key: configKey,
|
|
987
|
+
value: defaultValue,
|
|
988
|
+
isDefaultValue: true,
|
|
989
|
+
reason,
|
|
990
|
+
context: this.context
|
|
965
991
|
});
|
|
966
992
|
return defaultValue;
|
|
967
993
|
}
|
|
@@ -973,12 +999,31 @@ var DefaultConfigDirectorClient = class {
|
|
|
973
999
|
defaultValue,
|
|
974
1000
|
requestedType: parseResult.requestedType,
|
|
975
1001
|
evaluatedValue: parseResult.parsedValue,
|
|
1002
|
+
evaluatedValueId: parseResult.parsedValueId,
|
|
976
1003
|
usedDefault: parseResult.usedDefault,
|
|
977
1004
|
evaluationReason: parseResult.reason
|
|
978
1005
|
});
|
|
1006
|
+
this.dispatchEvaluationEvent({
|
|
1007
|
+
key: configKey,
|
|
1008
|
+
value: parseResult.parsedValue,
|
|
1009
|
+
valueId: parseResult.parsedValueId ?? void 0,
|
|
1010
|
+
isDefaultValue: parseResult.usedDefault,
|
|
1011
|
+
reason: parseResult.reason,
|
|
1012
|
+
context: this.context
|
|
1013
|
+
});
|
|
979
1014
|
this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);
|
|
980
1015
|
return parseResult.parsedValue;
|
|
981
1016
|
}
|
|
1017
|
+
dispatchEvaluationEvent(event) {
|
|
1018
|
+
setTimeout(() => this.emit("configEvaluated", { evaluation: event }), 0);
|
|
1019
|
+
}
|
|
1020
|
+
emit(name, payload) {
|
|
1021
|
+
try {
|
|
1022
|
+
this.eventEmitter.emit(name, payload);
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
this.logger.error(`[ConfigDirectorClient] Error executing event handlers for '${name}'`, error);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
982
1027
|
parseUrl(url, urlFactory) {
|
|
983
1028
|
if (!url) return;
|
|
984
1029
|
try {
|
|
@@ -1037,25 +1082,27 @@ var DefaultConfigDirectorClient = class {
|
|
|
1037
1082
|
//#endregion
|
|
1038
1083
|
//#region ../shared/src/telemetry/utils.ts
|
|
1039
1084
|
const CONFIG_VALUE_MAX_LENGTH = 500;
|
|
1040
|
-
const
|
|
1041
|
-
if (type === "json" || type == null && typeof value === "object")
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1085
|
+
const mapToTelemetryValue = (ev) => {
|
|
1086
|
+
if (ev.type === "json" || ev.type == null && typeof ev.value === "object") {
|
|
1087
|
+
if (ev.valueId) return {
|
|
1088
|
+
valueId: ev.valueId,
|
|
1089
|
+
type: "json"
|
|
1090
|
+
};
|
|
1091
|
+
try {
|
|
1092
|
+
return {
|
|
1093
|
+
value: JSON.stringify(ev.value),
|
|
1094
|
+
type: "json"
|
|
1095
|
+
};
|
|
1096
|
+
} catch {
|
|
1097
|
+
return {
|
|
1098
|
+
value: ev.value.toString(),
|
|
1099
|
+
type: "json"
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1057
1102
|
}
|
|
1058
|
-
|
|
1103
|
+
const stringValue = ev.value?.toString();
|
|
1104
|
+
if (stringValue && stringValue.length <= CONFIG_VALUE_MAX_LENGTH) return { value: stringValue };
|
|
1105
|
+
return ev.valueId ? { valueId: ev.valueId } : { value: stringValue };
|
|
1059
1106
|
};
|
|
1060
1107
|
//#endregion
|
|
1061
1108
|
//#region ../js-browser-client/src/telemetry/WebWorkerTelemetryClient.ts
|
|
@@ -1077,7 +1124,7 @@ var WebWorkerTelemetryClient = class {
|
|
|
1077
1124
|
flushIntervalDelay: options.flushIntervalDelay ?? 3e4
|
|
1078
1125
|
}
|
|
1079
1126
|
};
|
|
1080
|
-
this.worker = new Worker(URL.createObjectURL(new Blob(["(function(){let e=(e,
|
|
1127
|
+
this.worker = new Worker(URL.createObjectURL(new Blob(["(function(){let e=async(e,t)=>e.valueId?{valueId:e.valueId}:e.value?e.value.length<=500&&e.type!=`json`?{value:e.value}:{valueId:await t(e.value)}:e,t=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},n=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},r=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},i=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var a=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=e.urlFactory(`client/telemetry/v1`,e.baseUrl)}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){if(!this.executeRequests)return{success:!1,fatalError:!0};let i={clientSdkKey:this.sdkKey,context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(this.isReportEmpty(i))return{success:!0,fatalError:!1};let a=await this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return t(e.discreteEvents)&&t(e.aggregatedEvents)&&n(e.droppedEvents)}async sendReport(e){try{let t=await r(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=i(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},o=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},s=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||=new Date;let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},c=class{constructor(e){this.aggregator=new o,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new s(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}async flushAndScheduleNext(){(await this.flush()).fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay)}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),await this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},l=class{constructor(e){this.valueIdGenerator=e,this.valueIdGenerator=e}async process(t){let n=await Promise.all(t.events.map(async t=>({...t,defaultValue:await e(t.defaultValue,this.valueIdGenerator),evaluatedValue:await e(t.evaluatedValue,this.valueIdGenerator)})));return{...t,events:n}}},u=class extends c{constructor(e){super(e),this.reporter=new a(e),this.evaluationEventSnapshotPreprocessor=new l(e.valueIdGenerator)}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(e)}async forceFlush(){this.cancelScheduledFlush(),await this.flushAndScheduleNext()}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=await this.evaluationEventSnapshotPreprocessor.process(this.evaluationEventQueue.takeSnapshot());return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount}})}};let d=(e,t)=>new URL(e,t?.toString()),f=(e,t)=>{let n=BigInt(0),r=BigInt(62),i=Array.from(e).map(e=>e.toString(16).padStart(2,`0`)).join(``),a=BigInt(`0x`+i),o=``;for(;a>n;)o=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`[Number(a%r)]+o,a/=r;return o.padStart(t,`0`)},p=Math.ceil(128/Math.log2(62)),m=new TextEncoder,h=async e=>{let t=await crypto.subtle.digest(`SHA-256`,m.encode(e?.toString()??``));return f(new Uint8Array(t,0,16),p)},g,_=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),v=e=>{try{self.postMessage(e)}catch{self.postMessage(_(e.payload.level,e.payload.message))}},y={debug:function(e,...t){v(_(`debug`,e,t))},info:function(e,...t){v(_(`info`,e,t))},warn:function(e,...t){v(_(`warn`,e,t))},error:function(e,...t){v(_(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!g){y.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(y.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:g=new u({...t.payload,baseUrl:d(t.payload.baseUrl),urlFactory:d,valueIdGenerator:h,logger:y});break;case`EvaluatedConfigEvent`:g?.evaluatedConfig(t.payload);break;case`UpdateContext`:g?.updateContext(t.payload.context);break;case`Flush`:g?.forceFlush();break;case`Close`:(g?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"], { type: "application/javascript" })));
|
|
1081
1128
|
this.worker.onmessage = (message) => {
|
|
1082
1129
|
this.handleWorkerEvent(message.data);
|
|
1083
1130
|
};
|
|
@@ -1108,8 +1155,15 @@ var WebWorkerTelemetryClient = class {
|
|
|
1108
1155
|
sanitizeEvaluatedConfigEvent(event) {
|
|
1109
1156
|
return {
|
|
1110
1157
|
...event,
|
|
1111
|
-
defaultValue:
|
|
1112
|
-
|
|
1158
|
+
defaultValue: mapToTelemetryValue({
|
|
1159
|
+
value: event.defaultValue,
|
|
1160
|
+
type: event.type
|
|
1161
|
+
}),
|
|
1162
|
+
evaluatedValue: mapToTelemetryValue({
|
|
1163
|
+
value: event.evaluatedValue,
|
|
1164
|
+
valueId: event.evaluatedValueId,
|
|
1165
|
+
type: event.type
|
|
1166
|
+
})
|
|
1113
1167
|
};
|
|
1114
1168
|
}
|
|
1115
1169
|
handleWorkerEvent(event) {
|
|
@@ -1151,6 +1205,29 @@ var WebWorkerTelemetryClient = class {
|
|
|
1151
1205
|
}
|
|
1152
1206
|
};
|
|
1153
1207
|
//#endregion
|
|
1208
|
+
//#region ../shared/src/base62-encoder.ts
|
|
1209
|
+
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
1210
|
+
const toBase62 = (buf, length) => {
|
|
1211
|
+
const ZERO = BigInt(0);
|
|
1212
|
+
const BASE = BigInt(62);
|
|
1213
|
+
const hex = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1214
|
+
let n = BigInt("0x" + hex);
|
|
1215
|
+
let result = "";
|
|
1216
|
+
while (n > ZERO) {
|
|
1217
|
+
result = BASE62[Number(n % BASE)] + result;
|
|
1218
|
+
n /= BASE;
|
|
1219
|
+
}
|
|
1220
|
+
return result.padStart(length, "0");
|
|
1221
|
+
};
|
|
1222
|
+
const BASE62_LENGTH = Math.ceil(128 / Math.log2(62));
|
|
1223
|
+
//#endregion
|
|
1224
|
+
//#region ../js-browser-client/src/telemetry/value-id-generator.ts
|
|
1225
|
+
const encoder = new TextEncoder();
|
|
1226
|
+
const generateValueId = async (v) => {
|
|
1227
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", encoder.encode(v?.toString() ?? ""));
|
|
1228
|
+
return toBase62(new Uint8Array(hashBuffer, 0, 16), BASE62_LENGTH);
|
|
1229
|
+
};
|
|
1230
|
+
//#endregion
|
|
1154
1231
|
//#region ../js-browser-client/src/BrowserConfigDirectorClient.ts
|
|
1155
1232
|
const createBrowserClient = (clientSdkKey, sdkOptions, clientOptions) => {
|
|
1156
1233
|
const logger = clientOptions?.logger ?? createDefaultLogger$1();
|
|
@@ -1158,7 +1235,8 @@ const createBrowserClient = (clientSdkKey, sdkOptions, clientOptions) => {
|
|
|
1158
1235
|
sdkKey: clientSdkKey,
|
|
1159
1236
|
logger,
|
|
1160
1237
|
baseUrl: parseUrl(clientOptions?.connection?.url) ?? "https://client-sdk-api.configdirector.com",
|
|
1161
|
-
urlFactory: defaultUrlFactory
|
|
1238
|
+
urlFactory: defaultUrlFactory,
|
|
1239
|
+
valueIdGenerator: generateValueId
|
|
1162
1240
|
}), clientSdkKey, sdkOptions, {
|
|
1163
1241
|
...clientOptions,
|
|
1164
1242
|
logger
|
|
@@ -1217,7 +1295,7 @@ var plugin_client_default = defineNuxtPlugin((nuxtApp) => {
|
|
|
1217
1295
|
const { clientSdkKey, appName, appVersion, baseUrl } = runtimeConfig.public.configdirector;
|
|
1218
1296
|
const client = createBrowserClient(clientSdkKey, {
|
|
1219
1297
|
sdkName: "nuxt-sdk",
|
|
1220
|
-
sdkVersion: "0.
|
|
1298
|
+
sdkVersion: "0.9.0"
|
|
1221
1299
|
}, {
|
|
1222
1300
|
metadata: {
|
|
1223
1301
|
appName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as ConfigDirectorClient } from "../../types-CzmBIfqo.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/runtime/server/composables/useConfigDirectorClient.d.ts
|
|
4
4
|
declare const useConfigDirectorClient: () => ConfigDirectorClient;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as ConfigDirectorClient } from "../../types-CzmBIfqo.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/runtime/server/composables/useConfigDirectorClient.d.ts
|
|
4
4
|
declare const useConfigDirectorClient: () => ConfigDirectorClient;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { t as ClientHooks } from "../../types-CzmBIfqo.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime/server/composables/useConfigDirectorServerHooks.d.ts
|
|
4
|
+
declare const useConfigDirectorServerHooks: (hooks: ClientHooks) => void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { useConfigDirectorServerHooks };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { t as ClientHooks } from "../../types-CzmBIfqo.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime/server/composables/useConfigDirectorServerHooks.d.ts
|
|
4
|
+
declare const useConfigDirectorServerHooks: (hooks: ClientHooks) => void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { useConfigDirectorServerHooks };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useNitroApp } from "nitropack/runtime";
|
|
2
|
+
//#region src/runtime/server/composables/useConfigDirectorServerHooks.ts
|
|
3
|
+
const useConfigDirectorServerHooks = (hooks) => {
|
|
4
|
+
const client = useNitroApp().configDirectorClient;
|
|
5
|
+
if (!client) throw new Error("The ConfigDirector Nitro plugin was not initialized");
|
|
6
|
+
const entries = Object.entries(hooks);
|
|
7
|
+
for (const [event, handler] of entries) {
|
|
8
|
+
if (!handler) continue;
|
|
9
|
+
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
10
|
+
for (const h of handlers) client.on(event, h);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { useConfigDirectorServerHooks };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as ConfigValueType, n as ConfigEvaluation, t as ConfigDirectorContext } from "./types-CHmsCChN.mjs";
|
|
2
2
|
|
|
3
3
|
//#region ../js-client-core/src/Emitter.d.ts
|
|
4
4
|
type EventType = string | symbol;
|
|
@@ -11,7 +11,14 @@ interface EventProvider<TEventsRecord extends EventsRecord> {
|
|
|
11
11
|
}
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region ../js-client-core/src/types.d.ts
|
|
14
|
+
type ClientHooks = {
|
|
15
|
+
clientReady?: HookHandler<"clientReady"> | HookHandler<"clientReady">[];
|
|
16
|
+
configsUpdated?: HookHandler<"configsUpdated"> | HookHandler<"configsUpdated">[];
|
|
17
|
+
contextUpdated?: HookHandler<"contextUpdated"> | HookHandler<"contextUpdated">[];
|
|
18
|
+
configEvaluated?: HookHandler<"configEvaluated"> | HookHandler<"configEvaluated">[];
|
|
19
|
+
};
|
|
14
20
|
type ClientConnectAction = "initialization" | "context update" | "network resume";
|
|
21
|
+
type HookHandler<TEvent extends keyof ClientEvents> = (payload: ClientEvents[TEvent]) => void;
|
|
15
22
|
type ClientEvents = {
|
|
16
23
|
configsUpdated: {
|
|
17
24
|
keys: string[];
|
|
@@ -22,6 +29,9 @@ type ClientEvents = {
|
|
|
22
29
|
contextUpdated: {
|
|
23
30
|
context: ConfigDirectorContext | undefined;
|
|
24
31
|
};
|
|
32
|
+
configEvaluated: {
|
|
33
|
+
evaluation: ConfigEvaluation;
|
|
34
|
+
};
|
|
25
35
|
};
|
|
26
36
|
type WatchHandler<T extends ConfigValueType> = (message: T) => void;
|
|
27
37
|
/**
|
|
@@ -132,4 +142,4 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
|
|
|
132
142
|
dispose(): void;
|
|
133
143
|
}
|
|
134
144
|
//#endregion
|
|
135
|
-
export { ConfigDirectorClient as t };
|
|
145
|
+
export { ConfigDirectorClient as n, ClientHooks as t };
|
|
@@ -10,6 +10,16 @@ type ConfigState = {
|
|
|
10
10
|
key: string;
|
|
11
11
|
type: ConfigType;
|
|
12
12
|
value: string | undefined | null;
|
|
13
|
+
valueId?: string | undefined | null;
|
|
14
|
+
};
|
|
15
|
+
type EvaluationReason = "found-match" | "config-state-missing" | "client-not-ready" | "type-mismatch" | "value-missing" | "invalid-number" | "invalid-json" | "invalid-boolean";
|
|
16
|
+
type ConfigEvaluation = {
|
|
17
|
+
key: string;
|
|
18
|
+
value: ConfigValueType;
|
|
19
|
+
valueId?: string;
|
|
20
|
+
isDefaultValue: boolean;
|
|
21
|
+
reason: EvaluationReason;
|
|
22
|
+
context?: ConfigDirectorContext;
|
|
13
23
|
};
|
|
14
24
|
/**
|
|
15
25
|
* The user's context to be sent to ConfigDirector. This context will be used for targeting
|
|
@@ -47,4 +57,4 @@ type ConfigDirectorContext = {
|
|
|
47
57
|
anonymous?: boolean;
|
|
48
58
|
};
|
|
49
59
|
//#endregion
|
|
50
|
-
export {
|
|
60
|
+
export { ConfigValueType as i, ConfigEvaluation as n, ConfigState as r, ConfigDirectorContext as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as
|
|
1
|
+
import { i as ConfigValueType, n as ConfigEvaluation, r as ConfigState, t as ConfigDirectorContext } from "./types-CHmsCChN.mjs";
|
|
2
2
|
|
|
3
3
|
//#region ../js-server-sdk/src/types.d.ts
|
|
4
4
|
type ClientEvents = {
|
|
@@ -6,7 +6,16 @@ type ClientEvents = {
|
|
|
6
6
|
keys: string[];
|
|
7
7
|
};
|
|
8
8
|
clientReady: undefined;
|
|
9
|
+
configEvaluated: {
|
|
10
|
+
evaluation: ConfigEvaluation;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
type ClientHooks = {
|
|
14
|
+
clientReady?: HookHandler<"clientReady"> | HookHandler<"clientReady">[];
|
|
15
|
+
configsUpdated?: HookHandler<"configsUpdated"> | HookHandler<"configsUpdated">[];
|
|
16
|
+
configEvaluated?: HookHandler<"configEvaluated"> | HookHandler<"configEvaluated">[];
|
|
9
17
|
};
|
|
18
|
+
type HookHandler<TEvent extends keyof ClientEvents> = (payload: ClientEvents[TEvent]) => void;
|
|
10
19
|
type WatchHandler<T extends ConfigValueType> = (message: T) => void;
|
|
11
20
|
/**
|
|
12
21
|
* The ConfigDirector SDK client object.
|
|
@@ -93,4 +102,4 @@ interface ConfigDirectorClient {
|
|
|
93
102
|
dispose(): void;
|
|
94
103
|
}
|
|
95
104
|
//#endregion
|
|
96
|
-
export { ConfigDirectorClient as t };
|
|
105
|
+
export { ConfigDirectorClient as n, ClientHooks as t };
|
package/package.json
CHANGED