@configdirector/nuxt-sdk 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -4,5 +4,5 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "0.6.0"
7
+ "version": "0.8.0"
8
8
  }
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../../types-D9IS8RoC.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-D9IS8RoC.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-BWxsGnk2.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-BWxsGnk2.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-BWxsGnk2.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-BWxsGnk2.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-DGQ1B6mZ.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-DGQ1B6mZ.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,21 +1293,29 @@ 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;
1274
- if (payload.context?.id) this.contexts.set(payload.context.id, payload.context);
1300
+ if (payload.context?.id && !payload.context.anonymous) this.contexts.set(payload.context.id, payload.context);
1275
1301
  const event = {
1276
1302
  ...payload.evaluation,
1277
- contextId: payload.context?.id
1303
+ contextId: payload.context?.anonymous ? void 0 : payload.context?.id
1278
1304
  };
1279
1305
  this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(event));
1280
1306
  }
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.6.0"
1617
+ sdkVersion: "0.8.0"
1556
1618
  }, {
1557
1619
  logger,
1558
1620
  ...baseUrl ? { connection: { url: baseUrl } } : {}
@@ -407,7 +407,8 @@ var StreamingTransport = class {
407
407
  body: JSON.stringify({
408
408
  givenContext: context,
409
409
  metaContext: this.options.metaContext,
410
- clientSdkKey: this.options.clientSdkKey
410
+ clientSdkKey: this.options.clientSdkKey,
411
+ instanceId: this.options.instanceId
411
412
  }),
412
413
  onMessage: ({ data }) => {
413
414
  this.dispatchMessage(data);
@@ -501,13 +502,15 @@ const parseConfigValue = (configState, defaultValue) => {
501
502
  const requestedType = getRequestedType(defaultValue);
502
503
  if (!value) return {
503
504
  parsedValue: defaultValue,
505
+ parsedValueId: void 0,
504
506
  requestedType,
505
507
  usedDefault: true,
506
508
  reason: "value-missing"
507
509
  };
508
- if (configState.type === "json") return parseJson(value, defaultValue, requestedType);
510
+ if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
509
511
  if (typeof defaultValue === "string") return {
510
512
  parsedValue: value,
513
+ parsedValueId: configState.valueId,
511
514
  requestedType,
512
515
  usedDefault: false,
513
516
  reason: "found-match"
@@ -517,6 +520,7 @@ const parseConfigValue = (configState, defaultValue) => {
517
520
  const hasBoolean = typeof boolValue === "boolean";
518
521
  return {
519
522
  parsedValue: hasBoolean ? boolValue : defaultValue,
523
+ parsedValueId: hasBoolean ? configState.valueId : void 0,
520
524
  requestedType,
521
525
  usedDefault: !hasBoolean,
522
526
  reason: hasBoolean ? "found-match" : "invalid-boolean"
@@ -527,6 +531,7 @@ const parseConfigValue = (configState, defaultValue) => {
527
531
  const hasNumber = typeof numValue === "number";
528
532
  return {
529
533
  parsedValue: hasNumber ? numValue : defaultValue,
534
+ parsedValueId: hasNumber ? configState.valueId : void 0,
530
535
  requestedType,
531
536
  usedDefault: !hasNumber,
532
537
  reason: hasNumber ? "found-match" : "invalid-number"
@@ -537,6 +542,7 @@ const parseConfigValue = (configState, defaultValue) => {
537
542
  const hasNumber = typeof numValue === "number";
538
543
  return {
539
544
  parsedValue: hasNumber ? numValue : defaultValue,
545
+ parsedValueId: hasNumber ? configState.valueId : void 0,
540
546
  requestedType,
541
547
  usedDefault: !hasNumber,
542
548
  reason: hasNumber ? "found-match" : "invalid-number"
@@ -544,6 +550,7 @@ const parseConfigValue = (configState, defaultValue) => {
544
550
  }
545
551
  return {
546
552
  parsedValue: value,
553
+ parsedValueId: configState.valueId,
547
554
  requestedType,
548
555
  usedDefault: false,
549
556
  reason: "found-match"
@@ -567,9 +574,10 @@ const parseConfigFloat = (value) => {
567
574
  if (isNaN(num) || !isFinite(num)) return;
568
575
  return num;
569
576
  };
570
- const parseJson = (jsonString, defaultValue, requestedType) => {
577
+ const parseJson = (jsonString, defaultValue, requestedType, valueId) => {
571
578
  if (typeof defaultValue === "string") return {
572
579
  parsedValue: jsonString,
580
+ parsedValueId: valueId,
573
581
  requestedType,
574
582
  usedDefault: false,
575
583
  reason: "found-match"
@@ -577,6 +585,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
577
585
  try {
578
586
  return {
579
587
  parsedValue: JSON.parse(jsonString),
588
+ parsedValueId: valueId,
580
589
  requestedType,
581
590
  usedDefault: false,
582
591
  reason: "found-match"
@@ -584,6 +593,7 @@ const parseJson = (jsonString, defaultValue, requestedType) => {
584
593
  } catch {
585
594
  return {
586
595
  parsedValue: defaultValue,
596
+ parsedValueId: void 0,
587
597
  requestedType,
588
598
  usedDefault: true,
589
599
  reason: "invalid-json"
@@ -764,6 +774,7 @@ var PollingTransport = class extends AbstractPollingTransport {
764
774
  givenContext: context,
765
775
  metaContext: this.options.metaContext,
766
776
  clientSdkKey: this.options.clientSdkKey,
777
+ instanceId: this.options.instanceId,
767
778
  lastUpdateTimestamp: this.lastUpdateTimestamp
768
779
  })
769
780
  }, this.logger);
@@ -824,7 +835,9 @@ var DefaultConfigDirectorClient = class {
824
835
  readyResolve;
825
836
  currentContext;
826
837
  connectionMode;
838
+ instanceId;
827
839
  constructor(telemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
840
+ this.instanceId = crypto.randomUUID();
828
841
  this.logger = clientOptions?.logger ?? createDefaultLogger$1();
829
842
  this.timeout = clientOptions?.connection?.timeout ?? 3e3;
830
843
  const urlFactory = internalClientOptions?.urlFactory ?? defaultUrlFactory;
@@ -833,6 +846,7 @@ var DefaultConfigDirectorClient = class {
833
846
  const transportConstructor = this.getTransportConstructor(this.connectionMode);
834
847
  this.telemetryClient = telemetryClient;
835
848
  this.transport = new transportConstructor({
849
+ instanceId: this.instanceId,
836
850
  clientSdkKey,
837
851
  baseUrl,
838
852
  resolveUrl: urlFactory,
@@ -968,6 +982,7 @@ var DefaultConfigDirectorClient = class {
968
982
  defaultValue,
969
983
  requestedType: parseResult.requestedType,
970
984
  evaluatedValue: parseResult.parsedValue,
985
+ evaluatedValueId: parseResult.parsedValueId,
971
986
  usedDefault: parseResult.usedDefault,
972
987
  evaluationReason: parseResult.reason
973
988
  });
@@ -1032,25 +1047,27 @@ var DefaultConfigDirectorClient = class {
1032
1047
  //#endregion
1033
1048
  //#region ../shared/src/telemetry/utils.ts
1034
1049
  const CONFIG_VALUE_MAX_LENGTH = 500;
1035
- const sanitizeValue = (value, type) => {
1036
- if (type === "json" || type == null && typeof value === "object") try {
1037
- return { digest: djb2Hash(JSON.stringify(value)) };
1038
- } catch {
1039
- return { digest: value.toString() };
1040
- }
1041
- if (value.toString().length > CONFIG_VALUE_MAX_LENGTH) return { digest: djb2Hash(value.toString()) };
1042
- else return { value: value.toString() };
1043
- };
1044
- const djb2Hash = (data) => {
1045
- return djb2(new TextEncoder().encode(data)).toString(16).padStart(8, "0");
1046
- };
1047
- const djb2 = (bytes) => {
1048
- let hash = 5381;
1049
- for (let i = 0; i < bytes.length; i++) {
1050
- hash = (hash << 5) + hash + (bytes[i] ?? 0);
1051
- 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
+ }
1052
1067
  }
1053
- 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 };
1054
1071
  };
1055
1072
  //#endregion
1056
1073
  //#region ../js-browser-client/src/telemetry/WebWorkerTelemetryClient.ts
@@ -1072,7 +1089,7 @@ var WebWorkerTelemetryClient = class {
1072
1089
  flushIntervalDelay: options.flushIntervalDelay ?? 3e4
1073
1090
  }
1074
1091
  };
1075
- 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" })));
1076
1093
  this.worker.onmessage = (message) => {
1077
1094
  this.handleWorkerEvent(message.data);
1078
1095
  };
@@ -1103,8 +1120,15 @@ var WebWorkerTelemetryClient = class {
1103
1120
  sanitizeEvaluatedConfigEvent(event) {
1104
1121
  return {
1105
1122
  ...event,
1106
- defaultValue: sanitizeValue(event.defaultValue, event.type),
1107
- 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
+ })
1108
1132
  };
1109
1133
  }
1110
1134
  handleWorkerEvent(event) {
@@ -1212,7 +1236,7 @@ var plugin_client_default = defineNuxtPlugin((nuxtApp) => {
1212
1236
  const { clientSdkKey, appName, appVersion, baseUrl } = runtimeConfig.public.configdirector;
1213
1237
  const client = createBrowserClient(clientSdkKey, {
1214
1238
  sdkName: "nuxt-sdk",
1215
- sdkVersion: "0.6.0"
1239
+ sdkVersion: "0.8.0"
1216
1240
  }, {
1217
1241
  metadata: {
1218
1242
  appName,
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../../types-DGQ1B6mZ.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-DGQ1B6mZ.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
@@ -37,6 +38,14 @@ type ConfigDirectorContext = {
37
38
  traits?: {
38
39
  [key: string]: unknown;
39
40
  };
41
+ /**
42
+ * Whether or not to treat the context as anonymous during evaluation. When `true`, the
43
+ * context values will be used for targeting rules evaluation but the context will not be
44
+ * persisted and will not appear in the dashboard.
45
+ *
46
+ * @default false
47
+ */
48
+ anonymous?: boolean;
40
49
  };
41
50
  //#endregion
42
51
  export { ConfigState as n, ConfigValueType as r, ConfigDirectorContext as t };
@@ -1,4 +1,4 @@
1
- import { n as ConfigState, r as ConfigValueType, t as ConfigDirectorContext } from "./types-BWxsGnk2.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-BWxsGnk2.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.6.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",
@@ -63,13 +63,13 @@
63
63
  "@types/node": "latest",
64
64
  "changelogen": "^0.6.2",
65
65
  "eslint": "^10.2.1",
66
- "nuxt": "^4.4.6",
66
+ "nuxt": "^4.4.8",
67
67
  "playwright": "^1.60.0",
68
68
  "rimraf": "^6.1.3",
69
69
  "tsdown": "^0.21.7",
70
70
  "tslib": "^2.8.1",
71
71
  "typescript": "~6.0.3",
72
- "vite": "^8.0.10",
72
+ "vite": "^8.0.16",
73
73
  "vitest": "^4.1.8",
74
74
  "vue-tsc": "^3.2.7"
75
75
  }