@configdirector/nuxt-sdk 1.9.2 → 1.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.
Files changed (29) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/app/composables/useConfigDirectorClient.d.mts +1 -1
  3. package/dist/runtime/app/composables/useConfigDirectorClient.d.ts +1 -1
  4. package/dist/runtime/app/composables/useConfigDirectorClientHooks.d.mts +1 -1
  5. package/dist/runtime/app/composables/useConfigDirectorClientHooks.d.ts +1 -1
  6. package/dist/runtime/app/composables/useConfigDirectorContext.d.mts +1 -1
  7. package/dist/runtime/app/composables/useConfigDirectorContext.d.ts +1 -1
  8. package/dist/runtime/app/composables/useConfigDirectorContext.mjs +2 -1
  9. package/dist/runtime/app/composables/useConfigDirectorStatus.d.mts +1 -1
  10. package/dist/runtime/app/composables/useConfigDirectorStatus.d.ts +1 -1
  11. package/dist/runtime/app/composables/useConfigDirectorValue.d.mts +2 -2
  12. package/dist/runtime/app/composables/useConfigDirectorValue.d.ts +2 -2
  13. package/dist/runtime/context-kind-D6YBNij2.mjs +45 -0
  14. package/dist/runtime/nitro/plugin.d.mts +1 -1
  15. package/dist/runtime/nitro/plugin.d.ts +1 -1
  16. package/dist/runtime/nitro/plugin.mjs +98 -10
  17. package/dist/runtime/plugin.client.mjs +218 -38
  18. package/dist/runtime/plugin.server.mjs +50 -6
  19. package/dist/runtime/server/composables/useConfigDirectorClient.d.mts +1 -1
  20. package/dist/runtime/server/composables/useConfigDirectorClient.d.ts +1 -1
  21. package/dist/runtime/server/composables/useConfigDirectorServerHooks.d.mts +1 -1
  22. package/dist/runtime/server/composables/useConfigDirectorServerHooks.d.ts +1 -1
  23. package/dist/runtime/{types-Bgx2-3dY.d.mts → types-B9_4pADA.d.mts} +1 -1
  24. package/dist/runtime/{types-CvmjergQ.d.mts → types-DQhBe4Ei.d.mts} +13 -5
  25. package/dist/runtime/{types-CHmsCChN.d.mts → types-IPLvRsUO.d.mts} +22 -9
  26. package/dist/testing.d.mts +36 -14
  27. package/dist/testing.mjs +144 -36
  28. package/package.json +1 -1
  29. /package/dist/runtime/{types-CkzBXJwA.d.mts → types-F0fKKk6-.d.mts} +0 -0
@@ -393,6 +393,7 @@ var StreamingTransport = class {
393
393
  eventEmitter = new Emitter();
394
394
  url;
395
395
  timeoutTimer;
396
+ connectionGeneration = 0;
396
397
  constructor(options) {
397
398
  this.options = options;
398
399
  this.options = options;
@@ -400,6 +401,9 @@ var StreamingTransport = class {
400
401
  this.url = options.resolveUrl("client/sse/v1", options.baseUrl);
401
402
  }
402
403
  async connect(context, timeout) {
404
+ const generation = ++this.connectionGeneration;
405
+ const clientSession = await this.options.clientSession;
406
+ if (generation !== this.connectionGeneration) return this;
403
407
  if (this.eventSource) this.close();
404
408
  const eventSourcePromise = new Promise((resolve, reject) => {
405
409
  this.eventSource = createEventSourceClient({
@@ -411,7 +415,8 @@ var StreamingTransport = class {
411
415
  givenContext: context,
412
416
  metaContext: this.options.metaContext,
413
417
  clientSdkKey: this.options.clientSdkKey,
414
- instanceId: this.options.instanceId
418
+ instanceId: clientSession.instanceId,
419
+ ephemeral: clientSession.ephemeral
415
420
  }),
416
421
  onMessage: ({ data }) => {
417
422
  this.dispatchMessage(data);
@@ -476,6 +481,7 @@ var StreamingTransport = class {
476
481
  this.eventEmitter.clear();
477
482
  }
478
483
  close() {
484
+ this.connectionGeneration++;
479
485
  this.eventSource?.close();
480
486
  clearTimeout(this.timeoutTimer);
481
487
  }
@@ -740,10 +746,11 @@ const generateInstanceId = () => {
740
746
  });
741
747
  };
742
748
  //#endregion
743
- //#region ../js-client-core/src/instance-id.ts
749
+ //#region ../js-client-core/src/client-session.ts
744
750
  const STORAGE_KEY = "configdirector-sdk:instance-id";
745
751
  const RECORD_VERSION = 1;
746
752
  const MAX_AGE_MS = 360 * 60 * 1e3;
753
+ const STORAGE_DEADLINE_MS = 1e3;
747
754
  const CHECKSUM_SALT = "configdirector.instanceId.v1";
748
755
  const UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
749
756
  const fnv1a = (input, offsetBasis) => {
@@ -759,7 +766,7 @@ const checksumFor = (id, createdAt) => {
759
766
  const low = fnv1a(payload, 2166136261);
760
767
  return fnv1a(`${low}|${payload}`, 16777619).toString(16).padStart(8, "0") + low.toString(16).padStart(8, "0");
761
768
  };
762
- const getLocalStorage = () => {
769
+ const localStorageIfAvailable = () => {
763
770
  try {
764
771
  return globalThis.localStorage ?? void 0;
765
772
  } catch {
@@ -774,34 +781,37 @@ const isUsableRecord = (record) => {
774
781
  if (!UUID_V4_PATTERN.test(id)) return false;
775
782
  return checksum === checksumFor(id, createdAt);
776
783
  };
777
- const readStoredInstanceId = (storage, logger) => {
784
+ const readStoredInstanceId = async (storage, logger) => {
778
785
  let raw;
779
786
  try {
780
- raw = storage.getItem(STORAGE_KEY);
787
+ raw = await storage.getItem(STORAGE_KEY);
781
788
  } catch (error) {
782
- logger?.debug("[ConfigDirectorClient] Could not read the stored instance id, generating a new one", error);
783
- return;
789
+ logger.debug("[ConfigDirectorClient] Could not read the stored instance id, using an ephemeral one", error);
790
+ return { outcome: "failed" };
784
791
  }
785
- if (!raw) return;
792
+ if (typeof raw !== "string" || raw === "") return { outcome: "nothing usable" };
786
793
  let record;
787
794
  try {
788
795
  record = JSON.parse(raw);
789
796
  } catch {
790
- logger?.debug("[ConfigDirectorClient] The stored instance id is not valid JSON, generating a new one");
791
- return;
797
+ logger.debug("[ConfigDirectorClient] The stored instance id is not valid JSON, generating a new one");
798
+ return { outcome: "nothing usable" };
792
799
  }
793
800
  if (!isUsableRecord(record)) {
794
- logger?.debug("[ConfigDirectorClient] The stored instance id failed validation, generating a new one");
795
- return;
801
+ logger.debug("[ConfigDirectorClient] The stored instance id failed validation, generating a new one");
802
+ return { outcome: "nothing usable" };
796
803
  }
797
804
  const age = Date.now() - record.createdAt;
798
805
  if (age < 0 || age >= MAX_AGE_MS) {
799
- logger?.debug("[ConfigDirectorClient] The stored instance id has expired, generating a new one");
800
- return;
806
+ logger.debug("[ConfigDirectorClient] The stored instance id has expired, generating a new one");
807
+ return { outcome: "nothing usable" };
801
808
  }
802
- return record.id;
809
+ return {
810
+ outcome: "stored",
811
+ instanceId: record.id
812
+ };
803
813
  };
804
- const storeInstanceId = (storage, id, logger) => {
814
+ const persistInstanceId = async (storage, id, logger) => {
805
815
  const createdAt = Date.now();
806
816
  const record = {
807
817
  v: RECORD_VERSION,
@@ -810,19 +820,54 @@ const storeInstanceId = (storage, id, logger) => {
810
820
  checksum: checksumFor(id, createdAt)
811
821
  };
812
822
  try {
813
- storage.setItem(STORAGE_KEY, JSON.stringify(record));
823
+ await storage.setItem(STORAGE_KEY, JSON.stringify(record));
824
+ return true;
814
825
  } catch (error) {
815
- logger?.debug("[ConfigDirectorClient] Could not persist the instance id", error);
826
+ logger.debug("[ConfigDirectorClient] Could not persist the instance id, using it as an ephemeral one", error);
827
+ return false;
816
828
  }
817
829
  };
818
- const resolveInstanceId = (logger) => {
819
- const storage = getLocalStorage();
820
- if (!storage) return generateInstanceId();
821
- const storedInstanceId = readStoredInstanceId(storage, logger);
822
- if (storedInstanceId) return storedInstanceId;
823
- const instanceId = generateInstanceId();
824
- storeInstanceId(storage, instanceId, logger);
825
- return instanceId;
830
+ const settleFromStorage = async (storage, provisionalId, deadline, logger) => {
831
+ const read = await readStoredInstanceId(storage, logger);
832
+ switch (read.outcome) {
833
+ case "stored": return { instanceId: read.instanceId };
834
+ case "failed": return {
835
+ instanceId: provisionalId,
836
+ ephemeral: true
837
+ };
838
+ case "nothing usable": break;
839
+ }
840
+ if (deadline.passed) return {
841
+ instanceId: provisionalId,
842
+ ephemeral: true
843
+ };
844
+ return await persistInstanceId(storage, provisionalId, logger) ? { instanceId: provisionalId } : {
845
+ instanceId: provisionalId,
846
+ ephemeral: true
847
+ };
848
+ };
849
+ const resolveClientSession = (logger, storage = localStorageIfAvailable()) => {
850
+ const provisionalId = generateInstanceId();
851
+ if (!storage) {
852
+ logger.debug("[ConfigDirectorClient] No instance id storage is available, using an ephemeral instance id");
853
+ return Promise.resolve({
854
+ instanceId: provisionalId,
855
+ ephemeral: true
856
+ });
857
+ }
858
+ const deadline = { passed: false };
859
+ let deadlineTimer;
860
+ const fallbackAtDeadline = new Promise((resolve) => {
861
+ deadlineTimer = setTimeout(() => {
862
+ deadline.passed = true;
863
+ logger.warn(`[ConfigDirectorClient] The instance id storage did not respond within ${STORAGE_DEADLINE_MS}ms, using an ephemeral instance id`);
864
+ resolve({
865
+ instanceId: provisionalId,
866
+ ephemeral: true
867
+ });
868
+ }, STORAGE_DEADLINE_MS);
869
+ });
870
+ return Promise.race([settleFromStorage(storage, provisionalId, deadline, logger), fallbackAtDeadline]).finally(() => clearTimeout(deadlineTimer));
826
871
  };
827
872
  //#endregion
828
873
  //#region ../shared/src/transport/AbstractPollingTransport.ts
@@ -903,6 +948,7 @@ var PollingTransport = class extends AbstractPollingTransport {
903
948
  eventEmitter = new Emitter();
904
949
  url;
905
950
  lastUpdateTimestamp;
951
+ connectionGeneration = 0;
906
952
  constructor(options) {
907
953
  super();
908
954
  this.options = options;
@@ -912,19 +958,26 @@ var PollingTransport = class extends AbstractPollingTransport {
912
958
  this.pollingIntervalSeconds = options.pollingInterval ?? 60;
913
959
  }
914
960
  async connect(context, timeout) {
961
+ const generation = ++this.connectionGeneration;
915
962
  this.clearPollingInterval();
963
+ const clientSession = await this.options.clientSession;
964
+ if (generation !== this.connectionGeneration) return this;
916
965
  try {
917
- await this.fetchConfigs(context, timeout);
966
+ await this.fetchConfigs(context, timeout, clientSession);
918
967
  } finally {
919
968
  if (!this.fatalError) this.schedulePollingInterval(() => {
920
- this.fetchConfigs(context, timeout).catch((error) => {
969
+ this.fetchConfigs(context, timeout, clientSession).catch((error) => {
921
970
  this.logger.error("[PollingTransport] Error during polling:", error);
922
971
  });
923
972
  });
924
973
  }
925
974
  return this;
926
975
  }
927
- async fetchConfigs(context, timeout) {
976
+ close() {
977
+ this.connectionGeneration++;
978
+ super.close();
979
+ }
980
+ async fetchConfigs(context, timeout, clientSession) {
928
981
  if (this.fatalError) {
929
982
  this.logger.warn("[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.");
930
983
  return;
@@ -937,7 +990,8 @@ var PollingTransport = class extends AbstractPollingTransport {
937
990
  givenContext: context,
938
991
  metaContext: this.options.metaContext,
939
992
  clientSdkKey: this.options.clientSdkKey,
940
- instanceId: this.options.instanceId,
993
+ instanceId: clientSession.instanceId,
994
+ ephemeral: clientSession.ephemeral,
941
995
  lastUpdateTimestamp: this.lastUpdateTimestamp
942
996
  })
943
997
  }, this.logger);
@@ -978,6 +1032,50 @@ const read = (readValue) => {
978
1032
  const readUserAgent = () => read((globals) => globals.navigator?.userAgent);
979
1033
  const readHost = () => read((globals) => globals.location?.host);
980
1034
  //#endregion
1035
+ //#region ../shared/src/context-kind.ts
1036
+ const CONTEXT_KIND_MAXIMUM_LENGTH = 64;
1037
+ const KIND_SAMPLE_MAXIMUM_CHARACTERS = 128;
1038
+ const isContextKindCharacter = (character) => character >= "a" && character <= "z" || character >= "0" && character <= "9" || character === "-" || character === "_";
1039
+ const isValidContextKind = (kind) => {
1040
+ if (kind.length < 1 || kind.length > CONTEXT_KIND_MAXIMUM_LENGTH) return false;
1041
+ for (const character of kind) if (!isContextKindCharacter(character)) return false;
1042
+ return true;
1043
+ };
1044
+ const isAcceptedKind = (kind) => kind === void 0 || kind === null || typeof kind === "string" && isValidContextKind(kind);
1045
+ const stringFormOf = (kind) => {
1046
+ if (typeof kind === "string") return kind;
1047
+ try {
1048
+ return String(kind);
1049
+ } catch {
1050
+ return Object.prototype.toString.call(kind);
1051
+ }
1052
+ };
1053
+ const firstCharactersOf = (text, maximumCharacters) => {
1054
+ let sample = "";
1055
+ let characterCount = 0;
1056
+ for (const character of text) {
1057
+ if (characterCount === maximumCharacters) break;
1058
+ sample += character;
1059
+ characterCount += 1;
1060
+ }
1061
+ return sample;
1062
+ };
1063
+ const admittedContextOrNone = (context) => {
1064
+ const admission = admitContext(context);
1065
+ return admission.outcome === "admitted" ? admission.context : void 0;
1066
+ };
1067
+ const admitContext = (context) => {
1068
+ const kind = context?.kind;
1069
+ if (isAcceptedKind(kind)) return {
1070
+ outcome: "admitted",
1071
+ context
1072
+ };
1073
+ return {
1074
+ outcome: "ignored",
1075
+ kindSample: firstCharactersOf(stringFormOf(kind), KIND_SAMPLE_MAXIMUM_CHARACTERS)
1076
+ };
1077
+ };
1078
+ //#endregion
981
1079
  //#region ../js-client-core/src/DefaultConfigDirectorClient.ts
982
1080
  const MAX_EXPONENTIAL_DELAY = 9;
983
1081
  var DefaultConfigDirectorClient = class {
@@ -996,10 +1094,10 @@ var DefaultConfigDirectorClient = class {
996
1094
  readyResolve;
997
1095
  currentContext;
998
1096
  connectionMode;
999
- instanceId;
1097
+ clientSession;
1000
1098
  constructor(telemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
1001
1099
  this.logger = clientOptions?.logger ?? createDefaultLogger$1();
1002
- this.instanceId = resolveInstanceId(this.logger);
1100
+ this.clientSession = resolveClientSession(this.logger, internalClientOptions?.instanceIdStorage);
1003
1101
  this.timeout = clientOptions?.connection?.timeout ?? 3e3;
1004
1102
  this.validateSdkKeyPresence(clientSdkKey);
1005
1103
  const urlFactory = internalClientOptions?.urlFactory ?? defaultUrlFactory;
@@ -1054,7 +1152,7 @@ var DefaultConfigDirectorClient = class {
1054
1152
  }
1055
1153
  buildTransport(clientSdkKey, sdkOptions, baseUrl, urlFactory, clientOptions, internalClientOptions) {
1056
1154
  return new (this.getTransportConstructor(this.connectionMode))({
1057
- instanceId: this.instanceId,
1155
+ clientSession: this.clientSession,
1058
1156
  clientSdkKey,
1059
1157
  baseUrl,
1060
1158
  resolveUrl: urlFactory,
@@ -1081,10 +1179,23 @@ var DefaultConfigDirectorClient = class {
1081
1179
  }
1082
1180
  async initialize(context) {
1083
1181
  if (!this.hasReceivedConfigSet) this.initializing = true;
1084
- await this.connectToTransport(context, "initialization");
1182
+ await this.submitContext(context, "initialization");
1085
1183
  }
1086
1184
  async updateContext(context) {
1087
- await this.connectToTransport(context, "context update");
1185
+ await this.submitContext(context, "context update");
1186
+ }
1187
+ async submitContext(context, submission) {
1188
+ await this.connectToTransport(this.contextToConnectWith(context, submission), submission);
1189
+ }
1190
+ contextToConnectWith(suppliedContext, submission) {
1191
+ const admission = admitContext(suppliedContext);
1192
+ switch (admission.outcome) {
1193
+ case "admitted": return admission.context;
1194
+ case "ignored":
1195
+ this.logger.error(`[ConfigDirectorClient] Ignored the context supplied for ${submission} because its kind '${admission.kindSample}' is invalid; connecting as if no context was supplied. A context kind has 1 to 64 characters, using only lowercase letters, digits, '-' and '_'.`);
1196
+ this.telemetryClient.recordInvalidContext(admission.kindSample);
1197
+ return;
1198
+ }
1088
1199
  }
1089
1200
  get context() {
1090
1201
  return this.currentContext;
@@ -1296,6 +1407,59 @@ const mapToTelemetryValue = (ev) => {
1296
1407
  if (stringValue && stringValue.length <= CONFIG_VALUE_MAX_LENGTH) return { value: stringValue };
1297
1408
  return ev.valueId ? { valueId: ev.valueId } : { value: stringValue };
1298
1409
  };
1410
+ var InvalidContextSubmissions = class {
1411
+ countsByKindSample = /* @__PURE__ */ new Map();
1412
+ countBeyondSampleLimit = 0;
1413
+ startTime;
1414
+ record(kindSample) {
1415
+ this.startTime ??= /* @__PURE__ */ new Date();
1416
+ return this.add(kindSample, 1);
1417
+ }
1418
+ absorb(entries) {
1419
+ for (const entry of entries) {
1420
+ if (this.startTime === void 0 || entry.startTime < this.startTime) this.startTime = entry.startTime;
1421
+ this.add(entry.event.kindSample, entry.count);
1422
+ }
1423
+ }
1424
+ add(kindSample, count) {
1425
+ if (kindSample !== void 0) {
1426
+ const knownCount = this.countsByKindSample.get(kindSample);
1427
+ if (knownCount !== void 0) {
1428
+ this.countsByKindSample.set(kindSample, knownCount + count);
1429
+ return "known-kind-sample";
1430
+ }
1431
+ if (this.countsByKindSample.size < 20) {
1432
+ this.countsByKindSample.set(kindSample, count);
1433
+ return "new-kind-sample";
1434
+ }
1435
+ }
1436
+ const firstBeyondSampleLimit = this.countBeyondSampleLimit === 0;
1437
+ this.countBeyondSampleLimit += count;
1438
+ return firstBeyondSampleLimit ? "first-beyond-sample-limit" : "beyond-sample-limit";
1439
+ }
1440
+ takeSnapshot() {
1441
+ const endTime = /* @__PURE__ */ new Date();
1442
+ const startTime = this.startTime ?? endTime;
1443
+ const entryOf = (count, kindSample) => ({
1444
+ startTime,
1445
+ endTime,
1446
+ count,
1447
+ event: {
1448
+ reason: "invalid-context-kind",
1449
+ kindSample
1450
+ }
1451
+ });
1452
+ const sampledEntries = Array.from(this.countsByKindSample, ([kindSample, count]) => entryOf(count, kindSample));
1453
+ const entries = this.countBeyondSampleLimit > 0 ? [...sampledEntries, entryOf(this.countBeyondSampleLimit)] : sampledEntries;
1454
+ this.clear();
1455
+ return entries;
1456
+ }
1457
+ clear() {
1458
+ this.countsByKindSample.clear();
1459
+ this.countBeyondSampleLimit = 0;
1460
+ this.startTime = void 0;
1461
+ }
1462
+ };
1299
1463
  //#endregion
1300
1464
  //#region ../js-browser-client/src/telemetry/WebWorkerTelemetryClient.ts
1301
1465
  const DEFAULT_WORKER_CLOSE_TIMEOUT = 6e3;
@@ -1306,6 +1470,7 @@ var WebWorkerTelemetryClient = class {
1306
1470
  turnedOff = false;
1307
1471
  pendingEvaluations = [];
1308
1472
  pendingEvaluationLimit;
1473
+ pendingInvalidContextSubmissions = new InvalidContextSubmissions();
1309
1474
  closePromise;
1310
1475
  closeResolve;
1311
1476
  closeTimer;
@@ -1337,7 +1502,7 @@ var WebWorkerTelemetryClient = class {
1337
1502
  return;
1338
1503
  }
1339
1504
  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" })));
1505
+ 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{constructor(){this.countsByKindSample=new Map,this.countBeyondSampleLimit=0}record(e){return this.startTime??=new Date,this.add(e,1)}absorb(e){for(let t of e)(this.startTime===void 0||t.startTime<this.startTime)&&(this.startTime=t.startTime),this.add(t.event.kindSample,t.count)}add(e,t){if(e!==void 0){let n=this.countsByKindSample.get(e);if(n!==void 0)return this.countsByKindSample.set(e,n+t),`known-kind-sample`;if(this.countsByKindSample.size<20)return this.countsByKindSample.set(e,t),`new-kind-sample`}let n=this.countBeyondSampleLimit===0;return this.countBeyondSampleLimit+=t,n?`first-beyond-sample-limit`:`beyond-sample-limit`}takeSnapshot(){let e=new Date,t=this.startTime??e,n=(n,r)=>({startTime:t,endTime:e,count:n,event:{reason:`invalid-context-kind`,kindSample:r}}),r=Array.from(this.countsByKindSample,([e,t])=>n(t,e)),i=this.countBeyondSampleLimit>0?[...r,n(this.countBeyondSampleLimit)]:r;return this.clear(),i}clear(){this.countsByKindSample.clear(),this.countBeyondSampleLimit=0,this.startTime=void 0}},d=class extends c{constructor(e){super(e),this.invalidContextSubmissions=new u,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)}recordInvalidContext(e){this.invalidContextSubmissions.record(e)}absorbInvalidContextSubmissions(e){this.invalidContextSubmissions.absorb(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),invalidContext:this.invalidContextSubmissions.takeSnapshot()},droppedEvents:{evaluatedConfig:e.droppedCount}})}};let f=(e,t)=>new URL(e,t?.toString()),p=(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`)},m=Math.ceil(128/Math.log2(62)),h=new TextEncoder,g=async e=>{if(typeof crypto>`u`||crypto.subtle==null)return;let t=await crypto.subtle.digest(`SHA-256`,h.encode(e?.toString()??``));return p(new Uint8Array(t,0,16),m)},_,v=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),y=e=>{try{self.postMessage(e)}catch{self.postMessage(v(e.payload.level,e.payload.message))}},b={debug:function(e,...t){y(v(`debug`,e,t))},info:function(e,...t){y(v(`info`,e,t))},warn:function(e,...t){y(v(`warn`,e,t))},error:function(e,...t){y(v(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!_){b.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(b.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:if(_){b.warn(`[TelemetryWorker] Received a duplicate Initialize event, ignoring it`);break}_=new d({...t.payload,baseUrl:f(t.payload.baseUrl),urlFactory:f,valueIdGenerator:g,logger:b}),_.start();break;case`EvaluatedConfigEvent`:_?.evaluatedConfig(t.payload);break;case`InvalidContextSubmissions`:_?.absorbInvalidContextSubmissions(t.payload.entries);break;case`UpdateContext`:_?.updateContext(t.payload.context);break;case`Flush`:_?.forceFlush();break;case`Close`:(_?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"], { type: "application/javascript" })));
1341
1506
  } catch (error) {
1342
1507
  this.turnOff("[WebWorkerTelemetryClient] Telemetry is off because the telemetry Web Worker could not be created: ", error);
1343
1508
  return;
@@ -1364,8 +1529,19 @@ var WebWorkerTelemetryClient = class {
1364
1529
  }
1365
1530
  for (const evaluation of this.pendingEvaluations) worker.postMessage(evaluation);
1366
1531
  this.pendingEvaluations.length = 0;
1532
+ this.forwardInvalidContextSubmissions(worker);
1367
1533
  return worker;
1368
1534
  }
1535
+ forwardInvalidContextSubmissions(worker) {
1536
+ if (!worker) return;
1537
+ const entries = this.pendingInvalidContextSubmissions.takeSnapshot();
1538
+ if (entries.length === 0) return;
1539
+ const submissionsMessage = {
1540
+ type: "InvalidContextSubmissions",
1541
+ payload: { entries }
1542
+ };
1543
+ worker.postMessage(submissionsMessage);
1544
+ }
1369
1545
  async updateContext(value) {
1370
1546
  if (this.closePromise) return;
1371
1547
  const updateContextMessage = {
@@ -1387,6 +1563,10 @@ var WebWorkerTelemetryClient = class {
1387
1563
  if (this.pendingEvaluations.length >= this.pendingEvaluationLimit) this.pendingEvaluations.shift();
1388
1564
  this.pendingEvaluations.push(evaluatedConfigMessage);
1389
1565
  }
1566
+ recordInvalidContext(kindSample) {
1567
+ this.pendingInvalidContextSubmissions.record(kindSample);
1568
+ this.forwardInvalidContextSubmissions(this.worker);
1569
+ }
1390
1570
  sanitizeEvaluatedConfigEvent(event) {
1391
1571
  return {
1392
1572
  ...event,
@@ -1555,7 +1735,7 @@ const useConfigDirectorContext = () => {
1555
1735
  const rawContext = toRaw(context);
1556
1736
  const oldContext = JSON.stringify(state.value);
1557
1737
  const newContext = JSON.stringify(rawContext);
1558
- state.value = rawContext;
1738
+ state.value = admittedContextOrNone(rawContext);
1559
1739
  if (oldContext != newContext) {
1560
1740
  const timeout = getEffectiveTimeout(options?.timeoutMilliseconds);
1561
1741
  let timer;
@@ -1617,7 +1797,7 @@ const buildClient = (configdirector, logger) => {
1617
1797
  const { clientSdkKey, appName, appVersion, baseUrl, connection } = configdirector;
1618
1798
  return createBrowserClient(clientSdkKey, {
1619
1799
  sdkName: "nuxt-sdk",
1620
- sdkVersion: "1.9.2"
1800
+ sdkVersion: "1.10.0"
1621
1801
  }, {
1622
1802
  metadata: {
1623
1803
  appName,
@@ -14,6 +14,50 @@ const useConfigDirectorClient = () => {
14
14
  return { client: $configDirectorClient };
15
15
  };
16
16
  //#endregion
17
+ //#region ../shared/src/context-kind.ts
18
+ const CONTEXT_KIND_MAXIMUM_LENGTH = 64;
19
+ const KIND_SAMPLE_MAXIMUM_CHARACTERS = 128;
20
+ const isContextKindCharacter = (character) => character >= "a" && character <= "z" || character >= "0" && character <= "9" || character === "-" || character === "_";
21
+ const isValidContextKind = (kind) => {
22
+ if (kind.length < 1 || kind.length > CONTEXT_KIND_MAXIMUM_LENGTH) return false;
23
+ for (const character of kind) if (!isContextKindCharacter(character)) return false;
24
+ return true;
25
+ };
26
+ const isAcceptedKind = (kind) => kind === void 0 || kind === null || typeof kind === "string" && isValidContextKind(kind);
27
+ const stringFormOf = (kind) => {
28
+ if (typeof kind === "string") return kind;
29
+ try {
30
+ return String(kind);
31
+ } catch {
32
+ return Object.prototype.toString.call(kind);
33
+ }
34
+ };
35
+ const firstCharactersOf = (text, maximumCharacters) => {
36
+ let sample = "";
37
+ let characterCount = 0;
38
+ for (const character of text) {
39
+ if (characterCount === maximumCharacters) break;
40
+ sample += character;
41
+ characterCount += 1;
42
+ }
43
+ return sample;
44
+ };
45
+ const admittedContextOrNone = (context) => {
46
+ const admission = admitContext(context);
47
+ return admission.outcome === "admitted" ? admission.context : void 0;
48
+ };
49
+ const admitContext = (context) => {
50
+ const kind = context?.kind;
51
+ if (isAcceptedKind(kind)) return {
52
+ outcome: "admitted",
53
+ context
54
+ };
55
+ return {
56
+ outcome: "ignored",
57
+ kindSample: firstCharactersOf(stringFormOf(kind), KIND_SAMPLE_MAXIMUM_CHARACTERS)
58
+ };
59
+ };
60
+ //#endregion
17
61
  //#region src/runtime/app/composables/useConfigDirectorContext.ts
18
62
  const DEFAULT_TIMEOUT = 3e3;
19
63
  const getEffectiveTimeout = (option) => {
@@ -26,7 +70,7 @@ const useConfigDirectorContext = () => {
26
70
  const rawContext = toRaw(context);
27
71
  const oldContext = JSON.stringify(state.value);
28
72
  const newContext = JSON.stringify(rawContext);
29
- state.value = rawContext;
73
+ state.value = admittedContextOrNone(rawContext);
30
74
  if (oldContext != newContext) {
31
75
  const timeout = getEffectiveTimeout(options?.timeoutMilliseconds);
32
76
  let timer;
@@ -65,19 +109,19 @@ var plugin_server_default = defineNuxtPlugin((nuxtApp) => {
65
109
  } };
66
110
  });
67
111
  var SsrClient = class {
68
- currentContext;
112
+ suppliedContext;
69
113
  constructor(serverClient) {
70
114
  this.serverClient = serverClient;
71
115
  this.serverClient = serverClient;
72
116
  }
73
117
  async initialize(context) {
74
- this.currentContext = context;
118
+ this.suppliedContext = context;
75
119
  }
76
120
  async updateContext(context) {
77
- this.currentContext = context;
121
+ this.suppliedContext = context;
78
122
  }
79
123
  get context() {
80
- return this.currentContext;
124
+ return admittedContextOrNone(this.suppliedContext);
81
125
  }
82
126
  get isReady() {
83
127
  return this.serverClient.isReady;
@@ -86,7 +130,7 @@ var SsrClient = class {
86
130
  return false;
87
131
  }
88
132
  getValue(configKey, defaultValue) {
89
- return this.serverClient.getValue(configKey, defaultValue, this.currentContext);
133
+ return this.serverClient.getValue(configKey, defaultValue, this.suppliedContext);
90
134
  }
91
135
  watch() {
92
136
  return () => {};
@@ -1,4 +1,4 @@
1
- import { n as ConfigDirectorClient } from "../../types-Bgx2-3dY.mjs";
1
+ import { n as ConfigDirectorClient } from "../../types-B9_4pADA.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 { n as ConfigDirectorClient } from "../../types-Bgx2-3dY.mjs";
1
+ import { n as ConfigDirectorClient } from "../../types-B9_4pADA.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 ClientHooks } from "../../types-Bgx2-3dY.mjs";
1
+ import { t as ClientHooks } from "../../types-B9_4pADA.mjs";
2
2
 
3
3
  //#region src/runtime/server/composables/useConfigDirectorServerHooks.d.ts
4
4
  declare const useConfigDirectorServerHooks: (hooks: ClientHooks) => void;
@@ -1,4 +1,4 @@
1
- import { t as ClientHooks } from "../../types-Bgx2-3dY.mjs";
1
+ import { t as ClientHooks } from "../../types-B9_4pADA.mjs";
2
2
 
3
3
  //#region src/runtime/server/composables/useConfigDirectorServerHooks.d.ts
4
4
  declare const useConfigDirectorServerHooks: (hooks: ClientHooks) => void;
@@ -1,4 +1,4 @@
1
- import { i as ConfigValueType, n as ConfigEvaluation, r as ConfigState, t as ConfigDirectorContext } from "./types-CHmsCChN.mjs";
1
+ import { i as ConfigValueType, n as ConfigEvaluation, r as ConfigState, t as ConfigDirectorContext } from "./types-IPLvRsUO.mjs";
2
2
 
3
3
  //#region ../js-server-sdk/src/types.d.ts
4
4
  type ClientEvents = {
@@ -1,4 +1,4 @@
1
- import { i as ConfigValueType, n as ConfigEvaluation, t as ConfigDirectorContext } from "./types-CHmsCChN.mjs";
1
+ import { i as ConfigValueType, n as ConfigEvaluation, t as ConfigDirectorContext } from "./types-IPLvRsUO.mjs";
2
2
 
3
3
  //#region ../js-client-core/src/Emitter.d.ts
4
4
  type EventType = string | symbol;
@@ -60,17 +60,25 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
60
60
  * not attempt to re-connect and an error will be logged to the console or the provided
61
61
  * logger.
62
62
  *
63
- * @param context The current user's context to be used for evaluating targeting rules (optional).
63
+ * A context whose {@link ConfigDirectorContext.kind} is invalid is ignored as a whole: an error is
64
+ * logged, and the client connects as if no context had been provided.
65
+ *
66
+ * @param context The current context to be used for evaluating targeting rules (optional).
64
67
  */
65
68
  initialize(context?: ConfigDirectorContext): Promise<void>;
66
69
  /**
67
- * Updates the user's context and re-evaluates all config and flag values based on the new context.
70
+ * Updates the context and re-evaluates all config and flag values based on the new context.
71
+ *
72
+ * A context whose {@link ConfigDirectorContext.kind} is invalid is ignored as a whole: an error is
73
+ * logged, the previous context is no longer used, and the client connects as if no context had been
74
+ * provided.
68
75
  *
69
- * @param context The current user's context to be used for evaluating targeting rules (required).
76
+ * @param context The current context to be used for evaluating targeting rules (required).
70
77
  */
71
78
  updateContext(context: ConfigDirectorContext): Promise<void>;
72
79
  /**
73
- * Returns the current context the client is configured with, or undefined if there is no context.
80
+ * Returns the current context the client is configured with, or undefined if there is no context,
81
+ * including after a context with an invalid kind was ignored.
74
82
  *
75
83
  * Note that the value returned does not updated immediately after calling {@link updateContext}. Only
76
84
  * once the underlying connection was successful or timed out, the current context is updated. This is