@showwhat/core 2.0.0 → 2.1.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/README.md +6 -2
- package/dist/index.d.ts +83 -34
- package/dist/index.js +379 -290
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -74,9 +74,32 @@ var ConditionError = class extends ShowwhatError {
|
|
|
74
74
|
this.name = "ConditionError";
|
|
75
75
|
}
|
|
76
76
|
};
|
|
77
|
+
var UnknownConditionTypeError = class extends ShowwhatError {
|
|
78
|
+
constructor(conditionType, condition) {
|
|
79
|
+
super(`Unknown condition type "${conditionType}".`);
|
|
80
|
+
this.conditionType = conditionType;
|
|
81
|
+
this.condition = condition;
|
|
82
|
+
this.name = "UnknownConditionTypeError";
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/conditions/types.ts
|
|
87
|
+
import { z as z2 } from "zod";
|
|
88
|
+
|
|
89
|
+
// src/schemas/value.ts
|
|
90
|
+
import { z } from "zod";
|
|
91
|
+
var DataPrimitiveSchema = z.union([z.string(), z.number(), z.boolean()]);
|
|
92
|
+
var DataValueSchema = z.union([
|
|
93
|
+
DataPrimitiveSchema,
|
|
94
|
+
z.array(DataPrimitiveSchema),
|
|
95
|
+
z.lazy(() => z.record(z.string(), DataValueSchema))
|
|
96
|
+
]);
|
|
77
97
|
|
|
78
98
|
// src/conditions/types.ts
|
|
99
|
+
var AnnotationValueSchema = DataValueSchema;
|
|
100
|
+
var AnnotationsSchema = z2.record(z2.string(), AnnotationValueSchema);
|
|
79
101
|
var defaultCreateRegex = (pattern) => new RegExp(pattern);
|
|
102
|
+
var FALLBACK_EVALUATOR_KEY = /* @__PURE__ */ Symbol("fallback");
|
|
80
103
|
var noConditionEvaluator = async () => false;
|
|
81
104
|
|
|
82
105
|
// src/conditions/string.ts
|
|
@@ -93,7 +116,8 @@ async function evaluateString(condition, ctx, createRegex = defaultCreateRegex)
|
|
|
93
116
|
case "in":
|
|
94
117
|
return condition.value.includes(actual);
|
|
95
118
|
case "nin":
|
|
96
|
-
|
|
119
|
+
const incl = condition.value.includes(actual);
|
|
120
|
+
return !incl;
|
|
97
121
|
case "regex": {
|
|
98
122
|
const pattern = condition.value;
|
|
99
123
|
let regex;
|
|
@@ -141,8 +165,8 @@ async function evaluateNumber(condition, ctx) {
|
|
|
141
165
|
var numberEvaluator = ({ condition, context }) => evaluateNumber(condition, context);
|
|
142
166
|
|
|
143
167
|
// src/conditions/utils.ts
|
|
144
|
-
import { z } from "zod";
|
|
145
|
-
var IsoUtcDatetime =
|
|
168
|
+
import { z as z3 } from "zod";
|
|
169
|
+
var IsoUtcDatetime = z3.iso.datetime();
|
|
146
170
|
function parseDate(key, raw) {
|
|
147
171
|
if (!IsoUtcDatetime.safeParse(raw).success) {
|
|
148
172
|
throw new InvalidContextError(key, raw);
|
|
@@ -152,9 +176,13 @@ function parseDate(key, raw) {
|
|
|
152
176
|
|
|
153
177
|
// src/conditions/datetime.ts
|
|
154
178
|
async function evaluateDatetime(condition, ctx) {
|
|
155
|
-
if (!Object.hasOwn(ctx, condition.key))
|
|
179
|
+
if (!Object.hasOwn(ctx, condition.key)) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
156
182
|
const raw = ctx[condition.key];
|
|
157
|
-
if (typeof raw !== "string")
|
|
183
|
+
if (typeof raw !== "string") {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
158
186
|
const actual = parseDate(condition.key, raw);
|
|
159
187
|
const expected = new Date(condition.value);
|
|
160
188
|
switch (condition.op) {
|
|
@@ -174,14 +202,21 @@ var datetimeEvaluator = ({ condition, context }) => evaluateDatetime(condition,
|
|
|
174
202
|
|
|
175
203
|
// src/conditions/bool.ts
|
|
176
204
|
async function evaluateBool(condition, ctx) {
|
|
177
|
-
if (!Object.hasOwn(ctx, condition.key))
|
|
205
|
+
if (!Object.hasOwn(ctx, condition.key)) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
178
208
|
const raw = ctx[condition.key];
|
|
179
209
|
if (typeof raw === "boolean") {
|
|
180
210
|
return raw === condition.value;
|
|
181
211
|
}
|
|
182
|
-
if (typeof raw
|
|
183
|
-
|
|
184
|
-
|
|
212
|
+
if (typeof raw !== "string") {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
if (raw === "true") {
|
|
216
|
+
return condition.value === true;
|
|
217
|
+
}
|
|
218
|
+
if (raw === "false") {
|
|
219
|
+
return condition.value === false;
|
|
185
220
|
}
|
|
186
221
|
return false;
|
|
187
222
|
}
|
|
@@ -224,139 +259,6 @@ async function evaluateEndAt(condition, ctx) {
|
|
|
224
259
|
}
|
|
225
260
|
var endAtEvaluator = ({ condition, context }) => evaluateEndAt(condition, context);
|
|
226
261
|
|
|
227
|
-
// src/schemas/condition.ts
|
|
228
|
-
import { z as z2 } from "zod";
|
|
229
|
-
var PRIMITIVE_CONDITION_TYPES = {
|
|
230
|
-
string: "string",
|
|
231
|
-
number: "number",
|
|
232
|
-
bool: "bool",
|
|
233
|
-
datetime: "datetime"
|
|
234
|
-
};
|
|
235
|
-
var CONDITION_TYPES = {
|
|
236
|
-
...PRIMITIVE_CONDITION_TYPES,
|
|
237
|
-
env: "env",
|
|
238
|
-
startAt: "startAt",
|
|
239
|
-
endAt: "endAt",
|
|
240
|
-
and: "and",
|
|
241
|
-
or: "or"
|
|
242
|
-
};
|
|
243
|
-
var CONTEXT_KEYS = {
|
|
244
|
-
env: "env",
|
|
245
|
-
at: "at"
|
|
246
|
-
};
|
|
247
|
-
var StringConditionSchema = z2.object({
|
|
248
|
-
id: z2.string().optional(),
|
|
249
|
-
type: z2.literal("string"),
|
|
250
|
-
key: z2.string().min(1),
|
|
251
|
-
op: z2.enum(["eq", "neq", "in", "nin", "regex"]),
|
|
252
|
-
value: z2.union([z2.string(), z2.array(z2.string())])
|
|
253
|
-
}).superRefine((val, ctx) => {
|
|
254
|
-
const isArrayOp = val.op === "in" || val.op === "nin";
|
|
255
|
-
const isArray = Array.isArray(val.value);
|
|
256
|
-
if (isArrayOp && !isArray) {
|
|
257
|
-
ctx.addIssue({
|
|
258
|
-
code: "custom",
|
|
259
|
-
message: `"${val.op}" operator requires an array value`,
|
|
260
|
-
path: ["value"]
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
if (!isArrayOp && isArray) {
|
|
264
|
-
ctx.addIssue({
|
|
265
|
-
code: "custom",
|
|
266
|
-
message: `"${val.op}" operator requires a string value`,
|
|
267
|
-
path: ["value"]
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
var NumberConditionSchema = z2.object({
|
|
272
|
-
id: z2.string().optional(),
|
|
273
|
-
type: z2.literal("number"),
|
|
274
|
-
key: z2.string().min(1),
|
|
275
|
-
op: z2.enum(["eq", "neq", "gt", "gte", "lt", "lte", "in", "nin"]),
|
|
276
|
-
value: z2.union([z2.number(), z2.array(z2.number())])
|
|
277
|
-
}).superRefine((val, ctx) => {
|
|
278
|
-
const isArrayOp = val.op === "in" || val.op === "nin";
|
|
279
|
-
const isArray = Array.isArray(val.value);
|
|
280
|
-
if (isArrayOp && !isArray) {
|
|
281
|
-
ctx.addIssue({
|
|
282
|
-
code: "custom",
|
|
283
|
-
message: `"${val.op}" operator requires an array value`,
|
|
284
|
-
path: ["value"]
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
if (!isArrayOp && isArray) {
|
|
288
|
-
ctx.addIssue({
|
|
289
|
-
code: "custom",
|
|
290
|
-
message: `"${val.op}" operator requires a number value`,
|
|
291
|
-
path: ["value"]
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
var DatetimeConditionSchema = z2.object({
|
|
296
|
-
id: z2.string().optional(),
|
|
297
|
-
type: z2.literal("datetime"),
|
|
298
|
-
key: z2.string().min(1),
|
|
299
|
-
op: z2.enum(["eq", "gt", "gte", "lt", "lte"]),
|
|
300
|
-
value: z2.iso.datetime({ message: '"datetime" must be a valid ISO 8601 datetime' })
|
|
301
|
-
});
|
|
302
|
-
var BoolConditionSchema = z2.object({
|
|
303
|
-
id: z2.string().optional(),
|
|
304
|
-
type: z2.literal("bool"),
|
|
305
|
-
key: z2.string().min(1),
|
|
306
|
-
op: z2.literal("eq").optional(),
|
|
307
|
-
value: z2.boolean()
|
|
308
|
-
});
|
|
309
|
-
var EnvConditionSchema = z2.object({
|
|
310
|
-
id: z2.string().optional(),
|
|
311
|
-
type: z2.literal("env"),
|
|
312
|
-
op: z2.literal("eq").optional(),
|
|
313
|
-
value: z2.union([z2.string(), z2.array(z2.string())])
|
|
314
|
-
});
|
|
315
|
-
var StartAtConditionSchema = z2.object({
|
|
316
|
-
id: z2.string().optional(),
|
|
317
|
-
type: z2.literal("startAt"),
|
|
318
|
-
value: z2.iso.datetime({ message: '"startAt" must be a valid ISO 8601 datetime' })
|
|
319
|
-
});
|
|
320
|
-
var EndAtConditionSchema = z2.object({
|
|
321
|
-
id: z2.string().optional(),
|
|
322
|
-
type: z2.literal("endAt"),
|
|
323
|
-
value: z2.iso.datetime({ message: '"endAt" must be a valid ISO 8601 datetime' })
|
|
324
|
-
});
|
|
325
|
-
var BuiltinConditionSchema = z2.discriminatedUnion("type", [
|
|
326
|
-
StringConditionSchema,
|
|
327
|
-
NumberConditionSchema,
|
|
328
|
-
DatetimeConditionSchema,
|
|
329
|
-
BoolConditionSchema,
|
|
330
|
-
EnvConditionSchema,
|
|
331
|
-
StartAtConditionSchema,
|
|
332
|
-
EndAtConditionSchema
|
|
333
|
-
]);
|
|
334
|
-
var AndConditionSchema = z2.object({
|
|
335
|
-
id: z2.string().optional(),
|
|
336
|
-
type: z2.literal("and"),
|
|
337
|
-
conditions: z2.array(z2.lazy(() => ConditionSchema)).min(1)
|
|
338
|
-
});
|
|
339
|
-
var OrConditionSchema = z2.object({
|
|
340
|
-
id: z2.string().optional(),
|
|
341
|
-
type: z2.literal("or"),
|
|
342
|
-
conditions: z2.array(z2.lazy(() => ConditionSchema)).min(1)
|
|
343
|
-
});
|
|
344
|
-
var BLOCKED_OPEN_UNION_TYPES = new Set(Object.values(CONDITION_TYPES));
|
|
345
|
-
var ConditionSchema = z2.union([
|
|
346
|
-
BuiltinConditionSchema,
|
|
347
|
-
AndConditionSchema,
|
|
348
|
-
OrConditionSchema,
|
|
349
|
-
z2.looseObject({ type: z2.string() }).refine((val) => !BLOCKED_OPEN_UNION_TYPES.has(val.type), {
|
|
350
|
-
message: "Reserved condition type"
|
|
351
|
-
})
|
|
352
|
-
]);
|
|
353
|
-
function isAndCondition(c) {
|
|
354
|
-
return c.type === CONDITION_TYPES.and;
|
|
355
|
-
}
|
|
356
|
-
function isOrCondition(c) {
|
|
357
|
-
return c.type === CONDITION_TYPES.or;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
262
|
// src/logger.ts
|
|
361
263
|
var noopLogger = {
|
|
362
264
|
debug() {
|
|
@@ -369,7 +271,7 @@ var noopLogger = {
|
|
|
369
271
|
}
|
|
370
272
|
};
|
|
371
273
|
|
|
372
|
-
// src/conditions/
|
|
274
|
+
// src/conditions/evaluate.ts
|
|
373
275
|
async function evaluateCondition({
|
|
374
276
|
condition,
|
|
375
277
|
context,
|
|
@@ -378,71 +280,22 @@ async function evaluateCondition({
|
|
|
378
280
|
deps = {},
|
|
379
281
|
depth = "",
|
|
380
282
|
logger = noopLogger,
|
|
381
|
-
fallback,
|
|
382
283
|
createRegex = defaultCreateRegex
|
|
383
284
|
}) {
|
|
384
|
-
|
|
385
|
-
for (let i = 0; i < condition.conditions.length; i++) {
|
|
386
|
-
const childDepth = depth === "" ? `${i}` : `${depth}.${i}`;
|
|
387
|
-
const result2 = await evaluateCondition({
|
|
388
|
-
condition: condition.conditions[i],
|
|
389
|
-
context,
|
|
390
|
-
evaluators,
|
|
391
|
-
annotations,
|
|
392
|
-
deps,
|
|
393
|
-
depth: childDepth,
|
|
394
|
-
logger,
|
|
395
|
-
fallback,
|
|
396
|
-
createRegex
|
|
397
|
-
});
|
|
398
|
-
if (!result2) {
|
|
399
|
-
logger.debug("and condition short-circuited (child returned false)", {
|
|
400
|
-
childType: condition.conditions[i].type,
|
|
401
|
-
depth: childDepth
|
|
402
|
-
});
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return true;
|
|
407
|
-
}
|
|
408
|
-
if (isOrCondition(condition)) {
|
|
409
|
-
for (let i = 0; i < condition.conditions.length; i++) {
|
|
410
|
-
const childDepth = depth === "" ? `${i}` : `${depth}.${i}`;
|
|
411
|
-
const result2 = await evaluateCondition({
|
|
412
|
-
condition: condition.conditions[i],
|
|
413
|
-
context,
|
|
414
|
-
evaluators,
|
|
415
|
-
annotations,
|
|
416
|
-
deps,
|
|
417
|
-
depth: childDepth,
|
|
418
|
-
logger,
|
|
419
|
-
fallback,
|
|
420
|
-
createRegex
|
|
421
|
-
});
|
|
422
|
-
if (result2) {
|
|
423
|
-
logger.debug("or condition short-circuited (child returned true)", {
|
|
424
|
-
childType: condition.conditions[i].type,
|
|
425
|
-
depth: childDepth
|
|
426
|
-
});
|
|
427
|
-
return true;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return false;
|
|
431
|
-
}
|
|
432
|
-
const evaluator = evaluators[condition.type];
|
|
285
|
+
const evaluator = evaluators[condition.type] ?? evaluators[FALLBACK_EVALUATOR_KEY];
|
|
433
286
|
if (!evaluator) {
|
|
434
|
-
|
|
435
|
-
const result2 = await fallback({ condition, context, annotations, deps, depth, createRegex });
|
|
436
|
-
logger.debug("condition evaluated (fallback)", {
|
|
437
|
-
type: condition.type,
|
|
438
|
-
depth,
|
|
439
|
-
result: result2
|
|
440
|
-
});
|
|
441
|
-
return result2;
|
|
442
|
-
}
|
|
443
|
-
throw new ShowwhatError(`Unknown condition type "${condition.type}".`);
|
|
287
|
+
throw new UnknownConditionTypeError(condition.type, condition);
|
|
444
288
|
}
|
|
445
|
-
const result = await evaluator({
|
|
289
|
+
const result = await evaluator({
|
|
290
|
+
condition,
|
|
291
|
+
context,
|
|
292
|
+
annotations,
|
|
293
|
+
deps,
|
|
294
|
+
depth,
|
|
295
|
+
createRegex,
|
|
296
|
+
logger,
|
|
297
|
+
evaluators
|
|
298
|
+
});
|
|
446
299
|
logger.debug("condition evaluated", {
|
|
447
300
|
type: condition.type,
|
|
448
301
|
depth,
|
|
@@ -451,6 +304,74 @@ async function evaluateCondition({
|
|
|
451
304
|
return result;
|
|
452
305
|
}
|
|
453
306
|
|
|
307
|
+
// src/conditions/and.ts
|
|
308
|
+
var andEvaluator = async (args) => {
|
|
309
|
+
const { conditions } = args.condition;
|
|
310
|
+
const { depth } = args;
|
|
311
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
312
|
+
const childDepth = depth === "" ? `${i}` : `${depth}.${i}`;
|
|
313
|
+
const result = await evaluateCondition({
|
|
314
|
+
...args,
|
|
315
|
+
condition: conditions[i],
|
|
316
|
+
depth: childDepth
|
|
317
|
+
});
|
|
318
|
+
if (!result) {
|
|
319
|
+
args.logger?.debug("and condition short-circuited (child returned false)", {
|
|
320
|
+
childType: conditions[i].type,
|
|
321
|
+
depth: childDepth
|
|
322
|
+
});
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return true;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// src/conditions/or.ts
|
|
330
|
+
var orEvaluator = async (args) => {
|
|
331
|
+
const { conditions } = args.condition;
|
|
332
|
+
const { depth } = args;
|
|
333
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
334
|
+
const childDepth = depth === "" ? `${i}` : `${depth}.${i}`;
|
|
335
|
+
const result = await evaluateCondition({
|
|
336
|
+
...args,
|
|
337
|
+
condition: conditions[i],
|
|
338
|
+
depth: childDepth
|
|
339
|
+
});
|
|
340
|
+
if (result) {
|
|
341
|
+
args.logger?.debug("or condition short-circuited (child returned true)", {
|
|
342
|
+
childType: conditions[i].type,
|
|
343
|
+
depth: childDepth
|
|
344
|
+
});
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// src/conditions/check-annotations.ts
|
|
352
|
+
var checkAnnotationsEvaluator = async (args) => {
|
|
353
|
+
const { conditions } = args.condition;
|
|
354
|
+
const { depth, annotations: annotationsAsContext } = args;
|
|
355
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
356
|
+
const childDepth = depth === "" ? `${i}` : `${depth}.${i}`;
|
|
357
|
+
const result = await evaluateCondition({
|
|
358
|
+
...args,
|
|
359
|
+
condition: conditions[i],
|
|
360
|
+
context: annotationsAsContext,
|
|
361
|
+
annotations: {},
|
|
362
|
+
depth: childDepth
|
|
363
|
+
});
|
|
364
|
+
if (!result) {
|
|
365
|
+
args.logger?.debug("checkAnnotations condition short-circuited (child returned false)", {
|
|
366
|
+
childType: conditions[i].type,
|
|
367
|
+
depth: childDepth
|
|
368
|
+
});
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return true;
|
|
373
|
+
};
|
|
374
|
+
|
|
454
375
|
// src/conditions/index.ts
|
|
455
376
|
var builtinEvaluators = {
|
|
456
377
|
string: stringEvaluator,
|
|
@@ -459,7 +380,10 @@ var builtinEvaluators = {
|
|
|
459
380
|
bool: boolEvaluator,
|
|
460
381
|
env: envEvaluator,
|
|
461
382
|
startAt: startAtEvaluator,
|
|
462
|
-
endAt: endAtEvaluator
|
|
383
|
+
endAt: endAtEvaluator,
|
|
384
|
+
and: andEvaluator,
|
|
385
|
+
or: orEvaluator,
|
|
386
|
+
checkAnnotations: checkAnnotationsEvaluator
|
|
463
387
|
};
|
|
464
388
|
|
|
465
389
|
// src/resolver.ts
|
|
@@ -467,6 +391,9 @@ function getEvaluators(options) {
|
|
|
467
391
|
if (!options?.evaluators) {
|
|
468
392
|
throw new ShowwhatError("No evaluators registered. Pass evaluators via options.");
|
|
469
393
|
}
|
|
394
|
+
if (options.fallback) {
|
|
395
|
+
return { ...options.evaluators, [FALLBACK_EVALUATOR_KEY]: options.fallback };
|
|
396
|
+
}
|
|
470
397
|
return options.evaluators;
|
|
471
398
|
}
|
|
472
399
|
function getLogger(options) {
|
|
@@ -476,61 +403,44 @@ async function resolveVariation({
|
|
|
476
403
|
variations,
|
|
477
404
|
context,
|
|
478
405
|
deps,
|
|
479
|
-
options
|
|
406
|
+
options,
|
|
407
|
+
definitionKey
|
|
480
408
|
}) {
|
|
481
409
|
const evaluators = getEvaluators(options);
|
|
482
410
|
const logger = getLogger(options);
|
|
483
411
|
for (let i = 0; i < variations.length; i++) {
|
|
484
412
|
const variation = variations[i];
|
|
485
|
-
const
|
|
486
|
-
|
|
413
|
+
const conditions = Array.isArray(variation.conditions) ? variation.conditions : [];
|
|
414
|
+
const annotations = options?.createAnnotations?.(definitionKey) ?? {};
|
|
415
|
+
if (conditions.length === 0) {
|
|
487
416
|
logger.debug("variation matched (no conditions)", { variationIndex: i });
|
|
488
|
-
return { variation, variationIndex: i, annotations
|
|
417
|
+
return { variation, variationIndex: i, annotations };
|
|
489
418
|
}
|
|
490
|
-
const annotations = {};
|
|
491
419
|
const rulesMatch = await evaluateCondition({
|
|
492
|
-
condition: { type: "and", conditions
|
|
420
|
+
condition: { type: "and", conditions },
|
|
493
421
|
context,
|
|
494
422
|
evaluators,
|
|
495
423
|
annotations,
|
|
496
424
|
deps: deps ?? {},
|
|
497
425
|
logger,
|
|
498
|
-
|
|
499
|
-
createRegex: options?.createRegex
|
|
426
|
+
createRegex: options?.createRegex ?? defaultCreateRegex
|
|
500
427
|
});
|
|
501
428
|
if (!rulesMatch) {
|
|
502
429
|
logger.debug("variation did not match", {
|
|
503
430
|
variationIndex: i,
|
|
504
|
-
conditionCount:
|
|
431
|
+
conditionCount: conditions.length
|
|
505
432
|
});
|
|
506
433
|
continue;
|
|
507
434
|
}
|
|
508
435
|
logger.debug("variation matched", {
|
|
509
436
|
variationIndex: i,
|
|
510
|
-
conditionCount:
|
|
437
|
+
conditionCount: conditions.length
|
|
511
438
|
});
|
|
512
439
|
return { variation, variationIndex: i, annotations };
|
|
513
440
|
}
|
|
514
441
|
logger.debug("no variation matched", { variationCount: variations.length });
|
|
515
442
|
return null;
|
|
516
443
|
}
|
|
517
|
-
function toResolution(key, result) {
|
|
518
|
-
const conditionCount = Array.isArray(result.variation.conditions) ? result.variation.conditions.length : 0;
|
|
519
|
-
return {
|
|
520
|
-
success: true,
|
|
521
|
-
key,
|
|
522
|
-
value: result.variation.value,
|
|
523
|
-
meta: {
|
|
524
|
-
variation: {
|
|
525
|
-
index: result.variationIndex,
|
|
526
|
-
id: result.variation.id,
|
|
527
|
-
description: result.variation.description,
|
|
528
|
-
conditionCount
|
|
529
|
-
},
|
|
530
|
-
annotations: result.annotations
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
444
|
async function resolveKey(key, definitions, context, deps, options) {
|
|
535
445
|
const logger = getLogger(options);
|
|
536
446
|
const definition = definitions[key];
|
|
@@ -550,7 +460,8 @@ async function resolveKey(key, definitions, context, deps, options) {
|
|
|
550
460
|
variations: definition.variations,
|
|
551
461
|
context,
|
|
552
462
|
deps,
|
|
553
|
-
options
|
|
463
|
+
options,
|
|
464
|
+
definitionKey: key
|
|
554
465
|
});
|
|
555
466
|
if (!result) {
|
|
556
467
|
logger.warn("no matching variation", { key });
|
|
@@ -561,7 +472,21 @@ async function resolveKey(key, definitions, context, deps, options) {
|
|
|
561
472
|
variationIndex: result.variationIndex,
|
|
562
473
|
value: result.variation.value
|
|
563
474
|
});
|
|
564
|
-
|
|
475
|
+
const conditionCount = Array.isArray(result.variation.conditions) ? result.variation.conditions.length : 0;
|
|
476
|
+
return {
|
|
477
|
+
success: true,
|
|
478
|
+
key,
|
|
479
|
+
value: result.variation.value,
|
|
480
|
+
meta: {
|
|
481
|
+
variation: {
|
|
482
|
+
index: result.variationIndex,
|
|
483
|
+
id: result.variation.id,
|
|
484
|
+
description: result.variation.description,
|
|
485
|
+
conditionCount
|
|
486
|
+
},
|
|
487
|
+
annotations: result.annotations
|
|
488
|
+
}
|
|
489
|
+
};
|
|
565
490
|
}
|
|
566
491
|
async function resolve({
|
|
567
492
|
definitions,
|
|
@@ -587,36 +512,175 @@ async function resolve({
|
|
|
587
512
|
// src/parsers.ts
|
|
588
513
|
import yaml from "js-yaml";
|
|
589
514
|
|
|
590
|
-
// src/schemas/
|
|
591
|
-
import { z as
|
|
592
|
-
var
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
515
|
+
// src/schemas/condition.ts
|
|
516
|
+
import { z as z4 } from "zod";
|
|
517
|
+
var PRIMITIVE_CONDITION_TYPES = {
|
|
518
|
+
string: "string",
|
|
519
|
+
number: "number",
|
|
520
|
+
bool: "bool",
|
|
521
|
+
datetime: "datetime"
|
|
522
|
+
};
|
|
523
|
+
var CONDITION_TYPES = {
|
|
524
|
+
...PRIMITIVE_CONDITION_TYPES,
|
|
525
|
+
env: "env",
|
|
526
|
+
startAt: "startAt",
|
|
527
|
+
endAt: "endAt",
|
|
528
|
+
and: "and",
|
|
529
|
+
or: "or",
|
|
530
|
+
checkAnnotations: "checkAnnotations"
|
|
531
|
+
};
|
|
532
|
+
var CONTEXT_KEYS = {
|
|
533
|
+
env: "env",
|
|
534
|
+
at: "at"
|
|
535
|
+
};
|
|
536
|
+
var StringConditionSchema = z4.object({
|
|
537
|
+
id: z4.string().optional(),
|
|
538
|
+
type: z4.literal("string"),
|
|
539
|
+
key: z4.string().min(1),
|
|
540
|
+
op: z4.enum(["eq", "neq", "in", "nin", "regex"]),
|
|
541
|
+
value: z4.union([z4.string(), z4.array(z4.string())])
|
|
542
|
+
}).superRefine((val, ctx) => {
|
|
543
|
+
const isArrayOp = val.op === "in" || val.op === "nin";
|
|
544
|
+
const isArray = Array.isArray(val.value);
|
|
545
|
+
if (isArrayOp && !isArray) {
|
|
546
|
+
ctx.addIssue({
|
|
547
|
+
code: "custom",
|
|
548
|
+
message: `"${val.op}" operator requires an array value`,
|
|
549
|
+
path: ["value"]
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
if (!isArrayOp && isArray) {
|
|
553
|
+
ctx.addIssue({
|
|
554
|
+
code: "custom",
|
|
555
|
+
message: `"${val.op}" operator requires a string value`,
|
|
556
|
+
path: ["value"]
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
var NumberConditionSchema = z4.object({
|
|
561
|
+
id: z4.string().optional(),
|
|
562
|
+
type: z4.literal("number"),
|
|
563
|
+
key: z4.string().min(1),
|
|
564
|
+
op: z4.enum(["eq", "neq", "gt", "gte", "lt", "lte", "in", "nin"]),
|
|
565
|
+
value: z4.union([z4.number(), z4.array(z4.number())])
|
|
566
|
+
}).superRefine((val, ctx) => {
|
|
567
|
+
const isArrayOp = val.op === "in" || val.op === "nin";
|
|
568
|
+
const isArray = Array.isArray(val.value);
|
|
569
|
+
if (isArrayOp && !isArray) {
|
|
570
|
+
ctx.addIssue({
|
|
571
|
+
code: "custom",
|
|
572
|
+
message: `"${val.op}" operator requires an array value`,
|
|
573
|
+
path: ["value"]
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
if (!isArrayOp && isArray) {
|
|
577
|
+
ctx.addIssue({
|
|
578
|
+
code: "custom",
|
|
579
|
+
message: `"${val.op}" operator requires a number value`,
|
|
580
|
+
path: ["value"]
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
var DatetimeConditionSchema = z4.object({
|
|
585
|
+
id: z4.string().optional(),
|
|
586
|
+
type: z4.literal("datetime"),
|
|
587
|
+
key: z4.string().min(1),
|
|
588
|
+
op: z4.enum(["eq", "gt", "gte", "lt", "lte"]),
|
|
589
|
+
value: z4.iso.datetime({ message: '"datetime" must be a valid ISO 8601 datetime' })
|
|
590
|
+
});
|
|
591
|
+
var BoolConditionSchema = z4.object({
|
|
592
|
+
id: z4.string().optional(),
|
|
593
|
+
type: z4.literal("bool"),
|
|
594
|
+
key: z4.string().min(1),
|
|
595
|
+
op: z4.literal("eq").optional(),
|
|
596
|
+
value: z4.boolean()
|
|
597
|
+
});
|
|
598
|
+
var EnvConditionSchema = z4.object({
|
|
599
|
+
id: z4.string().optional(),
|
|
600
|
+
type: z4.literal("env"),
|
|
601
|
+
op: z4.literal("eq").optional(),
|
|
602
|
+
value: z4.union([z4.string(), z4.array(z4.string())])
|
|
603
|
+
});
|
|
604
|
+
var StartAtConditionSchema = z4.object({
|
|
605
|
+
id: z4.string().optional(),
|
|
606
|
+
type: z4.literal("startAt"),
|
|
607
|
+
value: z4.iso.datetime({ message: '"startAt" must be a valid ISO 8601 datetime' })
|
|
608
|
+
});
|
|
609
|
+
var EndAtConditionSchema = z4.object({
|
|
610
|
+
id: z4.string().optional(),
|
|
611
|
+
type: z4.literal("endAt"),
|
|
612
|
+
value: z4.iso.datetime({ message: '"endAt" must be a valid ISO 8601 datetime' })
|
|
613
|
+
});
|
|
614
|
+
var BuiltinConditionSchema = z4.discriminatedUnion("type", [
|
|
615
|
+
StringConditionSchema,
|
|
616
|
+
NumberConditionSchema,
|
|
617
|
+
DatetimeConditionSchema,
|
|
618
|
+
BoolConditionSchema,
|
|
619
|
+
EnvConditionSchema,
|
|
620
|
+
StartAtConditionSchema,
|
|
621
|
+
EndAtConditionSchema
|
|
597
622
|
]);
|
|
598
|
-
var
|
|
623
|
+
var AndConditionSchema = z4.object({
|
|
624
|
+
id: z4.string().optional(),
|
|
625
|
+
type: z4.literal("and"),
|
|
626
|
+
conditions: z4.array(z4.lazy(() => ConditionSchema)).min(1)
|
|
627
|
+
});
|
|
628
|
+
var OrConditionSchema = z4.object({
|
|
629
|
+
id: z4.string().optional(),
|
|
630
|
+
type: z4.literal("or"),
|
|
631
|
+
conditions: z4.array(z4.lazy(() => ConditionSchema)).min(1)
|
|
632
|
+
});
|
|
633
|
+
var CheckAnnotationsConditionSchema = z4.object({
|
|
634
|
+
id: z4.string().optional(),
|
|
635
|
+
type: z4.literal("checkAnnotations"),
|
|
636
|
+
conditions: z4.array(z4.lazy(() => ConditionSchema)).min(1)
|
|
637
|
+
});
|
|
638
|
+
var BLOCKED_OPEN_UNION_TYPES = new Set(Object.values(CONDITION_TYPES));
|
|
639
|
+
var ConditionSchema = z4.union([
|
|
640
|
+
BuiltinConditionSchema,
|
|
641
|
+
AndConditionSchema,
|
|
642
|
+
OrConditionSchema,
|
|
643
|
+
CheckAnnotationsConditionSchema,
|
|
644
|
+
z4.looseObject({ type: z4.string() }).refine((val) => !BLOCKED_OPEN_UNION_TYPES.has(val.type), {
|
|
645
|
+
message: "Reserved condition type"
|
|
646
|
+
})
|
|
647
|
+
]);
|
|
648
|
+
function isAndCondition(c) {
|
|
649
|
+
return c.type === CONDITION_TYPES.and;
|
|
650
|
+
}
|
|
651
|
+
function isOrCondition(c) {
|
|
652
|
+
return c.type === CONDITION_TYPES.or;
|
|
653
|
+
}
|
|
654
|
+
function isCheckAnnotationsCondition(c) {
|
|
655
|
+
return c.type === CONDITION_TYPES.checkAnnotations;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// src/schemas/context.ts
|
|
659
|
+
import { z as z5 } from "zod";
|
|
660
|
+
var ContextValueSchema = DataValueSchema;
|
|
661
|
+
var ContextSchema = z5.record(z5.string(), ContextValueSchema);
|
|
599
662
|
|
|
600
663
|
// src/schemas/variation.ts
|
|
601
|
-
import { z as
|
|
602
|
-
var VariationValueSchema =
|
|
603
|
-
var VariationSchema =
|
|
604
|
-
id:
|
|
664
|
+
import { z as z6 } from "zod";
|
|
665
|
+
var VariationValueSchema = z6.unknown();
|
|
666
|
+
var VariationSchema = z6.object({
|
|
667
|
+
id: z6.string().optional(),
|
|
605
668
|
value: VariationValueSchema,
|
|
606
|
-
conditions:
|
|
607
|
-
description:
|
|
669
|
+
conditions: z6.array(ConditionSchema).optional(),
|
|
670
|
+
description: z6.string().optional()
|
|
608
671
|
});
|
|
609
672
|
|
|
610
673
|
// src/schemas/definition.ts
|
|
611
|
-
import { z as
|
|
674
|
+
import { z as z8 } from "zod";
|
|
612
675
|
|
|
613
676
|
// src/schemas/preset.ts
|
|
614
|
-
import { z as
|
|
677
|
+
import { z as z7 } from "zod";
|
|
615
678
|
var PRIMITIVE_TYPES = new Set(Object.values(PRIMITIVE_CONDITION_TYPES));
|
|
616
|
-
var
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
679
|
+
var COMPOSITE_TYPES = /* @__PURE__ */ new Set(["and", "or", "checkAnnotations"]);
|
|
680
|
+
var PresetDefinitionSchema = z7.object({
|
|
681
|
+
type: z7.string().min(1),
|
|
682
|
+
key: z7.string().min(1).optional(),
|
|
683
|
+
overrides: z7.record(z7.string(), z7.unknown()).optional()
|
|
620
684
|
}).superRefine((val, ctx) => {
|
|
621
685
|
if (PRIMITIVE_TYPES.has(val.type) && !val.key) {
|
|
622
686
|
ctx.addIssue({
|
|
@@ -625,35 +689,57 @@ var PresetDefinitionSchema = z5.object({
|
|
|
625
689
|
path: ["key"]
|
|
626
690
|
});
|
|
627
691
|
}
|
|
692
|
+
if (COMPOSITE_TYPES.has(val.type)) {
|
|
693
|
+
const conditions = val.overrides?.conditions;
|
|
694
|
+
if (!Array.isArray(conditions) || conditions.length === 0) {
|
|
695
|
+
ctx.addIssue({
|
|
696
|
+
code: "custom",
|
|
697
|
+
message: `"overrides.conditions" must be a non-empty array for composite type ("${val.type}")`,
|
|
698
|
+
path: ["overrides", "conditions"]
|
|
699
|
+
});
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
703
|
+
const result = ConditionSchema.safeParse(conditions[i]);
|
|
704
|
+
if (!result.success) {
|
|
705
|
+
for (const issue of result.error.issues) {
|
|
706
|
+
ctx.addIssue({
|
|
707
|
+
...issue,
|
|
708
|
+
path: ["overrides", "conditions", i, ...issue.path]
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
628
714
|
});
|
|
629
|
-
var PresetsSchema =
|
|
715
|
+
var PresetsSchema = z7.record(z7.string(), PresetDefinitionSchema);
|
|
630
716
|
|
|
631
717
|
// src/schemas/definition.ts
|
|
632
|
-
var DefinitionSchema =
|
|
633
|
-
id:
|
|
634
|
-
active:
|
|
635
|
-
description:
|
|
636
|
-
variations:
|
|
718
|
+
var DefinitionSchema = z8.object({
|
|
719
|
+
id: z8.string().optional(),
|
|
720
|
+
active: z8.boolean().optional(),
|
|
721
|
+
description: z8.string().optional(),
|
|
722
|
+
variations: z8.array(VariationSchema).min(1)
|
|
637
723
|
});
|
|
638
|
-
var DefinitionsSchema =
|
|
639
|
-
var FileFormatSchema =
|
|
724
|
+
var DefinitionsSchema = z8.record(z8.string().min(1), DefinitionSchema);
|
|
725
|
+
var FileFormatSchema = z8.object({
|
|
640
726
|
definitions: DefinitionsSchema,
|
|
641
727
|
presets: PresetsSchema.optional()
|
|
642
728
|
}).strict();
|
|
643
729
|
|
|
644
730
|
// src/schemas/resolution.ts
|
|
645
|
-
import { z as
|
|
646
|
-
var ResolutionSchema =
|
|
647
|
-
key:
|
|
731
|
+
import { z as z9 } from "zod";
|
|
732
|
+
var ResolutionSchema = z9.object({
|
|
733
|
+
key: z9.string(),
|
|
648
734
|
value: VariationValueSchema,
|
|
649
|
-
meta:
|
|
650
|
-
variation:
|
|
651
|
-
index:
|
|
652
|
-
id:
|
|
653
|
-
description:
|
|
654
|
-
conditionCount:
|
|
735
|
+
meta: z9.object({
|
|
736
|
+
variation: z9.object({
|
|
737
|
+
index: z9.number().int().nonnegative(),
|
|
738
|
+
id: z9.string().optional(),
|
|
739
|
+
description: z9.string().optional(),
|
|
740
|
+
conditionCount: z9.number().int().nonnegative()
|
|
655
741
|
}),
|
|
656
|
-
annotations:
|
|
742
|
+
annotations: z9.record(z9.string(), z9.unknown())
|
|
657
743
|
})
|
|
658
744
|
});
|
|
659
745
|
|
|
@@ -731,47 +817,44 @@ var MemoryData = class _MemoryData {
|
|
|
731
817
|
async listKeys() {
|
|
732
818
|
return Object.keys(this.#flags);
|
|
733
819
|
}
|
|
734
|
-
|
|
820
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
821
|
+
async getPresets(_key) {
|
|
735
822
|
return structuredClone(this.#presets);
|
|
736
823
|
}
|
|
737
824
|
};
|
|
738
825
|
|
|
739
826
|
// src/presets.ts
|
|
740
827
|
var RESERVED_CONDITION_TYPES = /* @__PURE__ */ new Set([...Object.values(CONDITION_TYPES), "__custom"]);
|
|
741
|
-
var BUILTIN_EVALUATORS = {
|
|
742
|
-
string: stringEvaluator,
|
|
743
|
-
number: numberEvaluator,
|
|
744
|
-
bool: boolEvaluator,
|
|
745
|
-
datetime: datetimeEvaluator
|
|
746
|
-
};
|
|
747
828
|
function createPresetConditions(presets) {
|
|
748
829
|
const result = {};
|
|
749
830
|
for (const [name, preset] of Object.entries(presets)) {
|
|
750
831
|
if (RESERVED_CONDITION_TYPES.has(name)) {
|
|
751
832
|
throw new Error(`Preset name "${name}" collides with a built-in or reserved condition type`);
|
|
752
833
|
}
|
|
753
|
-
if (!PRIMITIVE_TYPES.has(preset.type)) {
|
|
754
|
-
continue;
|
|
755
|
-
}
|
|
756
|
-
const primitiveType = preset.type;
|
|
757
|
-
const delegateEvaluator = BUILTIN_EVALUATORS[primitiveType];
|
|
758
|
-
const presetKey = preset.key;
|
|
759
834
|
const overrides = preset.overrides ?? {};
|
|
760
|
-
const evaluator = ({
|
|
835
|
+
const evaluator = async ({
|
|
761
836
|
condition,
|
|
762
837
|
context,
|
|
763
838
|
annotations,
|
|
764
839
|
deps,
|
|
765
840
|
depth,
|
|
766
|
-
createRegex
|
|
841
|
+
createRegex,
|
|
842
|
+
evaluators,
|
|
843
|
+
logger
|
|
767
844
|
}) => {
|
|
768
845
|
const rec = condition;
|
|
769
|
-
|
|
770
|
-
|
|
846
|
+
const rewritten = { ...rec, ...overrides, type: preset.type };
|
|
847
|
+
if (preset.key) {
|
|
848
|
+
rewritten.key = preset.key;
|
|
849
|
+
}
|
|
850
|
+
return evaluateCondition({
|
|
851
|
+
condition: rewritten,
|
|
771
852
|
context,
|
|
853
|
+
evaluators,
|
|
772
854
|
annotations,
|
|
773
855
|
deps,
|
|
774
856
|
depth,
|
|
857
|
+
logger,
|
|
775
858
|
createRegex
|
|
776
859
|
});
|
|
777
860
|
};
|
|
@@ -780,6 +863,8 @@ function createPresetConditions(presets) {
|
|
|
780
863
|
return result;
|
|
781
864
|
}
|
|
782
865
|
export {
|
|
866
|
+
AnnotationValueSchema,
|
|
867
|
+
AnnotationsSchema,
|
|
783
868
|
BoolConditionSchema,
|
|
784
869
|
BuiltinConditionSchema,
|
|
785
870
|
CONDITION_TYPES,
|
|
@@ -787,7 +872,9 @@ export {
|
|
|
787
872
|
ConditionError,
|
|
788
873
|
ConditionSchema,
|
|
789
874
|
ContextSchema,
|
|
875
|
+
ContextValueSchema,
|
|
790
876
|
DataError,
|
|
877
|
+
DataValueSchema,
|
|
791
878
|
DatetimeConditionSchema,
|
|
792
879
|
DefinitionInactiveError,
|
|
793
880
|
DefinitionNotFoundError,
|
|
@@ -795,12 +882,12 @@ export {
|
|
|
795
882
|
DefinitionsSchema,
|
|
796
883
|
EndAtConditionSchema,
|
|
797
884
|
EnvConditionSchema,
|
|
885
|
+
FALLBACK_EVALUATOR_KEY,
|
|
798
886
|
FileFormatSchema,
|
|
799
887
|
InvalidContextError,
|
|
800
888
|
MemoryData,
|
|
801
889
|
NumberConditionSchema,
|
|
802
890
|
PRIMITIVE_CONDITION_TYPES,
|
|
803
|
-
PRIMITIVE_TYPES,
|
|
804
891
|
ParseError,
|
|
805
892
|
PresetsSchema,
|
|
806
893
|
ResolutionSchema,
|
|
@@ -808,6 +895,7 @@ export {
|
|
|
808
895
|
ShowwhatError,
|
|
809
896
|
StartAtConditionSchema,
|
|
810
897
|
StringConditionSchema,
|
|
898
|
+
UnknownConditionTypeError,
|
|
811
899
|
ValidationError,
|
|
812
900
|
VariationNotFoundError,
|
|
813
901
|
VariationSchema,
|
|
@@ -817,6 +905,7 @@ export {
|
|
|
817
905
|
defaultCreateRegex,
|
|
818
906
|
evaluateCondition,
|
|
819
907
|
isAndCondition,
|
|
908
|
+
isCheckAnnotationsCondition,
|
|
820
909
|
isOrCondition,
|
|
821
910
|
isWritable,
|
|
822
911
|
noConditionEvaluator,
|