@configdirector/nuxt-sdk 0.2.0 → 0.6.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.2.0"
7
+ "version": "0.6.0"
8
8
  }
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../../types-KSnINlzh.mjs";
1
+ import { t as ConfigDirectorClient } from "../../types-D9IS8RoC.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-KSnINlzh.mjs";
1
+ import { t as ConfigDirectorClient } from "../../types-D9IS8RoC.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-CQWNx-gI.mjs";
1
+ import { t as ConfigDirectorContext } from "../../types-BWxsGnk2.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-CQWNx-gI.mjs";
1
+ import { t as ConfigDirectorContext } from "../../types-BWxsGnk2.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 { n as ConfigValueType } from "../../types-CQWNx-gI.mjs";
1
+ import { r as ConfigValueType } from "../../types-BWxsGnk2.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 { n as ConfigValueType } from "../../types-CQWNx-gI.mjs";
1
+ import { r as ConfigValueType } from "../../types-BWxsGnk2.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-D9MFb76Q.mjs";
1
+ import { t as ConfigDirectorClient } from "../types-DGQ1B6mZ.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-D9MFb76Q.mjs";
1
+ import { t as ConfigDirectorClient } from "../types-DGQ1B6mZ.mjs";
2
2
  import * as _$nitropack from "nitropack";
3
3
 
4
4
  //#region src/runtime/nitro/plugin.d.ts
@@ -380,7 +380,7 @@ var StreamingTransport = class {
380
380
  this.dispatchMessage(data);
381
381
  },
382
382
  onConnect: () => {
383
- this.logger.debug("[EventSourceTransport] Connected");
383
+ this.logger.debug("[StreamingTransport] Connected");
384
384
  resolve(this);
385
385
  },
386
386
  shouldReconnect: (state) => {
@@ -390,11 +390,13 @@ var StreamingTransport = class {
390
390
  },
391
391
  calculateReconnectDelay: (state) => {
392
392
  const delay = Math.pow(2, Math.min(state.attempt, MAX_EXPONENTIAL_DELAY)) * 1e3;
393
- this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${delay}ms.`);
393
+ const logMessage = `[StreamingTransport] Scheduling reconnect attempt #${state.attempt} in ${delay}ms.`;
394
+ if (state.attempt <= 5) this.logger.info(logMessage);
395
+ else this.logger.warn(logMessage);
394
396
  return delay;
395
397
  },
396
398
  onDisconnect: () => {
397
- this.logger.debug("[EventSourceTransport] Disconnected");
399
+ this.logger.debug("[StreamingTransport] Disconnected");
398
400
  }
399
401
  });
400
402
  this.eventSource.connect();
@@ -408,7 +410,7 @@ var StreamingTransport = class {
408
410
  const json = JSON.parse(data);
409
411
  this.eventEmitter.emit("configBundleReceived", json);
410
412
  } catch (error) {
411
- this.logger.error("[EventSourceTransport] Error parsing and dispatching config data update: ", error);
413
+ this.logger.error("[StreamingTransport] Error parsing and dispatching config data update: ", error);
412
414
  }
413
415
  }
414
416
  prepareFatalError(responseStatus, error) {
@@ -851,6 +853,7 @@ const parseConfigValue = (configState, defaultValue) => {
851
853
  usedDefault: true,
852
854
  reason: "value-missing"
853
855
  };
856
+ if (configState.type === "json") return parseJson(value, defaultValue, requestedType);
854
857
  if (typeof defaultValue === "string") return {
855
858
  parsedValue: value,
856
859
  requestedType,
@@ -912,6 +915,29 @@ const parseConfigFloat = (value) => {
912
915
  if (isNaN(num) || !isFinite(num)) return;
913
916
  return num;
914
917
  };
918
+ const parseJson = (jsonString, defaultValue, requestedType) => {
919
+ if (typeof defaultValue === "string") return {
920
+ parsedValue: jsonString,
921
+ requestedType,
922
+ usedDefault: false,
923
+ reason: "found-match"
924
+ };
925
+ try {
926
+ return {
927
+ parsedValue: JSON.parse(jsonString),
928
+ requestedType,
929
+ usedDefault: false,
930
+ reason: "found-match"
931
+ };
932
+ } catch {
933
+ return {
934
+ parsedValue: defaultValue,
935
+ requestedType,
936
+ usedDefault: true,
937
+ reason: "invalid-json"
938
+ };
939
+ }
940
+ };
915
941
  //#endregion
916
942
  //#region ../shared/src/logger.ts
917
943
  const LEVELS = {
@@ -982,12 +1008,13 @@ const defaultUrlFactory = (input, base) => new URL(input, base?.toString());
982
1008
  //#region ../shared/src/telemetry/utils.ts
983
1009
  const CONFIG_VALUE_MAX_LENGTH = 500;
984
1010
  const sanitizeValue = (value, type) => {
985
- if (type === "json") try {
986
- return djb2Hash(JSON.stringify(value));
1011
+ if (type === "json" || type == null && typeof value === "object") try {
1012
+ return { digest: djb2Hash(JSON.stringify(value)) };
987
1013
  } catch {
988
- return value.toString().slice(0, CONFIG_VALUE_MAX_LENGTH);
1014
+ return { digest: value.toString() };
989
1015
  }
990
- return value.toString().slice(0, CONFIG_VALUE_MAX_LENGTH);
1016
+ if (value.toString().length > CONFIG_VALUE_MAX_LENGTH) return { digest: djb2Hash(value.toString()) };
1017
+ else return { value: value.toString() };
991
1018
  };
992
1019
  const djb2Hash = (data) => {
993
1020
  return djb2(new TextEncoder().encode(data)).toString(16).padStart(8, "0");
@@ -1420,14 +1447,16 @@ var DefaultConfigDirectorClient = class {
1420
1447
  }
1421
1448
  getValueFromConfigDefinition(configKey, configDefinition, defaultValue, context) {
1422
1449
  if (configDefinition) {
1423
- const parseResult = parseConfigValue(this.configEvaluator.evaluate(configDefinition, {
1450
+ const configState = this.configEvaluator.evaluate(configDefinition, {
1424
1451
  context,
1425
1452
  metadata: this.metaContext
1426
- }), defaultValue);
1453
+ });
1454
+ const parseResult = parseConfigValue(configState, defaultValue);
1427
1455
  this.usageEventCollector.evaluatedConfig({
1428
1456
  context,
1429
1457
  evaluation: {
1430
1458
  key: configKey,
1459
+ type: configState.type,
1431
1460
  defaultValue,
1432
1461
  requestedType: parseResult.requestedType,
1433
1462
  evaluatedValue: parseResult.parsedValue,
@@ -1483,6 +1512,15 @@ var DefaultConfigDirectorClient = class {
1483
1512
  unwatchAll() {
1484
1513
  this.handlersMap.clear();
1485
1514
  }
1515
+ getAllConfigs(options) {
1516
+ if (!this.configSet) return {};
1517
+ const entries = Object.entries(this.configSet.configs);
1518
+ const filtered = options?.configKeys ? entries.filter(([key]) => options.configKeys?.includes(key)) : entries;
1519
+ return Object.fromEntries(filtered.map(([key, config]) => [key, this.configEvaluator.evaluate(config, {
1520
+ context: options?.context,
1521
+ metadata: this.metaContext
1522
+ })]));
1523
+ }
1486
1524
  closeConnection() {
1487
1525
  this.logger.debug("[ConfigDirectorClient] closeConnection() has been called, closing connection to server");
1488
1526
  this.transport.close();
@@ -1514,7 +1552,7 @@ var plugin_default = defineNitroPlugin((nitroApp) => {
1514
1552
  const { serverSdkKey, baseUrl } = runtimeConfig.configdirector;
1515
1553
  const client = new DefaultConfigDirectorClient(serverSdkKey, {
1516
1554
  sdkName: "nuxt-sdk",
1517
- sdkVersion: "0.2.0"
1555
+ sdkVersion: "0.6.0"
1518
1556
  }, {
1519
1557
  logger,
1520
1558
  ...baseUrl ? { connection: { url: baseUrl } } : {}
@@ -413,7 +413,7 @@ var StreamingTransport = class {
413
413
  this.dispatchMessage(data);
414
414
  },
415
415
  onConnect: () => {
416
- this.logger.debug("[EventSourceTransport] Connected");
416
+ this.logger.debug("[StreamingTransport] Connected");
417
417
  resolve(this);
418
418
  },
419
419
  shouldReconnect: (state) => {
@@ -423,14 +423,16 @@ var StreamingTransport = class {
423
423
  },
424
424
  calculateReconnectDelay: (state) => {
425
425
  const delay = this.options.connectionRetryDelay(state.attempt);
426
- this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${delay}ms.`);
426
+ const logMessage = `[StreamingTransport] Scheduling reconnect attempt #${state.attempt} in ${delay}ms.`;
427
+ if (state.attempt <= 5) this.logger.info(logMessage);
428
+ else this.logger.warn(logMessage);
427
429
  return delay;
428
430
  },
429
431
  onError: (error) => {
430
- this.logger.debug("[EventSourceTransport] Error: ", error);
432
+ this.logger.debug("[StreamingTransport] Error: ", error);
431
433
  },
432
434
  onDisconnect: () => {
433
- this.logger.debug("[EventSourceTransport] Disconnected");
435
+ this.logger.debug("[StreamingTransport] Disconnected");
434
436
  }
435
437
  });
436
438
  this.eventSource.connect();
@@ -446,7 +448,7 @@ var StreamingTransport = class {
446
448
  const json = JSON.parse(data);
447
449
  this.eventEmitter.emit("configSetReceived", json);
448
450
  } catch (error) {
449
- this.logger.error("[EventSourceTransport] Error parsing and dispatching config data update: ", error);
451
+ this.logger.error("[StreamingTransport] Error parsing and dispatching config data update: ", error);
450
452
  }
451
453
  }
452
454
  prepareFatalError(responseStatus, error) {
@@ -503,6 +505,7 @@ const parseConfigValue = (configState, defaultValue) => {
503
505
  usedDefault: true,
504
506
  reason: "value-missing"
505
507
  };
508
+ if (configState.type === "json") return parseJson(value, defaultValue, requestedType);
506
509
  if (typeof defaultValue === "string") return {
507
510
  parsedValue: value,
508
511
  requestedType,
@@ -564,6 +567,29 @@ const parseConfigFloat = (value) => {
564
567
  if (isNaN(num) || !isFinite(num)) return;
565
568
  return num;
566
569
  };
570
+ const parseJson = (jsonString, defaultValue, requestedType) => {
571
+ if (typeof defaultValue === "string") return {
572
+ parsedValue: jsonString,
573
+ requestedType,
574
+ usedDefault: false,
575
+ reason: "found-match"
576
+ };
577
+ try {
578
+ return {
579
+ parsedValue: JSON.parse(jsonString),
580
+ requestedType,
581
+ usedDefault: false,
582
+ reason: "found-match"
583
+ };
584
+ } catch {
585
+ return {
586
+ parsedValue: defaultValue,
587
+ requestedType,
588
+ usedDefault: true,
589
+ reason: "invalid-json"
590
+ };
591
+ }
592
+ };
567
593
  //#endregion
568
594
  //#region ../shared/src/logger.ts
569
595
  const LEVELS = {
@@ -937,6 +963,7 @@ var DefaultConfigDirectorClient = class {
937
963
  const parseResult = parseConfigValue(configState, defaultValue);
938
964
  this.telemetryClient.evaluatedConfig({
939
965
  contextId: this.currentContext?.id,
966
+ type: configState.type,
940
967
  key: configKey,
941
968
  defaultValue,
942
969
  requestedType: parseResult.requestedType,
@@ -1006,12 +1033,13 @@ var DefaultConfigDirectorClient = class {
1006
1033
  //#region ../shared/src/telemetry/utils.ts
1007
1034
  const CONFIG_VALUE_MAX_LENGTH = 500;
1008
1035
  const sanitizeValue = (value, type) => {
1009
- if (type === "json") try {
1010
- return djb2Hash(JSON.stringify(value));
1036
+ if (type === "json" || type == null && typeof value === "object") try {
1037
+ return { digest: djb2Hash(JSON.stringify(value)) };
1011
1038
  } catch {
1012
- return value.toString().slice(0, CONFIG_VALUE_MAX_LENGTH);
1039
+ return { digest: value.toString() };
1013
1040
  }
1014
- return value.toString().slice(0, CONFIG_VALUE_MAX_LENGTH);
1041
+ if (value.toString().length > CONFIG_VALUE_MAX_LENGTH) return { digest: djb2Hash(value.toString()) };
1042
+ else return { value: value.toString() };
1015
1043
  };
1016
1044
  const djb2Hash = (data) => {
1017
1045
  return djb2(new TextEncoder().encode(data)).toString(16).padStart(8, "0");
@@ -1044,7 +1072,7 @@ var WebWorkerTelemetryClient = class {
1044
1072
  flushIntervalDelay: options.flushIntervalDelay ?? 3e4
1045
1073
  }
1046
1074
  };
1047
- this.worker = new Worker(URL.createObjectURL(new Blob(["(function(){let e=(e,n)=>{if(n===`json`)try{return t(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)},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" })));
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" })));
1048
1076
  this.worker.onmessage = (message) => {
1049
1077
  this.handleWorkerEvent(message.data);
1050
1078
  };
@@ -1184,7 +1212,7 @@ var plugin_client_default = defineNuxtPlugin((nuxtApp) => {
1184
1212
  const { clientSdkKey, appName, appVersion, baseUrl } = runtimeConfig.public.configdirector;
1185
1213
  const client = createBrowserClient(clientSdkKey, {
1186
1214
  sdkName: "nuxt-sdk",
1187
- sdkVersion: "0.2.0"
1215
+ sdkVersion: "0.6.0"
1188
1216
  }, {
1189
1217
  metadata: {
1190
1218
  appName,
@@ -1,4 +1,4 @@
1
- import { t as ConfigDirectorClient } from "../../types-D9MFb76Q.mjs";
1
+ import { t as ConfigDirectorClient } from "../../types-DGQ1B6mZ.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-D9MFb76Q.mjs";
1
+ import { t as ConfigDirectorClient } from "../../types-DGQ1B6mZ.mjs";
2
2
 
3
3
  //#region src/runtime/server/composables/useConfigDirectorClient.d.ts
4
4
  declare const useConfigDirectorClient: () => ConfigDirectorClient;
@@ -1,8 +1,16 @@
1
1
  //#region ../shared/src/types.d.ts
2
+ declare const ConfigTypeList: readonly ["custom", "boolean", "string", "integer", "float", "enum", "url", "json"];
3
+ type ConfigType = (typeof ConfigTypeList)[number];
2
4
  type ConfigEnumLikeType = {
3
5
  [key: string]: string | number;
4
6
  };
5
7
  type ConfigValueType = string | number | boolean | object | ConfigEnumLikeType;
8
+ type ConfigState = {
9
+ id: string;
10
+ key: string;
11
+ type: ConfigType;
12
+ value: string | undefined | null;
13
+ };
6
14
  /**
7
15
  * The user's context to be sent to ConfigDirector. This context will be used for targeting
8
16
  * rules evaluation.
@@ -31,4 +39,4 @@ type ConfigDirectorContext = {
31
39
  };
32
40
  };
33
41
  //#endregion
34
- export { ConfigValueType as n, ConfigDirectorContext as t };
42
+ export { ConfigState as n, ConfigValueType as r, ConfigDirectorContext as t };
@@ -1,4 +1,4 @@
1
- import { n as ConfigValueType, t as ConfigDirectorContext } from "./types-CQWNx-gI.mjs";
1
+ import { r as ConfigValueType, t as ConfigDirectorContext } from "./types-BWxsGnk2.mjs";
2
2
 
3
3
  //#region ../js-client-core/src/Emitter.d.ts
4
4
  type EventType = string | symbol;
@@ -1,4 +1,4 @@
1
- import { n as ConfigValueType, t as ConfigDirectorContext } from "./types-CQWNx-gI.mjs";
1
+ import { n as ConfigState, r as ConfigValueType, t as ConfigDirectorContext } from "./types-BWxsGnk2.mjs";
2
2
 
3
3
  //#region ../js-server-sdk/src/types.d.ts
4
4
  type ClientEvents = {
@@ -71,6 +71,17 @@ interface ConfigDirectorClient {
71
71
  * Removes all subscribers from all config keys
72
72
  */
73
73
  unwatchAll(): void;
74
+ /**
75
+ * Returns the evaluated {@link ConfigState} for every known config key, or only the keys
76
+ * listed in `configKeys` when provided.
77
+ *
78
+ * Intended for SSR hydration — does NOT record telemetry events. Returns an empty object
79
+ * when the client is not yet ready.
80
+ */
81
+ getAllConfigs(options?: {
82
+ context?: ConfigDirectorContext;
83
+ configKeys?: string[];
84
+ }): Record<string, ConfigState>;
74
85
  on<T extends keyof ClientEvents>(eventName: T, handler: (args: ClientEvents[T]) => void): void;
75
86
  off<T extends keyof ClientEvents>(eventName: T, handler?: (args: ClientEvents[T]) => void): void;
76
87
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configdirector/nuxt-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.6.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",
@@ -64,13 +64,13 @@
64
64
  "changelogen": "^0.6.2",
65
65
  "eslint": "^10.2.1",
66
66
  "nuxt": "^4.4.6",
67
- "playwright": "^1.52.0",
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
72
  "vite": "^8.0.10",
73
- "vitest": "^4.1.4",
73
+ "vitest": "^4.1.8",
74
74
  "vue-tsc": "^3.2.7"
75
75
  }
76
76
  }