@gunubin/vorm-form 0.2.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,15 +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,
26
- formToStandardSchema: () => formToStandardSchema,
27
+ isArrayFieldSchema: () => isArrayFieldSchema,
28
+ parseFieldPath: () => parseFieldPath,
27
29
  resolveMessage: () => resolveMessage,
30
+ validateArrayField: () => validateArrayField,
28
31
  validateField: () => validateField,
29
32
  validateForm: () => validateForm
30
33
  });
31
34
  module.exports = __toCommonJS(index_exports);
32
35
 
36
+ // src/types.ts
37
+ function isArrayFieldSchema(s) {
38
+ return "__array" in s && s.__array === true;
39
+ }
40
+
33
41
  // src/create-field.ts
34
42
  function createField(voOrConfig, options) {
35
43
  if (voOrConfig && "create" in voOrConfig && typeof voOrConfig.create === "function") {
@@ -76,13 +84,56 @@ function mergeMessages(definition, factory) {
76
84
  return { ...definition, ...factory };
77
85
  }
78
86
 
79
- // src/create-form-schema.ts
80
- function createFormSchema(config) {
81
- return {
82
- fields: config.fields,
83
- messages: config.messages,
84
- resolver: config.resolver
85
- };
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 };
86
137
  }
87
138
 
88
139
  // src/resolve-message.ts
@@ -125,15 +176,59 @@ function validateField(value, fieldSchema, formMessages) {
125
176
  return null;
126
177
  }
127
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
+
128
215
  // src/validate-form.ts
129
216
  function validateForm(values, schema) {
130
217
  const errors = {};
131
218
  for (const [name, fieldSchema] of Object.entries(schema.fields)) {
132
219
  const value = values[name];
133
220
  const formMessages = schema.messages?.[name];
134
- const error = validateField(value, fieldSchema, formMessages);
135
- if (error) {
136
- 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
+ }
137
232
  }
138
233
  }
139
234
  if (Object.keys(errors).length > 0) {
@@ -153,22 +248,66 @@ function buildOutputValues(values, fields) {
153
248
  const output = {};
154
249
  for (const [name, fieldSchema] of Object.entries(fields)) {
155
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;
156
264
  const isEmpty = value === void 0 || value === null || value === "";
157
265
  if (isEmpty) {
158
266
  output[name] = void 0;
159
267
  continue;
160
268
  }
161
- if (fieldSchema.vo) {
162
- value = fieldSchema.vo.create(value);
269
+ if (fs.vo) {
270
+ value = fs.vo.create(value);
163
271
  }
164
272
  output[name] = value;
165
273
  }
166
274
  return output;
167
275
  }
168
276
 
169
- // src/standard-schema.ts
170
- function formToStandardSchema(schema) {
171
- return {
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,
172
311
  "~standard": {
173
312
  version: 1,
174
313
  vendor: "vorm",
@@ -180,7 +319,7 @@ function formToStandardSchema(schema) {
180
319
  const issues = errorEntries.map(
181
320
  ([field, error]) => ({
182
321
  message: error.message || error.code,
183
- path: [field]
322
+ path: parseFieldPath(field)
184
323
  })
185
324
  );
186
325
  return { issues };
@@ -194,14 +333,18 @@ function formToStandardSchema(schema) {
194
333
  types: void 0
195
334
  }
196
335
  };
336
+ return schema;
197
337
  }
198
338
  // Annotate the CommonJS export names for ESM import in node:
199
339
  0 && (module.exports = {
200
340
  buildOutputValues,
341
+ createArrayField,
201
342
  createField,
202
343
  createFormSchema,
203
- formToStandardSchema,
344
+ isArrayFieldSchema,
345
+ parseFieldPath,
204
346
  resolveMessage,
347
+ validateArrayField,
205
348
  validateField,
206
349
  validateForm
207
350
  });
package/dist/index.d.cts CHANGED
@@ -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,24 +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>;
67
100
 
68
- declare function buildOutputValues(values: Record<string, unknown>, fields: Record<string, FieldSchema<any, any, boolean, any>>): Record<string, unknown>;
101
+ declare function validateForm<TFields extends Record<string, AnyFieldSchema>>(values: FormInputValues<TFields>, schema: FormSchema<TFields>): FormErrors;
69
102
 
70
- declare function resolveMessage(code: string, ...messageSources: (ErrorMessages | undefined)[]): string;
103
+ declare function buildOutputValues(values: Record<string, unknown>, fields: Record<string, AnyFieldSchema>): Record<string, unknown>;
71
104
 
72
- type AnyFields = Record<string, FieldSchema<any, any, boolean, any>>;
73
- /**
74
- * Wraps a FormSchema as a Standard Schema v1 compliant object.
75
- *
76
- * - Input: FormInputValues (plain types, e.g. { email: string; password: string })
77
- * - Output: FormOutputValues (branded types, e.g. { email: Email; password: Password })
78
- * - On failure, returns issues with field-level path segments
79
- */
80
- declare function formToStandardSchema<TFields extends AnyFields>(schema: FormSchema<TFields>): StandardSchemaV1<FormInputValues<TFields>, FormOutputValues<TFields>>;
105
+ declare function resolveMessage(code: string, ...messageSources: (ErrorMessages | undefined)[]): string;
81
106
 
82
- export { type ErrorMessageFn, type ErrorMessageMap, type ErrorMessages, type FieldError, type FieldSchema, type FormErrors, type FormInputValues, type FormOutputValues, type FormSchema, type FormSchemaConfig, buildOutputValues, createField, createFormSchema, formToStandardSchema, 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
@@ -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,24 +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>;
67
100
 
68
- declare function buildOutputValues(values: Record<string, unknown>, fields: Record<string, FieldSchema<any, any, boolean, any>>): Record<string, unknown>;
101
+ declare function validateForm<TFields extends Record<string, AnyFieldSchema>>(values: FormInputValues<TFields>, schema: FormSchema<TFields>): FormErrors;
69
102
 
70
- declare function resolveMessage(code: string, ...messageSources: (ErrorMessages | undefined)[]): string;
103
+ declare function buildOutputValues(values: Record<string, unknown>, fields: Record<string, AnyFieldSchema>): Record<string, unknown>;
71
104
 
72
- type AnyFields = Record<string, FieldSchema<any, any, boolean, any>>;
73
- /**
74
- * Wraps a FormSchema as a Standard Schema v1 compliant object.
75
- *
76
- * - Input: FormInputValues (plain types, e.g. { email: string; password: string })
77
- * - Output: FormOutputValues (branded types, e.g. { email: Email; password: Password })
78
- * - On failure, returns issues with field-level path segments
79
- */
80
- declare function formToStandardSchema<TFields extends AnyFields>(schema: FormSchema<TFields>): StandardSchemaV1<FormInputValues<TFields>, FormOutputValues<TFields>>;
105
+ declare function resolveMessage(code: string, ...messageSources: (ErrorMessages | undefined)[]): string;
81
106
 
82
- export { type ErrorMessageFn, type ErrorMessageMap, type ErrorMessages, type FieldError, type FieldSchema, type FormErrors, type FormInputValues, type FormOutputValues, type FormSchema, type FormSchemaConfig, buildOutputValues, createField, createFormSchema, formToStandardSchema, 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,22 +213,66 @@ 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
  }
136
241
 
137
- // src/standard-schema.ts
138
- function formToStandardSchema(schema) {
139
- return {
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,
140
276
  "~standard": {
141
277
  version: 1,
142
278
  vendor: "vorm",
@@ -148,7 +284,7 @@ function formToStandardSchema(schema) {
148
284
  const issues = errorEntries.map(
149
285
  ([field, error]) => ({
150
286
  message: error.message || error.code,
151
- path: [field]
287
+ path: parseFieldPath(field)
152
288
  })
153
289
  );
154
290
  return { issues };
@@ -162,13 +298,17 @@ function formToStandardSchema(schema) {
162
298
  types: void 0
163
299
  }
164
300
  };
301
+ return schema;
165
302
  }
166
303
  export {
167
304
  buildOutputValues,
305
+ createArrayField,
168
306
  createField,
169
307
  createFormSchema,
170
- formToStandardSchema,
308
+ isArrayFieldSchema,
309
+ parseFieldPath,
171
310
  resolveMessage,
311
+ validateArrayField,
172
312
  validateField,
173
313
  validateForm
174
314
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gunubin/vorm-form",
3
- "version": "0.2.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",