adaptform 2.0.5 → 2.1.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
@@ -20,8 +20,9 @@ var Field = class {
20
20
  this._valueClear = null;
21
21
  this.isValid = null;
22
22
  }
23
+ this.isTouched = false;
23
24
  }
24
- // ========== Геттеры/сеттеры ==========
25
+ // ========== Getters and setters ==========
25
26
  get plugins() {
26
27
  return [...this.options.plugins ?? []].sort((a, b) => {
27
28
  return (a.pluginOptions?.priority ?? 0) - (b.pluginOptions?.priority ?? 0);
@@ -31,13 +32,26 @@ var Field = class {
31
32
  return this.fieldType.options;
32
33
  }
33
34
  set options(options) {
34
- let cur_options = this.fieldType.options;
35
- this.fieldType.options = Object.assign(cur_options, options);
35
+ const currentOptions = this.fieldType.options;
36
+ this.fieldType.options = Object.assign(currentOptions, options);
36
37
  }
37
38
  get rawValue() {
38
39
  return this._rawValue;
39
40
  }
40
41
  set rawValue(input) {
42
+ this.setRawValue(input);
43
+ }
44
+ setRawValue(input, validate = true) {
45
+ this.assignRawValue(input);
46
+ this._errors = [];
47
+ this.isTouched = true;
48
+ if (validate) {
49
+ this.checkAndSetValid();
50
+ } else {
51
+ this.isValid = null;
52
+ }
53
+ }
54
+ assignRawValue(input) {
41
55
  let rawValue = input;
42
56
  for (const plugin of this.plugins) {
43
57
  if (plugin.toRawValue) {
@@ -52,9 +66,6 @@ var Field = class {
52
66
  }
53
67
  this._rawValue = rawValue;
54
68
  this._valueClear = this.fieldType.cast(valueClear);
55
- this._errors = [];
56
- this.isTouched = true;
57
- this.checkAndSetValid();
58
69
  }
59
70
  get errors() {
60
71
  return this._errors;
@@ -73,7 +84,7 @@ var Field = class {
73
84
  get valueClear() {
74
85
  return this._valueClear;
75
86
  }
76
- // ========== Валидация ==========
87
+ // ========== Validation ==========
77
88
  isRequired(form) {
78
89
  let isRequired;
79
90
  if (typeof this.options.isRequired === "function") {
@@ -123,55 +134,61 @@ var Field = class {
123
134
  get isEmpty() {
124
135
  let isEmpty = false;
125
136
  if (this._rawValue === null || this._rawValue === void 0) isEmpty = true;
126
- else if (this._rawValue === "") isEmpty = true;
137
+ else if (typeof this._rawValue === "string" && this._rawValue.trim() === "") isEmpty = true;
127
138
  else if (Array.isArray(this._rawValue) && this._rawValue.length === 0) isEmpty = true;
128
139
  return isEmpty;
129
140
  }
130
- // ========== Вспомогательные методы ==========
141
+ // ========== Helper methods ==========
131
142
  reset() {
132
143
  if (this.defaultValue !== null && this.defaultValue !== void 0) {
133
- this.rawValue = this.defaultValue;
144
+ this.assignRawValue(this.defaultValue);
134
145
  } else {
135
146
  this._rawValue = null;
136
147
  this._valueClear = null;
137
- this.isValid = null;
138
148
  }
139
149
  this._errors = [];
150
+ this.isValid = null;
140
151
  this.isTouched = false;
141
152
  }
142
153
  };
143
154
 
155
+ // src/core/fields/arrayUtils.ts
156
+ function isValidArrayIndex(index, length) {
157
+ return Number.isInteger(index) && index >= 0 && index < length;
158
+ }
159
+
144
160
  // src/core/fields/ArrayField.ts
145
161
  var ArrayField = class {
146
162
  constructor(factory, options = {}, initial = []) {
147
163
  this.factory = factory;
148
164
  this._items = [];
149
165
  this._errors = [];
166
+ this._hasValidationResult = false;
150
167
  this.isTouched = false;
151
168
  this.options = {
152
169
  isRequired: true,
153
170
  messages: {},
154
171
  ...options
155
172
  };
156
- this._items = initial.map((v) => this.factory(v));
173
+ this._items = initial.map((value) => this.factory(value));
157
174
  this.checkValid();
158
175
  }
159
176
  // ===== value =====
160
177
  set itemsValue(items) {
161
178
  this.clear();
162
- for (const i of items) {
163
- this.add(i, false);
179
+ for (const item of items) {
180
+ this.add(item, false);
164
181
  }
165
182
  this.checkValid();
166
183
  }
167
184
  get itemsValue() {
168
- return this._items.map((i) => i.valueClear);
185
+ return this._items.map((item) => item.valueClear);
169
186
  }
170
187
  get items() {
171
188
  return this._items;
172
189
  }
173
190
  // ===== actions =====
174
- add(value, force_check) {
191
+ add(value, force_check = true) {
175
192
  const field = this.factory(value);
176
193
  this.isTouched = true;
177
194
  this._items.push(field);
@@ -179,6 +196,7 @@ var ArrayField = class {
179
196
  return field;
180
197
  }
181
198
  remove(index) {
199
+ if (!isValidArrayIndex(index, this._items.length)) return;
182
200
  this._items.splice(index, 1);
183
201
  this.checkValid();
184
202
  }
@@ -197,6 +215,7 @@ var ArrayField = class {
197
215
  return isRequired;
198
216
  }
199
217
  checkValid(form) {
218
+ this._hasValidationResult = true;
200
219
  this._errors = [];
201
220
  if (this.isRequired(form) && this._items.length === 0) {
202
221
  this._errors.push(
@@ -222,7 +241,7 @@ var ArrayField = class {
222
241
  if (!ok) valid = false;
223
242
  }
224
243
  if (this.options.validate) {
225
- const values = this._items.map((i) => i.valueClear);
244
+ const values = this._items.map((item) => item.valueClear);
226
245
  const result = this.options.validate(values, form);
227
246
  if (result === false) {
228
247
  this._errors.push(
@@ -241,8 +260,9 @@ var ArrayField = class {
241
260
  return this.checkValid(form);
242
261
  }
243
262
  get isValid() {
263
+ if (!this._hasValidationResult) return false;
244
264
  if (this._errors.length > 0) return false;
245
- return this._items.every((i) => i.isValid === true);
265
+ return this._items.every((item) => item.isValid === true);
246
266
  }
247
267
  get error() {
248
268
  return this._errors[0] ?? "";
@@ -250,16 +270,19 @@ var ArrayField = class {
250
270
  set error(message) {
251
271
  if (message) {
252
272
  this._errors = [message];
273
+ this._hasValidationResult = true;
253
274
  } else {
254
275
  this._errors = [];
276
+ this._hasValidationResult = false;
255
277
  }
256
278
  }
257
279
  get errors() {
258
280
  return this._errors;
259
281
  }
260
282
  reset() {
261
- this._items.forEach((i) => i.reset());
283
+ this._items.forEach((item) => item.reset());
262
284
  this._errors = [];
285
+ this._hasValidationResult = false;
263
286
  this.isTouched = false;
264
287
  }
265
288
  };
@@ -271,38 +294,45 @@ var ArrayFormField = class {
271
294
  this.options = options;
272
295
  this._forms = [];
273
296
  this._errors = [];
297
+ this._hasValidationResult = false;
274
298
  this.isTouched = false;
275
299
  this._forms = initial.map((data) => {
276
300
  const form = new this.FormConstructor();
277
- form.fields = data;
301
+ form.setFields(data, false);
278
302
  return form;
279
303
  });
280
304
  this.checkValid();
281
305
  }
282
306
  // ===== value =====
283
307
  set forms(forms) {
284
- this.clear();
285
- for (const f of forms) {
286
- this.add(f, false);
308
+ this.setForms(forms);
309
+ }
310
+ setForms(forms, validate = true) {
311
+ this._forms = [];
312
+ for (const formData of forms) {
313
+ this.add(formData, false);
287
314
  }
288
- this.checkValid();
315
+ if (validate) this.checkValid();
289
316
  }
290
317
  get forms() {
291
318
  return this._forms;
292
319
  }
293
320
  get formsFieldsValue() {
294
- return this._forms.map((i) => i.fieldsValue);
321
+ return this._forms.map((form) => form.fieldsValue);
295
322
  }
296
323
  // ===== actions =====
297
- add(data, force_check) {
324
+ add(data, force_check = true) {
298
325
  const form = new this.FormConstructor();
299
- if (data) form.fields = data;
326
+ if (data !== void 0 && data !== null) {
327
+ form.setFields(data, false);
328
+ }
300
329
  this.isTouched = true;
301
330
  this._forms.push(form);
302
331
  if (force_check) this.checkValid();
303
332
  return form;
304
333
  }
305
334
  remove(index) {
335
+ if (!isValidArrayIndex(index, this._forms.length)) return;
306
336
  this._forms.splice(index, 1);
307
337
  this.checkValid();
308
338
  }
@@ -311,8 +341,9 @@ var ArrayFormField = class {
311
341
  this.checkValid();
312
342
  }
313
343
  reset() {
314
- this._forms.forEach((f) => f.reset());
344
+ this._forms.forEach((form) => form.reset());
315
345
  this._errors = [];
346
+ this._hasValidationResult = false;
316
347
  this.isTouched = false;
317
348
  }
318
349
  // ===== validation =====
@@ -326,8 +357,9 @@ var ArrayFormField = class {
326
357
  return isRequired;
327
358
  }
328
359
  checkValid(form) {
360
+ this._hasValidationResult = true;
329
361
  this._errors = [];
330
- if (this.isRequired(form) && this.forms.length === 0) {
362
+ if (this.isRequired(form) && this._forms.length === 0) {
331
363
  this._errors.push(
332
364
  this.options.messages?.isRequired ?? "\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0445\u043E\u0442\u044F \u0431\u044B \u043E\u0434\u0438\u043D \u044D\u043B\u0435\u043C\u0435\u043D\u0442"
333
365
  );
@@ -346,12 +378,12 @@ var ArrayFormField = class {
346
378
  return false;
347
379
  }
348
380
  let valid = true;
349
- for (const form2 of this.forms) {
350
- const ok = form2.checkValid(form2);
381
+ for (const nestedForm of this._forms) {
382
+ const ok = nestedForm.checkValid(nestedForm);
351
383
  if (!ok) valid = false;
352
384
  }
353
385
  if (this.options.validate) {
354
- const result = this.options.validate(this.forms, form);
386
+ const result = this.options.validate(this._forms, form);
355
387
  if (result === false) {
356
388
  this._errors.push(
357
389
  this.options.messages?.validate ?? "\u041E\u0448\u0438\u0431\u043A\u0430 \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u0438"
@@ -369,8 +401,9 @@ var ArrayFormField = class {
369
401
  return this.checkValid(form);
370
402
  }
371
403
  get isValid() {
404
+ if (!this._hasValidationResult) return false;
372
405
  if (this._errors.length > 0) return false;
373
- return this._forms.every((i) => i.checkValid() === true);
406
+ return this._forms.every((form) => form.isValid);
374
407
  }
375
408
  get errors() {
376
409
  return this._errors;
@@ -381,8 +414,10 @@ var ArrayFormField = class {
381
414
  set error(message) {
382
415
  if (message) {
383
416
  this._errors = [message];
417
+ this._hasValidationResult = true;
384
418
  } else {
385
419
  this._errors = [];
420
+ this._hasValidationResult = false;
386
421
  }
387
422
  }
388
423
  };
@@ -395,19 +430,19 @@ var ErrorsSchema = class _ErrorsSchema {
395
430
  this._valueTransformers = [];
396
431
  }
397
432
  /**
398
- * Добавляет статическое правило - точное соответствие ключа ошибки
399
- * @param errorKey - точный ключ ошибки из ответа сервера
400
- * @param fieldName - имя поля в форме или функция для динамического определения
433
+ * Add a static rule that matches an exact error key.
434
+ * @param errorKey - Exact error key returned by the server.
435
+ * @param fieldName - Form field name or a function that resolves it dynamically.
401
436
  */
402
437
  staticKey(errorKey, fieldName) {
403
438
  this._staticRules.set(errorKey, fieldName);
404
439
  return this;
405
440
  }
406
441
  /**
407
- * Добавляет динамическое правило с регулярным выражением
408
- * @param pattern - регулярное выражение для сопоставления ключа ошибки
409
- * @param mapping - имя поля или функция для извлечения имени поля из совпадения
410
- * @param options - дополнительные опции (приоритет и т.д.)
442
+ * Add a dynamic rule using a regular expression.
443
+ * @param pattern - Regular expression used to match an error key.
444
+ * @param mapping - Field name or function that derives it from the match.
445
+ * @param options - Additional options, such as rule priority.
411
446
  */
412
447
  dynamicKey(pattern, mapping, options) {
413
448
  this._dynamicRules.push({
@@ -419,22 +454,22 @@ var ErrorsSchema = class _ErrorsSchema {
419
454
  return this;
420
455
  }
421
456
  /**
422
- * Устанавливает обработчик для ошибок, которые не подошли ни под одно правило
457
+ * Set a handler for errors that do not match any rule.
423
458
  */
424
459
  fallback(handler) {
425
460
  this._fallbackHandler = handler;
426
461
  return this;
427
462
  }
428
463
  /**
429
- * Добавляет трансформер для значений ошибок
430
- * Полезно для нормализации сообщений (например, если сервер возвращает массив ошибок)
464
+ * Add a transformer for error values.
465
+ * Useful for normalizing messages, such as server responses containing arrays.
431
466
  */
432
467
  transformValue(transformer) {
433
468
  this._valueTransformers.push(transformer);
434
469
  return this;
435
470
  }
436
471
  /**
437
- * Быстрый метод для создания схемы из простого объекта маппинга
472
+ * Create a schema from a simple mapping object.
438
473
  */
439
474
  static fromObject(mapping) {
440
475
  const schema = new _ErrorsSchema();
@@ -444,24 +479,35 @@ var ErrorsSchema = class _ErrorsSchema {
444
479
  return schema;
445
480
  }
446
481
  /**
447
- * Создает схему для массива однотипных форм
482
+ * Create a schema for an array of similarly structured forms.
448
483
  */
449
484
  static forArray(itemSchema, arrayFieldName, indexExtractor) {
450
485
  const schema = new _ErrorsSchema();
486
+ for (const transformer of itemSchema._valueTransformers) {
487
+ schema.transformValue(transformer);
488
+ }
489
+ const escapedArrayFieldName = arrayFieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
490
+ const indexPattern = indexExtractor ? "(.+?)" : "(?:\\[(\\d+)\\]|\\.(\\d+))";
451
491
  schema.dynamicKey(
452
- new RegExp(`^${arrayFieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\[(\\d+)\\]\\.(.+)$`),
453
- (match) => {
454
- const index = match[1];
455
- const fieldName = match[2];
456
- return `${arrayFieldName}.${index}.${fieldName}`;
492
+ new RegExp(`^${escapedArrayFieldName}${indexPattern}\\.(.+)$`),
493
+ (match, value) => {
494
+ const sourceKey = match[0];
495
+ const index = indexExtractor ? indexExtractor(sourceKey) : Number(match[1] ?? match[2]);
496
+ if (!Number.isInteger(index) || index < 0) {
497
+ return "";
498
+ }
499
+ const fieldPath = match[indexExtractor ? 2 : 3];
500
+ const itemMapping = itemSchema.findMapping(fieldPath, value);
501
+ const mappedFieldPath = itemMapping ? typeof itemMapping === "function" ? itemMapping(value) : itemMapping : fieldPath;
502
+ return `${arrayFieldName}.${index}.${mappedFieldPath}`;
457
503
  },
458
504
  { priority: 100 }
459
505
  );
460
506
  return schema;
461
507
  }
462
508
  /**
463
- * Основной метод парсинга ошибок с поддержкой трансформеров
464
- * Поддерживает вложенные структуры через точечную нотацию
509
+ * Parse errors while applying configured value transformers.
510
+ * Supports nested structures using dot notation.
465
511
  */
466
512
  parseErrors(errors, prefix = "") {
467
513
  const result = {};
@@ -489,17 +535,12 @@ var ErrorsSchema = class _ErrorsSchema {
489
535
  } else {
490
536
  result[mapping] = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
491
537
  }
492
- } else if (this._fallbackHandler) {
493
- const fieldName = this._fallbackHandler(fullKey, transformedValue);
494
- if (fieldName) {
495
- result[fieldName] = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
496
- }
497
538
  }
498
539
  }
499
540
  return result;
500
541
  }
501
542
  /**
502
- * Парсит ошибки сервера и возвращает расширенную информацию
543
+ * Parse server errors and return detailed metadata.
503
544
  */
504
545
  parseErrorsDetailed(errors, prefix = "") {
505
546
  const result = {};
@@ -531,7 +572,7 @@ var ErrorsSchema = class _ErrorsSchema {
531
572
  return result;
532
573
  }
533
574
  /**
534
- * Создает схему для вложенной формы
575
+ * Create a schema for a nested form.
535
576
  */
536
577
  nest(prefix) {
537
578
  const nestedSchema = new _ErrorsSchema();
@@ -556,7 +597,7 @@ var ErrorsSchema = class _ErrorsSchema {
556
597
  return nestedSchema;
557
598
  }
558
599
  /**
559
- * Клонирует текущую схему
600
+ * Clone the current schema.
560
601
  */
561
602
  clone() {
562
603
  const cloned = new _ErrorsSchema();
@@ -571,7 +612,7 @@ var ErrorsSchema = class _ErrorsSchema {
571
612
  return cloned;
572
613
  }
573
614
  /**
574
- * Объединяет текущую схему с другой
615
+ * Merge the current schema with another schema.
575
616
  */
576
617
  merge(other) {
577
618
  const merged = this.clone();
@@ -598,7 +639,7 @@ var ErrorsSchema = class _ErrorsSchema {
598
639
  for (const rule of this._dynamicRules) {
599
640
  const match = key.match(rule.pattern);
600
641
  if (match) {
601
- const fieldName = typeof rule.mapping === "function" ? rule.mapping(match) : rule.mapping;
642
+ const fieldName = typeof rule.mapping === "function" ? rule.mapping(match, value) : rule.mapping;
602
643
  return fieldName ? { fieldName, matchedBy: "dynamic" } : null;
603
644
  }
604
645
  }
@@ -616,7 +657,7 @@ var ErrorsSchema = class _ErrorsSchema {
616
657
  const match = key.match(rule.pattern);
617
658
  if (match) {
618
659
  if (typeof rule.mapping === "function") {
619
- const fieldName = rule.mapping(match);
660
+ const fieldName = rule.mapping(match, value);
620
661
  return fieldName;
621
662
  }
622
663
  return rule.mapping;
@@ -656,7 +697,7 @@ var Form = class {
656
697
  isFormValid = false;
657
698
  }
658
699
  }
659
- return isFormValid;
700
+ return isFormValid && Object.keys(this._globalErrors).length === 0;
660
701
  }
661
702
  get fieldsValue() {
662
703
  const data = {};
@@ -673,16 +714,20 @@ var Form = class {
673
714
  return data;
674
715
  }
675
716
  set fields(data) {
717
+ this.setFields(data);
718
+ }
719
+ setFields(data, validate = true) {
676
720
  const fields = this._fields;
677
721
  for (const key of Object.keys(data)) {
678
722
  const field = fields[key];
679
723
  if (field) {
680
- if (field instanceof Field) field.rawValue = data[key];
724
+ if (field instanceof Field) field.setRawValue(data[key], false);
681
725
  else if (field instanceof ArrayField) field.itemsValue = data[key];
682
- else if (field instanceof ArrayFormField) field.forms = data[key];
726
+ else if (field instanceof ArrayFormField) field.setForms(data[key], false);
683
727
  else console.error(`Invalid field "${key}" type`);
684
728
  }
685
729
  }
730
+ if (validate) this.checkValid();
686
731
  }
687
732
  get allErrors() {
688
733
  const errors = {};
@@ -710,7 +755,7 @@ var Form = class {
710
755
  return errors;
711
756
  }
712
757
  /**
713
- * Рекурсивно собирает все ошибки из ArrayFormField
758
+ * Recursively collect all errors from an ArrayFormField.
714
759
  */
715
760
  collectArrayFormErrors(arrayField) {
716
761
  const allErrors = [];
@@ -730,19 +775,30 @@ var Form = class {
730
775
  return { ...this._globalErrors };
731
776
  }
732
777
  set errors(errors) {
778
+ this.clearErrorsRecursively();
733
779
  const fields = this._fields;
734
- this._globalErrors = {};
735
- for (const key of Object.keys(fields)) {
736
- fields[key].error = null;
737
- }
738
780
  const parsedErrors = this.errorsShema.parseErrors(errors);
739
781
  for (const [fieldName, errorMessage] of Object.entries(parsedErrors)) {
740
782
  this.applyErrorToField(fieldName, errorMessage, fields, this);
741
783
  }
742
784
  }
785
+ clearErrorsRecursively() {
786
+ this._globalErrors = {};
787
+ for (const field of Object.values(this._fields)) {
788
+ if (field instanceof Field) {
789
+ field.error = null;
790
+ } else if (field instanceof ArrayField) {
791
+ field.error = null;
792
+ for (const item of field.items) item.error = null;
793
+ } else if (field instanceof ArrayFormField) {
794
+ field.error = null;
795
+ for (const nestedForm of field.forms) nestedForm.clearErrorsRecursively();
796
+ }
797
+ }
798
+ }
743
799
  /**
744
- * Применяет ошибку к соответствующему полю
745
- * Поддерживает вложенные поля через точечную нотацию
800
+ * Apply an error to the corresponding field.
801
+ * Supports nested fields using dot notation.
746
802
  */
747
803
  applyErrorToField(fieldPath, errorMessage, fields, targetForm) {
748
804
  if (!targetForm) {
@@ -809,7 +865,7 @@ var Form = class {
809
865
  }
810
866
  return false;
811
867
  }
812
- // Проверить, валидна ли форма
868
+ // Check whether the form is valid.
813
869
  get isValid() {
814
870
  const fields = this._fields;
815
871
  for (const key of Object.keys(fields)) {
@@ -817,32 +873,50 @@ var Form = class {
817
873
  }
818
874
  return Object.keys(this._globalErrors).length === 0;
819
875
  }
820
- // Получить все значения в виде FormData (для отправки на сервер)
876
+ // Serialize form values for multipart requests.
821
877
  toFormData() {
822
878
  const formData = new FormData();
823
879
  const data = this.fieldsValue;
824
880
  for (const [key, value] of Object.entries(data)) {
825
- if (value instanceof Date) {
881
+ if (value === null || value === void 0) continue;
882
+ if (typeof Blob !== "undefined" && value instanceof Blob) {
883
+ formData.append(key, value);
884
+ } else if (value instanceof Date) {
826
885
  formData.append(key, value.toISOString());
827
- } else if (value !== null && value !== void 0) {
886
+ } else if (typeof value === "object") {
887
+ const serialized = JSON.stringify(value);
888
+ if (serialized !== void 0) formData.append(key, serialized);
889
+ } else {
828
890
  formData.append(key, String(value));
829
891
  }
830
892
  }
831
893
  return formData;
832
894
  }
833
- // Получить все значения в виде JSON
895
+ // Get all values in JSON-compatible form.
834
896
  toJSON() {
835
- const data = this.fieldsValue;
836
897
  const json = {};
837
- for (const [key, value] of Object.entries(data)) {
838
- if (value instanceof Date) {
839
- json[key] = value.toISOString();
840
- } else {
841
- json[key] = value;
842
- }
898
+ for (const [key, value] of Object.entries(this.fieldsValue)) {
899
+ json[key] = this.serializeJsonValue(value);
843
900
  }
844
901
  return json;
845
902
  }
903
+ serializeJsonValue(value) {
904
+ if (value instanceof Date) {
905
+ return value.toISOString();
906
+ }
907
+ if (Array.isArray(value)) {
908
+ return value.map((item) => this.serializeJsonValue(item));
909
+ }
910
+ if (value !== null && typeof value === "object") {
911
+ const prototype = Object.getPrototypeOf(value);
912
+ if (prototype === Object.prototype || prototype === null) {
913
+ return Object.fromEntries(
914
+ Object.entries(value).map(([key, nestedValue]) => [key, this.serializeJsonValue(nestedValue)])
915
+ );
916
+ }
917
+ }
918
+ return value;
919
+ }
846
920
  };
847
921
 
848
922
  // src/core/plugins/FieldPlugin.ts
@@ -862,13 +936,13 @@ var MaskPlugin = class extends BasePlugin {
862
936
  this.maskParts = [];
863
937
  this.config = config;
864
938
  }
865
- init(field) {
939
+ init(_field) {
866
940
  this.parseMask();
867
941
  }
868
- toRawValue(value, field) {
942
+ toRawValue(value, _field) {
869
943
  return this.applyMask(value);
870
944
  }
871
- toValueClear(value, field) {
945
+ toValueClear(value, _field) {
872
946
  return this.removeMask(value);
873
947
  }
874
948
  parseMask() {
@@ -1067,8 +1141,12 @@ var StringType = class extends FieldType {
1067
1141
  this.options.messages?.maxLength ?? `\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430 ${this.options.maxLength} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`
1068
1142
  );
1069
1143
  }
1070
- if (this.options.pattern != null && !this.options.pattern.test(value)) {
1071
- errors.push(this.options.messages?.pattern ?? "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442");
1144
+ if (this.options.pattern != null) {
1145
+ const { source, flags } = this.options.pattern;
1146
+ const stablePattern = new RegExp(source, flags.replace(/[gy]/g, ""));
1147
+ if (!stablePattern.test(value)) {
1148
+ errors.push(this.options.messages?.pattern ?? "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442");
1149
+ }
1072
1150
  }
1073
1151
  return errors;
1074
1152
  }
@@ -1081,15 +1159,21 @@ var StringType = class extends FieldType {
1081
1159
  var NumberType = class extends FieldType {
1082
1160
  cast(rawValue) {
1083
1161
  if (rawValue === null || rawValue === void 0) return null;
1084
- if (typeof rawValue === "number" && !isNaN(rawValue)) return rawValue;
1162
+ if (typeof rawValue === "number") return Number.isFinite(rawValue) ? rawValue : null;
1085
1163
  if (typeof rawValue === "string") {
1086
- const num = Number(rawValue);
1087
- return isNaN(num) ? null : num;
1164
+ const normalized = rawValue.trim();
1165
+ if (normalized.length === 0) return null;
1166
+ const num = Number(normalized);
1167
+ return Number.isFinite(num) ? num : null;
1088
1168
  }
1089
1169
  return null;
1090
1170
  }
1091
1171
  validate(value) {
1092
1172
  const errors = [];
1173
+ if (!Number.isFinite(value)) {
1174
+ errors.push(this.options.messages?.invalid ?? "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043A\u043E\u043D\u0435\u0447\u043D\u044B\u043C \u0447\u0438\u0441\u043B\u043E\u043C");
1175
+ return errors;
1176
+ }
1093
1177
  if (!Number.isInteger(value)) {
1094
1178
  errors.push(this.options.messages?.invalid ?? "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0446\u0435\u043B\u044B\u043C \u0447\u0438\u0441\u043B\u043E\u043C");
1095
1179
  }
@@ -1134,7 +1218,7 @@ var DecimalType = class extends FieldType {
1134
1218
  if (typeof rawValue === "string") {
1135
1219
  return this.castString(rawValue);
1136
1220
  }
1137
- if (typeof rawValue === "number" && !isNaN(rawValue)) {
1221
+ if (typeof rawValue === "number" && Number.isFinite(rawValue)) {
1138
1222
  return rawValue;
1139
1223
  }
1140
1224
  return null;
@@ -1150,13 +1234,13 @@ var DecimalType = class extends FieldType {
1150
1234
  }
1151
1235
  validate(value) {
1152
1236
  const errors = [];
1153
- value = typeof value === "number" ? this.numberToString(value) : value;
1237
+ value = typeof value === "number" ? String(value) : value;
1154
1238
  if (!this.isValidNumberString(value)) {
1155
1239
  errors.push(this.options.messages?.invalid ?? "\u041F\u0440\u0438\u043C\u0435\u0440: 1.00");
1156
1240
  return errors;
1157
1241
  }
1158
- const maxDigits = this.options.maxDigits || 8;
1159
- const decimalPlaces = this.options.decimalPlaces || 2;
1242
+ const maxDigits = this.options.maxDigits ?? 8;
1243
+ const decimalPlaces = this.options.decimalPlaces ?? 2;
1160
1244
  const maxIntDigits = maxDigits - decimalPlaces;
1161
1245
  const isNegative = value.startsWith("-");
1162
1246
  const normalizedValue = isNegative ? value.substring(1) : value;
@@ -1230,10 +1314,6 @@ var DecimalType = class extends FieldType {
1230
1314
  if (dotCount > 1) return false;
1231
1315
  return pattern.test(value);
1232
1316
  }
1233
- numberToString(value) {
1234
- const decimalPlaces = this.options.decimalPlaces;
1235
- return value.toFixed(decimalPlaces);
1236
- }
1237
1317
  getTypeName() {
1238
1318
  return "\u0434\u0435\u0441\u044F\u0442\u0438\u0447\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E";
1239
1319
  }
@@ -1310,7 +1390,7 @@ var DateType = class extends FieldType {
1310
1390
  getTypeName() {
1311
1391
  return this.options.isDatetime ? "\u0434\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043C\u044F" : "\u0434\u0430\u0442\u0430";
1312
1392
  }
1313
- // Сравнение дат с учётом времени
1393
+ // Compare dates with optional time precision.
1314
1394
  compareDates(value, boundary, type) {
1315
1395
  if (this.options.isDatetime) {
1316
1396
  if (type === "min") {
@@ -1339,6 +1419,8 @@ exports.BasePlugin = BasePlugin;
1339
1419
  exports.BooleanType = BooleanType;
1340
1420
  exports.DateType = DateType;
1341
1421
  exports.DecimalType = DecimalType;
1422
+ exports.DefaultErrorsSchema = DefaultErrorsSchema;
1423
+ exports.ErrorsSchema = ErrorsSchema;
1342
1424
  exports.Field = Field;
1343
1425
  exports.FieldType = FieldType;
1344
1426
  exports.Form = Form;