@configdirector/nuxt-sdk 1.9.1 → 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.
- package/dist/module.json +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClient.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClient.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClientHooks.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorClientHooks.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorContext.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorContext.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorContext.mjs +2 -1
- package/dist/runtime/app/composables/useConfigDirectorStatus.d.mts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorStatus.d.ts +1 -1
- package/dist/runtime/app/composables/useConfigDirectorValue.d.mts +2 -2
- package/dist/runtime/app/composables/useConfigDirectorValue.d.ts +2 -2
- package/dist/runtime/context-kind-D6YBNij2.mjs +45 -0
- package/dist/runtime/nitro/plugin.d.mts +1 -1
- package/dist/runtime/nitro/plugin.d.ts +1 -1
- package/dist/runtime/nitro/plugin.mjs +105 -11
- package/dist/runtime/plugin.client.mjs +218 -38
- package/dist/runtime/plugin.server.mjs +50 -6
- package/dist/runtime/server/composables/useConfigDirectorClient.d.mts +1 -1
- package/dist/runtime/server/composables/useConfigDirectorClient.d.ts +1 -1
- package/dist/runtime/server/composables/useConfigDirectorServerHooks.d.mts +1 -1
- package/dist/runtime/server/composables/useConfigDirectorServerHooks.d.ts +1 -1
- package/dist/runtime/{types-Bgx2-3dY.d.mts → types-B9_4pADA.d.mts} +1 -1
- package/dist/runtime/{types-CvmjergQ.d.mts → types-DQhBe4Ei.d.mts} +13 -5
- package/dist/runtime/{types-CHmsCChN.d.mts → types-IPLvRsUO.d.mts} +22 -9
- package/dist/testing.d.mts +36 -14
- package/dist/testing.mjs +144 -36
- package/package.json +1 -1
- /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:
|
|
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/
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
809
|
+
return {
|
|
810
|
+
outcome: "stored",
|
|
811
|
+
instanceId: record.id
|
|
812
|
+
};
|
|
803
813
|
};
|
|
804
|
-
const
|
|
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
|
|
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
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
1097
|
+
clientSession;
|
|
1000
1098
|
constructor(telemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
|
|
1001
1099
|
this.logger = clientOptions?.logger ?? createDefaultLogger$1();
|
|
1002
|
-
this.
|
|
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
|
-
|
|
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.
|
|
1182
|
+
await this.submitContext(context, "initialization");
|
|
1085
1183
|
}
|
|
1086
1184
|
async updateContext(context) {
|
|
1087
|
-
await this.
|
|
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
|
|
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.
|
|
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
|
-
|
|
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.
|
|
118
|
+
this.suppliedContext = context;
|
|
75
119
|
}
|
|
76
120
|
async updateContext(context) {
|
|
77
|
-
this.
|
|
121
|
+
this.suppliedContext = context;
|
|
78
122
|
}
|
|
79
123
|
get context() {
|
|
80
|
-
return this.
|
|
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.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|