@orkestrel/form 0.0.11 → 0.0.13
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/README.md +6 -1
- package/dist/src/browser/index.d.ts +6 -572
- package/dist/src/browser/index.js +84 -182
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +63 -15
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +30 -9
- package/dist/src/core/index.d.ts +30 -9
- package/dist/src/core/index.js +63 -15
- package/dist/src/core/index.js.map +1 -1
- package/package.json +10 -10
|
@@ -12,7 +12,7 @@ export declare const ALPHANUMERIC_PATTERN: Readonly<RegExp>;
|
|
|
12
12
|
*
|
|
13
13
|
* @remarks
|
|
14
14
|
* The runtime control-membership check keeps this boundary total for JavaScript callers that
|
|
15
|
-
*
|
|
15
|
+
* pass values outside the declared {@link FieldControl} contract.
|
|
16
16
|
*
|
|
17
17
|
* @param control - The field control to inspect.
|
|
18
18
|
* @param rule - The named rule to inspect.
|
|
@@ -354,7 +354,7 @@ export declare interface FieldChoice {
|
|
|
354
354
|
* const control: FieldControl = 'select'
|
|
355
355
|
* ```
|
|
356
356
|
*/
|
|
357
|
-
export declare type FieldControl = 'text' | 'editor' | 'password' | 'number' | 'date' | 'time' | 'datetime' | 'color' | 'confirm' | 'select' | 'checkbox' | 'file';
|
|
357
|
+
export declare type FieldControl = 'text' | 'editor' | 'password' | 'number' | 'date' | 'time' | 'datetime' | 'color' | 'confirm' | 'select' | 'checkbox' | 'list' | 'file';
|
|
358
358
|
|
|
359
359
|
/**
|
|
360
360
|
* Represents one failed check against one field.
|
|
@@ -377,11 +377,12 @@ export declare interface FieldError {
|
|
|
377
377
|
* `minimum` and `maximum` measure whatever the control makes countable: characters for
|
|
378
378
|
* `text`, `editor`, and `password`; magnitude for `number`; chronology for `date`, `time`,
|
|
379
379
|
* and `datetime`, whose operand is a string written in that control's own format; and the
|
|
380
|
-
* number of
|
|
380
|
+
* number of entries for `checkbox`, `list`, and `file`.
|
|
381
381
|
*
|
|
382
382
|
* `step` is the interval a numeric value must land on. `pattern` is regular-expression
|
|
383
383
|
* source. `email`, `url`, `integer`, and `alphanumeric` each assert one shape over the whole
|
|
384
|
-
* value
|
|
384
|
+
* value, or each entry of a `list`. `required` requires an entry for `list` and presence
|
|
385
|
+
* for other controls. `custom` runs last and is the only rule that sees the rest of the form.
|
|
385
386
|
*
|
|
386
387
|
* @example
|
|
387
388
|
* ```ts
|
|
@@ -415,7 +416,7 @@ export declare type FieldRuleName = Exclude<keyof FieldRule, 'custom'>;
|
|
|
415
416
|
*
|
|
416
417
|
* @remarks
|
|
417
418
|
* It runs after every named rule, and it runs on an absent value as well as a present one. That
|
|
418
|
-
* is what makes a rule such as "required
|
|
419
|
+
* is what makes a rule such as "required after the sibling says yes" expressible, and it means an
|
|
419
420
|
* unanswered field can carry both a `required` message and this validator's own.
|
|
420
421
|
*
|
|
421
422
|
* @param value - The value the field holds, or `undefined` when nobody has answered it.
|
|
@@ -440,7 +441,7 @@ export declare type FieldValidator = (value: FieldValue | undefined, values: For
|
|
|
440
441
|
*
|
|
441
442
|
* @remarks
|
|
442
443
|
* The variant follows the control: text-like controls hold a `string`, `number` holds a
|
|
443
|
-
* `number`, `confirm` holds a `boolean`, and `checkbox`
|
|
444
|
+
* `number`, `confirm` holds a `boolean`, and `checkbox`, `list`, and `file` hold a
|
|
444
445
|
* `readonly string[]`.
|
|
445
446
|
*/
|
|
446
447
|
export declare type FieldValue = string | number | boolean | readonly string[];
|
|
@@ -753,7 +754,7 @@ export declare type FormEventMap = {
|
|
|
753
754
|
* }
|
|
754
755
|
* ```
|
|
755
756
|
*/
|
|
756
|
-
export declare type FormField = TextField | EditorField | PasswordField | NumberField | DateField | TimeField | DatetimeField | ColorField | ConfirmField | SelectField | CheckboxField | FileField;
|
|
757
|
+
export declare type FormField = TextField | EditorField | PasswordField | NumberField | DateField | TimeField | DatetimeField | ColorField | ConfirmField | SelectField | CheckboxField | ListField | FileField;
|
|
757
758
|
|
|
758
759
|
/**
|
|
759
760
|
* Represents a named section of a form.
|
|
@@ -772,7 +773,7 @@ export declare interface FormGroup {
|
|
|
772
773
|
* Declares the contract a form exposes: the state it holds and the calls that move it.
|
|
773
774
|
*
|
|
774
775
|
* @remarks
|
|
775
|
-
* `valid` is true when the last completed evaluation found no error, and `dirty` is true
|
|
776
|
+
* `valid` is true when the last completed evaluation found no error, and `dirty` is true when
|
|
776
777
|
* an answer differs from its acknowledged baseline. Both are derived, never stored.
|
|
777
778
|
*
|
|
778
779
|
* `touched` holds the fields somebody has visited, which is what lets a renderer withhold an
|
|
@@ -1202,6 +1203,26 @@ export declare function isFormValues(input: unknown): input is FormValues;
|
|
|
1202
1203
|
/** Caps the number of entries one list-valued answer may hold, at 1024. */
|
|
1203
1204
|
export declare const LIST_LIMIT = 1024;
|
|
1204
1205
|
|
|
1206
|
+
/**
|
|
1207
|
+
* Represents an ordered list of free-text strings.
|
|
1208
|
+
*
|
|
1209
|
+
* @remarks
|
|
1210
|
+
* `required` requires at least one entry. `minimum` and `maximum` count entries.
|
|
1211
|
+
* `pattern`, `email`, `url`, `integer`, and `alphanumeric` check each entry and name
|
|
1212
|
+
* the failing value in its message. `custom` checks the whole list. Duplicates are retained.
|
|
1213
|
+
*
|
|
1214
|
+
* @example
|
|
1215
|
+
* ```ts
|
|
1216
|
+
* const to: ListField = {
|
|
1217
|
+
* control: 'list', name: 'to', rule: { required: true, email: true },
|
|
1218
|
+
* }
|
|
1219
|
+
* ```
|
|
1220
|
+
*/
|
|
1221
|
+
export declare interface ListField extends FieldBase {
|
|
1222
|
+
readonly control: 'list';
|
|
1223
|
+
readonly default?: readonly string[];
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1205
1226
|
/**
|
|
1206
1227
|
* Decides whether a raw binding value projects to an answered field.
|
|
1207
1228
|
*
|
|
@@ -1209,7 +1230,7 @@ export declare const LIST_LIMIT = 1024;
|
|
|
1209
1230
|
* Bind with `fill(name, matchesAnswer(raw) ? raw : undefined)`. This projection treats an absent
|
|
1210
1231
|
* value and a string containing only whitespace as unanswered. Every other field value is an
|
|
1211
1232
|
* answer, including an empty list, `false`, and zero. Core evaluation does not use this projection:
|
|
1212
|
-
* its `required` rule remains presence-only.
|
|
1233
|
+
* its `required` rule remains presence-only except that `list` requires an entry.
|
|
1213
1234
|
*
|
|
1214
1235
|
* @param value - The raw field value, or absence.
|
|
1215
1236
|
* @returns True if the binding preserves the value as an answer; false otherwise.
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare const ALPHANUMERIC_PATTERN: Readonly<RegExp>;
|
|
|
12
12
|
*
|
|
13
13
|
* @remarks
|
|
14
14
|
* The runtime control-membership check keeps this boundary total for JavaScript callers that
|
|
15
|
-
*
|
|
15
|
+
* pass values outside the declared {@link FieldControl} contract.
|
|
16
16
|
*
|
|
17
17
|
* @param control - The field control to inspect.
|
|
18
18
|
* @param rule - The named rule to inspect.
|
|
@@ -354,7 +354,7 @@ export declare interface FieldChoice {
|
|
|
354
354
|
* const control: FieldControl = 'select'
|
|
355
355
|
* ```
|
|
356
356
|
*/
|
|
357
|
-
export declare type FieldControl = 'text' | 'editor' | 'password' | 'number' | 'date' | 'time' | 'datetime' | 'color' | 'confirm' | 'select' | 'checkbox' | 'file';
|
|
357
|
+
export declare type FieldControl = 'text' | 'editor' | 'password' | 'number' | 'date' | 'time' | 'datetime' | 'color' | 'confirm' | 'select' | 'checkbox' | 'list' | 'file';
|
|
358
358
|
|
|
359
359
|
/**
|
|
360
360
|
* Represents one failed check against one field.
|
|
@@ -377,11 +377,12 @@ export declare interface FieldError {
|
|
|
377
377
|
* `minimum` and `maximum` measure whatever the control makes countable: characters for
|
|
378
378
|
* `text`, `editor`, and `password`; magnitude for `number`; chronology for `date`, `time`,
|
|
379
379
|
* and `datetime`, whose operand is a string written in that control's own format; and the
|
|
380
|
-
* number of
|
|
380
|
+
* number of entries for `checkbox`, `list`, and `file`.
|
|
381
381
|
*
|
|
382
382
|
* `step` is the interval a numeric value must land on. `pattern` is regular-expression
|
|
383
383
|
* source. `email`, `url`, `integer`, and `alphanumeric` each assert one shape over the whole
|
|
384
|
-
* value
|
|
384
|
+
* value, or each entry of a `list`. `required` requires an entry for `list` and presence
|
|
385
|
+
* for other controls. `custom` runs last and is the only rule that sees the rest of the form.
|
|
385
386
|
*
|
|
386
387
|
* @example
|
|
387
388
|
* ```ts
|
|
@@ -415,7 +416,7 @@ export declare type FieldRuleName = Exclude<keyof FieldRule, 'custom'>;
|
|
|
415
416
|
*
|
|
416
417
|
* @remarks
|
|
417
418
|
* It runs after every named rule, and it runs on an absent value as well as a present one. That
|
|
418
|
-
* is what makes a rule such as "required
|
|
419
|
+
* is what makes a rule such as "required after the sibling says yes" expressible, and it means an
|
|
419
420
|
* unanswered field can carry both a `required` message and this validator's own.
|
|
420
421
|
*
|
|
421
422
|
* @param value - The value the field holds, or `undefined` when nobody has answered it.
|
|
@@ -440,7 +441,7 @@ export declare type FieldValidator = (value: FieldValue | undefined, values: For
|
|
|
440
441
|
*
|
|
441
442
|
* @remarks
|
|
442
443
|
* The variant follows the control: text-like controls hold a `string`, `number` holds a
|
|
443
|
-
* `number`, `confirm` holds a `boolean`, and `checkbox`
|
|
444
|
+
* `number`, `confirm` holds a `boolean`, and `checkbox`, `list`, and `file` hold a
|
|
444
445
|
* `readonly string[]`.
|
|
445
446
|
*/
|
|
446
447
|
export declare type FieldValue = string | number | boolean | readonly string[];
|
|
@@ -753,7 +754,7 @@ export declare type FormEventMap = {
|
|
|
753
754
|
* }
|
|
754
755
|
* ```
|
|
755
756
|
*/
|
|
756
|
-
export declare type FormField = TextField | EditorField | PasswordField | NumberField | DateField | TimeField | DatetimeField | ColorField | ConfirmField | SelectField | CheckboxField | FileField;
|
|
757
|
+
export declare type FormField = TextField | EditorField | PasswordField | NumberField | DateField | TimeField | DatetimeField | ColorField | ConfirmField | SelectField | CheckboxField | ListField | FileField;
|
|
757
758
|
|
|
758
759
|
/**
|
|
759
760
|
* Represents a named section of a form.
|
|
@@ -772,7 +773,7 @@ export declare interface FormGroup {
|
|
|
772
773
|
* Declares the contract a form exposes: the state it holds and the calls that move it.
|
|
773
774
|
*
|
|
774
775
|
* @remarks
|
|
775
|
-
* `valid` is true when the last completed evaluation found no error, and `dirty` is true
|
|
776
|
+
* `valid` is true when the last completed evaluation found no error, and `dirty` is true when
|
|
776
777
|
* an answer differs from its acknowledged baseline. Both are derived, never stored.
|
|
777
778
|
*
|
|
778
779
|
* `touched` holds the fields somebody has visited, which is what lets a renderer withhold an
|
|
@@ -1202,6 +1203,26 @@ export declare function isFormValues(input: unknown): input is FormValues;
|
|
|
1202
1203
|
/** Caps the number of entries one list-valued answer may hold, at 1024. */
|
|
1203
1204
|
export declare const LIST_LIMIT = 1024;
|
|
1204
1205
|
|
|
1206
|
+
/**
|
|
1207
|
+
* Represents an ordered list of free-text strings.
|
|
1208
|
+
*
|
|
1209
|
+
* @remarks
|
|
1210
|
+
* `required` requires at least one entry. `minimum` and `maximum` count entries.
|
|
1211
|
+
* `pattern`, `email`, `url`, `integer`, and `alphanumeric` check each entry and name
|
|
1212
|
+
* the failing value in its message. `custom` checks the whole list. Duplicates are retained.
|
|
1213
|
+
*
|
|
1214
|
+
* @example
|
|
1215
|
+
* ```ts
|
|
1216
|
+
* const to: ListField = {
|
|
1217
|
+
* control: 'list', name: 'to', rule: { required: true, email: true },
|
|
1218
|
+
* }
|
|
1219
|
+
* ```
|
|
1220
|
+
*/
|
|
1221
|
+
export declare interface ListField extends FieldBase {
|
|
1222
|
+
readonly control: 'list';
|
|
1223
|
+
readonly default?: readonly string[];
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1205
1226
|
/**
|
|
1206
1227
|
* Decides whether a raw binding value projects to an answered field.
|
|
1207
1228
|
*
|
|
@@ -1209,7 +1230,7 @@ export declare const LIST_LIMIT = 1024;
|
|
|
1209
1230
|
* Bind with `fill(name, matchesAnswer(raw) ? raw : undefined)`. This projection treats an absent
|
|
1210
1231
|
* value and a string containing only whitespace as unanswered. Every other field value is an
|
|
1211
1232
|
* answer, including an empty list, `false`, and zero. Core evaluation does not use this projection:
|
|
1212
|
-
* its `required` rule remains presence-only.
|
|
1233
|
+
* its `required` rule remains presence-only except that `list` requires an entry.
|
|
1213
1234
|
*
|
|
1214
1235
|
* @param value - The raw field value, or absence.
|
|
1215
1236
|
* @returns True if the binding preserves the value as an answer; false otherwise.
|
package/dist/src/core/index.js
CHANGED
|
@@ -14,6 +14,7 @@ var FIELD_CONTROLS = Object.freeze([
|
|
|
14
14
|
"confirm",
|
|
15
15
|
"select",
|
|
16
16
|
"checkbox",
|
|
17
|
+
"list",
|
|
17
18
|
"file"
|
|
18
19
|
]);
|
|
19
20
|
/** Lists the members every field declares, whatever its control. */
|
|
@@ -66,6 +67,7 @@ var FIELD_KEYS = Object.freeze({
|
|
|
66
67
|
"choices",
|
|
67
68
|
"default"
|
|
68
69
|
]),
|
|
70
|
+
list: Object.freeze([...FIELD_BASE_KEYS, "default"]),
|
|
69
71
|
file: Object.freeze([
|
|
70
72
|
...FIELD_BASE_KEYS,
|
|
71
73
|
"accept",
|
|
@@ -324,6 +326,7 @@ function isFormField(input) {
|
|
|
324
326
|
const fallback = hasDefault ? input.default : void 0;
|
|
325
327
|
return arrayOf(isFieldChoice)(choices) && (!hasDefault || arrayOf(isString)(fallback));
|
|
326
328
|
}
|
|
329
|
+
case "list": return !Object.hasOwn(input, "default") || arrayOf(isString)(input.default);
|
|
327
330
|
case "file": {
|
|
328
331
|
const hasAccept = Object.hasOwn(input, "accept");
|
|
329
332
|
const accept = hasAccept ? input.accept : void 0;
|
|
@@ -448,6 +451,12 @@ function cloneFormField(field) {
|
|
|
448
451
|
choices: cloneChoices(field.choices),
|
|
449
452
|
...field.default === void 0 ? {} : { default: cloneValue(field.default) }
|
|
450
453
|
});
|
|
454
|
+
case "list": return Object.freeze({
|
|
455
|
+
...field,
|
|
456
|
+
...rule,
|
|
457
|
+
...meta,
|
|
458
|
+
...field.default === void 0 ? {} : { default: cloneValue(field.default) }
|
|
459
|
+
});
|
|
451
460
|
case "file": return Object.freeze({
|
|
452
461
|
...field,
|
|
453
462
|
...rule,
|
|
@@ -572,6 +581,7 @@ function matchesField(field, value) {
|
|
|
572
581
|
case "confirm": return isBoolean(value);
|
|
573
582
|
case "select": return isString(value) && !field.choices.some((choice) => choice.value === value && choice.disabled === true) && (field.open === true || field.choices.some((choice) => choice.value === value && choice.disabled !== true));
|
|
574
583
|
case "checkbox": return entries !== void 0 && entries.every((entry) => isString(entry) && field.choices.some((choice) => choice.value === entry && choice.disabled !== true)) && new Set(entries).size === entries.length;
|
|
584
|
+
case "list": return entries !== void 0;
|
|
575
585
|
case "file": return entries !== void 0 && (field.multiple === true || entries.length <= 1);
|
|
576
586
|
}
|
|
577
587
|
}
|
|
@@ -582,7 +592,7 @@ function matchesField(field, value) {
|
|
|
582
592
|
* Bind with `fill(name, matchesAnswer(raw) ? raw : undefined)`. This projection treats an absent
|
|
583
593
|
* value and a string containing only whitespace as unanswered. Every other field value is an
|
|
584
594
|
* answer, including an empty list, `false`, and zero. Core evaluation does not use this projection:
|
|
585
|
-
* its `required` rule remains presence-only.
|
|
595
|
+
* its `required` rule remains presence-only except that `list` requires an entry.
|
|
586
596
|
*
|
|
587
597
|
* @param value - The raw field value, or absence.
|
|
588
598
|
* @returns True if the binding preserves the value as an answer; false otherwise.
|
|
@@ -595,7 +605,7 @@ function matchesAnswer(value) {
|
|
|
595
605
|
*
|
|
596
606
|
* @remarks
|
|
597
607
|
* The runtime control-membership check keeps this boundary total for JavaScript callers that
|
|
598
|
-
*
|
|
608
|
+
* pass values outside the declared {@link FieldControl} contract.
|
|
599
609
|
*
|
|
600
610
|
* @param control - The field control to inspect.
|
|
601
611
|
* @param rule - The named rule to inspect.
|
|
@@ -630,7 +640,7 @@ function appliesRule(control, rule) {
|
|
|
630
640
|
function evaluateField(field, value, values, messages) {
|
|
631
641
|
const errors = [];
|
|
632
642
|
const rule = field.rule;
|
|
633
|
-
if (value === void 0) {
|
|
643
|
+
if (value === void 0 || field.control === "list" && isArray(value) && value.length === 0) {
|
|
634
644
|
if (rule?.required === true && appliesRule(field.control, "required")) errors.push(createFieldError(field, "required", void 0, messages));
|
|
635
645
|
}
|
|
636
646
|
if (rule === void 0) return Object.freeze(errors);
|
|
@@ -651,6 +661,7 @@ function evaluateField(field, value, values, messages) {
|
|
|
651
661
|
failed = isString(value) && isString(rule.minimum) && value < rule.minimum;
|
|
652
662
|
break;
|
|
653
663
|
case "checkbox":
|
|
664
|
+
case "list":
|
|
654
665
|
case "file": failed = isArray(value) && isFiniteNumber(rule.minimum) && value.length < rule.minimum;
|
|
655
666
|
}
|
|
656
667
|
if (failed) errors.push(createFieldError(field, "minimum", rule.minimum, messages));
|
|
@@ -672,6 +683,7 @@ function evaluateField(field, value, values, messages) {
|
|
|
672
683
|
failed = isString(value) && isString(rule.maximum) && value > rule.maximum;
|
|
673
684
|
break;
|
|
674
685
|
case "checkbox":
|
|
686
|
+
case "list":
|
|
675
687
|
case "file": failed = isArray(value) && isFiniteNumber(rule.maximum) && value.length > rule.maximum;
|
|
676
688
|
}
|
|
677
689
|
if (failed) errors.push(createFieldError(field, "maximum", rule.maximum, messages));
|
|
@@ -680,19 +692,45 @@ function evaluateField(field, value, values, messages) {
|
|
|
680
692
|
const multiple = (value - (isFiniteNumber(rule.minimum) ? rule.minimum : 0)) / rule.step;
|
|
681
693
|
if (!isFiniteNumber(rule.step) || rule.step === 0 || !isFiniteNumber(multiple) || Math.abs(multiple - Math.round(multiple)) > 1e-9) errors.push(createFieldError(field, "step", rule.step, messages));
|
|
682
694
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
695
|
+
const entries = isString(value) ? [value] : field.control === "list" && isArray(value) ? value : [];
|
|
696
|
+
for (const constraint of [
|
|
697
|
+
"pattern",
|
|
698
|
+
"email",
|
|
699
|
+
"url",
|
|
700
|
+
"alphanumeric",
|
|
701
|
+
"integer"
|
|
702
|
+
]) {
|
|
703
|
+
if (!appliesRule(field.control, constraint)) continue;
|
|
704
|
+
for (const entry of entries) {
|
|
705
|
+
let failed = false;
|
|
706
|
+
switch (constraint) {
|
|
707
|
+
case "pattern": {
|
|
708
|
+
const pattern = rule.pattern;
|
|
709
|
+
if (pattern !== void 0) {
|
|
710
|
+
const outcome = attempt(() => pattern.length <= 256 && new RegExp(pattern).test(entry));
|
|
711
|
+
failed = !outcome.success || !outcome.value;
|
|
712
|
+
}
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
case "email":
|
|
716
|
+
failed = rule.email === true && !EMAIL_PATTERN.test(entry);
|
|
717
|
+
break;
|
|
718
|
+
case "url":
|
|
719
|
+
failed = rule.url === true && !URL_PATTERN.test(entry);
|
|
720
|
+
break;
|
|
721
|
+
case "alphanumeric":
|
|
722
|
+
failed = rule.alphanumeric === true && !ALPHANUMERIC_PATTERN.test(entry);
|
|
723
|
+
break;
|
|
724
|
+
case "integer": failed = rule.integer === true && !INTEGER_PATTERN.test(entry);
|
|
725
|
+
}
|
|
726
|
+
if (failed) {
|
|
727
|
+
const error = createFieldError(field, constraint, void 0, messages);
|
|
728
|
+
errors.push(field.control === "list" ? Object.freeze({
|
|
729
|
+
...error,
|
|
730
|
+
message: `${JSON.stringify(entry)}: ${error.message}`
|
|
731
|
+
}) : error);
|
|
690
732
|
}
|
|
691
733
|
}
|
|
692
|
-
if (rule.email === true && appliesRule(field.control, "email") && !EMAIL_PATTERN.test(value)) errors.push(createFieldError(field, "email", void 0, messages));
|
|
693
|
-
if (rule.url === true && appliesRule(field.control, "url") && !URL_PATTERN.test(value)) errors.push(createFieldError(field, "url", void 0, messages));
|
|
694
|
-
if (rule.alphanumeric === true && appliesRule(field.control, "alphanumeric") && !ALPHANUMERIC_PATTERN.test(value)) errors.push(createFieldError(field, "alphanumeric", void 0, messages));
|
|
695
|
-
if (rule.integer === true && appliesRule(field.control, "integer") && !INTEGER_PATTERN.test(value)) errors.push(createFieldError(field, "integer", void 0, messages));
|
|
696
734
|
}
|
|
697
735
|
if (value !== void 0 && field.control === "number" && rule.integer === true && appliesRule(field.control, "integer") && !isInteger(value)) errors.push(createFieldError(field, "integer", void 0, messages));
|
|
698
736
|
if (rule.custom !== void 0) {
|
|
@@ -736,6 +774,7 @@ function computeDefaults(schema) {
|
|
|
736
774
|
const defaults = {};
|
|
737
775
|
for (const field of schema.fields) switch (field.control) {
|
|
738
776
|
case "checkbox":
|
|
777
|
+
case "list":
|
|
739
778
|
if (field.default !== void 0) defineEntry(defaults, field.name, cloneValue(field.default));
|
|
740
779
|
break;
|
|
741
780
|
case "password":
|
|
@@ -923,6 +962,9 @@ function serializeForm(schema) {
|
|
|
923
962
|
});
|
|
924
963
|
if (field.default !== void 0) entry.default = field.default;
|
|
925
964
|
break;
|
|
965
|
+
case "list":
|
|
966
|
+
if (field.default !== void 0) entry.default = field.default;
|
|
967
|
+
break;
|
|
926
968
|
case "file":
|
|
927
969
|
if (field.accept !== void 0) entry.accept = field.accept;
|
|
928
970
|
if (field.multiple !== void 0) entry.multiple = field.multiple;
|
|
@@ -1074,7 +1116,8 @@ function auditSchema(schema) {
|
|
|
1074
1116
|
case "color":
|
|
1075
1117
|
case "confirm":
|
|
1076
1118
|
case "select":
|
|
1077
|
-
case "checkbox":
|
|
1119
|
+
case "checkbox":
|
|
1120
|
+
case "list": if (field.default !== void 0 && !matchesField(field, field.default)) faults.push(`Field "${field.name}" has an invalid default`);
|
|
1078
1121
|
}
|
|
1079
1122
|
if (field.control === "select" || field.control === "checkbox") {
|
|
1080
1123
|
const choices = /* @__PURE__ */ new Set();
|
|
@@ -1093,6 +1136,11 @@ function auditSchema(schema) {
|
|
|
1093
1136
|
}
|
|
1094
1137
|
const rule = field.rule;
|
|
1095
1138
|
if (rule === void 0) continue;
|
|
1139
|
+
if (field.control === "list") {
|
|
1140
|
+
for (const bound of [rule.minimum, rule.maximum]) if (isFiniteNumber(bound) && (!isInteger(bound) || bound < 0)) faults.push(`Field "${field.name}" has a count that is not a nonnegative integer`);
|
|
1141
|
+
if (isFiniteNumber(rule.minimum) && rule.minimum > 1024) faults.push(`Field "${field.name}" has minimum greater than ${LIST_LIMIT}`);
|
|
1142
|
+
if (rule.required === true && isFiniteNumber(rule.maximum) && rule.maximum < 1) faults.push(`Field "${field.name}" is required but permits no entries`);
|
|
1143
|
+
}
|
|
1096
1144
|
const temporal = field.control === "date" || field.control === "time" || field.control === "datetime";
|
|
1097
1145
|
if (rule.minimum !== void 0 && !appliesRule(field.control, "minimum")) faults.push(`Field "${field.name}" has minimum on ${field.control}`);
|
|
1098
1146
|
else if (isString(rule.minimum) && !temporal) faults.push(`Field "${field.name}" has a string minimum on ${field.control}`);
|