@configdirector/nuxt-sdk 1.5.0 → 1.6.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/nitro/plugin.mjs +151 -45
- package/dist/runtime/plugin.client.mjs +22 -8
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -6938,9 +6938,6 @@ const equals = (value, target) => value !== null && target !== null && semver.eq
|
|
|
6938
6938
|
const ordered = (value, target, compare) => value !== null && target !== null && compare(value, target);
|
|
6939
6939
|
//#endregion
|
|
6940
6940
|
//#region ../config-evaluator/src/render.ts
|
|
6941
|
-
/**
|
|
6942
|
-
* Rendering a resolved context value to text.
|
|
6943
|
-
*/
|
|
6944
6941
|
/** A value the context does not carry. Compared as "", so a negative operator can still match. */
|
|
6945
6942
|
const ABSENT = Symbol("absent");
|
|
6946
6943
|
/** An attribute this SDK version does not know about. Not compared at all. */
|
|
@@ -6962,6 +6959,12 @@ const renderScalar = (value) => {
|
|
|
6962
6959
|
};
|
|
6963
6960
|
/** The raw value, for comparisons that inspect its JSON type rather than its text. */
|
|
6964
6961
|
const unwrap = (value) => value === ABSENT ? void 0 : value;
|
|
6962
|
+
const resolvedTypeOf = (value) => {
|
|
6963
|
+
if (value === UNKNOWN_ATTRIBUTE) return "unknown-attribute";
|
|
6964
|
+
if (value === ABSENT) return "absent";
|
|
6965
|
+
if (isScalar(value)) return "scalar";
|
|
6966
|
+
return Array.isArray(value) ? "array" : "object";
|
|
6967
|
+
};
|
|
6965
6968
|
//#endregion
|
|
6966
6969
|
//#region ../config-evaluator/src/array-comparison.ts
|
|
6967
6970
|
/**
|
|
@@ -6992,8 +6995,28 @@ var ConditionEvaluator = class {
|
|
|
6992
6995
|
* a condition can still match or not match on its own terms.
|
|
6993
6996
|
*/
|
|
6994
6997
|
evaluate(condition, context) {
|
|
6998
|
+
return this.explain(condition, context).matched;
|
|
6999
|
+
}
|
|
7000
|
+
/**
|
|
7001
|
+
* Decide whether a condition holds for a context, and say what the condition was compared
|
|
7002
|
+
* against: the raw resolved value and its shape.
|
|
7003
|
+
*/
|
|
7004
|
+
explain(condition, context) {
|
|
6995
7005
|
const value = this.resolve(condition, context);
|
|
6996
|
-
|
|
7006
|
+
const resolvedType = resolvedTypeOf(value);
|
|
7007
|
+
const resolvedValue = value === ABSENT || value === UNKNOWN_ATTRIBUTE ? void 0 : value;
|
|
7008
|
+
if (value === UNKNOWN_ATTRIBUTE) return {
|
|
7009
|
+
matched: false,
|
|
7010
|
+
resolvedValue,
|
|
7011
|
+
resolvedType
|
|
7012
|
+
};
|
|
7013
|
+
return {
|
|
7014
|
+
matched: this.compare(condition, value),
|
|
7015
|
+
resolvedValue,
|
|
7016
|
+
resolvedType
|
|
7017
|
+
};
|
|
7018
|
+
}
|
|
7019
|
+
compare(condition, value) {
|
|
6997
7020
|
const targetValues = condition.targetValues ?? [];
|
|
6998
7021
|
switch (condition.targetType) {
|
|
6999
7022
|
case "text": return compareText(render(value), condition.operator, targetValues);
|
|
@@ -7047,61 +7070,130 @@ var ConfigEvaluator = class {
|
|
|
7047
7070
|
id: config.id,
|
|
7048
7071
|
key: config.key,
|
|
7049
7072
|
type: config.type,
|
|
7050
|
-
value: this.
|
|
7073
|
+
value: this.explain(config, context).value
|
|
7051
7074
|
};
|
|
7052
7075
|
}
|
|
7053
|
-
|
|
7076
|
+
/**
|
|
7077
|
+
* Evaluate a config for a context and record how the value was reached: every rule in the
|
|
7078
|
+
* order it was walked with its outcome, the conditions that were checked with what they
|
|
7079
|
+
* resolved to, and the share a rollout assigned the context to. `evaluate` is this walk with
|
|
7080
|
+
* only the value kept, so the two cannot disagree.
|
|
7081
|
+
*/
|
|
7082
|
+
explain(config, context) {
|
|
7054
7083
|
const rules = [...config.target?.rules ?? []].sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
|
|
7084
|
+
const explanations = [];
|
|
7085
|
+
let served;
|
|
7055
7086
|
for (const rule of rules) {
|
|
7056
|
-
|
|
7057
|
-
|
|
7087
|
+
if (served) {
|
|
7088
|
+
explanations.push({
|
|
7089
|
+
ruleId: rule.id,
|
|
7090
|
+
outcome: "not-evaluated",
|
|
7091
|
+
conditions: [],
|
|
7092
|
+
bucket: void 0
|
|
7093
|
+
});
|
|
7094
|
+
continue;
|
|
7095
|
+
}
|
|
7096
|
+
const result = this.explainRule(rule, config, context);
|
|
7097
|
+
explanations.push(result.explanation);
|
|
7098
|
+
if (result.value !== void 0) served = {
|
|
7099
|
+
ruleId: rule.id,
|
|
7100
|
+
value: result.value
|
|
7101
|
+
};
|
|
7058
7102
|
}
|
|
7059
|
-
return
|
|
7103
|
+
return {
|
|
7104
|
+
value: served?.value ?? config.target?.defaultValue,
|
|
7105
|
+
servedBy: served ? {
|
|
7106
|
+
kind: "rule",
|
|
7107
|
+
ruleId: served.ruleId
|
|
7108
|
+
} : { kind: "default" },
|
|
7109
|
+
rules: explanations
|
|
7110
|
+
};
|
|
7060
7111
|
}
|
|
7061
|
-
|
|
7112
|
+
explainRule(rule, config, context) {
|
|
7113
|
+
const explanation = {
|
|
7114
|
+
ruleId: rule.id,
|
|
7115
|
+
outcome: "not-matched",
|
|
7116
|
+
conditions: [],
|
|
7117
|
+
bucket: void 0
|
|
7118
|
+
};
|
|
7062
7119
|
try {
|
|
7063
|
-
|
|
7064
|
-
|
|
7120
|
+
let value;
|
|
7121
|
+
if (rule.type == "percentage") value = this.explainPercentage(rule.percentages ?? [], config, context, explanation);
|
|
7122
|
+
else if (rule.type == "conditional") value = this.explainConditionalRule(rule, config, context, explanation);
|
|
7123
|
+
if (value !== void 0) explanation.outcome = "matched";
|
|
7124
|
+
return {
|
|
7125
|
+
explanation,
|
|
7126
|
+
value
|
|
7127
|
+
};
|
|
7065
7128
|
} catch (error) {
|
|
7066
7129
|
this.logger.warn(`There was an error while evaluating a targeting rule '${rule?.id}' for '${config?.key}'. The rule will be disregarded.`, {
|
|
7067
7130
|
error,
|
|
7068
7131
|
configKey: config?.key,
|
|
7069
7132
|
ruleId: rule?.id
|
|
7070
7133
|
});
|
|
7134
|
+
return {
|
|
7135
|
+
explanation: {
|
|
7136
|
+
ruleId: rule.id,
|
|
7137
|
+
outcome: "errored",
|
|
7138
|
+
conditions: [],
|
|
7139
|
+
bucket: void 0
|
|
7140
|
+
},
|
|
7141
|
+
value: void 0
|
|
7142
|
+
};
|
|
7071
7143
|
}
|
|
7072
|
-
return { success: false };
|
|
7073
7144
|
}
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
evaluatePercentage(percentages, config, context) {
|
|
7145
|
+
explainPercentage(percentages, config, context, explanation) {
|
|
7146
|
+
const contextIdentifier = context?.context?.id;
|
|
7147
|
+
const identifier = contextIdentifier ?? crypto.randomUUID();
|
|
7078
7148
|
const assignedPercentage = assignPercentage({
|
|
7079
7149
|
configId: config.id,
|
|
7080
|
-
contextIdentifier:
|
|
7150
|
+
contextIdentifier: identifier
|
|
7081
7151
|
});
|
|
7152
|
+
const shares = [];
|
|
7082
7153
|
let sum = 0;
|
|
7083
|
-
let
|
|
7154
|
+
let selected = void 0;
|
|
7084
7155
|
for (const percentage of percentages) {
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7156
|
+
const to = sum + percentage.percentage;
|
|
7157
|
+
shares.push({
|
|
7158
|
+
percentageId: percentage.id,
|
|
7159
|
+
from: sum,
|
|
7160
|
+
to,
|
|
7161
|
+
value: percentage.value?.toString()
|
|
7162
|
+
});
|
|
7163
|
+
if (selected === void 0 && assignedPercentage < to) selected = percentage;
|
|
7164
|
+
sum = to;
|
|
7090
7165
|
}
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7166
|
+
explanation.bucket = {
|
|
7167
|
+
identifier,
|
|
7168
|
+
identifierWasGenerated: contextIdentifier == null,
|
|
7169
|
+
assignedPercentage,
|
|
7170
|
+
shares,
|
|
7171
|
+
selectedPercentageId: selected?.id
|
|
7094
7172
|
};
|
|
7095
|
-
return
|
|
7096
|
-
}
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7173
|
+
return selected?.value != null ? selected.value.toString() : void 0;
|
|
7174
|
+
}
|
|
7175
|
+
explainConditionalRule(rule, config, context, explanation) {
|
|
7176
|
+
let failed = false;
|
|
7177
|
+
for (const condition of rule.conditions ?? []) {
|
|
7178
|
+
if (failed) {
|
|
7179
|
+
explanation.conditions.push({
|
|
7180
|
+
conditionId: condition.id,
|
|
7181
|
+
outcome: "not-evaluated"
|
|
7182
|
+
});
|
|
7183
|
+
continue;
|
|
7184
|
+
}
|
|
7185
|
+
const check = this.conditionEvaluator.explain(condition, context);
|
|
7186
|
+
explanation.conditions.push({
|
|
7187
|
+
conditionId: condition.id,
|
|
7188
|
+
outcome: check.matched ? "matched" : "not-matched",
|
|
7189
|
+
resolvedValue: check.resolvedValue,
|
|
7190
|
+
resolvedType: check.resolvedType
|
|
7191
|
+
});
|
|
7192
|
+
failed = !check.matched;
|
|
7193
|
+
}
|
|
7194
|
+
if (failed) return;
|
|
7195
|
+
if (rule.target == "value") return rule.value != null ? rule.value.toString() : void 0;
|
|
7196
|
+
if (rule.target == "percentage") return this.explainPercentage(rule.percentages ?? [], config, context, explanation);
|
|
7105
7197
|
}
|
|
7106
7198
|
};
|
|
7107
7199
|
//#endregion
|
|
@@ -7124,6 +7216,11 @@ const getRequestedType = (defaultValue) => {
|
|
|
7124
7216
|
const isNumericNativeType = (requestedType) => {
|
|
7125
7217
|
return requestedType === "number" || requestedType === "bigint";
|
|
7126
7218
|
};
|
|
7219
|
+
const configTypesNotReadableAsString = new Set([
|
|
7220
|
+
"boolean",
|
|
7221
|
+
"integer",
|
|
7222
|
+
"float"
|
|
7223
|
+
]);
|
|
7127
7224
|
const parseConfigValue = (configState, defaultValue) => {
|
|
7128
7225
|
const value = configState.value;
|
|
7129
7226
|
const requestedType = getRequestedType(defaultValue);
|
|
@@ -7135,13 +7232,22 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
7135
7232
|
reason: "value-missing"
|
|
7136
7233
|
};
|
|
7137
7234
|
if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
|
|
7138
|
-
if (typeof defaultValue === "string")
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7235
|
+
if (typeof defaultValue === "string") {
|
|
7236
|
+
if (configTypesNotReadableAsString.has(configState.type)) return {
|
|
7237
|
+
parsedValue: defaultValue,
|
|
7238
|
+
parsedValueId: void 0,
|
|
7239
|
+
requestedType,
|
|
7240
|
+
usedDefault: true,
|
|
7241
|
+
reason: "type-mismatch"
|
|
7242
|
+
};
|
|
7243
|
+
return {
|
|
7244
|
+
parsedValue: value,
|
|
7245
|
+
parsedValueId: configState.valueId,
|
|
7246
|
+
requestedType,
|
|
7247
|
+
usedDefault: false,
|
|
7248
|
+
reason: "found-match"
|
|
7249
|
+
};
|
|
7250
|
+
}
|
|
7145
7251
|
if (typeof defaultValue === "boolean" && (configState.type === "boolean" || configState.type === "string" || configState.type === "enum")) {
|
|
7146
7252
|
const boolValue = parseConfigBoolean(value);
|
|
7147
7253
|
const hasBoolean = typeof boolValue === "boolean";
|
|
@@ -7970,7 +8076,7 @@ var plugin_default = defineNitroPlugin((nitroApp) => {
|
|
|
7970
8076
|
const { serverSdkKey, baseUrl, connection } = runtimeConfig.configdirector;
|
|
7971
8077
|
const client = new DefaultConfigDirectorClient(serverSdkKey, {
|
|
7972
8078
|
sdkName: "nuxt-sdk",
|
|
7973
|
-
sdkVersion: "1.
|
|
8079
|
+
sdkVersion: "1.6.0"
|
|
7974
8080
|
}, {
|
|
7975
8081
|
logger,
|
|
7976
8082
|
connection: {
|
|
@@ -504,6 +504,11 @@ const getRequestedType = (defaultValue) => {
|
|
|
504
504
|
const isNumericNativeType = (requestedType) => {
|
|
505
505
|
return requestedType === "number" || requestedType === "bigint";
|
|
506
506
|
};
|
|
507
|
+
const configTypesNotReadableAsString = new Set([
|
|
508
|
+
"boolean",
|
|
509
|
+
"integer",
|
|
510
|
+
"float"
|
|
511
|
+
]);
|
|
507
512
|
const parseConfigValue = (configState, defaultValue) => {
|
|
508
513
|
const value = configState.value;
|
|
509
514
|
const requestedType = getRequestedType(defaultValue);
|
|
@@ -515,13 +520,22 @@ const parseConfigValue = (configState, defaultValue) => {
|
|
|
515
520
|
reason: "value-missing"
|
|
516
521
|
};
|
|
517
522
|
if (configState.type === "json") return parseJson(value, defaultValue, requestedType, configState.valueId);
|
|
518
|
-
if (typeof defaultValue === "string")
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
523
|
+
if (typeof defaultValue === "string") {
|
|
524
|
+
if (configTypesNotReadableAsString.has(configState.type)) return {
|
|
525
|
+
parsedValue: defaultValue,
|
|
526
|
+
parsedValueId: void 0,
|
|
527
|
+
requestedType,
|
|
528
|
+
usedDefault: true,
|
|
529
|
+
reason: "type-mismatch"
|
|
530
|
+
};
|
|
531
|
+
return {
|
|
532
|
+
parsedValue: value,
|
|
533
|
+
parsedValueId: configState.valueId,
|
|
534
|
+
requestedType,
|
|
535
|
+
usedDefault: false,
|
|
536
|
+
reason: "found-match"
|
|
537
|
+
};
|
|
538
|
+
}
|
|
525
539
|
if (typeof defaultValue === "boolean" && (configState.type === "boolean" || configState.type === "string" || configState.type === "enum")) {
|
|
526
540
|
const boolValue = parseConfigBoolean(value);
|
|
527
541
|
const hasBoolean = typeof boolValue === "boolean";
|
|
@@ -1463,7 +1477,7 @@ var plugin_client_default = defineNuxtPlugin((nuxtApp) => {
|
|
|
1463
1477
|
const { clientSdkKey, appName, appVersion, baseUrl } = runtimeConfig.public.configdirector;
|
|
1464
1478
|
const client = createBrowserClient(clientSdkKey, {
|
|
1465
1479
|
sdkName: "nuxt-sdk",
|
|
1466
|
-
sdkVersion: "1.
|
|
1480
|
+
sdkVersion: "1.6.0"
|
|
1467
1481
|
}, {
|
|
1468
1482
|
metadata: {
|
|
1469
1483
|
appName,
|
package/package.json
CHANGED