@configdirector/nuxt-sdk 1.8.0 → 1.9.1

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": "1.8.0"
7
+ "version": "1.9.1"
8
8
  }
@@ -7076,6 +7076,40 @@ var ConditionEvaluator = class {
7076
7076
  };
7077
7077
  const orAbsent = (value) => value === void 0 || value === null ? ABSENT : value;
7078
7078
  //#endregion
7079
+ //#region ../config-evaluator/src/SegmentEvaluator.ts
7080
+ var SegmentEvaluator = class {
7081
+ constructor(conditionEvaluator) {
7082
+ this.conditionEvaluator = conditionEvaluator;
7083
+ }
7084
+ explain(condition, segments, context) {
7085
+ const segment = segments?.[condition.segmentId];
7086
+ if (segment === void 0 || !holdsAttributeConditionsOnly(segment)) return {
7087
+ matched: false,
7088
+ segmentFound: false,
7089
+ matchedGroupIndex: void 0
7090
+ };
7091
+ const matchedGroupIndex = this.matchingGroupIndex(segment, context);
7092
+ const member = matchedGroupIndex !== void 0;
7093
+ return {
7094
+ matched: this.operatorHolds(condition, member),
7095
+ segmentFound: true,
7096
+ matchedGroupIndex
7097
+ };
7098
+ }
7099
+ operatorHolds(condition, member) {
7100
+ switch (condition.operator.toLowerCase()) {
7101
+ case "in": return member;
7102
+ case "not in": return !member;
7103
+ default: return false;
7104
+ }
7105
+ }
7106
+ matchingGroupIndex(segment, context) {
7107
+ const index = (segment.groups ?? []).findIndex((group) => (group ?? []).every((condition) => this.conditionEvaluator.evaluate(condition, context)));
7108
+ return index === -1 ? void 0 : index;
7109
+ }
7110
+ };
7111
+ const holdsAttributeConditionsOnly = (segment) => (segment.groups ?? []).every((group) => (group ?? []).every((condition) => condition.kind === void 0 || condition.kind === "attribute"));
7112
+ //#endregion
7079
7113
  //#region ../config-evaluator/src/percent-hashing.ts
7080
7114
  const SEED = 15426857351n;
7081
7115
  const assignPercentage = (data) => {
@@ -7086,26 +7120,28 @@ const assignPercentage = (data) => {
7086
7120
  //#region ../config-evaluator/src/ConfigEvaluator.ts
7087
7121
  var ConfigEvaluator = class {
7088
7122
  conditionEvaluator = new ConditionEvaluator();
7123
+ segmentEvaluator = new SegmentEvaluator(this.conditionEvaluator);
7089
7124
  constructor(logger) {
7090
7125
  this.logger = logger;
7091
7126
  if (!logger || !logger.warn || !logger.error || !logger.info) throw new TypeError("The provided logger is not a valid 'ConfigDirectorLogger'");
7092
7127
  this.logger = logger;
7093
7128
  }
7094
- evaluate(config, context) {
7129
+ evaluate(config, context, segments) {
7095
7130
  return {
7096
7131
  id: config.id,
7097
7132
  key: config.key,
7098
7133
  type: config.type,
7099
- value: this.explain(config, context).value
7134
+ value: this.explain(config, context, segments).value
7100
7135
  };
7101
7136
  }
7102
7137
  /**
7103
7138
  * Evaluate a config for a context and record how the value was reached: every rule in the
7104
7139
  * order it was walked with its outcome, the conditions that were checked with what they
7105
7140
  * resolved to, and the share a rollout assigned the context to. `evaluate` is this walk with
7106
- * only the value kept, so the two cannot disagree.
7141
+ * only the value kept, so the two cannot disagree. `segments` is the payload's segments map,
7142
+ * which a segment condition looks its segment up in; a segment it does not hold never matches.
7107
7143
  */
7108
- explain(config, context) {
7144
+ explain(config, context, segments) {
7109
7145
  const rules = [...config.target?.rules ?? []].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
7110
7146
  const explanations = [];
7111
7147
  let served;
@@ -7119,7 +7155,7 @@ var ConfigEvaluator = class {
7119
7155
  });
7120
7156
  continue;
7121
7157
  }
7122
- const result = this.explainRule(rule, config, context);
7158
+ const result = this.explainRule(rule, config, context, segments);
7123
7159
  explanations.push(result.explanation);
7124
7160
  if (result.value !== void 0) served = {
7125
7161
  ruleId: rule.id,
@@ -7135,7 +7171,7 @@ var ConfigEvaluator = class {
7135
7171
  rules: explanations
7136
7172
  };
7137
7173
  }
7138
- explainRule(rule, config, context) {
7174
+ explainRule(rule, config, context, segments) {
7139
7175
  const explanation = {
7140
7176
  ruleId: rule.id,
7141
7177
  outcome: "not-matched",
@@ -7145,7 +7181,7 @@ var ConfigEvaluator = class {
7145
7181
  try {
7146
7182
  let value;
7147
7183
  if (rule.type == "percentage") value = this.explainPercentage(rule.percentages ?? [], config, context, explanation);
7148
- else if (rule.type == "conditional") value = this.explainConditionalRule(rule, config, context, explanation);
7184
+ else if (rule.type == "conditional") value = this.explainConditionalRule(rule, config, context, segments, explanation);
7149
7185
  if (value !== void 0) explanation.outcome = "matched";
7150
7186
  return {
7151
7187
  explanation,
@@ -7197,7 +7233,7 @@ var ConfigEvaluator = class {
7197
7233
  };
7198
7234
  return selected?.value != null ? selected.value.toString() : void 0;
7199
7235
  }
7200
- explainConditionalRule(rule, config, context, explanation) {
7236
+ explainConditionalRule(rule, config, context, segments, explanation) {
7201
7237
  let failed = false;
7202
7238
  for (const condition of rule.conditions ?? []) {
7203
7239
  if (failed) {
@@ -7207,19 +7243,35 @@ var ConfigEvaluator = class {
7207
7243
  });
7208
7244
  continue;
7209
7245
  }
7210
- const check = this.conditionEvaluator.explain(condition, context);
7211
- explanation.conditions.push({
7212
- conditionId: condition.id,
7213
- outcome: check.matched ? "matched" : "not-matched",
7214
- resolvedValue: check.resolvedValue,
7215
- resolvedType: check.resolvedType
7216
- });
7217
- failed = !check.matched;
7246
+ const checked = this.explainCondition(condition, context, segments);
7247
+ explanation.conditions.push(checked);
7248
+ failed = checked.outcome !== "matched";
7218
7249
  }
7219
7250
  if (failed) return;
7220
7251
  if (rule.target == "value") return rule.value != null ? rule.value.toString() : void 0;
7221
7252
  if (rule.target == "percentage") return this.explainPercentage(rule.percentages ?? [], config, context, explanation);
7222
7253
  }
7254
+ explainCondition(condition, context, segments) {
7255
+ if (condition.kind === "segment") {
7256
+ const check = this.segmentEvaluator.explain(condition, segments, context);
7257
+ return {
7258
+ conditionId: condition.id,
7259
+ kind: "segment",
7260
+ outcome: check.matched ? "matched" : "not-matched",
7261
+ segmentId: condition.segmentId,
7262
+ segmentFound: check.segmentFound,
7263
+ matchedGroupIndex: check.matchedGroupIndex
7264
+ };
7265
+ }
7266
+ const check = this.conditionEvaluator.explain(condition, context);
7267
+ return {
7268
+ conditionId: condition.id,
7269
+ kind: "attribute",
7270
+ outcome: check.matched ? "matched" : "not-matched",
7271
+ resolvedValue: check.resolvedValue,
7272
+ resolvedType: check.resolvedType
7273
+ };
7274
+ }
7223
7275
  };
7224
7276
  //#endregion
7225
7277
  //#region ../shared/src/value-parser.ts
@@ -7880,10 +7932,16 @@ var DefaultConfigDirectorClient = class {
7880
7932
  this.readyResolve?.(false);
7881
7933
  const removedKeys = this.findKeysRemovedBy(configBundle);
7882
7934
  if (!this.configSet || configBundle.kind == "full") this.configSet = configBundle;
7883
- else this.configSet.configs = {
7884
- ...this.configSet.configs,
7885
- ...configBundle.configs
7886
- };
7935
+ else {
7936
+ this.configSet.configs = {
7937
+ ...this.configSet.configs,
7938
+ ...configBundle.configs
7939
+ };
7940
+ this.configSet.segments = {
7941
+ ...this.configSet.segments,
7942
+ ...configBundle.segments
7943
+ };
7944
+ }
7887
7945
  const keys = Object.keys(configBundle.configs);
7888
7946
  this.eventEmitter.emit("configsUpdated", {
7889
7947
  keys,
@@ -8027,7 +8085,7 @@ var DefaultConfigDirectorClient = class {
8027
8085
  const configState = this.configEvaluator.evaluate(configDefinition, {
8028
8086
  context,
8029
8087
  metadata: this.metaContext
8030
- });
8088
+ }, this.configSet?.segments);
8031
8089
  const parseResult = parseConfigValue(configState, defaultValue);
8032
8090
  this.usageEventCollector.evaluatedConfig({
8033
8091
  context,
@@ -8119,7 +8177,7 @@ var DefaultConfigDirectorClient = class {
8119
8177
  return Object.fromEntries(filtered.map(([key, config]) => [key, this.configEvaluator.evaluate(config, {
8120
8178
  context: options?.context,
8121
8179
  metadata: this.metaContext
8122
- })]));
8180
+ }, this.configSet?.segments)]));
8123
8181
  }
8124
8182
  closeConnection() {
8125
8183
  this.logger.debug("[ConfigDirectorClient] closeConnection() has been called, closing connection to server");
@@ -8161,7 +8219,7 @@ var plugin_default = defineNitroPlugin((nitroApp) => {
8161
8219
  const { serverSdkKey, baseUrl, connection } = runtimeConfig.configdirector;
8162
8220
  const client = new DefaultConfigDirectorClient(serverSdkKey, {
8163
8221
  sdkName: "nuxt-sdk",
8164
- sdkVersion: "1.8.0"
8222
+ sdkVersion: "1.9.1"
8165
8223
  }, {
8166
8224
  logger,
8167
8225
  connection: {
@@ -1104,7 +1104,7 @@ var DefaultConfigDirectorClient = class {
1104
1104
  const startTime = (/* @__PURE__ */ new Date()).getTime();
1105
1105
  await this.transport.connect(context ?? {}, this.timeout, caller);
1106
1106
  this.currentContext = context;
1107
- this.telemetryClient.updateContext(context);
1107
+ this.telemetryClient.updateContext(context).catch((error) => this.logger.warn("[ConfigDirectorClient] Telemetry could not update the context: ", error));
1108
1108
  this.emit("contextUpdated", { context });
1109
1109
  const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - startTime;
1110
1110
  const remainingTimeout = this.timeout - elapsedTime;
@@ -1261,7 +1261,7 @@ var DefaultConfigDirectorClient = class {
1261
1261
  this.logger.debug("[ConfigDirectorClient] close() has been called, closing connection to server");
1262
1262
  clearTimeout(this.timeoutTimer);
1263
1263
  this.readyResolve?.(true);
1264
- this.telemetryClient.close();
1264
+ this.telemetryClient.close().catch((error) => this.logger.warn("[ConfigDirectorClient] Telemetry could not close: ", error));
1265
1265
  this.transport.close();
1266
1266
  this.ready = false;
1267
1267
  this.initializing = false;
@@ -1303,6 +1303,7 @@ var WebWorkerTelemetryClient = class {
1303
1303
  logger;
1304
1304
  initializeMessage;
1305
1305
  worker;
1306
+ turnedOff = false;
1306
1307
  pendingEvaluations = [];
1307
1308
  pendingEvaluationLimit;
1308
1309
  closePromise;
@@ -1330,9 +1331,27 @@ var WebWorkerTelemetryClient = class {
1330
1331
  if (document.visibilityState === "hidden") this.flush();
1331
1332
  };
1332
1333
  }
1334
+ createWorkerOrTurnOff() {
1335
+ if (typeof Worker === "undefined") {
1336
+ this.turnOff("[WebWorkerTelemetryClient] Telemetry is off because Web Workers are not available in this environment.");
1337
+ return;
1338
+ }
1339
+ try {
1340
+ return 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.metaContext=e.metaContext,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,appName:this.metaContext.appName,appVersion:this.metaContext.appVersion},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&&e!=429}},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.started=!1,this.logger=e.logger,this.evaluationEventQueue=new s(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4,this.initialFlushIntervalDelay=e.initialFlushIntervalDelay??5e3}start(){this.started||!this.collectEvents||(this.started=!0,this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.initialFlushIntervalDelay))}async flushAndScheduleNext(){if(!this.collectEvents)return;let e;try{e=await this.flush()}catch(t){this.logger.warn(`[TelemetryEventCollector] Error flushing telemetry events: `,t),e={success:!1,fatalError:!1}}e.fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.collectEvents&&(this.cancelScheduledFlush(),this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay))}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout);try{await this.flush()}catch(e){this.logger.warn(`[TelemetryEventCollector] Error flushing telemetry events during close: `,e)}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=>{if(typeof crypto>`u`||crypto.subtle==null)return;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`:if(g){y.warn(`[TelemetryWorker] Received a duplicate Initialize event, ignoring it`);break}g=new u({...t.payload,baseUrl:d(t.payload.baseUrl),urlFactory:d,valueIdGenerator:h,logger:y}),g.start();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" })));
1341
+ } catch (error) {
1342
+ this.turnOff("[WebWorkerTelemetryClient] Telemetry is off because the telemetry Web Worker could not be created: ", error);
1343
+ return;
1344
+ }
1345
+ }
1346
+ turnOff(warning, ...details) {
1347
+ this.turnedOff = true;
1348
+ this.pendingEvaluations.length = 0;
1349
+ this.logger.warn(warning, ...details);
1350
+ }
1333
1351
  startWorker() {
1334
- if (this.worker) return this.worker;
1335
- const 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.metaContext=e.metaContext,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,appName:this.metaContext.appName,appVersion:this.metaContext.appVersion},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&&e!=429}},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.started=!1,this.logger=e.logger,this.evaluationEventQueue=new s(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4,this.initialFlushIntervalDelay=e.initialFlushIntervalDelay??5e3}start(){this.started||!this.collectEvents||(this.started=!0,this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.initialFlushIntervalDelay))}async flushAndScheduleNext(){if(!this.collectEvents)return;let e;try{e=await this.flush()}catch(t){this.logger.warn(`[TelemetryEventCollector] Error flushing telemetry events: `,t),e={success:!1,fatalError:!1}}e.fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.collectEvents&&(this.cancelScheduledFlush(),this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay))}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout);try{await this.flush()}catch(e){this.logger.warn(`[TelemetryEventCollector] Error flushing telemetry events during close: `,e)}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=>{if(typeof crypto>`u`||crypto.subtle==null)return;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`:if(g){y.warn(`[TelemetryWorker] Received a duplicate Initialize event, ignoring it`);break}g=new u({...t.payload,baseUrl:d(t.payload.baseUrl),urlFactory:d,valueIdGenerator:h,logger:y}),g.start();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" })));
1352
+ if (this.worker || this.turnedOff) return this.worker;
1353
+ const worker = this.createWorkerOrTurnOff();
1354
+ if (!worker) return;
1336
1355
  worker.onmessage = (message) => {
1337
1356
  this.handleWorkerEvent(message.data);
1338
1357
  };
@@ -1353,9 +1372,10 @@ var WebWorkerTelemetryClient = class {
1353
1372
  type: "UpdateContext",
1354
1373
  payload: { context: value }
1355
1374
  };
1356
- this.startWorker().postMessage(updateContextMessage);
1375
+ this.startWorker()?.postMessage(updateContextMessage);
1357
1376
  }
1358
1377
  evaluatedConfig(event) {
1378
+ if (this.turnedOff) return;
1359
1379
  const evaluatedConfigMessage = {
1360
1380
  type: "EvaluatedConfigEvent",
1361
1381
  payload: this.sanitizeEvaluatedConfigEvent(event)
@@ -1597,7 +1617,7 @@ const buildClient = (configdirector, logger) => {
1597
1617
  const { clientSdkKey, appName, appVersion, baseUrl, connection } = configdirector;
1598
1618
  return createBrowserClient(clientSdkKey, {
1599
1619
  sdkName: "nuxt-sdk",
1600
- sdkVersion: "1.8.0"
1620
+ sdkVersion: "1.9.1"
1601
1621
  }, {
1602
1622
  metadata: {
1603
1623
  appName,
package/dist/testing.mjs CHANGED
@@ -1086,7 +1086,7 @@ var DefaultConfigDirectorClient = class {
1086
1086
  const startTime = (/* @__PURE__ */ new Date()).getTime();
1087
1087
  await this.transport.connect(context ?? {}, this.timeout, caller);
1088
1088
  this.currentContext = context;
1089
- this.telemetryClient.updateContext(context);
1089
+ this.telemetryClient.updateContext(context).catch((error) => this.logger.warn("[ConfigDirectorClient] Telemetry could not update the context: ", error));
1090
1090
  this.emit("contextUpdated", { context });
1091
1091
  const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - startTime;
1092
1092
  const remainingTimeout = this.timeout - elapsedTime;
@@ -1243,7 +1243,7 @@ var DefaultConfigDirectorClient = class {
1243
1243
  this.logger.debug("[ConfigDirectorClient] close() has been called, closing connection to server");
1244
1244
  clearTimeout(this.timeoutTimer);
1245
1245
  this.readyResolve?.(true);
1246
- this.telemetryClient.close();
1246
+ this.telemetryClient.close().catch((error) => this.logger.warn("[ConfigDirectorClient] Telemetry could not close: ", error));
1247
1247
  this.transport.close();
1248
1248
  this.ready = false;
1249
1249
  this.initializing = false;
@@ -1638,7 +1638,7 @@ const installTestClient = (testClient) => {
1638
1638
  const createTestClient = (options) => {
1639
1639
  return createTestClient$1({
1640
1640
  sdkName: "nuxt-sdk",
1641
- sdkVersion: "1.8.0"
1641
+ sdkVersion: "1.9.1"
1642
1642
  }, options);
1643
1643
  };
1644
1644
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configdirector/nuxt-sdk",
3
- "version": "1.8.0",
3
+ "version": "1.9.1",
4
4
  "description": "Nuxt SDK for ConfigDirector. ConfigDirector is a remote configuration and feature flag service.",
5
5
  "license": "MIT",
6
6
  "author": "ConfigDirector",