@orkestrel/form 0.0.11 → 0.0.12
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 +5 -0
- package/dist/src/browser/index.d.ts +26 -5
- package/dist/src/browser/index.js +86 -6
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +62 -14
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +27 -6
- package/dist/src/core/index.d.ts +27 -6
- package/dist/src/core/index.js +62 -14
- package/dist/src/core/index.js.map +1 -1
- package/package.json +1 -1
package/dist/src/core/index.cjs
CHANGED
|
@@ -15,6 +15,7 @@ var FIELD_CONTROLS = Object.freeze([
|
|
|
15
15
|
"confirm",
|
|
16
16
|
"select",
|
|
17
17
|
"checkbox",
|
|
18
|
+
"list",
|
|
18
19
|
"file"
|
|
19
20
|
]);
|
|
20
21
|
/** Lists the members every field declares, whatever its control. */
|
|
@@ -67,6 +68,7 @@ var FIELD_KEYS = Object.freeze({
|
|
|
67
68
|
"choices",
|
|
68
69
|
"default"
|
|
69
70
|
]),
|
|
71
|
+
list: Object.freeze([...FIELD_BASE_KEYS, "default"]),
|
|
70
72
|
file: Object.freeze([
|
|
71
73
|
...FIELD_BASE_KEYS,
|
|
72
74
|
"accept",
|
|
@@ -325,6 +327,7 @@ function isFormField(input) {
|
|
|
325
327
|
const fallback = hasDefault ? input.default : void 0;
|
|
326
328
|
return (0, _orkestrel_contract.arrayOf)(isFieldChoice)(choices) && (!hasDefault || (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)(fallback));
|
|
327
329
|
}
|
|
330
|
+
case "list": return !Object.hasOwn(input, "default") || (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)(input.default);
|
|
328
331
|
case "file": {
|
|
329
332
|
const hasAccept = Object.hasOwn(input, "accept");
|
|
330
333
|
const accept = hasAccept ? input.accept : void 0;
|
|
@@ -449,6 +452,12 @@ function cloneFormField(field) {
|
|
|
449
452
|
choices: cloneChoices(field.choices),
|
|
450
453
|
...field.default === void 0 ? {} : { default: cloneValue(field.default) }
|
|
451
454
|
});
|
|
455
|
+
case "list": return Object.freeze({
|
|
456
|
+
...field,
|
|
457
|
+
...rule,
|
|
458
|
+
...meta,
|
|
459
|
+
...field.default === void 0 ? {} : { default: cloneValue(field.default) }
|
|
460
|
+
});
|
|
452
461
|
case "file": return Object.freeze({
|
|
453
462
|
...field,
|
|
454
463
|
...rule,
|
|
@@ -573,6 +582,7 @@ function matchesField(field, value) {
|
|
|
573
582
|
case "confirm": return (0, _orkestrel_contract.isBoolean)(value);
|
|
574
583
|
case "select": return (0, _orkestrel_contract.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));
|
|
575
584
|
case "checkbox": return entries !== void 0 && entries.every((entry) => (0, _orkestrel_contract.isString)(entry) && field.choices.some((choice) => choice.value === entry && choice.disabled !== true)) && new Set(entries).size === entries.length;
|
|
585
|
+
case "list": return entries !== void 0;
|
|
576
586
|
case "file": return entries !== void 0 && (field.multiple === true || entries.length <= 1);
|
|
577
587
|
}
|
|
578
588
|
}
|
|
@@ -583,7 +593,7 @@ function matchesField(field, value) {
|
|
|
583
593
|
* Bind with `fill(name, matchesAnswer(raw) ? raw : undefined)`. This projection treats an absent
|
|
584
594
|
* value and a string containing only whitespace as unanswered. Every other field value is an
|
|
585
595
|
* answer, including an empty list, `false`, and zero. Core evaluation does not use this projection:
|
|
586
|
-
* its `required` rule remains presence-only.
|
|
596
|
+
* its `required` rule remains presence-only except that `list` requires an entry.
|
|
587
597
|
*
|
|
588
598
|
* @param value - The raw field value, or absence.
|
|
589
599
|
* @returns True if the binding preserves the value as an answer; false otherwise.
|
|
@@ -631,7 +641,7 @@ function appliesRule(control, rule) {
|
|
|
631
641
|
function evaluateField(field, value, values, messages) {
|
|
632
642
|
const errors = [];
|
|
633
643
|
const rule = field.rule;
|
|
634
|
-
if (value === void 0) {
|
|
644
|
+
if (value === void 0 || field.control === "list" && (0, _orkestrel_contract.isArray)(value) && value.length === 0) {
|
|
635
645
|
if (rule?.required === true && appliesRule(field.control, "required")) errors.push(createFieldError(field, "required", void 0, messages));
|
|
636
646
|
}
|
|
637
647
|
if (rule === void 0) return Object.freeze(errors);
|
|
@@ -652,6 +662,7 @@ function evaluateField(field, value, values, messages) {
|
|
|
652
662
|
failed = (0, _orkestrel_contract.isString)(value) && (0, _orkestrel_contract.isString)(rule.minimum) && value < rule.minimum;
|
|
653
663
|
break;
|
|
654
664
|
case "checkbox":
|
|
665
|
+
case "list":
|
|
655
666
|
case "file": failed = (0, _orkestrel_contract.isArray)(value) && (0, _orkestrel_contract.isFiniteNumber)(rule.minimum) && value.length < rule.minimum;
|
|
656
667
|
}
|
|
657
668
|
if (failed) errors.push(createFieldError(field, "minimum", rule.minimum, messages));
|
|
@@ -673,6 +684,7 @@ function evaluateField(field, value, values, messages) {
|
|
|
673
684
|
failed = (0, _orkestrel_contract.isString)(value) && (0, _orkestrel_contract.isString)(rule.maximum) && value > rule.maximum;
|
|
674
685
|
break;
|
|
675
686
|
case "checkbox":
|
|
687
|
+
case "list":
|
|
676
688
|
case "file": failed = (0, _orkestrel_contract.isArray)(value) && (0, _orkestrel_contract.isFiniteNumber)(rule.maximum) && value.length > rule.maximum;
|
|
677
689
|
}
|
|
678
690
|
if (failed) errors.push(createFieldError(field, "maximum", rule.maximum, messages));
|
|
@@ -681,19 +693,45 @@ function evaluateField(field, value, values, messages) {
|
|
|
681
693
|
const multiple = (value - ((0, _orkestrel_contract.isFiniteNumber)(rule.minimum) ? rule.minimum : 0)) / rule.step;
|
|
682
694
|
if (!(0, _orkestrel_contract.isFiniteNumber)(rule.step) || rule.step === 0 || !(0, _orkestrel_contract.isFiniteNumber)(multiple) || Math.abs(multiple - Math.round(multiple)) > 1e-9) errors.push(createFieldError(field, "step", rule.step, messages));
|
|
683
695
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
696
|
+
const entries = (0, _orkestrel_contract.isString)(value) ? [value] : field.control === "list" && (0, _orkestrel_contract.isArray)(value) ? value : [];
|
|
697
|
+
for (const constraint of [
|
|
698
|
+
"pattern",
|
|
699
|
+
"email",
|
|
700
|
+
"url",
|
|
701
|
+
"alphanumeric",
|
|
702
|
+
"integer"
|
|
703
|
+
]) {
|
|
704
|
+
if (!appliesRule(field.control, constraint)) continue;
|
|
705
|
+
for (const entry of entries) {
|
|
706
|
+
let failed = false;
|
|
707
|
+
switch (constraint) {
|
|
708
|
+
case "pattern": {
|
|
709
|
+
const pattern = rule.pattern;
|
|
710
|
+
if (pattern !== void 0) {
|
|
711
|
+
const outcome = (0, _orkestrel_contract.attempt)(() => pattern.length <= 256 && new RegExp(pattern).test(entry));
|
|
712
|
+
failed = !outcome.success || !outcome.value;
|
|
713
|
+
}
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
case "email":
|
|
717
|
+
failed = rule.email === true && !EMAIL_PATTERN.test(entry);
|
|
718
|
+
break;
|
|
719
|
+
case "url":
|
|
720
|
+
failed = rule.url === true && !URL_PATTERN.test(entry);
|
|
721
|
+
break;
|
|
722
|
+
case "alphanumeric":
|
|
723
|
+
failed = rule.alphanumeric === true && !ALPHANUMERIC_PATTERN.test(entry);
|
|
724
|
+
break;
|
|
725
|
+
case "integer": failed = rule.integer === true && !INTEGER_PATTERN.test(entry);
|
|
726
|
+
}
|
|
727
|
+
if (failed) {
|
|
728
|
+
const error = createFieldError(field, constraint, void 0, messages);
|
|
729
|
+
errors.push(field.control === "list" ? Object.freeze({
|
|
730
|
+
...error,
|
|
731
|
+
message: `${JSON.stringify(entry)}: ${error.message}`
|
|
732
|
+
}) : error);
|
|
691
733
|
}
|
|
692
734
|
}
|
|
693
|
-
if (rule.email === true && appliesRule(field.control, "email") && !EMAIL_PATTERN.test(value)) errors.push(createFieldError(field, "email", void 0, messages));
|
|
694
|
-
if (rule.url === true && appliesRule(field.control, "url") && !URL_PATTERN.test(value)) errors.push(createFieldError(field, "url", void 0, messages));
|
|
695
|
-
if (rule.alphanumeric === true && appliesRule(field.control, "alphanumeric") && !ALPHANUMERIC_PATTERN.test(value)) errors.push(createFieldError(field, "alphanumeric", void 0, messages));
|
|
696
|
-
if (rule.integer === true && appliesRule(field.control, "integer") && !INTEGER_PATTERN.test(value)) errors.push(createFieldError(field, "integer", void 0, messages));
|
|
697
735
|
}
|
|
698
736
|
if (value !== void 0 && field.control === "number" && rule.integer === true && appliesRule(field.control, "integer") && !(0, _orkestrel_contract.isInteger)(value)) errors.push(createFieldError(field, "integer", void 0, messages));
|
|
699
737
|
if (rule.custom !== void 0) {
|
|
@@ -737,6 +775,7 @@ function computeDefaults(schema) {
|
|
|
737
775
|
const defaults = {};
|
|
738
776
|
for (const field of schema.fields) switch (field.control) {
|
|
739
777
|
case "checkbox":
|
|
778
|
+
case "list":
|
|
740
779
|
if (field.default !== void 0) defineEntry(defaults, field.name, cloneValue(field.default));
|
|
741
780
|
break;
|
|
742
781
|
case "password":
|
|
@@ -924,6 +963,9 @@ function serializeForm(schema) {
|
|
|
924
963
|
});
|
|
925
964
|
if (field.default !== void 0) entry.default = field.default;
|
|
926
965
|
break;
|
|
966
|
+
case "list":
|
|
967
|
+
if (field.default !== void 0) entry.default = field.default;
|
|
968
|
+
break;
|
|
927
969
|
case "file":
|
|
928
970
|
if (field.accept !== void 0) entry.accept = field.accept;
|
|
929
971
|
if (field.multiple !== void 0) entry.multiple = field.multiple;
|
|
@@ -1075,7 +1117,8 @@ function auditSchema(schema) {
|
|
|
1075
1117
|
case "color":
|
|
1076
1118
|
case "confirm":
|
|
1077
1119
|
case "select":
|
|
1078
|
-
case "checkbox":
|
|
1120
|
+
case "checkbox":
|
|
1121
|
+
case "list": if (field.default !== void 0 && !matchesField(field, field.default)) faults.push(`Field "${field.name}" has an invalid default`);
|
|
1079
1122
|
}
|
|
1080
1123
|
if (field.control === "select" || field.control === "checkbox") {
|
|
1081
1124
|
const choices = /* @__PURE__ */ new Set();
|
|
@@ -1094,6 +1137,11 @@ function auditSchema(schema) {
|
|
|
1094
1137
|
}
|
|
1095
1138
|
const rule = field.rule;
|
|
1096
1139
|
if (rule === void 0) continue;
|
|
1140
|
+
if (field.control === "list") {
|
|
1141
|
+
for (const bound of [rule.minimum, rule.maximum]) if ((0, _orkestrel_contract.isFiniteNumber)(bound) && (!(0, _orkestrel_contract.isInteger)(bound) || bound < 0)) faults.push(`Field "${field.name}" has a count that is not a nonnegative integer`);
|
|
1142
|
+
if ((0, _orkestrel_contract.isFiniteNumber)(rule.minimum) && rule.minimum > 1024) faults.push(`Field "${field.name}" has minimum greater than ${LIST_LIMIT}`);
|
|
1143
|
+
if (rule.required === true && (0, _orkestrel_contract.isFiniteNumber)(rule.maximum) && rule.maximum < 1) faults.push(`Field "${field.name}" is required but permits no entries`);
|
|
1144
|
+
}
|
|
1097
1145
|
const temporal = field.control === "date" || field.control === "time" || field.control === "datetime";
|
|
1098
1146
|
if (rule.minimum !== void 0 && !appliesRule(field.control, "minimum")) faults.push(`Field "${field.name}" has minimum on ${field.control}`);
|
|
1099
1147
|
else if ((0, _orkestrel_contract.isString)(rule.minimum) && !temporal) faults.push(`Field "${field.name}" has a string minimum on ${field.control}`);
|