@configdirector/nuxt-sdk 0.9.5 → 0.10.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.9.5"
7
+ "version": "0.10.0"
8
8
  }
@@ -7049,13 +7049,14 @@ var ConfigEvaluator = class {
7049
7049
  configId: config.id,
7050
7050
  contextIdentifier: context?.context?.id ?? crypto.randomUUID()
7051
7051
  });
7052
- let index = 0;
7053
7052
  let sum = 0;
7054
7053
  let bucket = void 0;
7055
- while (!bucket && index < percentages.length) {
7056
- if (assignedPercentage <= percentages[index].percentage + sum) bucket = percentages[index];
7057
- sum += percentages[index].percentage;
7058
- index += 1;
7054
+ for (const percentage of percentages) {
7055
+ if (assignedPercentage <= percentage.percentage + sum) {
7056
+ bucket = percentage;
7057
+ break;
7058
+ }
7059
+ sum += percentage.percentage;
7059
7060
  }
7060
7061
  if (bucket?.value != null) return {
7061
7062
  success: true,
@@ -7064,13 +7065,7 @@ var ConfigEvaluator = class {
7064
7065
  return { success: false };
7065
7066
  }
7066
7067
  evaluateConditionalRule(rule, config, context) {
7067
- const conditions = rule.conditions ?? [];
7068
- let index = 0;
7069
- let condition = void 0;
7070
- while (!condition && index < conditions.length) {
7071
- if (this.conditionEvaluator.evaluate(conditions[index], context)) condition = conditions[index];
7072
- index += 1;
7073
- }
7068
+ const condition = (rule.conditions ?? []).find((candidate) => this.conditionEvaluator.evaluate(candidate, context));
7074
7069
  if (condition && rule.target == "value" && rule.value != null) return {
7075
7070
  success: true,
7076
7071
  value: rule.value.toString()
@@ -7318,11 +7313,13 @@ const isDroppedEventsEmpty = (droppedEvents) => {
7318
7313
  //#region ../js-server-sdk/src/telemetry/ServerEventReporter.ts
7319
7314
  var ServerEventReporter = class {
7320
7315
  sdkKey;
7316
+ sdkIdentity;
7321
7317
  logger;
7322
7318
  url;
7323
7319
  executeRequests = true;
7324
7320
  constructor(options) {
7325
7321
  this.sdkKey = options.sdkKey;
7322
+ this.sdkIdentity = options.sdkIdentity;
7326
7323
  this.logger = options.logger;
7327
7324
  this.url = new URL("server/telemetry/v1", options.baseUrl.toString());
7328
7325
  }
@@ -7333,6 +7330,10 @@ var ServerEventReporter = class {
7333
7330
  };
7334
7331
  const eventReport = {
7335
7332
  serverSdkKey: this.sdkKey,
7333
+ metaContext: {
7334
+ sdkName: this.sdkIdentity.sdkName,
7335
+ sdkVersion: this.sdkIdentity.sdkVersion
7336
+ },
7336
7337
  discreteEvents,
7337
7338
  aggregatedEvents,
7338
7339
  droppedEvents
@@ -7652,6 +7653,7 @@ var DefaultConfigDirectorClient = class {
7652
7653
  const queueLimit = clientOptions?.telemetry?.eventQueueLimit ?? DEFAULT_EVENT_QUEUE_LIMIT;
7653
7654
  this.usageEventCollector = new ServerTelemetryEventCollector({
7654
7655
  sdkKey: serverSdkKey,
7656
+ sdkIdentity: sdkOptions,
7655
7657
  logger: this.logger,
7656
7658
  baseUrl,
7657
7659
  flushIntervalDelay: clientOptions?.telemetry?.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
@@ -7894,7 +7896,7 @@ var plugin_default = defineNitroPlugin((nitroApp) => {
7894
7896
  const { serverSdkKey, baseUrl } = runtimeConfig.configdirector;
7895
7897
  const client = new DefaultConfigDirectorClient(serverSdkKey, {
7896
7898
  sdkName: "nuxt-sdk",
7897
- sdkVersion: "0.9.5"
7899
+ sdkVersion: "0.10.0"
7898
7900
  }, {
7899
7901
  logger,
7900
7902
  ...baseUrl ? { connection: { url: baseUrl } } : {}
@@ -1134,13 +1134,14 @@ var WebWorkerTelemetryClient = class {
1134
1134
  type: "Initialize",
1135
1135
  payload: {
1136
1136
  sdkKey: options.sdkKey,
1137
+ sdkIdentity: options.sdkIdentity,
1137
1138
  baseUrl: options.baseUrl.toString(),
1138
1139
  evaluationQueueLimit: options.evaluationQueueLimit ?? 1e3,
1139
1140
  initialFlushIntervalDelay: options.initialFlushIntervalDelay ?? 5e3,
1140
1141
  flushIntervalDelay: options.flushIntervalDelay ?? 3e4
1141
1142
  }
1142
1143
  };
1143
- this.worker = new Worker(URL.createObjectURL(new Blob(["(function(){let e=async(e,t)=>e.valueId?{valueId:e.valueId}:e.value?e.value.length<=500&&e.type!=`json`?{value:e.value}:{valueId:await t(e.value)}:e,t=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},n=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},r=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},i=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var a=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=e.urlFactory(`client/telemetry/v1`,e.baseUrl)}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){if(!this.executeRequests)return{success:!1,fatalError:!0};let i={clientSdkKey:this.sdkKey,context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(this.isReportEmpty(i))return{success:!0,fatalError:!1};let a=await this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return t(e.discreteEvents)&&t(e.aggregatedEvents)&&n(e.droppedEvents)}async sendReport(e){try{let t=await r(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=i(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},o=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},s=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||=new Date;let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},c=class{constructor(e){this.aggregator=new o,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new s(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}async flushAndScheduleNext(){(await this.flush()).fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay)}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),await this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},l=class{constructor(e){this.valueIdGenerator=e,this.valueIdGenerator=e}async process(t){let n=await Promise.all(t.events.map(async t=>({...t,defaultValue:await e(t.defaultValue,this.valueIdGenerator),evaluatedValue:await e(t.evaluatedValue,this.valueIdGenerator)})));return{...t,events:n}}},u=class extends c{constructor(e){super(e),this.reporter=new a(e),this.evaluationEventSnapshotPreprocessor=new l(e.valueIdGenerator)}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(e)}async forceFlush(){this.cancelScheduledFlush(),await this.flushAndScheduleNext()}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=await this.evaluationEventSnapshotPreprocessor.process(this.evaluationEventQueue.takeSnapshot());return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount}})}};let d=(e,t)=>new URL(e,t?.toString()),f=(e,t)=>{let n=BigInt(0),r=BigInt(62),i=Array.from(e).map(e=>e.toString(16).padStart(2,`0`)).join(``),a=BigInt(`0x`+i),o=``;for(;a>n;)o=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`[Number(a%r)]+o,a/=r;return o.padStart(t,`0`)},p=Math.ceil(128/Math.log2(62)),m=new TextEncoder,h=async e=>{let t=await crypto.subtle.digest(`SHA-256`,m.encode(e?.toString()??``));return f(new Uint8Array(t,0,16),p)},g,_=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),v=e=>{try{self.postMessage(e)}catch{self.postMessage(_(e.payload.level,e.payload.message))}},y={debug:function(e,...t){v(_(`debug`,e,t))},info:function(e,...t){v(_(`info`,e,t))},warn:function(e,...t){v(_(`warn`,e,t))},error:function(e,...t){v(_(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!g){y.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(y.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:g=new u({...t.payload,baseUrl:d(t.payload.baseUrl),urlFactory:d,valueIdGenerator:h,logger:y});break;case`EvaluatedConfigEvent`:g?.evaluatedConfig(t.payload);break;case`UpdateContext`:g?.updateContext(t.payload.context);break;case`Flush`:g?.forceFlush();break;case`Close`:(g?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"], { type: "application/javascript" })));
1144
+ 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.sdkIdentity=e.sdkIdentity,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,metaContext:{sdkName:this.sdkIdentity.sdkName,sdkVersion:this.sdkIdentity.sdkVersion},context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(this.isReportEmpty(i))return{success:!0,fatalError:!1};let a=await this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return t(e.discreteEvents)&&t(e.aggregatedEvents)&&n(e.droppedEvents)}async sendReport(e){try{let t=await r(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=i(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},o=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},s=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||=new Date;let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},c=class{constructor(e){this.aggregator=new o,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new s(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}async flushAndScheduleNext(){(await this.flush()).fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay)}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),await this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},l=class{constructor(e){this.valueIdGenerator=e,this.valueIdGenerator=e}async process(t){let n=await Promise.all(t.events.map(async t=>({...t,defaultValue:await e(t.defaultValue,this.valueIdGenerator),evaluatedValue:await e(t.evaluatedValue,this.valueIdGenerator)})));return{...t,events:n}}},u=class extends c{constructor(e){super(e),this.reporter=new a(e),this.evaluationEventSnapshotPreprocessor=new l(e.valueIdGenerator)}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(e)}async forceFlush(){this.cancelScheduledFlush(),await this.flushAndScheduleNext()}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=await this.evaluationEventSnapshotPreprocessor.process(this.evaluationEventQueue.takeSnapshot());return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount}})}};let d=(e,t)=>new URL(e,t?.toString()),f=(e,t)=>{let n=BigInt(0),r=BigInt(62),i=Array.from(e).map(e=>e.toString(16).padStart(2,`0`)).join(``),a=BigInt(`0x`+i),o=``;for(;a>n;)o=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`[Number(a%r)]+o,a/=r;return o.padStart(t,`0`)},p=Math.ceil(128/Math.log2(62)),m=new TextEncoder,h=async e=>{let t=await crypto.subtle.digest(`SHA-256`,m.encode(e?.toString()??``));return f(new Uint8Array(t,0,16),p)},g,_=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),v=e=>{try{self.postMessage(e)}catch{self.postMessage(_(e.payload.level,e.payload.message))}},y={debug:function(e,...t){v(_(`debug`,e,t))},info:function(e,...t){v(_(`info`,e,t))},warn:function(e,...t){v(_(`warn`,e,t))},error:function(e,...t){v(_(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!g){y.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(y.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:g=new u({...t.payload,baseUrl:d(t.payload.baseUrl),urlFactory:d,valueIdGenerator:h,logger:y});break;case`EvaluatedConfigEvent`:g?.evaluatedConfig(t.payload);break;case`UpdateContext`:g?.updateContext(t.payload.context);break;case`Flush`:g?.forceFlush();break;case`Close`:(g?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"], { type: "application/javascript" })));
1144
1145
  this.worker.onmessage = (message) => {
1145
1146
  this.handleWorkerEvent(message.data);
1146
1147
  };
@@ -1247,10 +1248,15 @@ const generateValueId = async (v) => {
1247
1248
  //#region ../js-browser-client/src/BrowserConfigDirectorClient.ts
1248
1249
  const createBrowserClient = (clientSdkKey, sdkOptions, clientOptions) => {
1249
1250
  const logger = clientOptions?.logger ?? createDefaultLogger$1();
1251
+ const baseUrl = parseUrl(clientOptions?.connection?.url) ?? "https://client-sdk-api.configdirector.com";
1250
1252
  return new DefaultConfigDirectorClient(new WebWorkerTelemetryClient({
1251
1253
  sdkKey: clientSdkKey,
1254
+ sdkIdentity: {
1255
+ sdkName: sdkOptions.sdkName,
1256
+ sdkVersion: sdkOptions.sdkVersion
1257
+ },
1252
1258
  logger,
1253
- baseUrl: parseUrl(clientOptions?.connection?.url) ?? "https://client-sdk-api.configdirector.com",
1259
+ baseUrl,
1254
1260
  urlFactory: defaultUrlFactory,
1255
1261
  valueIdGenerator: generateValueId
1256
1262
  }), clientSdkKey, sdkOptions, {
@@ -1311,7 +1317,7 @@ var plugin_client_default = defineNuxtPlugin((nuxtApp) => {
1311
1317
  const { clientSdkKey, appName, appVersion, baseUrl } = runtimeConfig.public.configdirector;
1312
1318
  const client = createBrowserClient(clientSdkKey, {
1313
1319
  sdkName: "nuxt-sdk",
1314
- sdkVersion: "0.9.5"
1320
+ sdkVersion: "0.10.0"
1315
1321
  }, {
1316
1322
  metadata: {
1317
1323
  appName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configdirector/nuxt-sdk",
3
- "version": "0.9.5",
3
+ "version": "0.10.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",
@@ -40,12 +40,14 @@
40
40
  ],
41
41
  "scripts": {
42
42
  "build": "nuxt prepare && tsdown",
43
+ "verify:dist": "node ../../scripts/verify-dist-version.mjs",
43
44
  "prepack": "nuxt prepare && tsdown",
44
- "release": "nuxt prepare && yarn lint && yarn ci:test && yarn build && npm publish --access public",
45
+ "release": "nuxt prepare && yarn lint && yarn ci:test && yarn build && yarn verify:dist && npm publish --access public",
45
46
  "lint": "eslint .",
46
47
  "ci:test": "vitest run",
47
48
  "test": "vitest watch",
48
- "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
49
+ "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
50
+ "typecheck": "tsc --noEmit"
49
51
  },
50
52
  "dependencies": {
51
53
  "@jsonjoy.com/json-pointer": "^18.1.0",
@@ -58,9 +60,9 @@
58
60
  "devDependencies": {
59
61
  "@nuxt/devtools": "^3.2.4",
60
62
  "@nuxt/eslint-config": "^1.15.2",
61
- "@nuxt/schema": "^4.4.2",
63
+ "@nuxt/schema": "4.4.8",
62
64
  "@nuxt/test-utils": "^4.0.2",
63
- "@types/node": "latest",
65
+ "@types/node": "^24.13.3",
64
66
  "changelogen": "^0.6.2",
65
67
  "eslint": "^10.7.0",
66
68
  "nuxt": "^4.4.8",