@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 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-form-schema.ts
79
- function createFormSchema(config) {
80
- return {
81
- fields: config.fields,
82
- messages: config.messages,
83
- resolver: config.resolver
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
- const error = validateField(value, fieldSchema, formMessages);
134
- if (error) {
135
- errors[name] = error;
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 (fieldSchema.vo) {
161
- value = fieldSchema.vo.create(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, FieldSchema<any, any, boolean, any>>> = {
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, FieldSchema<any, any, boolean, any>>> = {
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, FieldSchema<any, any, boolean, any>>> = {
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, FieldSchema<any, any, boolean, any>>> = {
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
- declare function createFormSchema<TFields extends Record<string, FieldSchema<any, any, boolean, any>>>(config: FormSchemaConfig<TFields>): FormSchema<TFields>;
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 validateForm<TFields extends Record<string, FieldSchema<any, any, boolean, any>>>(values: FormInputValues<TFields>, schema: FormSchema<TFields>): FormErrors;
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, FieldSchema<any, any, boolean, any>>): Record<string, unknown>;
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, FieldSchema<any, any, boolean, any>>> = {
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, FieldSchema<any, any, boolean, any>>> = {
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, FieldSchema<any, any, boolean, any>>> = {
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, FieldSchema<any, any, boolean, any>>> = {
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
- declare function createFormSchema<TFields extends Record<string, FieldSchema<any, any, boolean, any>>>(config: FormSchemaConfig<TFields>): FormSchema<TFields>;
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 validateForm<TFields extends Record<string, FieldSchema<any, any, boolean, any>>>(values: FormInputValues<TFields>, schema: FormSchema<TFields>): FormErrors;
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, FieldSchema<any, any, boolean, any>>): Record<string, unknown>;
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-form-schema.ts
48
- function createFormSchema(config) {
49
- return {
50
- fields: config.fields,
51
- messages: config.messages,
52
- resolver: config.resolver
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
- const error = validateField(value, fieldSchema, formMessages);
103
- if (error) {
104
- errors[name] = error;
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 (fieldSchema.vo) {
130
- value = fieldSchema.vo.create(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.1.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.2.0"
41
+ "@gunubin/vorm-core": "0.3.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "typescript": "^5.9.3",