@waypointjs/core 0.1.5 → 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 +121 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -8
- package/dist/index.d.ts +40 -8
- package/dist/index.js +121 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ interface ValidationRule {
|
|
|
36
36
|
message: string;
|
|
37
37
|
/** For "custom" type: identifier of the validator function registered at runtime */
|
|
38
38
|
customValidatorId?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Cross-field reference — "stepId.fieldId" path.
|
|
41
|
+
* When set, the comparison target is resolved from journey data instead of using `value`.
|
|
42
|
+
*/
|
|
43
|
+
refField?: string;
|
|
39
44
|
}
|
|
40
45
|
type BuiltinFieldType = "text" | "number" | "email" | "password" | "tel" | "url" | "textarea" | "select" | "multiselect" | "checkbox" | "radio" | "date" | "file";
|
|
41
46
|
/** Field type — either a builtin or a custom type id registered at runtime */
|
|
@@ -44,12 +49,25 @@ interface SelectOption {
|
|
|
44
49
|
label: string;
|
|
45
50
|
value: string | number;
|
|
46
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* A dynamic default rule: if the condition matches, use the given value
|
|
54
|
+
* as the field's default. First matching rule wins.
|
|
55
|
+
*/
|
|
56
|
+
interface DynamicDefaultRule {
|
|
57
|
+
when: ConditionGroup;
|
|
58
|
+
value: unknown;
|
|
59
|
+
}
|
|
47
60
|
interface FieldDefinition {
|
|
48
61
|
id: string;
|
|
49
62
|
type: FieldType;
|
|
50
63
|
label: string;
|
|
51
64
|
placeholder?: string;
|
|
52
65
|
defaultValue?: unknown;
|
|
66
|
+
/**
|
|
67
|
+
* Conditional defaults — evaluated in order, first match wins.
|
|
68
|
+
* Falls back to `defaultValue` if no rule matches.
|
|
69
|
+
*/
|
|
70
|
+
dynamicDefault?: DynamicDefaultRule[];
|
|
53
71
|
/** Options for select/multiselect/radio fields (hardcoded) */
|
|
54
72
|
options?: SelectOption[];
|
|
55
73
|
/** Reference to an external enum — options resolved at runtime from app-provided enums */
|
|
@@ -70,6 +88,8 @@ interface StepDefinition$1 {
|
|
|
70
88
|
visibleWhen?: ConditionGroup;
|
|
71
89
|
/** Whether this step can be used as a resume point */
|
|
72
90
|
enableResumeFromHere?: boolean;
|
|
91
|
+
/** Whether the user can skip this step without filling required fields */
|
|
92
|
+
skippable?: boolean;
|
|
73
93
|
}
|
|
74
94
|
interface ExternalVariable {
|
|
75
95
|
id: string;
|
|
@@ -140,20 +160,21 @@ type JourneyData = Record<string, Record<string, unknown>>;
|
|
|
140
160
|
type ExternalVars = Record<string, unknown>;
|
|
141
161
|
/**
|
|
142
162
|
* Resolves a field path to its value from the data context.
|
|
143
|
-
* - "stepId.fieldId"
|
|
144
|
-
* - "$ext.varId"
|
|
163
|
+
* - "stepId.fieldId" → journey data
|
|
164
|
+
* - "$ext.varId" → external variable
|
|
165
|
+
* - "$step.stepId.skipped" → whether the step was skipped (boolean)
|
|
145
166
|
*/
|
|
146
|
-
declare function resolveFieldValue(path: string, data: JourneyData, externalVars: ExternalVars): unknown;
|
|
167
|
+
declare function resolveFieldValue(path: string, data: JourneyData, externalVars: ExternalVars, skippedSteps?: string[]): unknown;
|
|
147
168
|
/**
|
|
148
169
|
* Evaluates a condition group against the current data context.
|
|
149
170
|
* Returns true if the group's conditions are satisfied.
|
|
150
171
|
*/
|
|
151
|
-
declare function evaluateConditionGroup(group: ConditionGroup, data: JourneyData, externalVars: ExternalVars, externalEnums?: ExternalEnum[]): boolean;
|
|
172
|
+
declare function evaluateConditionGroup(group: ConditionGroup, data: JourneyData, externalVars: ExternalVars, externalEnums?: ExternalEnum[], skippedSteps?: string[]): boolean;
|
|
152
173
|
/**
|
|
153
174
|
* Convenience: returns true if no condition is defined (always visible),
|
|
154
175
|
* or if the condition group evaluates to true.
|
|
155
176
|
*/
|
|
156
|
-
declare function isVisible(visibleWhen: ConditionGroup | undefined, data: JourneyData, externalVars: ExternalVars, externalEnums?: ExternalEnum[]): boolean;
|
|
177
|
+
declare function isVisible(visibleWhen: ConditionGroup | undefined, data: JourneyData, externalVars: ExternalVars, externalEnums?: ExternalEnum[], skippedSteps?: string[]): boolean;
|
|
157
178
|
|
|
158
179
|
/** A field after condition evaluation */
|
|
159
180
|
interface ResolvedField {
|
|
@@ -168,6 +189,11 @@ interface ResolvedField {
|
|
|
168
189
|
* Use `field.resolvedOptions ?? field.definition.options` when rendering.
|
|
169
190
|
*/
|
|
170
191
|
resolvedOptions?: SelectOption[];
|
|
192
|
+
/**
|
|
193
|
+
* Resolved dynamic default value (first matching `dynamicDefault` rule).
|
|
194
|
+
* Use `field.resolvedDefaultValue ?? field.definition.defaultValue` for initial form values.
|
|
195
|
+
*/
|
|
196
|
+
resolvedDefaultValue?: unknown;
|
|
171
197
|
}
|
|
172
198
|
/** A step after condition evaluation */
|
|
173
199
|
interface ResolvedStep {
|
|
@@ -195,7 +221,7 @@ interface ResolvedTree {
|
|
|
195
221
|
*
|
|
196
222
|
* This function is pure: same inputs always produce same outputs.
|
|
197
223
|
*/
|
|
198
|
-
declare function resolveTree(schema: WaypointSchema, data: JourneyData, externalVars: ExternalVars, externalEnums?: ExternalEnum[]): ResolvedTree;
|
|
224
|
+
declare function resolveTree(schema: WaypointSchema, data: JourneyData, externalVars: ExternalVars, externalEnums?: ExternalEnum[], skippedSteps?: string[]): ResolvedTree;
|
|
199
225
|
/**
|
|
200
226
|
* Returns the index of the step with the given id in the resolved (visible) tree.
|
|
201
227
|
* Returns -1 if not found.
|
|
@@ -230,6 +256,8 @@ interface WaypointRuntimeState {
|
|
|
230
256
|
currentStepId: string | null;
|
|
231
257
|
/** Step IDs visited in order */
|
|
232
258
|
history: string[];
|
|
259
|
+
/** Step IDs that have been skipped by the user */
|
|
260
|
+
skippedSteps: string[];
|
|
233
261
|
isSubmitting: boolean;
|
|
234
262
|
/** True once onComplete has been called (all steps validated) */
|
|
235
263
|
completed: boolean;
|
|
@@ -256,6 +284,10 @@ interface WaypointRuntimeActions {
|
|
|
256
284
|
setCurrentStep(stepId: string): void;
|
|
257
285
|
setIsSubmitting(b: boolean): void;
|
|
258
286
|
setCompleted(b: boolean): void;
|
|
287
|
+
/** Marks a step as skipped (user chose to bypass it) */
|
|
288
|
+
skipStep(stepId: string): void;
|
|
289
|
+
/** Removes a step from the skipped list (e.g. when user goes back and fills it) */
|
|
290
|
+
unskipStep(stepId: string): void;
|
|
259
291
|
/**
|
|
260
292
|
* Truncates history to include only steps up to and including stepId.
|
|
261
293
|
* Called before navigating forward so stale steps from a previous path are removed.
|
|
@@ -306,7 +338,7 @@ declare function registerCustomValidator(id: string, fn: (value: unknown) => boo
|
|
|
306
338
|
* - Fields without a `required` validation rule are wrapped in `.optional()`.
|
|
307
339
|
* - Numeric fields use `z.coerce.number()` so string inputs are coerced.
|
|
308
340
|
*/
|
|
309
|
-
declare function buildZodSchema(fields: ResolvedField[], externalEnums?: ExternalEnum[]): z.ZodObject<z.ZodRawShape>;
|
|
341
|
+
declare function buildZodSchema(fields: ResolvedField[], externalEnums?: ExternalEnum[], data?: JourneyData): z.ZodObject<z.ZodRawShape>;
|
|
310
342
|
|
|
311
343
|
/**
|
|
312
344
|
* Runtime validation for WaypointSchema JSON.
|
|
@@ -400,4 +432,4 @@ declare function extractURLParamsFromTree(pathname: string, allSteps: StepDefini
|
|
|
400
432
|
declare function extractOnlyMissingParams(pathname: string, allSteps: StepDefinition[], missingParamNames: string[]): WaypointParams;
|
|
401
433
|
declare function mergeContextParams(userParams: WaypointParams | undefined, pathname: string, allSteps: StepDefinition[], targetURL?: string): WaypointParams;
|
|
402
434
|
|
|
403
|
-
export { type BuiltinFieldType, type ConditionGroup, type ConditionOperator, type ConditionRule, type CreateRuntimeStoreOptions, type CustomTypeDefinition, type ExternalEnum, type ExternalVariable, type ExternalVars, type FieldDefinition, type FieldType, type JourneyData, type JourneyState, type JourneyTreeStep, type JourneyTreeType, type PersistenceMode, type ResolvedField, type ResolvedStep, type ResolvedTree, type RuntimeStore, type SchemaValidationResult, type SelectOption, type StepDefinition$1 as StepDefinition, URLTemplateEngine, type ValidationRule, type ValidationRuleType, type WaypointParams, type WaypointRuntimeActions, type WaypointRuntimeState, type WaypointRuntimeStore, type WaypointSchema, assertSchema, buildZodSchema, calculateProgress, calculateProgressFromState, createRuntimeStore, evaluateConditionGroup, extractOnlyMissingParams, extractURLParamsFromTree, findLastValidStep, findMatchingStep, findStepIndex, getCurrentStep, getMissingBlockingVars, getNextStep, getNextStepFromState, getPreviousStep, getPreviousStepFromState, getResolvedTree, hasPersistedState, isVisible, mergeContextParams, registerCustomValidator, resolveFieldValue, resolveTree, validateSchema };
|
|
435
|
+
export { type BuiltinFieldType, type ConditionGroup, type ConditionOperator, type ConditionRule, type CreateRuntimeStoreOptions, type CustomTypeDefinition, type DynamicDefaultRule, type ExternalEnum, type ExternalVariable, type ExternalVars, type FieldDefinition, type FieldType, type JourneyData, type JourneyState, type JourneyTreeStep, type JourneyTreeType, type PersistenceMode, type ResolvedField, type ResolvedStep, type ResolvedTree, type RuntimeStore, type SchemaValidationResult, type SelectOption, type StepDefinition$1 as StepDefinition, URLTemplateEngine, type ValidationRule, type ValidationRuleType, type WaypointParams, type WaypointRuntimeActions, type WaypointRuntimeState, type WaypointRuntimeStore, type WaypointSchema, assertSchema, buildZodSchema, calculateProgress, calculateProgressFromState, createRuntimeStore, evaluateConditionGroup, extractOnlyMissingParams, extractURLParamsFromTree, findLastValidStep, findMatchingStep, findStepIndex, getCurrentStep, getMissingBlockingVars, getNextStep, getNextStepFromState, getPreviousStep, getPreviousStepFromState, getResolvedTree, hasPersistedState, isVisible, mergeContextParams, registerCustomValidator, resolveFieldValue, resolveTree, validateSchema };
|
package/dist/index.js
CHANGED
|
@@ -3,11 +3,15 @@ import { persist, createJSONStorage } from 'zustand/middleware';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
// src/conditions.ts
|
|
6
|
-
function resolveFieldValue(path, data, externalVars) {
|
|
6
|
+
function resolveFieldValue(path, data, externalVars, skippedSteps) {
|
|
7
7
|
if (path.startsWith("$ext.")) {
|
|
8
8
|
const varId = path.slice(5);
|
|
9
9
|
return externalVars[varId];
|
|
10
10
|
}
|
|
11
|
+
if (path.startsWith("$step.") && path.endsWith(".skipped")) {
|
|
12
|
+
const stepId2 = path.slice(6, -8);
|
|
13
|
+
return skippedSteps?.includes(stepId2) ?? false;
|
|
14
|
+
}
|
|
11
15
|
const dotIndex = path.indexOf(".");
|
|
12
16
|
if (dotIndex === -1) return void 0;
|
|
13
17
|
const stepId = path.slice(0, dotIndex);
|
|
@@ -63,8 +67,8 @@ function evaluateOperator(operator, actual, expected) {
|
|
|
63
67
|
return false;
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
|
-
function evaluateRule(rule, data, externalVars, externalEnums) {
|
|
67
|
-
const actual = resolveFieldValue(rule.field, data, externalVars);
|
|
70
|
+
function evaluateRule(rule, data, externalVars, externalEnums, skippedSteps) {
|
|
71
|
+
const actual = resolveFieldValue(rule.field, data, externalVars, skippedSteps);
|
|
68
72
|
if ((rule.operator === "inEnum" || rule.operator === "notInEnum") && externalEnums) {
|
|
69
73
|
const enumDef = externalEnums.find((e) => e.id === String(rule.value));
|
|
70
74
|
const values = enumDef?.values.map((v) => String(v.value)) ?? [];
|
|
@@ -72,20 +76,20 @@ function evaluateRule(rule, data, externalVars, externalEnums) {
|
|
|
72
76
|
}
|
|
73
77
|
return evaluateOperator(rule.operator, actual, rule.value);
|
|
74
78
|
}
|
|
75
|
-
function evaluateConditionGroup(group, data, externalVars, externalEnums) {
|
|
79
|
+
function evaluateConditionGroup(group, data, externalVars, externalEnums, skippedSteps) {
|
|
76
80
|
const ruleResults = group.rules.map(
|
|
77
|
-
(rule) => evaluateRule(rule, data, externalVars, externalEnums)
|
|
81
|
+
(rule) => evaluateRule(rule, data, externalVars, externalEnums, skippedSteps)
|
|
78
82
|
);
|
|
79
83
|
const groupResults = (group.groups ?? []).map(
|
|
80
|
-
(subGroup) => evaluateConditionGroup(subGroup, data, externalVars, externalEnums)
|
|
84
|
+
(subGroup) => evaluateConditionGroup(subGroup, data, externalVars, externalEnums, skippedSteps)
|
|
81
85
|
);
|
|
82
86
|
const allResults = [...ruleResults, ...groupResults];
|
|
83
87
|
if (allResults.length === 0) return true;
|
|
84
88
|
return group.combinator === "and" ? allResults.every(Boolean) : allResults.some(Boolean);
|
|
85
89
|
}
|
|
86
|
-
function isVisible(visibleWhen, data, externalVars, externalEnums) {
|
|
90
|
+
function isVisible(visibleWhen, data, externalVars, externalEnums, skippedSteps) {
|
|
87
91
|
if (!visibleWhen) return true;
|
|
88
|
-
return evaluateConditionGroup(visibleWhen, data, externalVars, externalEnums);
|
|
92
|
+
return evaluateConditionGroup(visibleWhen, data, externalVars, externalEnums, skippedSteps);
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
// src/tree-resolver.ts
|
|
@@ -109,22 +113,32 @@ function findMissingBlockingVars(externalVariables, externalVars, visibleSteps)
|
|
|
109
113
|
return value === void 0 || value === null;
|
|
110
114
|
}).map((extVar) => extVar.id);
|
|
111
115
|
}
|
|
112
|
-
function resolveTree(schema, data, externalVars, externalEnums) {
|
|
116
|
+
function resolveTree(schema, data, externalVars, externalEnums, skippedSteps) {
|
|
113
117
|
const visibleSteps = [];
|
|
114
118
|
const hiddenSteps = [];
|
|
115
119
|
for (const stepDef of schema.steps) {
|
|
116
|
-
const stepVisible = isVisible(stepDef.visibleWhen, data, externalVars, externalEnums);
|
|
120
|
+
const stepVisible = isVisible(stepDef.visibleWhen, data, externalVars, externalEnums, skippedSteps);
|
|
117
121
|
const resolvedFields = stepDef.fields.map((fieldDef) => {
|
|
118
122
|
let resolvedOptions;
|
|
119
123
|
if (fieldDef.externalEnumId && externalEnums) {
|
|
120
124
|
const enumDef = externalEnums.find((e) => e.id === fieldDef.externalEnumId);
|
|
121
125
|
resolvedOptions = enumDef?.values;
|
|
122
126
|
}
|
|
127
|
+
let resolvedDefaultValue;
|
|
128
|
+
if (fieldDef.dynamicDefault && fieldDef.dynamicDefault.length > 0) {
|
|
129
|
+
for (const rule of fieldDef.dynamicDefault) {
|
|
130
|
+
if (evaluateConditionGroup(rule.when, data, externalVars, externalEnums, skippedSteps)) {
|
|
131
|
+
resolvedDefaultValue = rule.value;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
123
136
|
return {
|
|
124
137
|
definition: fieldDef,
|
|
125
|
-
visible: isVisible(fieldDef.visibleWhen, data, externalVars, externalEnums),
|
|
138
|
+
visible: isVisible(fieldDef.visibleWhen, data, externalVars, externalEnums, skippedSteps),
|
|
126
139
|
dependenciesMet: areDependenciesMet(fieldDef.dependsOn, data, externalVars),
|
|
127
|
-
resolvedOptions
|
|
140
|
+
resolvedOptions,
|
|
141
|
+
resolvedDefaultValue
|
|
128
142
|
};
|
|
129
143
|
});
|
|
130
144
|
const resolvedStep = {
|
|
@@ -191,7 +205,7 @@ function getResolvedTree(state) {
|
|
|
191
205
|
if (!state.schema) {
|
|
192
206
|
return { steps: [], hiddenSteps: [], missingExternalVars: [] };
|
|
193
207
|
}
|
|
194
|
-
return resolveTree(state.schema, state.data, state.externalVars);
|
|
208
|
+
return resolveTree(state.schema, state.data, state.externalVars, void 0, state.skippedSteps);
|
|
195
209
|
}
|
|
196
210
|
function getCurrentStep(state) {
|
|
197
211
|
if (!state.currentStepId) return void 0;
|
|
@@ -223,6 +237,7 @@ var initialState = {
|
|
|
223
237
|
externalVars: {},
|
|
224
238
|
currentStepId: null,
|
|
225
239
|
history: [],
|
|
240
|
+
skippedSteps: [],
|
|
226
241
|
isSubmitting: false,
|
|
227
242
|
completed: false
|
|
228
243
|
};
|
|
@@ -238,6 +253,7 @@ function buildStateCreator() {
|
|
|
238
253
|
externalVars,
|
|
239
254
|
currentStepId: firstStepId,
|
|
240
255
|
history: firstStepId ? [firstStepId] : [],
|
|
256
|
+
skippedSteps: [],
|
|
241
257
|
isSubmitting: false,
|
|
242
258
|
completed: false
|
|
243
259
|
});
|
|
@@ -279,6 +295,16 @@ function buildStateCreator() {
|
|
|
279
295
|
setCompleted(b) {
|
|
280
296
|
set({ completed: b });
|
|
281
297
|
},
|
|
298
|
+
skipStep(stepId) {
|
|
299
|
+
set((state) => ({
|
|
300
|
+
skippedSteps: state.skippedSteps.includes(stepId) ? state.skippedSteps : [...state.skippedSteps, stepId]
|
|
301
|
+
}));
|
|
302
|
+
},
|
|
303
|
+
unskipStep(stepId) {
|
|
304
|
+
set((state) => ({
|
|
305
|
+
skippedSteps: state.skippedSteps.filter((id) => id !== stepId)
|
|
306
|
+
}));
|
|
307
|
+
},
|
|
282
308
|
truncateHistoryAt(stepId) {
|
|
283
309
|
set((state) => {
|
|
284
310
|
const idx = state.history.indexOf(stepId);
|
|
@@ -317,6 +343,7 @@ function createRuntimeStore(options = {}) {
|
|
|
317
343
|
data: state.data,
|
|
318
344
|
currentStepId: state.currentStepId,
|
|
319
345
|
history: state.history,
|
|
346
|
+
skippedSteps: state.skippedSteps,
|
|
320
347
|
completed: state.completed
|
|
321
348
|
})
|
|
322
349
|
})
|
|
@@ -333,7 +360,13 @@ var customValidatorRegistry = {};
|
|
|
333
360
|
function registerCustomValidator(id, fn) {
|
|
334
361
|
customValidatorRegistry[id] = fn;
|
|
335
362
|
}
|
|
336
|
-
function
|
|
363
|
+
function resolveRuleValue(rule, data) {
|
|
364
|
+
if (rule.refField && data) {
|
|
365
|
+
return resolveFieldValue(rule.refField, data, {});
|
|
366
|
+
}
|
|
367
|
+
return rule.value;
|
|
368
|
+
}
|
|
369
|
+
function buildFieldSchema(field, externalEnums, data) {
|
|
337
370
|
const rules = field.validation ?? [];
|
|
338
371
|
const isRequired = rules.some((r) => r.type === "required");
|
|
339
372
|
const isNumeric = field.type === "number";
|
|
@@ -351,28 +384,47 @@ function buildFieldSchema(field, externalEnums) {
|
|
|
351
384
|
let numSchema = z.coerce.number({
|
|
352
385
|
invalid_type_error: "Must be a number"
|
|
353
386
|
});
|
|
387
|
+
const numRefineRules = [];
|
|
354
388
|
for (const rule of rules) {
|
|
389
|
+
const rv = resolveRuleValue(rule, data);
|
|
390
|
+
const isRef = !!rule.refField;
|
|
355
391
|
if (rule.type === "min" || rule.type === "greaterThanOrEqual") {
|
|
356
|
-
const n = Number(
|
|
357
|
-
if (!isNaN(n))
|
|
392
|
+
const n = Number(rv);
|
|
393
|
+
if (!isNaN(n)) {
|
|
394
|
+
if (isRef) numRefineRules.push({ fn: (v) => v >= n, message: rule.message });
|
|
395
|
+
else numSchema = numSchema.gte(n, rule.message);
|
|
396
|
+
}
|
|
358
397
|
} else if (rule.type === "max" || rule.type === "lessThanOrEqual") {
|
|
359
|
-
const n = Number(
|
|
360
|
-
if (!isNaN(n))
|
|
398
|
+
const n = Number(rv);
|
|
399
|
+
if (!isNaN(n)) {
|
|
400
|
+
if (isRef) numRefineRules.push({ fn: (v) => v <= n, message: rule.message });
|
|
401
|
+
else numSchema = numSchema.lte(n, rule.message);
|
|
402
|
+
}
|
|
361
403
|
} else if (rule.type === "greaterThan") {
|
|
362
|
-
const n = Number(
|
|
363
|
-
if (!isNaN(n))
|
|
404
|
+
const n = Number(rv);
|
|
405
|
+
if (!isNaN(n)) {
|
|
406
|
+
if (isRef) numRefineRules.push({ fn: (v) => v > n, message: rule.message });
|
|
407
|
+
else numSchema = numSchema.gt(n, rule.message);
|
|
408
|
+
}
|
|
364
409
|
} else if (rule.type === "lessThan") {
|
|
365
|
-
const n = Number(
|
|
366
|
-
if (!isNaN(n))
|
|
410
|
+
const n = Number(rv);
|
|
411
|
+
if (!isNaN(n)) {
|
|
412
|
+
if (isRef) numRefineRules.push({ fn: (v) => v < n, message: rule.message });
|
|
413
|
+
else numSchema = numSchema.lt(n, rule.message);
|
|
414
|
+
}
|
|
367
415
|
} else if (rule.type === "equals") {
|
|
368
|
-
const n = Number(
|
|
369
|
-
if (!isNaN(n))
|
|
416
|
+
const n = Number(rv);
|
|
417
|
+
if (!isNaN(n)) numRefineRules.push({ fn: (v) => v === n, message: rule.message });
|
|
370
418
|
} else if (rule.type === "notEquals") {
|
|
371
|
-
const n = Number(
|
|
372
|
-
if (!isNaN(n))
|
|
419
|
+
const n = Number(rv);
|
|
420
|
+
if (!isNaN(n)) numRefineRules.push({ fn: (v) => v !== n, message: rule.message });
|
|
373
421
|
}
|
|
374
422
|
}
|
|
375
|
-
|
|
423
|
+
let numFinal = isRequired ? numSchema : numSchema.optional();
|
|
424
|
+
for (const { fn, message } of numRefineRules) {
|
|
425
|
+
numFinal = numFinal.refine((v) => v == null || fn(v), message);
|
|
426
|
+
}
|
|
427
|
+
return numFinal;
|
|
376
428
|
}
|
|
377
429
|
let strSchema = z.string();
|
|
378
430
|
const refineRules = [];
|
|
@@ -402,54 +454,70 @@ function buildFieldSchema(field, externalEnums) {
|
|
|
402
454
|
strSchema = strSchema.regex(new RegExp(String(rule.value)), rule.message);
|
|
403
455
|
}
|
|
404
456
|
break;
|
|
405
|
-
case "equals":
|
|
406
|
-
|
|
407
|
-
|
|
457
|
+
case "equals": {
|
|
458
|
+
const rv = resolveRuleValue(rule, data);
|
|
459
|
+
if (rv !== void 0) {
|
|
460
|
+
const eq = String(rv);
|
|
408
461
|
refineRules.push({ fn: (v) => String(v) === eq, message: rule.message });
|
|
409
462
|
}
|
|
410
463
|
break;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
464
|
+
}
|
|
465
|
+
case "notEquals": {
|
|
466
|
+
const rv = resolveRuleValue(rule, data);
|
|
467
|
+
if (rv !== void 0) {
|
|
468
|
+
const neq = String(rv);
|
|
414
469
|
refineRules.push({ fn: (v) => String(v) !== neq, message: rule.message });
|
|
415
470
|
}
|
|
416
471
|
break;
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
472
|
+
}
|
|
473
|
+
case "greaterThan": {
|
|
474
|
+
const rv = resolveRuleValue(rule, data);
|
|
475
|
+
if (rv !== void 0) {
|
|
476
|
+
const gt = Number(rv);
|
|
420
477
|
refineRules.push({ fn: (v) => Number(v) > gt, message: rule.message });
|
|
421
478
|
}
|
|
422
479
|
break;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
480
|
+
}
|
|
481
|
+
case "greaterThanOrEqual": {
|
|
482
|
+
const rv = resolveRuleValue(rule, data);
|
|
483
|
+
if (rv !== void 0) {
|
|
484
|
+
const gte = Number(rv);
|
|
426
485
|
refineRules.push({ fn: (v) => Number(v) >= gte, message: rule.message });
|
|
427
486
|
}
|
|
428
487
|
break;
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
488
|
+
}
|
|
489
|
+
case "lessThan": {
|
|
490
|
+
const rv = resolveRuleValue(rule, data);
|
|
491
|
+
if (rv !== void 0) {
|
|
492
|
+
const lt = Number(rv);
|
|
432
493
|
refineRules.push({ fn: (v) => Number(v) < lt, message: rule.message });
|
|
433
494
|
}
|
|
434
495
|
break;
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
496
|
+
}
|
|
497
|
+
case "lessThanOrEqual": {
|
|
498
|
+
const rv = resolveRuleValue(rule, data);
|
|
499
|
+
if (rv !== void 0) {
|
|
500
|
+
const lte = Number(rv);
|
|
438
501
|
refineRules.push({ fn: (v) => Number(v) <= lte, message: rule.message });
|
|
439
502
|
}
|
|
440
503
|
break;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
504
|
+
}
|
|
505
|
+
case "contains": {
|
|
506
|
+
const rv = resolveRuleValue(rule, data);
|
|
507
|
+
if (rv !== void 0) {
|
|
508
|
+
const sub = String(rv);
|
|
444
509
|
refineRules.push({ fn: (v) => String(v).includes(sub), message: rule.message });
|
|
445
510
|
}
|
|
446
511
|
break;
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
512
|
+
}
|
|
513
|
+
case "notContains": {
|
|
514
|
+
const rv = resolveRuleValue(rule, data);
|
|
515
|
+
if (rv !== void 0) {
|
|
516
|
+
const nsub = String(rv);
|
|
450
517
|
refineRules.push({ fn: (v) => !String(v).includes(nsub), message: rule.message });
|
|
451
518
|
}
|
|
452
519
|
break;
|
|
520
|
+
}
|
|
453
521
|
case "matches":
|
|
454
522
|
if (rule.value !== void 0 && rule.value !== null) {
|
|
455
523
|
const rx = new RegExp(String(rule.value));
|
|
@@ -487,11 +555,11 @@ function buildFieldSchema(field, externalEnums) {
|
|
|
487
555
|
}
|
|
488
556
|
return finalSchema;
|
|
489
557
|
}
|
|
490
|
-
function buildZodSchema(fields, externalEnums) {
|
|
558
|
+
function buildZodSchema(fields, externalEnums, data) {
|
|
491
559
|
const shape = {};
|
|
492
560
|
for (const resolvedField of fields) {
|
|
493
561
|
if (!resolvedField.visible) continue;
|
|
494
|
-
shape[resolvedField.definition.id] = buildFieldSchema(resolvedField.definition, externalEnums);
|
|
562
|
+
shape[resolvedField.definition.id] = buildFieldSchema(resolvedField.definition, externalEnums, data);
|
|
495
563
|
}
|
|
496
564
|
return z.object(shape);
|
|
497
565
|
}
|