@configdirector/nuxt-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/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "0.7.0"
7
+ "version": "0.8.0"
8
8
  }
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../../types-BkRZRTVv.mjs";
1
+ import { t as ConfigDirectorClient } from "../../types-DE44nrVT.mjs";
2
2
 
3
3
  //#region src/runtime/app/composables/useConfigDirectorClient.d.ts
4
4
  declare const useConfigDirectorClient: () => {
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../../types-BkRZRTVv.mjs";
1
+ import { t as ConfigDirectorClient } from "../../types-DE44nrVT.mjs";
2
2
 
3
3
  //#region src/runtime/app/composables/useConfigDirectorClient.d.ts
4
4
  declare const useConfigDirectorClient: () => {
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorContext } from "../../types-f2z6DgES.mjs";
1
+ import { t as ConfigDirectorContext } from "../../types-BqdrorBY.mjs";
2
2
  import { Ref } from "vue";
3
3
 
4
4
  //#region src/runtime/app/composables/useConfigDirectorContext.d.ts
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorContext } from "../../types-f2z6DgES.mjs";
1
+ import { t as ConfigDirectorContext } from "../../types-BqdrorBY.mjs";
2
2
  import { Ref } from "vue";
3
3
 
4
4
  //#region src/runtime/app/composables/useConfigDirectorContext.d.ts
@@ -1,4 +1,4 @@
1
- import { r as ConfigValueType } from "../../types-f2z6DgES.mjs";
1
+ import { r as ConfigValueType } from "../../types-BqdrorBY.mjs";
2
2
  import { t as ClientStatus } from "../../types-CkzBXJwA.mjs";
3
3
  import { DeepReadonly, Ref, ShallowRef } from "vue";
4
4
 
@@ -1,4 +1,4 @@
1
- import { r as ConfigValueType } from "../../types-f2z6DgES.mjs";
1
+ import { r as ConfigValueType } from "../../types-BqdrorBY.mjs";
2
2
  import { t as ClientStatus } from "../../types-CkzBXJwA.mjs";
3
3
  import { DeepReadonly, Ref, ShallowRef } from "vue";
4
4
 
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../types-BDwzL0dp.mjs";
1
+ import { t as ConfigDirectorClient } from "../types-Ce8HdKnT.mjs";
2
2
  import * as _$nitropack from "nitropack";
3
3
 
4
4
  //#region src/runtime/nitro/plugin.d.ts
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../types-BDwzL0dp.mjs";
1
+ import { t as ConfigDirectorClient } from "../types-Ce8HdKnT.mjs";
2
2
  import * as _$nitropack from "nitropack";
3
3
 
4
4
  //#region src/runtime/nitro/plugin.d.ts
@@ -849,13 +849,15 @@ const parseConfigValue = (configState, defaultValue) => {
849
849
  const requestedType = getRequestedType(defaultValue);
850
850
  if (!value) return {
851
851
  parsedValue: defaultValue,
852
+ parsedValueId: void 0,
852
853
  requestedType,
853
854
  usedDefault: true,
854
855
  reason: "value-missing"
855
856
  };
856
- if (configState.type === "json") return parseJson(value, defaultValue, requestedType);
857
+ if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
857
858
  if (typeof defaultValue === "string") return {
858
859
  parsedValue: value,
860
+ parsedValueId: configState.valueId,
859
861
  requestedType,
860
862
  usedDefault: false,
861
863
  reason: "found-match"
@@ -865,6 +867,7 @@ const parseConfigValue = (configState, defaultValue) => {
865
867
  const hasBoolean = typeof boolValue === "boolean";
866
868
  return {
867
869
  parsedValue: hasBoolean ? boolValue : defaultValue,
870
+ parsedValueId: hasBoolean ? configState.valueId : void 0,
868
871
  requestedType,
869
872
  usedDefault: !hasBoolean,
870
873
  reason: hasBoolean ? "found-match" : "invalid-boolean"
@@ -875,6 +878,7 @@ const parseConfigValue = (configState, defaultValue) => {
875
878
  const hasNumber = typeof numValue === "number";
876
879
  return {
877
880
  parsedValue: hasNumber ? numValue : defaultValue,
881
+ parsedValueId: hasNumber ? configState.valueId : void 0,
878
882
  requestedType,
879
883
  usedDefault: !hasNumber,
880
884
  reason: hasNumber ? "found-match" : "invalid-number"
@@ -885,6 +889,7 @@ const parseConfigValue = (configState, defaultValue) => {
885
889
  const hasNumber = typeof numValue === "number";
886
890
  return {
887
891
  parsedValue: hasNumber ? numValue : defaultValue,
892
+ parsedValueId: hasNumber ? configState.valueId : void 0,
888
893
  requestedType,
889
894
  usedDefault: !hasNumber,
890
895
  reason: hasNumber ? "found-match" : "invalid-number"
@@ -892,6 +897,7 @@ const parseConfigValue = (configState, defaultValue) => {
892
897
  }
893
898
  return {
894
899
  parsedValue: value,
900
+ parsedValueId: configState.valueId,
895
901
  requestedType,
896
902
  usedDefault: false,
897
903
  reason: "found-match"
@@ -915,9 +921,10 @@ const parseConfigFloat = (value) => {
915
921
  if (isNaN(num) || !isFinite(num)) return;
916
922
  return num;
917
923
  };
918
- const parseJson = (jsonString, defaultValue, requestedType) => {
924
+ const parseJson = (jsonString, defaultValue, requestedType, valueId) => {
919
925
  if (typeof defaultValue === "string") return {
920
926
  parsedValue: jsonString,
927
+ parsedValueId: valueId,
921
928
  requestedType,
922
929
  usedDefault: false,
923
930
  reason: "found-match"
@@ -925,6 +932,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
925
932
  try {
926
933
  return {
927
934
  parsedValue: JSON.parse(jsonString),
935
+ parsedValueId: valueId,
928
936
  requestedType,
929
937
  usedDefault: false,
930
938
  reason: "found-match"
@@ -932,6 +940,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
932
940
  } catch {
933
941
  return {
934
942
  parsedValue: defaultValue,
943
+ parsedValueId: void 0,
935
944
  requestedType,
936
945
  usedDefault: true,
937
946
  reason: "invalid-json"
@@ -1007,25 +1016,35 @@ const defaultUrlFactory = (input, base) => new URL(input, base?.toString());
1007
1016
  //#endregion
1008
1017
  //#region ../shared/src/telemetry/utils.ts
1009
1018
  const CONFIG_VALUE_MAX_LENGTH = 500;
1010
- const sanitizeValue = (value, type) => {
1011
- if (type === "json" || type == null && typeof value === "object") try {
1012
- return { digest: djb2Hash(JSON.stringify(value)) };
1013
- } catch {
1014
- return { digest: value.toString() };
1019
+ const compactTelemetryValue = async (tv, valueIdGenerator) => {
1020
+ if (tv.valueId) return { valueId: tv.valueId };
1021
+ if (tv.value) {
1022
+ if (tv.value.length <= CONFIG_VALUE_MAX_LENGTH && tv.type != "json") return { value: tv.value };
1023
+ return { valueId: await valueIdGenerator(tv.value) };
1015
1024
  }
1016
- if (value.toString().length > CONFIG_VALUE_MAX_LENGTH) return { digest: djb2Hash(value.toString()) };
1017
- else return { value: value.toString() };
1018
- };
1019
- const djb2Hash = (data) => {
1020
- return djb2(new TextEncoder().encode(data)).toString(16).padStart(8, "0");
1025
+ return tv;
1021
1026
  };
1022
- const djb2 = (bytes) => {
1023
- let hash = 5381;
1024
- for (let i = 0; i < bytes.length; i++) {
1025
- hash = (hash << 5) + hash + (bytes[i] ?? 0);
1026
- hash = hash >>> 0;
1027
+ const mapToTelemetryValue = (ev) => {
1028
+ if (ev.type === "json" || ev.type == null && typeof ev.value === "object") {
1029
+ if (ev.valueId) return {
1030
+ valueId: ev.valueId,
1031
+ type: "json"
1032
+ };
1033
+ try {
1034
+ return {
1035
+ value: JSON.stringify(ev.value),
1036
+ type: "json"
1037
+ };
1038
+ } catch {
1039
+ return {
1040
+ value: ev.value.toString(),
1041
+ type: "json"
1042
+ };
1043
+ }
1027
1044
  }
1028
- return hash;
1045
+ const stringValue = ev.value?.toString();
1046
+ if (stringValue && stringValue.length <= CONFIG_VALUE_MAX_LENGTH) return { value: stringValue };
1047
+ return ev.valueId ? { valueId: ev.valueId } : { value: stringValue };
1029
1048
  };
1030
1049
  const isEventListEmpty = (eventList) => {
1031
1050
  const keys = Object.keys(eventList);
@@ -1189,9 +1208,6 @@ var TelemetryEventCollector = class {
1189
1208
  const initialDelay = options.initialFlushIntervalDelay ?? 5e3;
1190
1209
  this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), initialDelay);
1191
1210
  }
1192
- sanitizeValue(value, type) {
1193
- return sanitizeValue(value, type);
1194
- }
1195
1211
  async flushAndScheduleNext() {
1196
1212
  if ((await this.flush()).fatalError) {
1197
1213
  this.collectEvents = false;
@@ -1199,19 +1215,6 @@ var TelemetryEventCollector = class {
1199
1215
  this.logger.warn("[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.");
1200
1216
  } else this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.flushIntervalDelay);
1201
1217
  }
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
1218
  cancelScheduledFlush() {
1216
1219
  clearTimeout(this.flushTimeout);
1217
1220
  }
@@ -1258,9 +1261,31 @@ var LimitedMap = class extends Map {
1258
1261
  }
1259
1262
  };
1260
1263
  //#endregion
1264
+ //#region ../shared/src/telemetry/EvaluatedConfigEventPreprocessor.ts
1265
+ var EvaluatedConfigEventPreprocessor = class {
1266
+ constructor(valueIdGenerator) {
1267
+ this.valueIdGenerator = valueIdGenerator;
1268
+ this.valueIdGenerator = valueIdGenerator;
1269
+ }
1270
+ async process(snapshot) {
1271
+ const events = await Promise.all(snapshot.events.map(async (e) => {
1272
+ return {
1273
+ ...e,
1274
+ defaultValue: await compactTelemetryValue(e.defaultValue, this.valueIdGenerator),
1275
+ evaluatedValue: await compactTelemetryValue(e.evaluatedValue, this.valueIdGenerator)
1276
+ };
1277
+ }));
1278
+ return {
1279
+ ...snapshot,
1280
+ events
1281
+ };
1282
+ }
1283
+ };
1284
+ //#endregion
1261
1285
  //#region ../js-server-sdk/src/telemetry/ServerTelemetryEventCollector.ts
1262
1286
  const DEFAULT_CONTEXTS_LIMIT = 1e3;
1263
1287
  var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
1288
+ evaluationEventSnapshotPreprocessor;
1264
1289
  reporter;
1265
1290
  _context;
1266
1291
  contexts;
@@ -1268,6 +1293,7 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
1268
1293
  super(options);
1269
1294
  this.reporter = new ServerEventReporter(options);
1270
1295
  this.contexts = new LimitedMap(options.contextLimit ?? DEFAULT_CONTEXTS_LIMIT);
1296
+ this.evaluationEventSnapshotPreprocessor = new EvaluatedConfigEventPreprocessor(options.valueIdGenerator);
1271
1297
  }
1272
1298
  evaluatedConfig(payload) {
1273
1299
  if (!this.collectEvents) return;
@@ -1281,8 +1307,15 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
1281
1307
  sanitizeEvaluatedConfigEvent(event) {
1282
1308
  return {
1283
1309
  ...event,
1284
- defaultValue: this.sanitizeValue(event.defaultValue, event.type),
1285
- evaluatedValue: this.sanitizeValue(event.evaluatedValue, event.type)
1310
+ defaultValue: mapToTelemetryValue({
1311
+ value: event.defaultValue,
1312
+ type: event.type
1313
+ }),
1314
+ evaluatedValue: mapToTelemetryValue({
1315
+ value: event.evaluatedValue,
1316
+ valueId: event.evaluatedValueId,
1317
+ type: event.type
1318
+ })
1286
1319
  };
1287
1320
  }
1288
1321
  snapshotContexts() {
@@ -1299,7 +1332,7 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
1299
1332
  success: false,
1300
1333
  fatalError: false
1301
1334
  };
1302
- const evaluationSnapshot = this.evaluationEventQueue.takeSnapshot();
1335
+ const evaluationSnapshot = await this.evaluationEventSnapshotPreprocessor.process(this.evaluationEventQueue.takeSnapshot());
1303
1336
  const contextsSnapshot = this.snapshotContexts();
1304
1337
  return await this.reporter?.report({
1305
1338
  context: this._context,
@@ -1313,6 +1346,33 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
1313
1346
  }
1314
1347
  };
1315
1348
  //#endregion
1349
+ //#region ../shared/src/base62-encoder.ts
1350
+ const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1351
+ const toBase62 = (buf, length) => {
1352
+ const ZERO = BigInt(0);
1353
+ const BASE = BigInt(62);
1354
+ const hex = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
1355
+ let n = BigInt("0x" + hex);
1356
+ let result = "";
1357
+ while (n > ZERO) {
1358
+ result = BASE62[Number(n % BASE)] + result;
1359
+ n /= BASE;
1360
+ }
1361
+ return result.padStart(length, "0");
1362
+ };
1363
+ //#endregion
1364
+ //#region ../shared/src/value-id-generator.ts
1365
+ const DIGEST_BYTES = 16;
1366
+ const BASE62_LENGTH = Math.ceil(DIGEST_BYTES * 8 / Math.log2(62));
1367
+ const sha256 = async (value) => {
1368
+ const encoded = new TextEncoder().encode(value);
1369
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", encoded);
1370
+ return new Uint8Array(hashBuffer).subarray(0, DIGEST_BYTES);
1371
+ };
1372
+ const generateValueId = async (v) => {
1373
+ return toBase62(await sha256(v?.toString() ?? ""), BASE62_LENGTH);
1374
+ };
1375
+ //#endregion
1316
1376
  //#region ../js-server-sdk/src/DefaultConfigDirectorClient.ts
1317
1377
  const defaultBaseUrl = new URL("https://server-sdk-api.configdirector.com");
1318
1378
  const DEFAULT_FLUSH_INTERVAL = 3e4;
@@ -1347,7 +1407,8 @@ var DefaultConfigDirectorClient = class {
1347
1407
  flushIntervalDelay: clientOptions?.telemetry?.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
1348
1408
  evaluationQueueLimit: Math.ceil(queueLimit * .7),
1349
1409
  contextLimit: Math.floor(queueLimit * .3),
1350
- urlFactory: defaultUrlFactory
1410
+ urlFactory: defaultUrlFactory,
1411
+ valueIdGenerator: generateValueId
1351
1412
  });
1352
1413
  this.transport = new transportConstructor({
1353
1414
  serverSdkKey,
@@ -1460,6 +1521,7 @@ var DefaultConfigDirectorClient = class {
1460
1521
  defaultValue,
1461
1522
  requestedType: parseResult.requestedType,
1462
1523
  evaluatedValue: parseResult.parsedValue,
1524
+ evaluatedValueId: parseResult.parsedValueId,
1463
1525
  usedDefault: parseResult.usedDefault,
1464
1526
  evaluationReason: parseResult.reason
1465
1527
  }
@@ -1552,7 +1614,7 @@ var plugin_default = defineNitroPlugin((nitroApp) => {
1552
1614
  const { serverSdkKey, baseUrl } = runtimeConfig.configdirector;
1553
1615
  const client = new DefaultConfigDirectorClient(serverSdkKey, {
1554
1616
  sdkName: "nuxt-sdk",
1555
- sdkVersion: "0.7.0"
1617
+ sdkVersion: "0.8.0"
1556
1618
  }, {
1557
1619
  logger,
1558
1620
  ...baseUrl ? { connection: { url: baseUrl } } : {}
@@ -502,13 +502,15 @@ 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"
@@ -518,6 +520,7 @@ const parseConfigValue = (configState, defaultValue) => {
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,6 +531,7 @@ 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"
@@ -538,6 +542,7 @@ const parseConfigValue = (configState, defaultValue) => {
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"
@@ -973,6 +982,7 @@ var DefaultConfigDirectorClient = class {
973
982
  defaultValue,
974
983
  requestedType: parseResult.requestedType,
975
984
  evaluatedValue: parseResult.parsedValue,
985
+ evaluatedValueId: parseResult.parsedValueId,
976
986
  usedDefault: parseResult.usedDefault,
977
987
  evaluationReason: parseResult.reason
978
988
  });
@@ -1037,25 +1047,27 @@ var DefaultConfigDirectorClient = class {
1037
1047
  //#endregion
1038
1048
  //#region ../shared/src/telemetry/utils.ts
1039
1049
  const CONFIG_VALUE_MAX_LENGTH = 500;
1040
- const sanitizeValue = (value, type) => {
1041
- if (type === "json" || type == null && typeof value === "object") try {
1042
- return { digest: djb2Hash(JSON.stringify(value)) };
1043
- } catch {
1044
- return { digest: value.toString() };
1045
- }
1046
- if (value.toString().length > CONFIG_VALUE_MAX_LENGTH) return { digest: djb2Hash(value.toString()) };
1047
- else return { value: value.toString() };
1048
- };
1049
- const djb2Hash = (data) => {
1050
- return djb2(new TextEncoder().encode(data)).toString(16).padStart(8, "0");
1051
- };
1052
- const djb2 = (bytes) => {
1053
- let hash = 5381;
1054
- for (let i = 0; i < bytes.length; i++) {
1055
- hash = (hash << 5) + hash + (bytes[i] ?? 0);
1056
- hash = hash >>> 0;
1050
+ const mapToTelemetryValue = (ev) => {
1051
+ if (ev.type === "json" || ev.type == null && typeof ev.value === "object") {
1052
+ if (ev.valueId) return {
1053
+ valueId: ev.valueId,
1054
+ type: "json"
1055
+ };
1056
+ try {
1057
+ return {
1058
+ value: JSON.stringify(ev.value),
1059
+ type: "json"
1060
+ };
1061
+ } catch {
1062
+ return {
1063
+ value: ev.value.toString(),
1064
+ type: "json"
1065
+ };
1066
+ }
1057
1067
  }
1058
- return hash;
1068
+ const stringValue = ev.value?.toString();
1069
+ if (stringValue && stringValue.length <= CONFIG_VALUE_MAX_LENGTH) return { value: stringValue };
1070
+ return ev.valueId ? { valueId: ev.valueId } : { value: stringValue };
1059
1071
  };
1060
1072
  //#endregion
1061
1073
  //#region ../js-browser-client/src/telemetry/WebWorkerTelemetryClient.ts
@@ -1077,7 +1089,7 @@ var WebWorkerTelemetryClient = class {
1077
1089
  flushIntervalDelay: options.flushIntervalDelay ?? 3e4
1078
1090
  }
1079
1091
  };
1080
- this.worker = new Worker(URL.createObjectURL(new Blob(["(function(){let e=(e,n)=>{if(n===`json`||n==null&&typeof e==`object`)try{return{digest:t(JSON.stringify(e))}}catch{return{digest:e.toString()}}return e.toString().length>500?{digest:t(e.toString())}:{value:e.toString()}},t=e=>n(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),n=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+(e[n]??0),t>>>=0;return t},r=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},i=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},a=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}},o=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var s=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 r(e.discreteEvents)&&r(e.aggregatedEvents)&&i(e.droppedEvents)}async sendReport(e){try{let t=await a(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=o(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}},c=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}))}},l=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}},u=class{constructor(e){this.aggregator=new c,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new l(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(t,n){return e(t,n)}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)}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=this.evaluationEventQueue.takeSnapshot();return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount}})}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),await this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},d=class extends u{constructor(e){super(e),this.reporter=new s(e)}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()}};let f=(e,t)=>new URL(e,t?.toString()),p,m=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),h=e=>{try{self.postMessage(e)}catch{self.postMessage(m(e.payload.level,e.payload.message))}},g={debug:function(e,...t){h(m(`debug`,e,t))},info:function(e,...t){h(m(`info`,e,t))},warn:function(e,...t){h(m(`warn`,e,t))},error:function(e,...t){h(m(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!p){g.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(g.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:p=new d({...t.payload,baseUrl:f(t.payload.baseUrl),urlFactory:f,logger:g});break;case`EvaluatedConfigEvent`:p?.evaluatedConfig(t.payload);break;case`UpdateContext`:p?.updateContext(t.payload.context);break;case`Flush`:p?.forceFlush();break;case`Close`:(p?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"], { type: "application/javascript" })));
1092
+ 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=async e=>{let t=new TextEncoder().encode(e),n=await globalThis.crypto.subtle.digest(`SHA-256`,t);return new Uint8Array(n).subarray(0,16)},h=async e=>f(await m(e?.toString()??``),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
1093
  this.worker.onmessage = (message) => {
1082
1094
  this.handleWorkerEvent(message.data);
1083
1095
  };
@@ -1108,8 +1120,15 @@ var WebWorkerTelemetryClient = class {
1108
1120
  sanitizeEvaluatedConfigEvent(event) {
1109
1121
  return {
1110
1122
  ...event,
1111
- defaultValue: sanitizeValue(event.defaultValue, event.type),
1112
- evaluatedValue: sanitizeValue(event.evaluatedValue, event.type)
1123
+ defaultValue: mapToTelemetryValue({
1124
+ value: event.defaultValue,
1125
+ type: event.type
1126
+ }),
1127
+ evaluatedValue: mapToTelemetryValue({
1128
+ value: event.evaluatedValue,
1129
+ valueId: event.evaluatedValueId,
1130
+ type: event.type
1131
+ })
1113
1132
  };
1114
1133
  }
1115
1134
  handleWorkerEvent(event) {
@@ -1217,7 +1236,7 @@ var plugin_client_default = defineNuxtPlugin((nuxtApp) => {
1217
1236
  const { clientSdkKey, appName, appVersion, baseUrl } = runtimeConfig.public.configdirector;
1218
1237
  const client = createBrowserClient(clientSdkKey, {
1219
1238
  sdkName: "nuxt-sdk",
1220
- sdkVersion: "0.7.0"
1239
+ sdkVersion: "0.8.0"
1221
1240
  }, {
1222
1241
  metadata: {
1223
1242
  appName,
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../../types-BDwzL0dp.mjs";
1
+ import { t as ConfigDirectorClient } from "../../types-Ce8HdKnT.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 { t as ConfigDirectorClient } from "../../types-BDwzL0dp.mjs";
1
+ import { t as ConfigDirectorClient } from "../../types-Ce8HdKnT.mjs";
2
2
 
3
3
  //#region src/runtime/server/composables/useConfigDirectorClient.d.ts
4
4
  declare const useConfigDirectorClient: () => ConfigDirectorClient;
@@ -10,6 +10,7 @@ type ConfigState = {
10
10
  key: string;
11
11
  type: ConfigType;
12
12
  value: string | undefined | null;
13
+ valueId?: string | undefined | null;
13
14
  };
14
15
  /**
15
16
  * The user's context to be sent to ConfigDirector. This context will be used for targeting
@@ -1,4 +1,4 @@
1
- import { n as ConfigState, r as ConfigValueType, t as ConfigDirectorContext } from "./types-f2z6DgES.mjs";
1
+ import { n as ConfigState, r as ConfigValueType, t as ConfigDirectorContext } from "./types-BqdrorBY.mjs";
2
2
 
3
3
  //#region ../js-server-sdk/src/types.d.ts
4
4
  type ClientEvents = {
@@ -1,4 +1,4 @@
1
- import { r as ConfigValueType, t as ConfigDirectorContext } from "./types-f2z6DgES.mjs";
1
+ import { r as ConfigValueType, t as ConfigDirectorContext } from "./types-BqdrorBY.mjs";
2
2
 
3
3
  //#region ../js-client-core/src/Emitter.d.ts
4
4
  type EventType = string | symbol;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configdirector/nuxt-sdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Nuxt SDK for ConfigDirector. ConfigDirector is a remote configuration and feature flag service.",
5
5
  "license": "MIT",
6
6
  "author": "ConfigDirector",