@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.
- 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 +98 -10
- 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
package/dist/module.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ClientHooks } from "../../types-
|
|
1
|
+
import { t as ClientHooks } from "../../types-DQhBe4Ei.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/runtime/app/composables/useConfigDirectorClientHooks.d.ts
|
|
4
4
|
declare const useConfigDirectorClientHooks: (hooks: ClientHooks) => void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ClientHooks } from "../../types-
|
|
1
|
+
import { t as ClientHooks } from "../../types-DQhBe4Ei.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/runtime/app/composables/useConfigDirectorClientHooks.d.ts
|
|
4
4
|
declare const useConfigDirectorClientHooks: (hooks: ClientHooks) => void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useConfigDirectorClient } from "./useConfigDirectorClient.mjs";
|
|
2
|
+
import { n as admittedContextOrNone } from "../../context-kind-D6YBNij2.mjs";
|
|
2
3
|
import { useState } from "#app";
|
|
3
4
|
import { readonly, toRaw } from "vue";
|
|
4
5
|
//#region src/runtime/app/composables/useConfigDirectorContext.ts
|
|
@@ -13,7 +14,7 @@ const useConfigDirectorContext = () => {
|
|
|
13
14
|
const rawContext = toRaw(context);
|
|
14
15
|
const oldContext = JSON.stringify(state.value);
|
|
15
16
|
const newContext = JSON.stringify(rawContext);
|
|
16
|
-
state.value = rawContext;
|
|
17
|
+
state.value = admittedContextOrNone(rawContext);
|
|
17
18
|
if (oldContext != newContext) {
|
|
18
19
|
const timeout = getEffectiveTimeout(options?.timeoutMilliseconds);
|
|
19
20
|
let timer;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as ConfigValueType } from "../../types-
|
|
2
|
-
import { t as ClientStatus } from "../../types-
|
|
1
|
+
import { i as ConfigValueType } from "../../types-IPLvRsUO.mjs";
|
|
2
|
+
import { t as ClientStatus } from "../../types-F0fKKk6-.mjs";
|
|
3
3
|
import { DeepReadonly, Ref, ShallowRef } from "vue";
|
|
4
4
|
|
|
5
5
|
//#region src/runtime/app/composables/useConfigDirectorValue.d.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as ConfigValueType } from "../../types-
|
|
2
|
-
import { t as ClientStatus } from "../../types-
|
|
1
|
+
import { i as ConfigValueType } from "../../types-IPLvRsUO.mjs";
|
|
2
|
+
import { t as ClientStatus } from "../../types-F0fKKk6-.mjs";
|
|
3
3
|
import { DeepReadonly, Ref, ShallowRef } from "vue";
|
|
4
4
|
|
|
5
5
|
//#region src/runtime/app/composables/useConfigDirectorValue.d.ts
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region ../shared/src/context-kind.ts
|
|
2
|
+
const CONTEXT_KIND_MAXIMUM_LENGTH = 64;
|
|
3
|
+
const KIND_SAMPLE_MAXIMUM_CHARACTERS = 128;
|
|
4
|
+
const isContextKindCharacter = (character) => character >= "a" && character <= "z" || character >= "0" && character <= "9" || character === "-" || character === "_";
|
|
5
|
+
const isValidContextKind = (kind) => {
|
|
6
|
+
if (kind.length < 1 || kind.length > CONTEXT_KIND_MAXIMUM_LENGTH) return false;
|
|
7
|
+
for (const character of kind) if (!isContextKindCharacter(character)) return false;
|
|
8
|
+
return true;
|
|
9
|
+
};
|
|
10
|
+
const isAcceptedKind = (kind) => kind === void 0 || kind === null || typeof kind === "string" && isValidContextKind(kind);
|
|
11
|
+
const stringFormOf = (kind) => {
|
|
12
|
+
if (typeof kind === "string") return kind;
|
|
13
|
+
try {
|
|
14
|
+
return String(kind);
|
|
15
|
+
} catch {
|
|
16
|
+
return Object.prototype.toString.call(kind);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const firstCharactersOf = (text, maximumCharacters) => {
|
|
20
|
+
let sample = "";
|
|
21
|
+
let characterCount = 0;
|
|
22
|
+
for (const character of text) {
|
|
23
|
+
if (characterCount === maximumCharacters) break;
|
|
24
|
+
sample += character;
|
|
25
|
+
characterCount += 1;
|
|
26
|
+
}
|
|
27
|
+
return sample;
|
|
28
|
+
};
|
|
29
|
+
const admittedContextOrNone = (context) => {
|
|
30
|
+
const admission = admitContext(context);
|
|
31
|
+
return admission.outcome === "admitted" ? admission.context : void 0;
|
|
32
|
+
};
|
|
33
|
+
const admitContext = (context) => {
|
|
34
|
+
const kind = context?.kind;
|
|
35
|
+
if (isAcceptedKind(kind)) return {
|
|
36
|
+
outcome: "admitted",
|
|
37
|
+
context
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
outcome: "ignored",
|
|
41
|
+
kindSample: firstCharactersOf(stringFormOf(kind), KIND_SAMPLE_MAXIMUM_CHARACTERS)
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { admittedContextOrNone as n, admitContext as t };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as admitContext } from "../context-kind-D6YBNij2.mjs";
|
|
1
2
|
import EventEmitter, { EventEmitter as EventEmitter$1 } from "node:events";
|
|
2
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
4
|
import * as semver from "semver";
|
|
@@ -7017,6 +7018,7 @@ const compareArray = (givenValue, operator, targetValues) => {
|
|
|
7017
7018
|
default: return false;
|
|
7018
7019
|
}
|
|
7019
7020
|
};
|
|
7021
|
+
const effectiveContextKind = (context) => context?.context?.kind ?? "user";
|
|
7020
7022
|
//#endregion
|
|
7021
7023
|
//#region ../config-evaluator/src/ConditionEvaluator.ts
|
|
7022
7024
|
var ConditionEvaluator = class {
|
|
@@ -7065,6 +7067,7 @@ var ConditionEvaluator = class {
|
|
|
7065
7067
|
switch (condition.attribute) {
|
|
7066
7068
|
case "identifier": return orAbsent(context?.context?.id);
|
|
7067
7069
|
case "name": return orAbsent(context?.context?.name);
|
|
7070
|
+
case "kind": return effectiveContextKind(context);
|
|
7068
7071
|
case "appName": return orAbsent(context?.metadata?.appName);
|
|
7069
7072
|
case "appVersion": return orAbsent(context?.metadata?.appVersion);
|
|
7070
7073
|
case "traits":
|
|
@@ -7112,9 +7115,9 @@ const holdsAttributeConditionsOnly = (segment) => (segment.groups ?? []).every((
|
|
|
7112
7115
|
//#endregion
|
|
7113
7116
|
//#region ../config-evaluator/src/percent-hashing.ts
|
|
7114
7117
|
const SEED = 15426857351n;
|
|
7115
|
-
const assignPercentage = (
|
|
7116
|
-
const
|
|
7117
|
-
return Number(rapidhash(
|
|
7118
|
+
const assignPercentage = ({ configId, contextIdentifier, contextKind }) => {
|
|
7119
|
+
const hashInput = contextKind === "user" ? `${contextIdentifier}-${configId}` : `${contextIdentifier}-${configId}:${contextKind}`;
|
|
7120
|
+
return Number(rapidhash(hashInput, { seed: SEED }) % 1000n) / 10;
|
|
7118
7121
|
};
|
|
7119
7122
|
//#endregion
|
|
7120
7123
|
//#region ../config-evaluator/src/ConfigEvaluator.ts
|
|
@@ -7208,7 +7211,8 @@ var ConfigEvaluator = class {
|
|
|
7208
7211
|
const identifier = context?.context?.id ?? void 0;
|
|
7209
7212
|
const assignedPercentage = identifier === void 0 ? 0 : assignPercentage({
|
|
7210
7213
|
configId: config.id,
|
|
7211
|
-
contextIdentifier: identifier
|
|
7214
|
+
contextIdentifier: identifier,
|
|
7215
|
+
contextKind: effectiveContextKind(context)
|
|
7212
7216
|
});
|
|
7213
7217
|
const shares = [];
|
|
7214
7218
|
let sum = 0;
|
|
@@ -7511,6 +7515,59 @@ const resolvePollingInterval = (configuredSeconds, defaults) => {
|
|
|
7511
7515
|
warning: `pollingInterval of ${configuredSeconds} seconds is below the minimum of ${defaults.minimumSeconds} seconds. Using ${defaults.minimumSeconds} seconds.`
|
|
7512
7516
|
};
|
|
7513
7517
|
};
|
|
7518
|
+
var InvalidContextSubmissions = class {
|
|
7519
|
+
countsByKindSample = /* @__PURE__ */ new Map();
|
|
7520
|
+
countBeyondSampleLimit = 0;
|
|
7521
|
+
startTime;
|
|
7522
|
+
record(kindSample) {
|
|
7523
|
+
this.startTime ??= /* @__PURE__ */ new Date();
|
|
7524
|
+
return this.add(kindSample, 1);
|
|
7525
|
+
}
|
|
7526
|
+
absorb(entries) {
|
|
7527
|
+
for (const entry of entries) {
|
|
7528
|
+
if (this.startTime === void 0 || entry.startTime < this.startTime) this.startTime = entry.startTime;
|
|
7529
|
+
this.add(entry.event.kindSample, entry.count);
|
|
7530
|
+
}
|
|
7531
|
+
}
|
|
7532
|
+
add(kindSample, count) {
|
|
7533
|
+
if (kindSample !== void 0) {
|
|
7534
|
+
const knownCount = this.countsByKindSample.get(kindSample);
|
|
7535
|
+
if (knownCount !== void 0) {
|
|
7536
|
+
this.countsByKindSample.set(kindSample, knownCount + count);
|
|
7537
|
+
return "known-kind-sample";
|
|
7538
|
+
}
|
|
7539
|
+
if (this.countsByKindSample.size < 20) {
|
|
7540
|
+
this.countsByKindSample.set(kindSample, count);
|
|
7541
|
+
return "new-kind-sample";
|
|
7542
|
+
}
|
|
7543
|
+
}
|
|
7544
|
+
const firstBeyondSampleLimit = this.countBeyondSampleLimit === 0;
|
|
7545
|
+
this.countBeyondSampleLimit += count;
|
|
7546
|
+
return firstBeyondSampleLimit ? "first-beyond-sample-limit" : "beyond-sample-limit";
|
|
7547
|
+
}
|
|
7548
|
+
takeSnapshot() {
|
|
7549
|
+
const endTime = /* @__PURE__ */ new Date();
|
|
7550
|
+
const startTime = this.startTime ?? endTime;
|
|
7551
|
+
const entryOf = (count, kindSample) => ({
|
|
7552
|
+
startTime,
|
|
7553
|
+
endTime,
|
|
7554
|
+
count,
|
|
7555
|
+
event: {
|
|
7556
|
+
reason: "invalid-context-kind",
|
|
7557
|
+
kindSample
|
|
7558
|
+
}
|
|
7559
|
+
});
|
|
7560
|
+
const sampledEntries = Array.from(this.countsByKindSample, ([kindSample, count]) => entryOf(count, kindSample));
|
|
7561
|
+
const entries = this.countBeyondSampleLimit > 0 ? [...sampledEntries, entryOf(this.countBeyondSampleLimit)] : sampledEntries;
|
|
7562
|
+
this.clear();
|
|
7563
|
+
return entries;
|
|
7564
|
+
}
|
|
7565
|
+
clear() {
|
|
7566
|
+
this.countsByKindSample.clear();
|
|
7567
|
+
this.countBeyondSampleLimit = 0;
|
|
7568
|
+
this.startTime = void 0;
|
|
7569
|
+
}
|
|
7570
|
+
};
|
|
7514
7571
|
//#endregion
|
|
7515
7572
|
//#region ../shared/src/telemetry/utils.ts
|
|
7516
7573
|
const CONFIG_VALUE_MAX_LENGTH = 500;
|
|
@@ -7821,6 +7878,7 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
7821
7878
|
reporter;
|
|
7822
7879
|
_context;
|
|
7823
7880
|
contexts;
|
|
7881
|
+
invalidContextSubmissions = new InvalidContextSubmissions();
|
|
7824
7882
|
constructor(options) {
|
|
7825
7883
|
super(options);
|
|
7826
7884
|
this.reporter = new ServerEventReporter(options);
|
|
@@ -7829,13 +7887,23 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
7829
7887
|
}
|
|
7830
7888
|
evaluatedConfig(payload) {
|
|
7831
7889
|
if (!this.collectEvents) return;
|
|
7832
|
-
|
|
7890
|
+
const context = payload.context ?? void 0;
|
|
7891
|
+
const contextKind = effectiveContextKind({ context });
|
|
7892
|
+
const contextId = context?.anonymous ? void 0 : context?.id;
|
|
7893
|
+
if (contextId) this.contexts.set(`${contextKind}:${contextId}`, {
|
|
7894
|
+
...context,
|
|
7895
|
+
kind: contextKind
|
|
7896
|
+
});
|
|
7833
7897
|
const event = {
|
|
7834
7898
|
...payload.evaluation,
|
|
7835
|
-
contextId
|
|
7899
|
+
contextId,
|
|
7900
|
+
contextKind: contextId === void 0 ? void 0 : contextKind
|
|
7836
7901
|
};
|
|
7837
7902
|
this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(event));
|
|
7838
7903
|
}
|
|
7904
|
+
recordInvalidContext(kindSample) {
|
|
7905
|
+
return this.invalidContextSubmissions.record(kindSample);
|
|
7906
|
+
}
|
|
7839
7907
|
sanitizeEvaluatedConfigEvent(event) {
|
|
7840
7908
|
return {
|
|
7841
7909
|
...event,
|
|
@@ -7869,7 +7937,10 @@ var ServerTelemetryEventCollector = class extends TelemetryEventCollector {
|
|
|
7869
7937
|
return await this.reporter?.report({
|
|
7870
7938
|
context: this._context,
|
|
7871
7939
|
discreteEvents: { capturedContexts: contextsSnapshot.contexts },
|
|
7872
|
-
aggregatedEvents: {
|
|
7940
|
+
aggregatedEvents: {
|
|
7941
|
+
evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot),
|
|
7942
|
+
invalidContext: this.invalidContextSubmissions.takeSnapshot()
|
|
7943
|
+
},
|
|
7873
7944
|
droppedEvents: {
|
|
7874
7945
|
evaluatedConfig: evaluationSnapshot.droppedCount,
|
|
7875
7946
|
capturedContexts: contextsSnapshot.droppedCount
|
|
@@ -7903,6 +7974,12 @@ const generateValueId = async (v) => {
|
|
|
7903
7974
|
const defaultBaseUrl = new URL("https://server-sdk-api.configdirector.com");
|
|
7904
7975
|
const DEFAULT_FLUSH_INTERVAL = 3e4;
|
|
7905
7976
|
const DEFAULT_EVENT_QUEUE_LIMIT = 5e3;
|
|
7977
|
+
const logInvalidContext = {
|
|
7978
|
+
"new-kind-sample": (logger, kindSample) => logger.error(`[ConfigDirectorClient] Ignored a context with the invalid kind '${kindSample}' and evaluated as if no context was supplied. A context kind has 1 to 64 characters, using only lowercase letters, digits, '-' and '_'.`),
|
|
7979
|
+
"known-kind-sample": () => {},
|
|
7980
|
+
"first-beyond-sample-limit": (logger) => logger.error(`[ConfigDirectorClient] Ignored a context with another invalid kind and evaluated as if no context was supplied. 20 different invalid kinds were logged recently, so further invalid kinds are not logged individually.`),
|
|
7981
|
+
"beyond-sample-limit": () => {}
|
|
7982
|
+
};
|
|
7906
7983
|
const usesAnySegment = (definition, segmentIds) => (definition.target?.rules ?? []).some((rule) => rule.type === "conditional" && (rule.conditions ?? []).some((condition) => condition.kind === "segment" && segmentIds.has(condition.segmentId)));
|
|
7907
7984
|
var DefaultConfigDirectorClient = class {
|
|
7908
7985
|
logger;
|
|
@@ -8086,7 +8163,8 @@ var DefaultConfigDirectorClient = class {
|
|
|
8086
8163
|
const configDefinition = this.configSet?.configs[configKey];
|
|
8087
8164
|
return this.getValueFromConfigDefinition(configKey, configDefinition, defaultValue, context);
|
|
8088
8165
|
}
|
|
8089
|
-
getValueFromConfigDefinition(configKey, configDefinition, defaultValue,
|
|
8166
|
+
getValueFromConfigDefinition(configKey, configDefinition, defaultValue, suppliedContext) {
|
|
8167
|
+
const context = this.contextToEvaluate(suppliedContext);
|
|
8090
8168
|
if (configDefinition) {
|
|
8091
8169
|
const configState = this.configEvaluator.evaluate(configDefinition, {
|
|
8092
8170
|
context,
|
|
@@ -8140,6 +8218,15 @@ var DefaultConfigDirectorClient = class {
|
|
|
8140
8218
|
return defaultValue;
|
|
8141
8219
|
}
|
|
8142
8220
|
}
|
|
8221
|
+
contextToEvaluate(suppliedContext) {
|
|
8222
|
+
const admission = admitContext(suppliedContext);
|
|
8223
|
+
switch (admission.outcome) {
|
|
8224
|
+
case "admitted": return admission.context;
|
|
8225
|
+
case "ignored":
|
|
8226
|
+
logInvalidContext[this.usageEventCollector.recordInvalidContext(admission.kindSample)](this.logger, admission.kindSample);
|
|
8227
|
+
return;
|
|
8228
|
+
}
|
|
8229
|
+
}
|
|
8143
8230
|
dispatchEvaluationEvent(event) {
|
|
8144
8231
|
setTimeout(() => this.eventEmitter.emit("configEvaluated", { evaluation: event }), 0);
|
|
8145
8232
|
}
|
|
@@ -8177,11 +8264,12 @@ var DefaultConfigDirectorClient = class {
|
|
|
8177
8264
|
this.handlersMap.clear();
|
|
8178
8265
|
}
|
|
8179
8266
|
getAllConfigs(options) {
|
|
8267
|
+
const context = this.contextToEvaluate(options?.context);
|
|
8180
8268
|
if (!this.configSet) return {};
|
|
8181
8269
|
const entries = Object.entries(this.configSet.configs);
|
|
8182
8270
|
const filtered = options?.configKeys ? entries.filter(([key]) => options.configKeys?.includes(key)) : entries;
|
|
8183
8271
|
return Object.fromEntries(filtered.map(([key, config]) => [key, this.configEvaluator.evaluate(config, {
|
|
8184
|
-
context
|
|
8272
|
+
context,
|
|
8185
8273
|
metadata: this.metaContext
|
|
8186
8274
|
}, this.configSet?.segments)]));
|
|
8187
8275
|
}
|
|
@@ -8225,7 +8313,7 @@ var plugin_default = defineNitroPlugin((nitroApp) => {
|
|
|
8225
8313
|
const { serverSdkKey, baseUrl, connection } = runtimeConfig.configdirector;
|
|
8226
8314
|
const client = new DefaultConfigDirectorClient(serverSdkKey, {
|
|
8227
8315
|
sdkName: "nuxt-sdk",
|
|
8228
|
-
sdkVersion: "1.
|
|
8316
|
+
sdkVersion: "1.10.0"
|
|
8229
8317
|
}, {
|
|
8230
8318
|
logger,
|
|
8231
8319
|
connection: {
|