@configdirector/nuxt-sdk 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/nitro/plugin.mjs +157 -46
- package/dist/runtime/plugin.client.mjs +28 -9
- 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
|
-
}
|
|
7074
|
-
evaluatePercentageRule(rule, config, context) {
|
|
7075
|
-
return this.evaluatePercentage(rule.percentages ?? [], config, context);
|
|
7076
7144
|
}
|
|
7077
|
-
|
|
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
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
evaluateConditionalRule(rule, config, context) {
|
|
7098
|
-
const conditionsMet = (rule.conditions ?? []).every((candidate) => this.conditionEvaluator.evaluate(candidate, context));
|
|
7099
|
-
if (conditionsMet && rule.target == "value" && rule.value != null) return {
|
|
7100
|
-
success: true,
|
|
7101
|
-
value: rule.value.toString()
|
|
7166
|
+
explanation.bucket = {
|
|
7167
|
+
identifier,
|
|
7168
|
+
identifierWasGenerated: contextIdentifier == null,
|
|
7169
|
+
assignedPercentage,
|
|
7170
|
+
shares,
|
|
7171
|
+
selectedPercentageId: selected?.id
|
|
7102
7172
|
};
|
|
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";
|
|
@@ -7363,6 +7469,7 @@ const isDroppedEventsEmpty = (droppedEvents) => {
|
|
|
7363
7469
|
var ServerEventReporter = class {
|
|
7364
7470
|
sdkKey;
|
|
7365
7471
|
sdkIdentity;
|
|
7472
|
+
metaContext;
|
|
7366
7473
|
logger;
|
|
7367
7474
|
url;
|
|
7368
7475
|
executeRequests = true;
|
|
@@ -7370,6 +7477,7 @@ var ServerEventReporter = class {
|
|
|
7370
7477
|
this.sdkKey = options.sdkKey;
|
|
7371
7478
|
this.sdkIdentity = options.sdkIdentity;
|
|
7372
7479
|
this.logger = options.logger;
|
|
7480
|
+
this.metaContext = options.metaContext;
|
|
7373
7481
|
this.url = new URL("server/telemetry/v1", options.baseUrl.toString());
|
|
7374
7482
|
}
|
|
7375
7483
|
async report({ discreteEvents, aggregatedEvents, droppedEvents }) {
|
|
@@ -7381,7 +7489,9 @@ var ServerEventReporter = class {
|
|
|
7381
7489
|
serverSdkKey: this.sdkKey,
|
|
7382
7490
|
metaContext: {
|
|
7383
7491
|
sdkName: this.sdkIdentity.sdkName,
|
|
7384
|
-
sdkVersion: this.sdkIdentity.sdkVersion
|
|
7492
|
+
sdkVersion: this.sdkIdentity.sdkVersion,
|
|
7493
|
+
appName: this.metaContext.appName,
|
|
7494
|
+
appVersion: this.metaContext.appVersion
|
|
7385
7495
|
},
|
|
7386
7496
|
discreteEvents,
|
|
7387
7497
|
aggregatedEvents,
|
|
@@ -7721,6 +7831,7 @@ var DefaultConfigDirectorClient = class {
|
|
|
7721
7831
|
this.usageEventCollector = new ServerTelemetryEventCollector({
|
|
7722
7832
|
sdkKey: serverSdkKey,
|
|
7723
7833
|
sdkIdentity: sdkOptions,
|
|
7834
|
+
metaContext: clientOptions?.metadata ?? {},
|
|
7724
7835
|
logger: this.logger,
|
|
7725
7836
|
baseUrl,
|
|
7726
7837
|
flushIntervalDelay: clientOptions?.telemetry?.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
|
|
@@ -7970,7 +8081,7 @@ var plugin_default = defineNitroPlugin((nitroApp) => {
|
|
|
7970
8081
|
const { serverSdkKey, baseUrl, connection } = runtimeConfig.configdirector;
|
|
7971
8082
|
const client = new DefaultConfigDirectorClient(serverSdkKey, {
|
|
7972
8083
|
sdkName: "nuxt-sdk",
|
|
7973
|
-
sdkVersion: "1.
|
|
8084
|
+
sdkVersion: "1.6.1"
|
|
7974
8085
|
}, {
|
|
7975
8086
|
logger,
|
|
7976
8087
|
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";
|
|
@@ -1266,13 +1280,14 @@ var WebWorkerTelemetryClient = class {
|
|
|
1266
1280
|
payload: {
|
|
1267
1281
|
sdkKey: options.sdkKey,
|
|
1268
1282
|
sdkIdentity: options.sdkIdentity,
|
|
1283
|
+
metaContext: options.metaContext,
|
|
1269
1284
|
baseUrl: options.baseUrl.toString(),
|
|
1270
1285
|
evaluationQueueLimit: options.evaluationQueueLimit ?? 1e3,
|
|
1271
1286
|
initialFlushIntervalDelay: options.initialFlushIntervalDelay ?? 5e3,
|
|
1272
1287
|
flushIntervalDelay: options.flushIntervalDelay ?? 3e4
|
|
1273
1288
|
}
|
|
1274
1289
|
};
|
|
1275
|
-
this.worker = new Worker(URL.createObjectURL(new Blob(["(function(){let e=async(e,t)=>e.valueId?{valueId:e.valueId}:e.value?e.value.length<=500&&e.type!=`json`?{value:e.value}:{valueId:await t(e.value)}:e,t=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},n=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},r=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},i=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var a=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.sdkIdentity=e.sdkIdentity,this.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},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.logger=e.logger,this.evaluationEventQueue=new s(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}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});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" })));
|
|
1290
|
+
this.worker = new Worker(URL.createObjectURL(new Blob(["(function(){let e=async(e,t)=>e.valueId?{valueId:e.valueId}:e.value?e.value.length<=500&&e.type!=`json`?{value:e.value}:{valueId:await t(e.value)}:e,t=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},n=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},r=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},i=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var a=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.sdkIdentity=e.sdkIdentity,this.metaContext=e.metaContext,this.url=e.urlFactory(`client/telemetry/v1`,e.baseUrl)}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){if(!this.executeRequests)return{success:!1,fatalError:!0};let i={clientSdkKey:this.sdkKey,metaContext:{sdkName:this.sdkIdentity.sdkName,sdkVersion:this.sdkIdentity.sdkVersion,appName:this.metaContext.appName,appVersion:this.metaContext.appVersion},context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(this.isReportEmpty(i))return{success:!0,fatalError:!1};let a=await this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return t(e.discreteEvents)&&t(e.aggregatedEvents)&&n(e.droppedEvents)}async sendReport(e){try{let t=await r(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=i(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500&&e!=429}},o=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},s=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||=new Date;let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},c=class{constructor(e){this.aggregator=new o,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new s(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}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});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" })));
|
|
1276
1291
|
this.worker.onmessage = (message) => {
|
|
1277
1292
|
this.handleWorkerEvent(message.data);
|
|
1278
1293
|
};
|
|
@@ -1401,6 +1416,10 @@ const createBrowserClient = (clientSdkKey, sdkOptions, clientOptions) => {
|
|
|
1401
1416
|
sdkName: sdkOptions.sdkName,
|
|
1402
1417
|
sdkVersion: sdkOptions.sdkVersion
|
|
1403
1418
|
},
|
|
1419
|
+
metaContext: {
|
|
1420
|
+
appName: clientOptions?.metadata?.appName,
|
|
1421
|
+
appVersion: clientOptions?.metadata?.appVersion
|
|
1422
|
+
},
|
|
1404
1423
|
logger,
|
|
1405
1424
|
baseUrl,
|
|
1406
1425
|
urlFactory: defaultUrlFactory,
|
|
@@ -1463,7 +1482,7 @@ var plugin_client_default = defineNuxtPlugin((nuxtApp) => {
|
|
|
1463
1482
|
const { clientSdkKey, appName, appVersion, baseUrl } = runtimeConfig.public.configdirector;
|
|
1464
1483
|
const client = createBrowserClient(clientSdkKey, {
|
|
1465
1484
|
sdkName: "nuxt-sdk",
|
|
1466
|
-
sdkVersion: "1.
|
|
1485
|
+
sdkVersion: "1.6.1"
|
|
1467
1486
|
}, {
|
|
1468
1487
|
metadata: {
|
|
1469
1488
|
appName,
|
package/package.json
CHANGED