@waypointjs/core 0.1.4 → 0.1.6
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/index.cjs +194 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -11
- package/dist/index.d.ts +61 -11
- package/dist/index.js +194 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5,11 +5,15 @@ var middleware = require('zustand/middleware');
|
|
|
5
5
|
var zod = require('zod');
|
|
6
6
|
|
|
7
7
|
// src/conditions.ts
|
|
8
|
-
function resolveFieldValue(path, data, externalVars) {
|
|
8
|
+
function resolveFieldValue(path, data, externalVars, skippedSteps) {
|
|
9
9
|
if (path.startsWith("$ext.")) {
|
|
10
10
|
const varId = path.slice(5);
|
|
11
11
|
return externalVars[varId];
|
|
12
12
|
}
|
|
13
|
+
if (path.startsWith("$step.") && path.endsWith(".skipped")) {
|
|
14
|
+
const stepId2 = path.slice(6, -8);
|
|
15
|
+
return skippedSteps?.includes(stepId2) ?? false;
|
|
16
|
+
}
|
|
13
17
|
const dotIndex = path.indexOf(".");
|
|
14
18
|
if (dotIndex === -1) return void 0;
|
|
15
19
|
const stepId = path.slice(0, dotIndex);
|
|
@@ -65,24 +69,29 @@ function evaluateOperator(operator, actual, expected) {
|
|
|
65
69
|
return false;
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
|
-
function evaluateRule(rule, data, externalVars) {
|
|
69
|
-
const actual = resolveFieldValue(rule.field, data, externalVars);
|
|
72
|
+
function evaluateRule(rule, data, externalVars, externalEnums, skippedSteps) {
|
|
73
|
+
const actual = resolveFieldValue(rule.field, data, externalVars, skippedSteps);
|
|
74
|
+
if ((rule.operator === "inEnum" || rule.operator === "notInEnum") && externalEnums) {
|
|
75
|
+
const enumDef = externalEnums.find((e) => e.id === String(rule.value));
|
|
76
|
+
const values = enumDef?.values.map((v) => String(v.value)) ?? [];
|
|
77
|
+
return rule.operator === "inEnum" ? values.includes(String(actual)) : !values.includes(String(actual));
|
|
78
|
+
}
|
|
70
79
|
return evaluateOperator(rule.operator, actual, rule.value);
|
|
71
80
|
}
|
|
72
|
-
function evaluateConditionGroup(group, data, externalVars) {
|
|
81
|
+
function evaluateConditionGroup(group, data, externalVars, externalEnums, skippedSteps) {
|
|
73
82
|
const ruleResults = group.rules.map(
|
|
74
|
-
(rule) => evaluateRule(rule, data, externalVars)
|
|
83
|
+
(rule) => evaluateRule(rule, data, externalVars, externalEnums, skippedSteps)
|
|
75
84
|
);
|
|
76
85
|
const groupResults = (group.groups ?? []).map(
|
|
77
|
-
(subGroup) => evaluateConditionGroup(subGroup, data, externalVars)
|
|
86
|
+
(subGroup) => evaluateConditionGroup(subGroup, data, externalVars, externalEnums, skippedSteps)
|
|
78
87
|
);
|
|
79
88
|
const allResults = [...ruleResults, ...groupResults];
|
|
80
89
|
if (allResults.length === 0) return true;
|
|
81
90
|
return group.combinator === "and" ? allResults.every(Boolean) : allResults.some(Boolean);
|
|
82
91
|
}
|
|
83
|
-
function isVisible(visibleWhen, data, externalVars) {
|
|
92
|
+
function isVisible(visibleWhen, data, externalVars, externalEnums, skippedSteps) {
|
|
84
93
|
if (!visibleWhen) return true;
|
|
85
|
-
return evaluateConditionGroup(visibleWhen, data, externalVars);
|
|
94
|
+
return evaluateConditionGroup(visibleWhen, data, externalVars, externalEnums, skippedSteps);
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
// src/tree-resolver.ts
|
|
@@ -106,16 +115,34 @@ function findMissingBlockingVars(externalVariables, externalVars, visibleSteps)
|
|
|
106
115
|
return value === void 0 || value === null;
|
|
107
116
|
}).map((extVar) => extVar.id);
|
|
108
117
|
}
|
|
109
|
-
function resolveTree(schema, data, externalVars) {
|
|
118
|
+
function resolveTree(schema, data, externalVars, externalEnums, skippedSteps) {
|
|
110
119
|
const visibleSteps = [];
|
|
111
120
|
const hiddenSteps = [];
|
|
112
121
|
for (const stepDef of schema.steps) {
|
|
113
|
-
const stepVisible = isVisible(stepDef.visibleWhen, data, externalVars);
|
|
114
|
-
const resolvedFields = stepDef.fields.map((fieldDef) =>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
const stepVisible = isVisible(stepDef.visibleWhen, data, externalVars, externalEnums, skippedSteps);
|
|
123
|
+
const resolvedFields = stepDef.fields.map((fieldDef) => {
|
|
124
|
+
let resolvedOptions;
|
|
125
|
+
if (fieldDef.externalEnumId && externalEnums) {
|
|
126
|
+
const enumDef = externalEnums.find((e) => e.id === fieldDef.externalEnumId);
|
|
127
|
+
resolvedOptions = enumDef?.values;
|
|
128
|
+
}
|
|
129
|
+
let resolvedDefaultValue;
|
|
130
|
+
if (fieldDef.dynamicDefault && fieldDef.dynamicDefault.length > 0) {
|
|
131
|
+
for (const rule of fieldDef.dynamicDefault) {
|
|
132
|
+
if (evaluateConditionGroup(rule.when, data, externalVars, externalEnums, skippedSteps)) {
|
|
133
|
+
resolvedDefaultValue = rule.value;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
definition: fieldDef,
|
|
140
|
+
visible: isVisible(fieldDef.visibleWhen, data, externalVars, externalEnums, skippedSteps),
|
|
141
|
+
dependenciesMet: areDependenciesMet(fieldDef.dependsOn, data, externalVars),
|
|
142
|
+
resolvedOptions,
|
|
143
|
+
resolvedDefaultValue
|
|
144
|
+
};
|
|
145
|
+
});
|
|
119
146
|
const resolvedStep = {
|
|
120
147
|
definition: stepDef,
|
|
121
148
|
visible: stepVisible,
|
|
@@ -180,7 +207,7 @@ function getResolvedTree(state) {
|
|
|
180
207
|
if (!state.schema) {
|
|
181
208
|
return { steps: [], hiddenSteps: [], missingExternalVars: [] };
|
|
182
209
|
}
|
|
183
|
-
return resolveTree(state.schema, state.data, state.externalVars);
|
|
210
|
+
return resolveTree(state.schema, state.data, state.externalVars, void 0, state.skippedSteps);
|
|
184
211
|
}
|
|
185
212
|
function getCurrentStep(state) {
|
|
186
213
|
if (!state.currentStepId) return void 0;
|
|
@@ -212,6 +239,7 @@ var initialState = {
|
|
|
212
239
|
externalVars: {},
|
|
213
240
|
currentStepId: null,
|
|
214
241
|
history: [],
|
|
242
|
+
skippedSteps: [],
|
|
215
243
|
isSubmitting: false,
|
|
216
244
|
completed: false
|
|
217
245
|
};
|
|
@@ -227,6 +255,7 @@ function buildStateCreator() {
|
|
|
227
255
|
externalVars,
|
|
228
256
|
currentStepId: firstStepId,
|
|
229
257
|
history: firstStepId ? [firstStepId] : [],
|
|
258
|
+
skippedSteps: [],
|
|
230
259
|
isSubmitting: false,
|
|
231
260
|
completed: false
|
|
232
261
|
});
|
|
@@ -268,6 +297,16 @@ function buildStateCreator() {
|
|
|
268
297
|
setCompleted(b) {
|
|
269
298
|
set({ completed: b });
|
|
270
299
|
},
|
|
300
|
+
skipStep(stepId) {
|
|
301
|
+
set((state) => ({
|
|
302
|
+
skippedSteps: state.skippedSteps.includes(stepId) ? state.skippedSteps : [...state.skippedSteps, stepId]
|
|
303
|
+
}));
|
|
304
|
+
},
|
|
305
|
+
unskipStep(stepId) {
|
|
306
|
+
set((state) => ({
|
|
307
|
+
skippedSteps: state.skippedSteps.filter((id) => id !== stepId)
|
|
308
|
+
}));
|
|
309
|
+
},
|
|
271
310
|
truncateHistoryAt(stepId) {
|
|
272
311
|
set((state) => {
|
|
273
312
|
const idx = state.history.indexOf(stepId);
|
|
@@ -306,6 +345,7 @@ function createRuntimeStore(options = {}) {
|
|
|
306
345
|
data: state.data,
|
|
307
346
|
currentStepId: state.currentStepId,
|
|
308
347
|
history: state.history,
|
|
348
|
+
skippedSteps: state.skippedSteps,
|
|
309
349
|
completed: state.completed
|
|
310
350
|
})
|
|
311
351
|
})
|
|
@@ -322,7 +362,13 @@ var customValidatorRegistry = {};
|
|
|
322
362
|
function registerCustomValidator(id, fn) {
|
|
323
363
|
customValidatorRegistry[id] = fn;
|
|
324
364
|
}
|
|
325
|
-
function
|
|
365
|
+
function resolveRuleValue(rule, data) {
|
|
366
|
+
if (rule.refField && data) {
|
|
367
|
+
return resolveFieldValue(rule.refField, data, {});
|
|
368
|
+
}
|
|
369
|
+
return rule.value;
|
|
370
|
+
}
|
|
371
|
+
function buildFieldSchema(field, externalEnums, data) {
|
|
326
372
|
const rules = field.validation ?? [];
|
|
327
373
|
const isRequired = rules.some((r) => r.type === "required");
|
|
328
374
|
const isNumeric = field.type === "number";
|
|
@@ -340,16 +386,47 @@ function buildFieldSchema(field) {
|
|
|
340
386
|
let numSchema = zod.z.coerce.number({
|
|
341
387
|
invalid_type_error: "Must be a number"
|
|
342
388
|
});
|
|
389
|
+
const numRefineRules = [];
|
|
343
390
|
for (const rule of rules) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
391
|
+
const rv = resolveRuleValue(rule, data);
|
|
392
|
+
const isRef = !!rule.refField;
|
|
393
|
+
if (rule.type === "min" || rule.type === "greaterThanOrEqual") {
|
|
394
|
+
const n = Number(rv);
|
|
395
|
+
if (!isNaN(n)) {
|
|
396
|
+
if (isRef) numRefineRules.push({ fn: (v) => v >= n, message: rule.message });
|
|
397
|
+
else numSchema = numSchema.gte(n, rule.message);
|
|
398
|
+
}
|
|
399
|
+
} else if (rule.type === "max" || rule.type === "lessThanOrEqual") {
|
|
400
|
+
const n = Number(rv);
|
|
401
|
+
if (!isNaN(n)) {
|
|
402
|
+
if (isRef) numRefineRules.push({ fn: (v) => v <= n, message: rule.message });
|
|
403
|
+
else numSchema = numSchema.lte(n, rule.message);
|
|
404
|
+
}
|
|
405
|
+
} else if (rule.type === "greaterThan") {
|
|
406
|
+
const n = Number(rv);
|
|
407
|
+
if (!isNaN(n)) {
|
|
408
|
+
if (isRef) numRefineRules.push({ fn: (v) => v > n, message: rule.message });
|
|
409
|
+
else numSchema = numSchema.gt(n, rule.message);
|
|
410
|
+
}
|
|
411
|
+
} else if (rule.type === "lessThan") {
|
|
412
|
+
const n = Number(rv);
|
|
413
|
+
if (!isNaN(n)) {
|
|
414
|
+
if (isRef) numRefineRules.push({ fn: (v) => v < n, message: rule.message });
|
|
415
|
+
else numSchema = numSchema.lt(n, rule.message);
|
|
416
|
+
}
|
|
417
|
+
} else if (rule.type === "equals") {
|
|
418
|
+
const n = Number(rv);
|
|
419
|
+
if (!isNaN(n)) numRefineRules.push({ fn: (v) => v === n, message: rule.message });
|
|
420
|
+
} else if (rule.type === "notEquals") {
|
|
421
|
+
const n = Number(rv);
|
|
422
|
+
if (!isNaN(n)) numRefineRules.push({ fn: (v) => v !== n, message: rule.message });
|
|
350
423
|
}
|
|
351
424
|
}
|
|
352
|
-
|
|
425
|
+
let numFinal = isRequired ? numSchema : numSchema.optional();
|
|
426
|
+
for (const { fn, message } of numRefineRules) {
|
|
427
|
+
numFinal = numFinal.refine((v) => v == null || fn(v), message);
|
|
428
|
+
}
|
|
429
|
+
return numFinal;
|
|
353
430
|
}
|
|
354
431
|
let strSchema = zod.z.string();
|
|
355
432
|
const refineRules = [];
|
|
@@ -379,27 +456,112 @@ function buildFieldSchema(field) {
|
|
|
379
456
|
strSchema = strSchema.regex(new RegExp(String(rule.value)), rule.message);
|
|
380
457
|
}
|
|
381
458
|
break;
|
|
459
|
+
case "equals": {
|
|
460
|
+
const rv = resolveRuleValue(rule, data);
|
|
461
|
+
if (rv !== void 0) {
|
|
462
|
+
const eq = String(rv);
|
|
463
|
+
refineRules.push({ fn: (v) => String(v) === eq, message: rule.message });
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case "notEquals": {
|
|
468
|
+
const rv = resolveRuleValue(rule, data);
|
|
469
|
+
if (rv !== void 0) {
|
|
470
|
+
const neq = String(rv);
|
|
471
|
+
refineRules.push({ fn: (v) => String(v) !== neq, message: rule.message });
|
|
472
|
+
}
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
case "greaterThan": {
|
|
476
|
+
const rv = resolveRuleValue(rule, data);
|
|
477
|
+
if (rv !== void 0) {
|
|
478
|
+
const gt = Number(rv);
|
|
479
|
+
refineRules.push({ fn: (v) => Number(v) > gt, message: rule.message });
|
|
480
|
+
}
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
case "greaterThanOrEqual": {
|
|
484
|
+
const rv = resolveRuleValue(rule, data);
|
|
485
|
+
if (rv !== void 0) {
|
|
486
|
+
const gte = Number(rv);
|
|
487
|
+
refineRules.push({ fn: (v) => Number(v) >= gte, message: rule.message });
|
|
488
|
+
}
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
case "lessThan": {
|
|
492
|
+
const rv = resolveRuleValue(rule, data);
|
|
493
|
+
if (rv !== void 0) {
|
|
494
|
+
const lt = Number(rv);
|
|
495
|
+
refineRules.push({ fn: (v) => Number(v) < lt, message: rule.message });
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
case "lessThanOrEqual": {
|
|
500
|
+
const rv = resolveRuleValue(rule, data);
|
|
501
|
+
if (rv !== void 0) {
|
|
502
|
+
const lte = Number(rv);
|
|
503
|
+
refineRules.push({ fn: (v) => Number(v) <= lte, message: rule.message });
|
|
504
|
+
}
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
case "contains": {
|
|
508
|
+
const rv = resolveRuleValue(rule, data);
|
|
509
|
+
if (rv !== void 0) {
|
|
510
|
+
const sub = String(rv);
|
|
511
|
+
refineRules.push({ fn: (v) => String(v).includes(sub), message: rule.message });
|
|
512
|
+
}
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
case "notContains": {
|
|
516
|
+
const rv = resolveRuleValue(rule, data);
|
|
517
|
+
if (rv !== void 0) {
|
|
518
|
+
const nsub = String(rv);
|
|
519
|
+
refineRules.push({ fn: (v) => !String(v).includes(nsub), message: rule.message });
|
|
520
|
+
}
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
case "matches":
|
|
524
|
+
if (rule.value !== void 0 && rule.value !== null) {
|
|
525
|
+
const rx = new RegExp(String(rule.value));
|
|
526
|
+
refineRules.push({ fn: (v) => rx.test(String(v)), message: rule.message });
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
case "inEnum":
|
|
530
|
+
if (rule.value && externalEnums) {
|
|
531
|
+
const enumDef = externalEnums.find((e) => e.id === String(rule.value));
|
|
532
|
+
if (enumDef) {
|
|
533
|
+
const values = enumDef.values.map((v) => String(v.value));
|
|
534
|
+
refineRules.push({ fn: (v) => values.includes(String(v)), message: rule.message });
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
break;
|
|
538
|
+
case "notInEnum":
|
|
539
|
+
if (rule.value && externalEnums) {
|
|
540
|
+
const enumDef = externalEnums.find((e) => e.id === String(rule.value));
|
|
541
|
+
if (enumDef) {
|
|
542
|
+
const values = enumDef.values.map((v) => String(v.value));
|
|
543
|
+
refineRules.push({ fn: (v) => !values.includes(String(v)), message: rule.message });
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
break;
|
|
382
547
|
case "custom":
|
|
383
548
|
if (rule.customValidatorId && customValidatorRegistry[rule.customValidatorId]) {
|
|
384
|
-
refineRules.push({
|
|
549
|
+
refineRules.push({ fn: (v) => Boolean(customValidatorRegistry[rule.customValidatorId]?.(v)), message: rule.message });
|
|
385
550
|
}
|
|
386
551
|
break;
|
|
387
552
|
}
|
|
388
553
|
}
|
|
389
554
|
let finalSchema = isRequired ? strSchema : strSchema.optional();
|
|
390
|
-
for (const {
|
|
391
|
-
finalSchema = finalSchema.refine(
|
|
392
|
-
(val) => Boolean(customValidatorRegistry[id]?.(val)),
|
|
393
|
-
message
|
|
394
|
-
);
|
|
555
|
+
for (const { fn, message } of refineRules) {
|
|
556
|
+
finalSchema = finalSchema.refine(fn, message);
|
|
395
557
|
}
|
|
396
558
|
return finalSchema;
|
|
397
559
|
}
|
|
398
|
-
function buildZodSchema(fields) {
|
|
560
|
+
function buildZodSchema(fields, externalEnums, data) {
|
|
399
561
|
const shape = {};
|
|
400
562
|
for (const resolvedField of fields) {
|
|
401
563
|
if (!resolvedField.visible) continue;
|
|
402
|
-
shape[resolvedField.definition.id] = buildFieldSchema(resolvedField.definition);
|
|
564
|
+
shape[resolvedField.definition.id] = buildFieldSchema(resolvedField.definition, externalEnums, data);
|
|
403
565
|
}
|
|
404
566
|
return zod.z.object(shape);
|
|
405
567
|
}
|