@gunubin/vorm-form 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +187 -12
- package/dist/index.d.cts +46 -11
- package/dist/index.d.ts +46 -11
- package/dist/index.js +183 -12
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -21,14 +21,23 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
buildOutputValues: () => buildOutputValues,
|
|
24
|
+
createArrayField: () => createArrayField,
|
|
24
25
|
createField: () => createField,
|
|
25
26
|
createFormSchema: () => createFormSchema,
|
|
27
|
+
isArrayFieldSchema: () => isArrayFieldSchema,
|
|
28
|
+
parseFieldPath: () => parseFieldPath,
|
|
26
29
|
resolveMessage: () => resolveMessage,
|
|
30
|
+
validateArrayField: () => validateArrayField,
|
|
27
31
|
validateField: () => validateField,
|
|
28
32
|
validateForm: () => validateForm
|
|
29
33
|
});
|
|
30
34
|
module.exports = __toCommonJS(index_exports);
|
|
31
35
|
|
|
36
|
+
// src/types.ts
|
|
37
|
+
function isArrayFieldSchema(s) {
|
|
38
|
+
return "__array" in s && s.__array === true;
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
// src/create-field.ts
|
|
33
42
|
function createField(voOrConfig, options) {
|
|
34
43
|
if (voOrConfig && "create" in voOrConfig && typeof voOrConfig.create === "function") {
|
|
@@ -75,13 +84,56 @@ function mergeMessages(definition, factory) {
|
|
|
75
84
|
return { ...definition, ...factory };
|
|
76
85
|
}
|
|
77
86
|
|
|
78
|
-
// src/create-
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
// src/create-array-field.ts
|
|
88
|
+
function createArrayField(voOrConfig, options) {
|
|
89
|
+
if (voOrConfig && "create" in voOrConfig && typeof voOrConfig.create === "function") {
|
|
90
|
+
let factory2 = function(config2 = {}) {
|
|
91
|
+
return {
|
|
92
|
+
__array: true,
|
|
93
|
+
item: {
|
|
94
|
+
vo: voDef,
|
|
95
|
+
required: true,
|
|
96
|
+
messages: mergeMessages2(definitionMessages2, config2.messages),
|
|
97
|
+
rules: [...voDef.rules]
|
|
98
|
+
},
|
|
99
|
+
required: config2.required ?? false,
|
|
100
|
+
minLength: config2.minLength,
|
|
101
|
+
maxLength: config2.maxLength,
|
|
102
|
+
messages: mergeMessages2(definitionMessages2, config2.messages)
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
var factory = factory2;
|
|
106
|
+
const voDef = voOrConfig;
|
|
107
|
+
const definitionMessages2 = options?.messages;
|
|
108
|
+
return factory2;
|
|
109
|
+
}
|
|
110
|
+
const config = voOrConfig ?? {};
|
|
111
|
+
const definitionMessages = config.messages;
|
|
112
|
+
const definitionRules = config.rules ?? [];
|
|
113
|
+
function factory(factoryConfig = {}) {
|
|
114
|
+
return {
|
|
115
|
+
__array: true,
|
|
116
|
+
item: {
|
|
117
|
+
vo: null,
|
|
118
|
+
required: true,
|
|
119
|
+
messages: mergeMessages2(definitionMessages, factoryConfig.messages),
|
|
120
|
+
rules: [...definitionRules]
|
|
121
|
+
},
|
|
122
|
+
required: factoryConfig.required ?? false,
|
|
123
|
+
minLength: factoryConfig.minLength,
|
|
124
|
+
maxLength: factoryConfig.maxLength,
|
|
125
|
+
messages: mergeMessages2(definitionMessages, factoryConfig.messages)
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return factory;
|
|
129
|
+
}
|
|
130
|
+
function mergeMessages2(definition, factory) {
|
|
131
|
+
if (!definition && !factory) return {};
|
|
132
|
+
if (!definition) return factory;
|
|
133
|
+
if (!factory) return definition;
|
|
134
|
+
if (typeof factory === "function") return factory;
|
|
135
|
+
if (typeof definition === "function") return factory;
|
|
136
|
+
return { ...definition, ...factory };
|
|
85
137
|
}
|
|
86
138
|
|
|
87
139
|
// src/resolve-message.ts
|
|
@@ -124,15 +176,59 @@ function validateField(value, fieldSchema, formMessages) {
|
|
|
124
176
|
return null;
|
|
125
177
|
}
|
|
126
178
|
|
|
179
|
+
// src/validate-array-field.ts
|
|
180
|
+
function validateArrayField(value, schema, formMessages) {
|
|
181
|
+
const errors = {};
|
|
182
|
+
if (schema.required) {
|
|
183
|
+
if (value === void 0 || value === null || Array.isArray(value) && value.length === 0) {
|
|
184
|
+
const code = "REQUIRED";
|
|
185
|
+
const message = resolveMessage(code, formMessages, schema.messages);
|
|
186
|
+
errors[""] = { code, message };
|
|
187
|
+
return errors;
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
if (value === void 0 || value === null || Array.isArray(value) && value.length === 0) {
|
|
191
|
+
return errors;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (schema.minLength !== void 0 && value.length < schema.minLength) {
|
|
195
|
+
const code = "MIN_LENGTH";
|
|
196
|
+
const message = resolveMessage(code, formMessages, schema.messages);
|
|
197
|
+
errors[""] = { code, message };
|
|
198
|
+
return errors;
|
|
199
|
+
}
|
|
200
|
+
if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
|
|
201
|
+
const code = "MAX_LENGTH";
|
|
202
|
+
const message = resolveMessage(code, formMessages, schema.messages);
|
|
203
|
+
errors[""] = { code, message };
|
|
204
|
+
return errors;
|
|
205
|
+
}
|
|
206
|
+
for (let i = 0; i < value.length; i++) {
|
|
207
|
+
const itemError = validateField(value[i], schema.item, formMessages);
|
|
208
|
+
if (itemError) {
|
|
209
|
+
errors[`[${i}]`] = itemError;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return errors;
|
|
213
|
+
}
|
|
214
|
+
|
|
127
215
|
// src/validate-form.ts
|
|
128
216
|
function validateForm(values, schema) {
|
|
129
217
|
const errors = {};
|
|
130
218
|
for (const [name, fieldSchema] of Object.entries(schema.fields)) {
|
|
131
219
|
const value = values[name];
|
|
132
220
|
const formMessages = schema.messages?.[name];
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
221
|
+
if (isArrayFieldSchema(fieldSchema)) {
|
|
222
|
+
const arrayErrors = validateArrayField(value, fieldSchema, formMessages);
|
|
223
|
+
for (const [suffix, error] of Object.entries(arrayErrors)) {
|
|
224
|
+
const key = suffix === "" ? name : `${name}${suffix}`;
|
|
225
|
+
errors[key] = error;
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
const error = validateField(value, fieldSchema, formMessages);
|
|
229
|
+
if (error) {
|
|
230
|
+
errors[name] = error;
|
|
231
|
+
}
|
|
136
232
|
}
|
|
137
233
|
}
|
|
138
234
|
if (Object.keys(errors).length > 0) {
|
|
@@ -152,24 +248,103 @@ function buildOutputValues(values, fields) {
|
|
|
152
248
|
const output = {};
|
|
153
249
|
for (const [name, fieldSchema] of Object.entries(fields)) {
|
|
154
250
|
let value = values[name];
|
|
251
|
+
if (isArrayFieldSchema(fieldSchema)) {
|
|
252
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
253
|
+
output[name] = void 0;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (fieldSchema.item.vo) {
|
|
257
|
+
output[name] = value.map((item) => fieldSchema.item.vo.create(item));
|
|
258
|
+
} else {
|
|
259
|
+
output[name] = [...value];
|
|
260
|
+
}
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const fs = fieldSchema;
|
|
155
264
|
const isEmpty = value === void 0 || value === null || value === "";
|
|
156
265
|
if (isEmpty) {
|
|
157
266
|
output[name] = void 0;
|
|
158
267
|
continue;
|
|
159
268
|
}
|
|
160
|
-
if (
|
|
161
|
-
value =
|
|
269
|
+
if (fs.vo) {
|
|
270
|
+
value = fs.vo.create(value);
|
|
162
271
|
}
|
|
163
272
|
output[name] = value;
|
|
164
273
|
}
|
|
165
274
|
return output;
|
|
166
275
|
}
|
|
276
|
+
|
|
277
|
+
// src/create-form-schema.ts
|
|
278
|
+
function parseFieldPath(key) {
|
|
279
|
+
const result = [];
|
|
280
|
+
let i = 0;
|
|
281
|
+
let current = "";
|
|
282
|
+
while (i < key.length) {
|
|
283
|
+
if (key[i] === "[") {
|
|
284
|
+
if (current) {
|
|
285
|
+
result.push(current);
|
|
286
|
+
current = "";
|
|
287
|
+
}
|
|
288
|
+
i++;
|
|
289
|
+
let num = "";
|
|
290
|
+
while (i < key.length && key[i] !== "]") {
|
|
291
|
+
num += key[i];
|
|
292
|
+
i++;
|
|
293
|
+
}
|
|
294
|
+
result.push(Number(num));
|
|
295
|
+
i++;
|
|
296
|
+
} else {
|
|
297
|
+
current += key[i];
|
|
298
|
+
i++;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (current) {
|
|
302
|
+
result.push(current);
|
|
303
|
+
}
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
function createFormSchema(config) {
|
|
307
|
+
const schema = {
|
|
308
|
+
fields: config.fields,
|
|
309
|
+
messages: config.messages,
|
|
310
|
+
resolver: config.resolver,
|
|
311
|
+
"~standard": {
|
|
312
|
+
version: 1,
|
|
313
|
+
vendor: "vorm",
|
|
314
|
+
validate(value) {
|
|
315
|
+
const values = value;
|
|
316
|
+
const errors = validateForm(values, schema);
|
|
317
|
+
const errorEntries = Object.entries(errors);
|
|
318
|
+
if (errorEntries.length > 0) {
|
|
319
|
+
const issues = errorEntries.map(
|
|
320
|
+
([field, error]) => ({
|
|
321
|
+
message: error.message || error.code,
|
|
322
|
+
path: parseFieldPath(field)
|
|
323
|
+
})
|
|
324
|
+
);
|
|
325
|
+
return { issues };
|
|
326
|
+
}
|
|
327
|
+
const output = buildOutputValues(
|
|
328
|
+
values,
|
|
329
|
+
schema.fields
|
|
330
|
+
);
|
|
331
|
+
return { value: output };
|
|
332
|
+
},
|
|
333
|
+
types: void 0
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
return schema;
|
|
337
|
+
}
|
|
167
338
|
// Annotate the CommonJS export names for ESM import in node:
|
|
168
339
|
0 && (module.exports = {
|
|
169
340
|
buildOutputValues,
|
|
341
|
+
createArrayField,
|
|
170
342
|
createField,
|
|
171
343
|
createFormSchema,
|
|
344
|
+
isArrayFieldSchema,
|
|
345
|
+
parseFieldPath,
|
|
172
346
|
resolveMessage,
|
|
347
|
+
validateArrayField,
|
|
173
348
|
validateField,
|
|
174
349
|
validateForm
|
|
175
350
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VOLike, ValidationRule } from '@gunubin/vorm-core';
|
|
1
|
+
import { VOLike, ValidationRule, StandardSchemaV1 } from '@gunubin/vorm-core';
|
|
2
2
|
|
|
3
3
|
type ErrorMessageMap<C extends string = string> = {
|
|
4
4
|
[K in C]?: string;
|
|
@@ -15,26 +15,36 @@ type FieldSchema<T, TOutput, TRequired extends boolean, TCodes extends string =
|
|
|
15
15
|
parse?: (raw: string) => T;
|
|
16
16
|
format?: (value: T) => string;
|
|
17
17
|
};
|
|
18
|
+
type ArrayFieldSchema<T, TOutput, TRequired extends boolean, TCodes extends string = string> = {
|
|
19
|
+
__array: true;
|
|
20
|
+
item: FieldSchema<T, TOutput, true, TCodes>;
|
|
21
|
+
required: TRequired;
|
|
22
|
+
minLength?: number;
|
|
23
|
+
maxLength?: number;
|
|
24
|
+
messages: ErrorMessages<TCodes | 'REQUIRED' | 'MIN_LENGTH' | 'MAX_LENGTH'>;
|
|
25
|
+
};
|
|
26
|
+
type AnyFieldSchema = FieldSchema<any, any, boolean, any> | ArrayFieldSchema<any, any, boolean, any>;
|
|
27
|
+
declare function isArrayFieldSchema(s: AnyFieldSchema): s is ArrayFieldSchema<any, any, boolean, any>;
|
|
18
28
|
type FieldError = {
|
|
19
29
|
code: string;
|
|
20
30
|
message: string;
|
|
21
31
|
};
|
|
22
32
|
type FormErrors = Record<string, FieldError>;
|
|
23
|
-
type FormSchemaConfig<TFields extends Record<string,
|
|
33
|
+
type FormSchemaConfig<TFields extends Record<string, AnyFieldSchema>> = {
|
|
24
34
|
fields: TFields;
|
|
25
35
|
messages?: Record<string, ErrorMessages>;
|
|
26
36
|
resolver?: (values: FormInputValues<TFields>) => FormErrors | null;
|
|
27
37
|
};
|
|
28
|
-
type FormSchema<TFields extends Record<string,
|
|
38
|
+
type FormSchema<TFields extends Record<string, AnyFieldSchema>> = StandardSchemaV1<FormInputValues<TFields>, FormOutputValues<TFields>> & {
|
|
29
39
|
fields: TFields;
|
|
30
40
|
messages?: Record<string, ErrorMessages>;
|
|
31
41
|
resolver?: (values: FormInputValues<TFields>) => FormErrors | null;
|
|
32
42
|
};
|
|
33
|
-
type FormInputValues<TFields extends Record<string,
|
|
34
|
-
[K in keyof TFields]: TFields[K] extends FieldSchema<infer TInput, any, infer TRequired, any> ? TRequired extends true ? TInput : TInput | undefined : never;
|
|
43
|
+
type FormInputValues<TFields extends Record<string, AnyFieldSchema>> = {
|
|
44
|
+
[K in keyof TFields]: TFields[K] extends ArrayFieldSchema<infer TInput, any, infer TRequired, any> ? TRequired extends true ? TInput[] : TInput[] | undefined : TFields[K] extends FieldSchema<infer TInput, any, infer TRequired, any> ? TRequired extends true ? TInput : TInput | undefined : never;
|
|
35
45
|
};
|
|
36
|
-
type FormOutputValues<TFields extends Record<string,
|
|
37
|
-
[K in keyof TFields]: TFields[K] extends FieldSchema<any, infer TOutput, infer TRequired, any> ? TRequired extends true ? TOutput : TOutput | undefined : never;
|
|
46
|
+
type FormOutputValues<TFields extends Record<string, AnyFieldSchema>> = {
|
|
47
|
+
[K in keyof TFields]: TFields[K] extends ArrayFieldSchema<any, infer TOutput, infer TRequired, any> ? TRequired extends true ? TOutput[] : TOutput[] | undefined : TFields[K] extends FieldSchema<any, infer TOutput, infer TRequired, any> ? TRequired extends true ? TOutput : TOutput | undefined : never;
|
|
38
48
|
};
|
|
39
49
|
|
|
40
50
|
type FieldFactory<TInput, TOutput, TCodes extends string> = {
|
|
@@ -59,14 +69,39 @@ declare function createField<T, TOut = T, TCodes extends string = string>(config
|
|
|
59
69
|
format?: (value: T) => string;
|
|
60
70
|
}): FieldFactory<T, TOut, TCodes>;
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
type ArrayFieldFactory<TInput, TOutput, TCodes extends string> = {
|
|
73
|
+
(config: {
|
|
74
|
+
required: true;
|
|
75
|
+
minLength?: number;
|
|
76
|
+
maxLength?: number;
|
|
77
|
+
messages?: ErrorMessages<TCodes | 'REQUIRED' | 'MIN_LENGTH' | 'MAX_LENGTH'>;
|
|
78
|
+
}): ArrayFieldSchema<TInput, TOutput, true, TCodes>;
|
|
79
|
+
(config?: {
|
|
80
|
+
required?: false;
|
|
81
|
+
minLength?: number;
|
|
82
|
+
maxLength?: number;
|
|
83
|
+
messages?: ErrorMessages<TCodes | 'MIN_LENGTH' | 'MAX_LENGTH'>;
|
|
84
|
+
}): ArrayFieldSchema<TInput, TOutput, false, TCodes>;
|
|
85
|
+
};
|
|
86
|
+
declare function createArrayField<TInput, TOutput, TCodes extends string>(vo: VOLike<TInput, TOutput, TCodes>, options?: {
|
|
87
|
+
messages?: ErrorMessages<TCodes>;
|
|
88
|
+
}): ArrayFieldFactory<TInput, TOutput, TCodes>;
|
|
89
|
+
declare function createArrayField<T, TOut = T, TCodes extends string = string>(config?: {
|
|
90
|
+
rules?: ValidationRule<T, TCodes>[];
|
|
91
|
+
messages?: ErrorMessages<TCodes>;
|
|
92
|
+
}): ArrayFieldFactory<T, TOut, TCodes>;
|
|
93
|
+
|
|
94
|
+
declare function parseFieldPath(key: string): (string | number)[];
|
|
95
|
+
declare function createFormSchema<TFields extends Record<string, AnyFieldSchema>>(config: FormSchemaConfig<TFields>): FormSchema<TFields>;
|
|
63
96
|
|
|
64
97
|
declare function validateField<T>(value: T | undefined | null, fieldSchema: FieldSchema<T, any, boolean, any>, formMessages?: ErrorMessages): FieldError | null;
|
|
65
98
|
|
|
66
|
-
declare function
|
|
99
|
+
declare function validateArrayField(value: unknown[] | undefined | null, schema: ArrayFieldSchema<any, any, boolean, any>, formMessages?: ErrorMessages): Record<string, FieldError>;
|
|
100
|
+
|
|
101
|
+
declare function validateForm<TFields extends Record<string, AnyFieldSchema>>(values: FormInputValues<TFields>, schema: FormSchema<TFields>): FormErrors;
|
|
67
102
|
|
|
68
|
-
declare function buildOutputValues(values: Record<string, unknown>, fields: Record<string,
|
|
103
|
+
declare function buildOutputValues(values: Record<string, unknown>, fields: Record<string, AnyFieldSchema>): Record<string, unknown>;
|
|
69
104
|
|
|
70
105
|
declare function resolveMessage(code: string, ...messageSources: (ErrorMessages | undefined)[]): string;
|
|
71
106
|
|
|
72
|
-
export { type ErrorMessageFn, type ErrorMessageMap, type ErrorMessages, type FieldError, type FieldSchema, type FormErrors, type FormInputValues, type FormOutputValues, type FormSchema, type FormSchemaConfig, buildOutputValues, createField, createFormSchema, resolveMessage, validateField, validateForm };
|
|
107
|
+
export { type AnyFieldSchema, type ArrayFieldSchema, type ErrorMessageFn, type ErrorMessageMap, type ErrorMessages, type FieldError, type FieldSchema, type FormErrors, type FormInputValues, type FormOutputValues, type FormSchema, type FormSchemaConfig, buildOutputValues, createArrayField, createField, createFormSchema, isArrayFieldSchema, parseFieldPath, resolveMessage, validateArrayField, validateField, validateForm };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VOLike, ValidationRule } from '@gunubin/vorm-core';
|
|
1
|
+
import { VOLike, ValidationRule, StandardSchemaV1 } from '@gunubin/vorm-core';
|
|
2
2
|
|
|
3
3
|
type ErrorMessageMap<C extends string = string> = {
|
|
4
4
|
[K in C]?: string;
|
|
@@ -15,26 +15,36 @@ type FieldSchema<T, TOutput, TRequired extends boolean, TCodes extends string =
|
|
|
15
15
|
parse?: (raw: string) => T;
|
|
16
16
|
format?: (value: T) => string;
|
|
17
17
|
};
|
|
18
|
+
type ArrayFieldSchema<T, TOutput, TRequired extends boolean, TCodes extends string = string> = {
|
|
19
|
+
__array: true;
|
|
20
|
+
item: FieldSchema<T, TOutput, true, TCodes>;
|
|
21
|
+
required: TRequired;
|
|
22
|
+
minLength?: number;
|
|
23
|
+
maxLength?: number;
|
|
24
|
+
messages: ErrorMessages<TCodes | 'REQUIRED' | 'MIN_LENGTH' | 'MAX_LENGTH'>;
|
|
25
|
+
};
|
|
26
|
+
type AnyFieldSchema = FieldSchema<any, any, boolean, any> | ArrayFieldSchema<any, any, boolean, any>;
|
|
27
|
+
declare function isArrayFieldSchema(s: AnyFieldSchema): s is ArrayFieldSchema<any, any, boolean, any>;
|
|
18
28
|
type FieldError = {
|
|
19
29
|
code: string;
|
|
20
30
|
message: string;
|
|
21
31
|
};
|
|
22
32
|
type FormErrors = Record<string, FieldError>;
|
|
23
|
-
type FormSchemaConfig<TFields extends Record<string,
|
|
33
|
+
type FormSchemaConfig<TFields extends Record<string, AnyFieldSchema>> = {
|
|
24
34
|
fields: TFields;
|
|
25
35
|
messages?: Record<string, ErrorMessages>;
|
|
26
36
|
resolver?: (values: FormInputValues<TFields>) => FormErrors | null;
|
|
27
37
|
};
|
|
28
|
-
type FormSchema<TFields extends Record<string,
|
|
38
|
+
type FormSchema<TFields extends Record<string, AnyFieldSchema>> = StandardSchemaV1<FormInputValues<TFields>, FormOutputValues<TFields>> & {
|
|
29
39
|
fields: TFields;
|
|
30
40
|
messages?: Record<string, ErrorMessages>;
|
|
31
41
|
resolver?: (values: FormInputValues<TFields>) => FormErrors | null;
|
|
32
42
|
};
|
|
33
|
-
type FormInputValues<TFields extends Record<string,
|
|
34
|
-
[K in keyof TFields]: TFields[K] extends FieldSchema<infer TInput, any, infer TRequired, any> ? TRequired extends true ? TInput : TInput | undefined : never;
|
|
43
|
+
type FormInputValues<TFields extends Record<string, AnyFieldSchema>> = {
|
|
44
|
+
[K in keyof TFields]: TFields[K] extends ArrayFieldSchema<infer TInput, any, infer TRequired, any> ? TRequired extends true ? TInput[] : TInput[] | undefined : TFields[K] extends FieldSchema<infer TInput, any, infer TRequired, any> ? TRequired extends true ? TInput : TInput | undefined : never;
|
|
35
45
|
};
|
|
36
|
-
type FormOutputValues<TFields extends Record<string,
|
|
37
|
-
[K in keyof TFields]: TFields[K] extends FieldSchema<any, infer TOutput, infer TRequired, any> ? TRequired extends true ? TOutput : TOutput | undefined : never;
|
|
46
|
+
type FormOutputValues<TFields extends Record<string, AnyFieldSchema>> = {
|
|
47
|
+
[K in keyof TFields]: TFields[K] extends ArrayFieldSchema<any, infer TOutput, infer TRequired, any> ? TRequired extends true ? TOutput[] : TOutput[] | undefined : TFields[K] extends FieldSchema<any, infer TOutput, infer TRequired, any> ? TRequired extends true ? TOutput : TOutput | undefined : never;
|
|
38
48
|
};
|
|
39
49
|
|
|
40
50
|
type FieldFactory<TInput, TOutput, TCodes extends string> = {
|
|
@@ -59,14 +69,39 @@ declare function createField<T, TOut = T, TCodes extends string = string>(config
|
|
|
59
69
|
format?: (value: T) => string;
|
|
60
70
|
}): FieldFactory<T, TOut, TCodes>;
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
type ArrayFieldFactory<TInput, TOutput, TCodes extends string> = {
|
|
73
|
+
(config: {
|
|
74
|
+
required: true;
|
|
75
|
+
minLength?: number;
|
|
76
|
+
maxLength?: number;
|
|
77
|
+
messages?: ErrorMessages<TCodes | 'REQUIRED' | 'MIN_LENGTH' | 'MAX_LENGTH'>;
|
|
78
|
+
}): ArrayFieldSchema<TInput, TOutput, true, TCodes>;
|
|
79
|
+
(config?: {
|
|
80
|
+
required?: false;
|
|
81
|
+
minLength?: number;
|
|
82
|
+
maxLength?: number;
|
|
83
|
+
messages?: ErrorMessages<TCodes | 'MIN_LENGTH' | 'MAX_LENGTH'>;
|
|
84
|
+
}): ArrayFieldSchema<TInput, TOutput, false, TCodes>;
|
|
85
|
+
};
|
|
86
|
+
declare function createArrayField<TInput, TOutput, TCodes extends string>(vo: VOLike<TInput, TOutput, TCodes>, options?: {
|
|
87
|
+
messages?: ErrorMessages<TCodes>;
|
|
88
|
+
}): ArrayFieldFactory<TInput, TOutput, TCodes>;
|
|
89
|
+
declare function createArrayField<T, TOut = T, TCodes extends string = string>(config?: {
|
|
90
|
+
rules?: ValidationRule<T, TCodes>[];
|
|
91
|
+
messages?: ErrorMessages<TCodes>;
|
|
92
|
+
}): ArrayFieldFactory<T, TOut, TCodes>;
|
|
93
|
+
|
|
94
|
+
declare function parseFieldPath(key: string): (string | number)[];
|
|
95
|
+
declare function createFormSchema<TFields extends Record<string, AnyFieldSchema>>(config: FormSchemaConfig<TFields>): FormSchema<TFields>;
|
|
63
96
|
|
|
64
97
|
declare function validateField<T>(value: T | undefined | null, fieldSchema: FieldSchema<T, any, boolean, any>, formMessages?: ErrorMessages): FieldError | null;
|
|
65
98
|
|
|
66
|
-
declare function
|
|
99
|
+
declare function validateArrayField(value: unknown[] | undefined | null, schema: ArrayFieldSchema<any, any, boolean, any>, formMessages?: ErrorMessages): Record<string, FieldError>;
|
|
100
|
+
|
|
101
|
+
declare function validateForm<TFields extends Record<string, AnyFieldSchema>>(values: FormInputValues<TFields>, schema: FormSchema<TFields>): FormErrors;
|
|
67
102
|
|
|
68
|
-
declare function buildOutputValues(values: Record<string, unknown>, fields: Record<string,
|
|
103
|
+
declare function buildOutputValues(values: Record<string, unknown>, fields: Record<string, AnyFieldSchema>): Record<string, unknown>;
|
|
69
104
|
|
|
70
105
|
declare function resolveMessage(code: string, ...messageSources: (ErrorMessages | undefined)[]): string;
|
|
71
106
|
|
|
72
|
-
export { type ErrorMessageFn, type ErrorMessageMap, type ErrorMessages, type FieldError, type FieldSchema, type FormErrors, type FormInputValues, type FormOutputValues, type FormSchema, type FormSchemaConfig, buildOutputValues, createField, createFormSchema, resolveMessage, validateField, validateForm };
|
|
107
|
+
export { type AnyFieldSchema, type ArrayFieldSchema, type ErrorMessageFn, type ErrorMessageMap, type ErrorMessages, type FieldError, type FieldSchema, type FormErrors, type FormInputValues, type FormOutputValues, type FormSchema, type FormSchemaConfig, buildOutputValues, createArrayField, createField, createFormSchema, isArrayFieldSchema, parseFieldPath, resolveMessage, validateArrayField, validateField, validateForm };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
function isArrayFieldSchema(s) {
|
|
3
|
+
return "__array" in s && s.__array === true;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
// src/create-field.ts
|
|
2
7
|
function createField(voOrConfig, options) {
|
|
3
8
|
if (voOrConfig && "create" in voOrConfig && typeof voOrConfig.create === "function") {
|
|
@@ -44,13 +49,56 @@ function mergeMessages(definition, factory) {
|
|
|
44
49
|
return { ...definition, ...factory };
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
// src/create-
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
// src/create-array-field.ts
|
|
53
|
+
function createArrayField(voOrConfig, options) {
|
|
54
|
+
if (voOrConfig && "create" in voOrConfig && typeof voOrConfig.create === "function") {
|
|
55
|
+
let factory2 = function(config2 = {}) {
|
|
56
|
+
return {
|
|
57
|
+
__array: true,
|
|
58
|
+
item: {
|
|
59
|
+
vo: voDef,
|
|
60
|
+
required: true,
|
|
61
|
+
messages: mergeMessages2(definitionMessages2, config2.messages),
|
|
62
|
+
rules: [...voDef.rules]
|
|
63
|
+
},
|
|
64
|
+
required: config2.required ?? false,
|
|
65
|
+
minLength: config2.minLength,
|
|
66
|
+
maxLength: config2.maxLength,
|
|
67
|
+
messages: mergeMessages2(definitionMessages2, config2.messages)
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
var factory = factory2;
|
|
71
|
+
const voDef = voOrConfig;
|
|
72
|
+
const definitionMessages2 = options?.messages;
|
|
73
|
+
return factory2;
|
|
74
|
+
}
|
|
75
|
+
const config = voOrConfig ?? {};
|
|
76
|
+
const definitionMessages = config.messages;
|
|
77
|
+
const definitionRules = config.rules ?? [];
|
|
78
|
+
function factory(factoryConfig = {}) {
|
|
79
|
+
return {
|
|
80
|
+
__array: true,
|
|
81
|
+
item: {
|
|
82
|
+
vo: null,
|
|
83
|
+
required: true,
|
|
84
|
+
messages: mergeMessages2(definitionMessages, factoryConfig.messages),
|
|
85
|
+
rules: [...definitionRules]
|
|
86
|
+
},
|
|
87
|
+
required: factoryConfig.required ?? false,
|
|
88
|
+
minLength: factoryConfig.minLength,
|
|
89
|
+
maxLength: factoryConfig.maxLength,
|
|
90
|
+
messages: mergeMessages2(definitionMessages, factoryConfig.messages)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return factory;
|
|
94
|
+
}
|
|
95
|
+
function mergeMessages2(definition, factory) {
|
|
96
|
+
if (!definition && !factory) return {};
|
|
97
|
+
if (!definition) return factory;
|
|
98
|
+
if (!factory) return definition;
|
|
99
|
+
if (typeof factory === "function") return factory;
|
|
100
|
+
if (typeof definition === "function") return factory;
|
|
101
|
+
return { ...definition, ...factory };
|
|
54
102
|
}
|
|
55
103
|
|
|
56
104
|
// src/resolve-message.ts
|
|
@@ -93,15 +141,59 @@ function validateField(value, fieldSchema, formMessages) {
|
|
|
93
141
|
return null;
|
|
94
142
|
}
|
|
95
143
|
|
|
144
|
+
// src/validate-array-field.ts
|
|
145
|
+
function validateArrayField(value, schema, formMessages) {
|
|
146
|
+
const errors = {};
|
|
147
|
+
if (schema.required) {
|
|
148
|
+
if (value === void 0 || value === null || Array.isArray(value) && value.length === 0) {
|
|
149
|
+
const code = "REQUIRED";
|
|
150
|
+
const message = resolveMessage(code, formMessages, schema.messages);
|
|
151
|
+
errors[""] = { code, message };
|
|
152
|
+
return errors;
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
if (value === void 0 || value === null || Array.isArray(value) && value.length === 0) {
|
|
156
|
+
return errors;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (schema.minLength !== void 0 && value.length < schema.minLength) {
|
|
160
|
+
const code = "MIN_LENGTH";
|
|
161
|
+
const message = resolveMessage(code, formMessages, schema.messages);
|
|
162
|
+
errors[""] = { code, message };
|
|
163
|
+
return errors;
|
|
164
|
+
}
|
|
165
|
+
if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
|
|
166
|
+
const code = "MAX_LENGTH";
|
|
167
|
+
const message = resolveMessage(code, formMessages, schema.messages);
|
|
168
|
+
errors[""] = { code, message };
|
|
169
|
+
return errors;
|
|
170
|
+
}
|
|
171
|
+
for (let i = 0; i < value.length; i++) {
|
|
172
|
+
const itemError = validateField(value[i], schema.item, formMessages);
|
|
173
|
+
if (itemError) {
|
|
174
|
+
errors[`[${i}]`] = itemError;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return errors;
|
|
178
|
+
}
|
|
179
|
+
|
|
96
180
|
// src/validate-form.ts
|
|
97
181
|
function validateForm(values, schema) {
|
|
98
182
|
const errors = {};
|
|
99
183
|
for (const [name, fieldSchema] of Object.entries(schema.fields)) {
|
|
100
184
|
const value = values[name];
|
|
101
185
|
const formMessages = schema.messages?.[name];
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
186
|
+
if (isArrayFieldSchema(fieldSchema)) {
|
|
187
|
+
const arrayErrors = validateArrayField(value, fieldSchema, formMessages);
|
|
188
|
+
for (const [suffix, error] of Object.entries(arrayErrors)) {
|
|
189
|
+
const key = suffix === "" ? name : `${name}${suffix}`;
|
|
190
|
+
errors[key] = error;
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
const error = validateField(value, fieldSchema, formMessages);
|
|
194
|
+
if (error) {
|
|
195
|
+
errors[name] = error;
|
|
196
|
+
}
|
|
105
197
|
}
|
|
106
198
|
}
|
|
107
199
|
if (Object.keys(errors).length > 0) {
|
|
@@ -121,23 +213,102 @@ function buildOutputValues(values, fields) {
|
|
|
121
213
|
const output = {};
|
|
122
214
|
for (const [name, fieldSchema] of Object.entries(fields)) {
|
|
123
215
|
let value = values[name];
|
|
216
|
+
if (isArrayFieldSchema(fieldSchema)) {
|
|
217
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
218
|
+
output[name] = void 0;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (fieldSchema.item.vo) {
|
|
222
|
+
output[name] = value.map((item) => fieldSchema.item.vo.create(item));
|
|
223
|
+
} else {
|
|
224
|
+
output[name] = [...value];
|
|
225
|
+
}
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const fs = fieldSchema;
|
|
124
229
|
const isEmpty = value === void 0 || value === null || value === "";
|
|
125
230
|
if (isEmpty) {
|
|
126
231
|
output[name] = void 0;
|
|
127
232
|
continue;
|
|
128
233
|
}
|
|
129
|
-
if (
|
|
130
|
-
value =
|
|
234
|
+
if (fs.vo) {
|
|
235
|
+
value = fs.vo.create(value);
|
|
131
236
|
}
|
|
132
237
|
output[name] = value;
|
|
133
238
|
}
|
|
134
239
|
return output;
|
|
135
240
|
}
|
|
241
|
+
|
|
242
|
+
// src/create-form-schema.ts
|
|
243
|
+
function parseFieldPath(key) {
|
|
244
|
+
const result = [];
|
|
245
|
+
let i = 0;
|
|
246
|
+
let current = "";
|
|
247
|
+
while (i < key.length) {
|
|
248
|
+
if (key[i] === "[") {
|
|
249
|
+
if (current) {
|
|
250
|
+
result.push(current);
|
|
251
|
+
current = "";
|
|
252
|
+
}
|
|
253
|
+
i++;
|
|
254
|
+
let num = "";
|
|
255
|
+
while (i < key.length && key[i] !== "]") {
|
|
256
|
+
num += key[i];
|
|
257
|
+
i++;
|
|
258
|
+
}
|
|
259
|
+
result.push(Number(num));
|
|
260
|
+
i++;
|
|
261
|
+
} else {
|
|
262
|
+
current += key[i];
|
|
263
|
+
i++;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (current) {
|
|
267
|
+
result.push(current);
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
function createFormSchema(config) {
|
|
272
|
+
const schema = {
|
|
273
|
+
fields: config.fields,
|
|
274
|
+
messages: config.messages,
|
|
275
|
+
resolver: config.resolver,
|
|
276
|
+
"~standard": {
|
|
277
|
+
version: 1,
|
|
278
|
+
vendor: "vorm",
|
|
279
|
+
validate(value) {
|
|
280
|
+
const values = value;
|
|
281
|
+
const errors = validateForm(values, schema);
|
|
282
|
+
const errorEntries = Object.entries(errors);
|
|
283
|
+
if (errorEntries.length > 0) {
|
|
284
|
+
const issues = errorEntries.map(
|
|
285
|
+
([field, error]) => ({
|
|
286
|
+
message: error.message || error.code,
|
|
287
|
+
path: parseFieldPath(field)
|
|
288
|
+
})
|
|
289
|
+
);
|
|
290
|
+
return { issues };
|
|
291
|
+
}
|
|
292
|
+
const output = buildOutputValues(
|
|
293
|
+
values,
|
|
294
|
+
schema.fields
|
|
295
|
+
);
|
|
296
|
+
return { value: output };
|
|
297
|
+
},
|
|
298
|
+
types: void 0
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
return schema;
|
|
302
|
+
}
|
|
136
303
|
export {
|
|
137
304
|
buildOutputValues,
|
|
305
|
+
createArrayField,
|
|
138
306
|
createField,
|
|
139
307
|
createFormSchema,
|
|
308
|
+
isArrayFieldSchema,
|
|
309
|
+
parseFieldPath,
|
|
140
310
|
resolveMessage,
|
|
311
|
+
validateArrayField,
|
|
141
312
|
validateField,
|
|
142
313
|
validateForm
|
|
143
314
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gunubin/vorm-form",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Field schemas, form validation, and output building for vorm",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"form",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@gunubin/vorm-core": "0.
|
|
41
|
+
"@gunubin/vorm-core": "0.3.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"typescript": "^5.9.3",
|