@marktiderman/genesis-forms 0.8.0 → 0.9.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/README.md +299 -10
- package/dist/destinations.cjs +202 -39
- package/dist/destinations.cjs.map +1 -1
- package/dist/destinations.d.cts +67 -21
- package/dist/destinations.d.ts +67 -21
- package/dist/destinations.js +202 -40
- package/dist/destinations.js.map +1 -1
- package/dist/{engine-Bj23YJlB.d.cts → engine-BaHPC2-1.d.cts} +2 -2
- package/dist/{engine-CzinTJph.d.ts → engine-Da99Tv-E.d.ts} +2 -2
- package/dist/index.cjs +996 -167
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +288 -11
- package/dist/index.d.ts +288 -11
- package/dist/index.js +975 -168
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +563 -173
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +3 -474
- package/dist/server.d.ts +3 -474
- package/dist/server.js +558 -174
- package/dist/server.js.map +1 -1
- package/dist/submit-protocol-CFi-0Qd0.d.ts +840 -0
- package/dist/submit-protocol-D0cOckmK.d.cts +840 -0
- package/dist/testing.cjs +250 -154
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +4 -4
- package/dist/testing.d.ts +4 -4
- package/dist/testing.js +250 -154
- package/dist/testing.js.map +1 -1
- package/dist/{types-jqDKcmP4.d.cts → types-C0pUfew3.d.cts} +7 -1
- package/dist/{types-jqDKcmP4.d.ts → types-C0pUfew3.d.ts} +7 -1
- package/dist/{validate-DvfQHYnu.d.ts → validate-DpmBssFk.d.ts} +258 -140
- package/dist/{validate-irIPubxc.d.cts → validate-U5LoW3eR.d.cts} +258 -140
- package/package.json +2 -2
- package/dist/upload-protocol-BOe018kL.d.cts +0 -122
- package/dist/upload-protocol-BUO74WkS.d.ts +0 -122
package/dist/testing.js
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
1
|
+
// src/props-check.ts
|
|
2
|
+
var RESERVED_NAMES = new Set(Object.getOwnPropertyNames(Object.prototype));
|
|
3
|
+
var isPlainRecord = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
4
|
+
var isWholeNumber = (v) => typeof v === "number" && Number.isInteger(v);
|
|
5
|
+
var isFiniteNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
6
|
+
function propsChecker(prefix, report) {
|
|
7
|
+
const at = (path) => path ? `${prefix}.${path}` : prefix;
|
|
8
|
+
const issue = (path, message) => report(at(path), message);
|
|
9
|
+
return {
|
|
10
|
+
issue,
|
|
11
|
+
text(v, path, nonEmpty = false) {
|
|
12
|
+
if (typeof v !== "string" || nonEmpty && !v) issue(path, nonEmpty ? "Required." : "Required text.");
|
|
13
|
+
},
|
|
14
|
+
optionalText(v, path) {
|
|
15
|
+
if (v !== void 0 && typeof v !== "string") issue(path, "Must be text.");
|
|
16
|
+
},
|
|
17
|
+
optionalBoolean(v, path) {
|
|
18
|
+
if (v !== void 0 && typeof v !== "boolean") issue(path, "Must be true or false.");
|
|
19
|
+
},
|
|
20
|
+
optionalInt(v, path, floor) {
|
|
21
|
+
if (v !== void 0 && !(isWholeNumber(v) && v >= floor)) issue(path, `Must be a whole number of ${floor} or more.`);
|
|
22
|
+
},
|
|
23
|
+
ordered(low, high, path, what) {
|
|
24
|
+
if (isWholeNumber(low) && isWholeNumber(high) && low > high) issue(path, `Can't be more than the ${what}.`);
|
|
25
|
+
},
|
|
26
|
+
name(v, path, what) {
|
|
27
|
+
if (typeof v !== "string" || !v) {
|
|
28
|
+
issue(path, `${what} must be a non-empty string.`);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (RESERVED_NAMES.has(v)) {
|
|
32
|
+
issue(path, `"${v}" is reserved and cannot be used as ${what.toLowerCase()}.`);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
1
40
|
// src/formula.ts
|
|
2
41
|
var FORMULA_FUNCTIONS = ["sum", "avg", "min", "max"];
|
|
3
42
|
var MAX_FORMULA_LENGTH = 2e3;
|
|
@@ -532,6 +571,17 @@ var calculatedFieldType = {
|
|
|
532
571
|
value: { json: true },
|
|
533
572
|
operators: numberOps,
|
|
534
573
|
a11y: { semantics: "control" },
|
|
574
|
+
// A number, unless the format says text; without a format it can hold either, so it isn't known.
|
|
575
|
+
literal: (props) => props.format === "text" ? "string" : props.format === "number" || props.format === "percent" ? "number" : void 0,
|
|
576
|
+
// The formula itself is checked once every key is known (`parseForm`).
|
|
577
|
+
checkProps: (props, check) => {
|
|
578
|
+
if (props.format !== void 0 && props.format !== "number" && props.format !== "percent" && props.format !== "text") {
|
|
579
|
+
check.issue("format", "Must be number, percent or text.");
|
|
580
|
+
}
|
|
581
|
+
if (props.decimals !== void 0 && !(isWholeNumber(props.decimals) && props.decimals >= 0 && props.decimals <= MAX_CALCULATED_DECIMALS)) {
|
|
582
|
+
check.issue("decimals", `Must be a whole number from 0 to ${MAX_CALCULATED_DECIMALS}.`);
|
|
583
|
+
}
|
|
584
|
+
},
|
|
535
585
|
// The engine works the value out; any number, text or true / false it made is valid.
|
|
536
586
|
validate: (value) => typeof value === "number" || typeof value === "string" || typeof value === "boolean" ? null : "This value is worked out from other answers.",
|
|
537
587
|
summarize: (value, field) => formatCalculated(value, field)
|
|
@@ -589,12 +639,42 @@ var MAX_HIGHLIGHT_NOTE_LENGTH = 1e3;
|
|
|
589
639
|
var MAX_HIGHLIGHT_QUOTE_LENGTH = 2e3;
|
|
590
640
|
var HIGHLIGHT_NOTE_MESSAGE = "Add a note to each highlight.";
|
|
591
641
|
var sourceText = (field) => typeof field.props?.text === "string" && field.props.text ? field.props.text : void 0;
|
|
642
|
+
var listProps = (props, check) => {
|
|
643
|
+
check.optionalInt(props.min, "min", 0);
|
|
644
|
+
check.optionalInt(props.max, "max", 1);
|
|
645
|
+
check.optionalInt(props.itemMaxLength, "itemMaxLength", 1);
|
|
646
|
+
check.optionalText(props.placeholder, "placeholder");
|
|
647
|
+
check.ordered(props.min, props.max ?? 10, "min", "most items");
|
|
648
|
+
};
|
|
649
|
+
var tableProps = (props, check) => {
|
|
650
|
+
const columns = props.columns;
|
|
651
|
+
if (!Array.isArray(columns) || columns.length < 1 || columns.length > MAX_TABLE_COLUMNS) {
|
|
652
|
+
check.issue("columns", `Must be a list of 1 to ${MAX_TABLE_COLUMNS} columns.`);
|
|
653
|
+
} else {
|
|
654
|
+
const seen = /* @__PURE__ */ new Set();
|
|
655
|
+
columns.forEach((c, j) => {
|
|
656
|
+
const at = `columns[${j}]`;
|
|
657
|
+
if (!isPlainRecord(c)) return check.issue(at, "Each column needs a key and a label.");
|
|
658
|
+
if (check.name(c.key, `${at}.key`, "A column key")) {
|
|
659
|
+
if (seen.has(c.key)) check.issue(`${at}.key`, `Duplicate column key "${c.key}".`);
|
|
660
|
+
seen.add(c.key);
|
|
661
|
+
}
|
|
662
|
+
check.text(c.label, `${at}.label`);
|
|
663
|
+
check.optionalBoolean(c.long, `${at}.long`);
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
check.optionalInt(props.minRows, "minRows", 0);
|
|
667
|
+
check.optionalInt(props.maxRows, "maxRows", 1);
|
|
668
|
+
check.ordered(props.minRows, props.maxRows ?? 3, "minRows", "most rows");
|
|
669
|
+
};
|
|
592
670
|
var structuredFieldTypes = [
|
|
593
671
|
{
|
|
594
672
|
type: "list",
|
|
595
673
|
label: "List",
|
|
596
674
|
category: "text",
|
|
597
675
|
value: { json: true },
|
|
676
|
+
literal: "string",
|
|
677
|
+
checkProps: listProps,
|
|
598
678
|
// logic.ts `contains` on an array is exact item equality, same as multi-choice, so it is offered.
|
|
599
679
|
operators: ["contains", "answered", "empty"],
|
|
600
680
|
a11y: { semantics: "group" },
|
|
@@ -620,6 +700,7 @@ var structuredFieldTypes = [
|
|
|
620
700
|
label: "Table",
|
|
621
701
|
category: "advanced",
|
|
622
702
|
value: { json: true },
|
|
703
|
+
checkProps: tableProps,
|
|
623
704
|
operators: ["answered", "empty"],
|
|
624
705
|
a11y: { semantics: "group" },
|
|
625
706
|
isEmpty: (value) => emptyList(value, (r) => isObject(r) && Object.values(r).every(blankText)),
|
|
@@ -663,6 +744,10 @@ var structuredFieldTypes = [
|
|
|
663
744
|
label: "Notes at moments",
|
|
664
745
|
category: "media",
|
|
665
746
|
value: { json: true },
|
|
747
|
+
checkProps: (props, check) => {
|
|
748
|
+
check.optionalInt(props.maxNotes, "maxNotes", 1);
|
|
749
|
+
check.optionalInt(props.textMaxLength, "textMaxLength", 1);
|
|
750
|
+
},
|
|
666
751
|
operators: ["answered", "empty"],
|
|
667
752
|
a11y: { semantics: "group" },
|
|
668
753
|
isEmpty: (value) => emptyList(value, (n) => isObject(n) && blankText(n.text)),
|
|
@@ -686,6 +771,10 @@ var structuredFieldTypes = [
|
|
|
686
771
|
label: "Highlights",
|
|
687
772
|
category: "media",
|
|
688
773
|
value: { json: true },
|
|
774
|
+
checkProps: (props, check) => {
|
|
775
|
+
check.optionalText(props.text, "text");
|
|
776
|
+
check.optionalInt(props.maxHighlights, "maxHighlights", 1);
|
|
777
|
+
},
|
|
689
778
|
operators: ["answered", "empty"],
|
|
690
779
|
a11y: { semantics: "group" },
|
|
691
780
|
isEmpty: (value) => emptyList(value, () => false),
|
|
@@ -777,6 +866,22 @@ var photoFieldType = {
|
|
|
777
866
|
value: { json: true },
|
|
778
867
|
operators: ["answered", "empty"],
|
|
779
868
|
a11y: { semantics: "group" },
|
|
869
|
+
checkProps: (props, check) => {
|
|
870
|
+
if (props.maxFiles !== void 0 && !(isWholeNumber(props.maxFiles) && props.maxFiles >= 1 && props.maxFiles <= MAX_PHOTO_FILES)) {
|
|
871
|
+
check.issue("maxFiles", `Must be a whole number from 1 to ${MAX_PHOTO_FILES}.`);
|
|
872
|
+
}
|
|
873
|
+
if (props.maxSizeMb !== void 0 && !(isWholeNumber(props.maxSizeMb) && props.maxSizeMb >= 1 && props.maxSizeMb <= MAX_PHOTO_SIZE_MB)) {
|
|
874
|
+
check.issue("maxSizeMb", `Must be a whole number from 1 to ${MAX_PHOTO_SIZE_MB}.`);
|
|
875
|
+
}
|
|
876
|
+
if (props.formats !== void 0) {
|
|
877
|
+
const formats = props.formats;
|
|
878
|
+
if (!Array.isArray(formats) || formats.length === 0 || !formats.every(isPhotoFormat)) {
|
|
879
|
+
check.issue("formats", "Must be a list of one or more of jpeg, png, heic and webp.");
|
|
880
|
+
} else if (new Set(formats).size !== formats.length) {
|
|
881
|
+
check.issue("formats", "Name each format once.");
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
},
|
|
780
885
|
isEmpty: (value) => value === void 0 || value === null || Array.isArray(value) && value.length === 0,
|
|
781
886
|
validate: (value, field) => {
|
|
782
887
|
if (!Array.isArray(value) || !value.every(isPhotoRef)) return "Add a photo.";
|
|
@@ -794,22 +899,59 @@ var photoFieldType = {
|
|
|
794
899
|
};
|
|
795
900
|
|
|
796
901
|
// src/fields.ts
|
|
797
|
-
var
|
|
902
|
+
var VALIDATOR_FAILED_MESSAGE = "This answer couldn't be checked.";
|
|
903
|
+
var REGISTRY = /* @__PURE__ */ Symbol.for("@marktiderman/genesis-forms/field-types@2");
|
|
904
|
+
var BUILT_IN = /* @__PURE__ */ Symbol.for("@marktiderman/genesis-forms/built-in-field-types@2");
|
|
798
905
|
var globalScope = globalThis;
|
|
799
906
|
var registry = globalScope[REGISTRY] ??= /* @__PURE__ */ new Map();
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
);
|
|
907
|
+
var builtIn = globalScope[BUILT_IN] ??= /* @__PURE__ */ new Set();
|
|
908
|
+
var freezeRecord = (v) => v && typeof v === "object" && !Array.isArray(v) ? Object.freeze({ ...v }) : v;
|
|
909
|
+
function frozen(manifest) {
|
|
910
|
+
const copy = { ...manifest };
|
|
911
|
+
for (const key of ["choice", "a11y", "value"]) if (key in copy) copy[key] = freezeRecord(copy[key]);
|
|
912
|
+
if (Array.isArray(copy.operators)) {
|
|
913
|
+
const operators = Object.freeze([...copy.operators]);
|
|
914
|
+
Object.defineProperty(copy, "operators", { get: () => [...operators], enumerable: true });
|
|
807
915
|
}
|
|
808
|
-
|
|
916
|
+
return Object.freeze(copy);
|
|
917
|
+
}
|
|
918
|
+
function registerBuiltIn(manifest) {
|
|
919
|
+
builtIn.add(manifest.type);
|
|
920
|
+
if (!registry.has(manifest.type)) registry.set(manifest.type, frozen(manifest));
|
|
809
921
|
}
|
|
810
922
|
function getFieldType(type) {
|
|
811
923
|
return registry.get(type);
|
|
812
924
|
}
|
|
925
|
+
function choiceKind(type) {
|
|
926
|
+
return registry.get(type)?.choice;
|
|
927
|
+
}
|
|
928
|
+
function isChoiceField(block) {
|
|
929
|
+
return !!block && block.kind === "field" && typeof block.type === "string" && choiceKind(block.type) !== void 0;
|
|
930
|
+
}
|
|
931
|
+
function literalType(type, props) {
|
|
932
|
+
const literal = registry.get(type)?.literal;
|
|
933
|
+
if (typeof literal !== "function") return literal;
|
|
934
|
+
try {
|
|
935
|
+
return literal(props);
|
|
936
|
+
} catch {
|
|
937
|
+
return void 0;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
var CONTENT_REGISTRY = /* @__PURE__ */ Symbol.for("@marktiderman/genesis-forms/content-types@1");
|
|
941
|
+
var contentScope = globalThis;
|
|
942
|
+
var contentRegistry = contentScope[CONTENT_REGISTRY] ??= /* @__PURE__ */ new Map();
|
|
943
|
+
function getContentType(type) {
|
|
944
|
+
return contentRegistry.get(type);
|
|
945
|
+
}
|
|
946
|
+
function listContentTypes() {
|
|
947
|
+
return [...contentRegistry.values()];
|
|
948
|
+
}
|
|
949
|
+
for (const manifest of [
|
|
950
|
+
{ type: "heading", label: "Heading" },
|
|
951
|
+
{ type: "text", label: "Text" }
|
|
952
|
+
]) {
|
|
953
|
+
if (!contentRegistry.has(manifest.type)) contentRegistry.set(manifest.type, frozen(manifest));
|
|
954
|
+
}
|
|
813
955
|
function isEmptyValue(value, field) {
|
|
814
956
|
const manifest = field ? registry.get(field.type) : void 0;
|
|
815
957
|
if (manifest?.isEmpty) return manifest.isEmpty(value);
|
|
@@ -992,6 +1134,18 @@ function isPhoneNumber(value) {
|
|
|
992
1134
|
}
|
|
993
1135
|
return digits >= 7 && digits <= 15;
|
|
994
1136
|
}
|
|
1137
|
+
var oneOptionValidate = (value, field) => typeof value === "string" && (choiceOptions(field).some((o) => o.value === value) || typedOther(field, value)) ? null : "Choose one of the options.";
|
|
1138
|
+
var oneOptionSummary = (value, field) => optionLabel(field, String(value));
|
|
1139
|
+
var optionsValidate = (value, field) => {
|
|
1140
|
+
const allowed = new Set(choiceOptions(field).map((o) => o.value));
|
|
1141
|
+
const others = Array.isArray(value) ? value.filter((v) => typeof v === "string" && !allowed.has(v)) : [];
|
|
1142
|
+
return Array.isArray(value) && new Set(value).size === value.length && value.every((v) => typeof v === "string") && (others.length === 0 || others.length === 1 && typedOther(field, others[0])) ? null : "Choose from the options.";
|
|
1143
|
+
};
|
|
1144
|
+
var optionsSummary = (value, field) => Array.isArray(value) ? value.map((v) => optionLabel(field, String(v))).join(", ") : String(value);
|
|
1145
|
+
var scaleProps = (props, check) => {
|
|
1146
|
+
check.optionalText(props.lowLabel, "lowLabel");
|
|
1147
|
+
check.optionalText(props.highLabel, "highLabel");
|
|
1148
|
+
};
|
|
995
1149
|
var builtins = [
|
|
996
1150
|
{
|
|
997
1151
|
type: "short-text",
|
|
@@ -1000,6 +1154,7 @@ var builtins = [
|
|
|
1000
1154
|
value: { json: true },
|
|
1001
1155
|
operators: textOps,
|
|
1002
1156
|
a11y: { semantics: "control" },
|
|
1157
|
+
literal: "string",
|
|
1003
1158
|
validate: textValidate(500)
|
|
1004
1159
|
},
|
|
1005
1160
|
{
|
|
@@ -1009,6 +1164,7 @@ var builtins = [
|
|
|
1009
1164
|
value: { json: true },
|
|
1010
1165
|
operators: textOps,
|
|
1011
1166
|
a11y: { semantics: "control" },
|
|
1167
|
+
literal: "string",
|
|
1012
1168
|
validate: textValidate(5e3)
|
|
1013
1169
|
},
|
|
1014
1170
|
{
|
|
@@ -1018,6 +1174,7 @@ var builtins = [
|
|
|
1018
1174
|
value: { json: true },
|
|
1019
1175
|
operators: textOps,
|
|
1020
1176
|
a11y: { semantics: "control", autocomplete: "email", inputMode: "email" },
|
|
1177
|
+
literal: "string",
|
|
1021
1178
|
validate: (value) => typeof value === "string" && isEmailAddress(value.trim()) ? null : "Enter an email address like name@example.com.",
|
|
1022
1179
|
normalize: (value) => typeof value === "string" ? value.trim() : value
|
|
1023
1180
|
},
|
|
@@ -1028,6 +1185,7 @@ var builtins = [
|
|
|
1028
1185
|
value: { json: true },
|
|
1029
1186
|
operators: ["eq", "neq", "answered", "empty"],
|
|
1030
1187
|
a11y: { semantics: "control", autocomplete: "tel", inputMode: "tel" },
|
|
1188
|
+
literal: "string",
|
|
1031
1189
|
validate: (value) => typeof value === "string" && isPhoneNumber(value.trim()) ? null : "Enter a phone number with 7 to 15 digits.",
|
|
1032
1190
|
normalize: (value) => typeof value === "string" ? value.trim() : value,
|
|
1033
1191
|
summarize: (value) => String(value)
|
|
@@ -1039,6 +1197,13 @@ var builtins = [
|
|
|
1039
1197
|
value: { json: true },
|
|
1040
1198
|
operators: numberOps2,
|
|
1041
1199
|
a11y: { semantics: "group" },
|
|
1200
|
+
literal: "number",
|
|
1201
|
+
checkProps: (props, check) => {
|
|
1202
|
+
scaleProps(props, check);
|
|
1203
|
+
if (props.display !== void 0 && props.display !== "numbers" && props.display !== "stars") {
|
|
1204
|
+
check.issue("display", "Must be numbers or stars.");
|
|
1205
|
+
}
|
|
1206
|
+
},
|
|
1042
1207
|
validate: (value, field) => {
|
|
1043
1208
|
const { min, max } = intRange(field, 1, 5);
|
|
1044
1209
|
return Number.isInteger(value) && value >= min && value <= max ? null : `Choose a rating from ${min} to ${max}.`;
|
|
@@ -1056,6 +1221,8 @@ var builtins = [
|
|
|
1056
1221
|
value: { json: true },
|
|
1057
1222
|
operators: numberOps2,
|
|
1058
1223
|
a11y: { semantics: "group" },
|
|
1224
|
+
literal: "number",
|
|
1225
|
+
checkProps: scaleProps,
|
|
1059
1226
|
validate: (value) => Number.isInteger(value) && value >= 0 && value <= 10 ? null : "Choose a score from 0 to 10.",
|
|
1060
1227
|
summarize: (value, field) => {
|
|
1061
1228
|
const end = scaleEndLabel(field, value, 0, 10);
|
|
@@ -1069,8 +1236,10 @@ var builtins = [
|
|
|
1069
1236
|
value: { json: true },
|
|
1070
1237
|
operators: ["eq", "neq", "in", "answered", "empty"],
|
|
1071
1238
|
a11y: { semantics: "group" },
|
|
1072
|
-
|
|
1073
|
-
|
|
1239
|
+
literal: "string",
|
|
1240
|
+
choice: { multiple: false },
|
|
1241
|
+
validate: oneOptionValidate,
|
|
1242
|
+
summarize: oneOptionSummary
|
|
1074
1243
|
},
|
|
1075
1244
|
{
|
|
1076
1245
|
type: "multi-choice",
|
|
@@ -1079,12 +1248,10 @@ var builtins = [
|
|
|
1079
1248
|
value: { json: true },
|
|
1080
1249
|
operators: ["contains", "answered", "empty"],
|
|
1081
1250
|
a11y: { semantics: "group" },
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
},
|
|
1087
|
-
summarize: (value, field) => Array.isArray(value) ? value.map((v) => optionLabel(field, String(v))).join(", ") : String(value)
|
|
1251
|
+
literal: "string",
|
|
1252
|
+
choice: { multiple: true },
|
|
1253
|
+
validate: optionsValidate,
|
|
1254
|
+
summarize: optionsSummary
|
|
1088
1255
|
},
|
|
1089
1256
|
{
|
|
1090
1257
|
type: "yes-no",
|
|
@@ -1093,6 +1260,7 @@ var builtins = [
|
|
|
1093
1260
|
value: { json: true },
|
|
1094
1261
|
operators: ["eq", "neq", "answered", "empty"],
|
|
1095
1262
|
a11y: { semantics: "group" },
|
|
1263
|
+
literal: "boolean",
|
|
1096
1264
|
validate: (value) => typeof value === "boolean" ? null : "Choose yes or no.",
|
|
1097
1265
|
summarize: (value) => value ? "Yes" : "No"
|
|
1098
1266
|
},
|
|
@@ -1103,8 +1271,10 @@ var builtins = [
|
|
|
1103
1271
|
value: { json: true },
|
|
1104
1272
|
operators: ["eq", "neq", "in", "answered", "empty"],
|
|
1105
1273
|
a11y: { semantics: "control" },
|
|
1106
|
-
|
|
1107
|
-
|
|
1274
|
+
literal: "string",
|
|
1275
|
+
choice: { multiple: false },
|
|
1276
|
+
validate: oneOptionValidate,
|
|
1277
|
+
summarize: oneOptionSummary
|
|
1108
1278
|
},
|
|
1109
1279
|
{
|
|
1110
1280
|
type: "number",
|
|
@@ -1113,6 +1283,16 @@ var builtins = [
|
|
|
1113
1283
|
value: { json: true },
|
|
1114
1284
|
operators: numberOps2,
|
|
1115
1285
|
a11y: { semantics: "control", inputMode: "decimal" },
|
|
1286
|
+
literal: "number",
|
|
1287
|
+
checkProps: (props, check) => {
|
|
1288
|
+
for (const k of ["min", "max", "step"]) {
|
|
1289
|
+
if (props[k] !== void 0 && !isFiniteNumber(props[k])) check.issue(k, "Must be a number.");
|
|
1290
|
+
}
|
|
1291
|
+
if (isFiniteNumber(props.step) && props.step <= 0) check.issue("step", "Must be above 0.");
|
|
1292
|
+
if (isFiniteNumber(props.min) && isFiniteNumber(props.max) && props.min > props.max) {
|
|
1293
|
+
check.issue("min", "Can't be more than the largest number.");
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1116
1296
|
validate: (value, field) => {
|
|
1117
1297
|
if (typeof value !== "number" || !Number.isFinite(value)) return "Enter a number.";
|
|
1118
1298
|
const { min, max, step } = numberLimits(field);
|
|
@@ -1139,6 +1319,17 @@ var builtins = [
|
|
|
1139
1319
|
value: { json: true },
|
|
1140
1320
|
operators: ["eq", "neq", "answered", "empty"],
|
|
1141
1321
|
a11y: { semantics: "control" },
|
|
1322
|
+
literal: "string",
|
|
1323
|
+
checkProps: (props, check) => {
|
|
1324
|
+
for (const k of ["min", "max"]) {
|
|
1325
|
+
if (props[k] !== void 0 && !(typeof props[k] === "string" && isIsoDate(props[k]))) {
|
|
1326
|
+
check.issue(k, "Must be a date like 2026-10-22.");
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
if (typeof props.min === "string" && typeof props.max === "string" && isIsoDate(props.min) && isIsoDate(props.max) && props.min > props.max) {
|
|
1330
|
+
check.issue("min", "Can't be after the latest date.");
|
|
1331
|
+
}
|
|
1332
|
+
},
|
|
1142
1333
|
validate: (value, field) => {
|
|
1143
1334
|
if (typeof value !== "string" || !isIsoDate(value.trim())) return "Enter a date like 2026-10-22.";
|
|
1144
1335
|
const date = value.trim();
|
|
@@ -1161,6 +1352,8 @@ var builtins = [
|
|
|
1161
1352
|
value: { json: true },
|
|
1162
1353
|
operators: ["answered", "empty"],
|
|
1163
1354
|
a11y: { semantics: "group" },
|
|
1355
|
+
literal: "boolean",
|
|
1356
|
+
checkProps: (props, check) => check.optionalText(props.text, "text"),
|
|
1164
1357
|
// Unchecked is unanswered, so a required consent must be checked.
|
|
1165
1358
|
isEmpty: (value) => value === void 0 || value === null || value === false,
|
|
1166
1359
|
validate: (value) => value === true ? null : "Check the box to agree.",
|
|
@@ -1210,7 +1403,7 @@ function dateLimits(field) {
|
|
|
1210
1403
|
if (typeof max === "string" && isIsoDate(max)) out.max = max;
|
|
1211
1404
|
return out;
|
|
1212
1405
|
}
|
|
1213
|
-
for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType, photoFieldType])
|
|
1406
|
+
for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType, photoFieldType]) registerBuiltIn(manifest);
|
|
1214
1407
|
|
|
1215
1408
|
// src/events.ts
|
|
1216
1409
|
function isPlainObject(value) {
|
|
@@ -1427,8 +1620,8 @@ function fieldMap(def) {
|
|
|
1427
1620
|
}
|
|
1428
1621
|
|
|
1429
1622
|
// src/options.ts
|
|
1430
|
-
var
|
|
1431
|
-
var
|
|
1623
|
+
var isChoice = isChoiceField;
|
|
1624
|
+
var isMultiple = (field) => choiceKind(field.type)?.multiple === true;
|
|
1432
1625
|
function put(record, key, value) {
|
|
1433
1626
|
Object.defineProperty(record, key, { value, enumerable: true, writable: true, configurable: true });
|
|
1434
1627
|
}
|
|
@@ -1506,7 +1699,7 @@ function isPrefillParam(param) {
|
|
|
1506
1699
|
return typeof param === "string" && param.length <= MAX_PREFILL_PARAM_LENGTH && /^[A-Za-z0-9_.-]+$/.test(param);
|
|
1507
1700
|
}
|
|
1508
1701
|
function prefillSetting(field) {
|
|
1509
|
-
if (!
|
|
1702
|
+
if (!choiceKind(field.type)) return void 0;
|
|
1510
1703
|
const raw = field.props?.prefill;
|
|
1511
1704
|
if (!raw || typeof raw !== "object" || Array.isArray(raw) || !isPrefillParam(raw.param)) return void 0;
|
|
1512
1705
|
return { param: raw.param, hideWhenPrefilled: raw.hideWhenPrefilled === true };
|
|
@@ -1534,7 +1727,7 @@ function prefillFromLink(definition, params, values = {}) {
|
|
|
1534
1727
|
if (!raw.length) continue;
|
|
1535
1728
|
const shown2 = new Set(visibleOptions(definition, block, seen).map((o) => o.value));
|
|
1536
1729
|
let value;
|
|
1537
|
-
if (block
|
|
1730
|
+
if (isMultiple(block)) {
|
|
1538
1731
|
const picked = [];
|
|
1539
1732
|
for (const r of raw) {
|
|
1540
1733
|
for (const v of shown2.has(r) ? [r] : r.split(",").map((s) => s.trim())) {
|
|
@@ -1605,8 +1798,13 @@ function validateField(field, value, options = {}) {
|
|
|
1605
1798
|
}
|
|
1606
1799
|
const manifest = getFieldType(field.type);
|
|
1607
1800
|
if (!manifest) return `Unknown field type "${field.type}".`;
|
|
1608
|
-
|
|
1609
|
-
|
|
1801
|
+
let builtIn2;
|
|
1802
|
+
try {
|
|
1803
|
+
builtIn2 = manifest.validate?.(value, field) ?? null;
|
|
1804
|
+
} catch {
|
|
1805
|
+
builtIn2 = VALIDATOR_FAILED_MESSAGE;
|
|
1806
|
+
}
|
|
1807
|
+
if (builtIn2) return builtIn2;
|
|
1610
1808
|
return options.custom ? runCustom(options.custom, value) : null;
|
|
1611
1809
|
}
|
|
1612
1810
|
|
|
@@ -2090,25 +2288,6 @@ function shown(v) {
|
|
|
2090
2288
|
}
|
|
2091
2289
|
var isObject2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
2092
2290
|
var isInt = (v) => typeof v === "number" && Number.isInteger(v);
|
|
2093
|
-
var LITERAL_TYPE = {
|
|
2094
|
-
"short-text": "string",
|
|
2095
|
-
"long-text": "string",
|
|
2096
|
-
email: "string",
|
|
2097
|
-
phone: "string",
|
|
2098
|
-
rating: "number",
|
|
2099
|
-
nps: "number",
|
|
2100
|
-
"single-choice": "string",
|
|
2101
|
-
"multi-choice": "string",
|
|
2102
|
-
"yes-no": "boolean",
|
|
2103
|
-
list: "string",
|
|
2104
|
-
dropdown: "string",
|
|
2105
|
-
number: "number",
|
|
2106
|
-
date: "string",
|
|
2107
|
-
consent: "boolean",
|
|
2108
|
-
// A calculated field holds a number unless its format says text; see `literalType`.
|
|
2109
|
-
calculated: "number"
|
|
2110
|
-
};
|
|
2111
|
-
var CHOICE_TYPES = /* @__PURE__ */ new Set(["single-choice", "multi-choice", "dropdown"]);
|
|
2112
2291
|
var ORDERING = /* @__PURE__ */ new Set(["lt", "lte", "gt", "gte"]);
|
|
2113
2292
|
var OPERATORS = /* @__PURE__ */ new Set([
|
|
2114
2293
|
"eq",
|
|
@@ -2122,8 +2301,11 @@ var OPERATORS = /* @__PURE__ */ new Set([
|
|
|
2122
2301
|
"answered",
|
|
2123
2302
|
"empty"
|
|
2124
2303
|
]);
|
|
2125
|
-
var RESERVED = new Set(Object.getOwnPropertyNames(Object.prototype));
|
|
2126
2304
|
var MAX_CONDITION_DEPTH = 32;
|
|
2305
|
+
function contentKinds() {
|
|
2306
|
+
const names = listContentTypes().map((c) => c.type);
|
|
2307
|
+
return names.length > 1 ? `${names.slice(0, -1).join(", ")} or ${names[names.length - 1]}` : names[0] ?? "a registered content type";
|
|
2308
|
+
}
|
|
2127
2309
|
function parseForm(input) {
|
|
2128
2310
|
const issues = [];
|
|
2129
2311
|
const warnings = [];
|
|
@@ -2153,7 +2335,7 @@ function parseForm(input) {
|
|
|
2153
2335
|
}
|
|
2154
2336
|
const fits = (x) => typeof x === "string" && x.length <= OPTION_LIMITS.attributeValue;
|
|
2155
2337
|
for (const [name2, v] of Object.entries(attributes)) {
|
|
2156
|
-
if (!name2.trim() || name2.length > OPTION_LIMITS.attributeName ||
|
|
2338
|
+
if (!name2.trim() || name2.length > OPTION_LIMITS.attributeName || RESERVED_NAMES.has(name2)) {
|
|
2157
2339
|
issue(`${at}.attributes`, `${shown(name2)} isn't a usable attribute name (at most ${OPTION_LIMITS.attributeName} characters).`);
|
|
2158
2340
|
} else if (!fits(v) && !(Array.isArray(v) && v.length <= OPTION_LIMITS.attributeItems && v.every(fits))) {
|
|
2159
2341
|
issue(
|
|
@@ -2168,104 +2350,18 @@ function parseForm(input) {
|
|
|
2168
2350
|
issue(path, `${what} must be a non-empty string.`);
|
|
2169
2351
|
return false;
|
|
2170
2352
|
}
|
|
2171
|
-
if (
|
|
2353
|
+
if (RESERVED_NAMES.has(v)) {
|
|
2172
2354
|
issue(path, `"${v}" is reserved and cannot be used as ${what.toLowerCase()}.`);
|
|
2173
2355
|
return false;
|
|
2174
2356
|
}
|
|
2175
2357
|
return true;
|
|
2176
2358
|
};
|
|
2177
|
-
const
|
|
2178
|
-
if (
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
const structuredProps = (type, props, path) => {
|
|
2184
|
-
if (type === "list") {
|
|
2185
|
-
optionalInt(props.min, `${path}.min`, 0);
|
|
2186
|
-
optionalInt(props.max, `${path}.max`, 1);
|
|
2187
|
-
optionalInt(props.itemMaxLength, `${path}.itemMaxLength`, 1);
|
|
2188
|
-
optionalText(props.placeholder, `${path}.placeholder`);
|
|
2189
|
-
ordered(props.min, props.max ?? 10, `${path}.min`, "most items");
|
|
2190
|
-
} else if (type === "table") {
|
|
2191
|
-
const columns = props.columns;
|
|
2192
|
-
if (!Array.isArray(columns) || columns.length < 1 || columns.length > MAX_TABLE_COLUMNS) {
|
|
2193
|
-
issue(`${path}.columns`, `Must be a list of 1 to ${MAX_TABLE_COLUMNS} columns.`);
|
|
2194
|
-
} else {
|
|
2195
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2196
|
-
columns.forEach((c, j) => {
|
|
2197
|
-
const at = `${path}.columns[${j}]`;
|
|
2198
|
-
if (!isObject2(c)) return issue(at, "Each column needs a key and a label.");
|
|
2199
|
-
if (name(c.key, `${at}.key`, "A column key")) {
|
|
2200
|
-
if (seen.has(c.key)) issue(`${at}.key`, `Duplicate column key "${c.key}".`);
|
|
2201
|
-
seen.add(c.key);
|
|
2202
|
-
}
|
|
2203
|
-
text(c.label, `${at}.label`);
|
|
2204
|
-
optionalBoolean(c.long, `${at}.long`);
|
|
2205
|
-
});
|
|
2206
|
-
}
|
|
2207
|
-
optionalInt(props.minRows, `${path}.minRows`, 0);
|
|
2208
|
-
optionalInt(props.maxRows, `${path}.maxRows`, 1);
|
|
2209
|
-
ordered(props.minRows, props.maxRows ?? 3, `${path}.minRows`, "most rows");
|
|
2210
|
-
} else if (type === "media-notes") {
|
|
2211
|
-
optionalInt(props.maxNotes, `${path}.maxNotes`, 1);
|
|
2212
|
-
optionalInt(props.textMaxLength, `${path}.textMaxLength`, 1);
|
|
2213
|
-
} else if (type === "text-highlights") {
|
|
2214
|
-
optionalText(props.text, `${path}.text`);
|
|
2215
|
-
optionalInt(props.maxHighlights, `${path}.maxHighlights`, 1);
|
|
2216
|
-
} else if (type === CALCULATED_TYPE) {
|
|
2217
|
-
if (props.format !== void 0 && props.format !== "number" && props.format !== "percent" && props.format !== "text") {
|
|
2218
|
-
issue(`${path}.format`, "Must be number, percent or text.");
|
|
2219
|
-
}
|
|
2220
|
-
if (props.decimals !== void 0 && !(isInt(props.decimals) && props.decimals >= 0 && props.decimals <= MAX_CALCULATED_DECIMALS)) {
|
|
2221
|
-
issue(`${path}.decimals`, `Must be a whole number from 0 to ${MAX_CALCULATED_DECIMALS}.`);
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
};
|
|
2225
|
-
const isFiniteNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
2226
|
-
const fieldProps = (type, props, path) => {
|
|
2227
|
-
if (type === "rating" || type === "nps") {
|
|
2228
|
-
optionalText(props.lowLabel, `${path}.lowLabel`);
|
|
2229
|
-
optionalText(props.highLabel, `${path}.highLabel`);
|
|
2230
|
-
}
|
|
2231
|
-
if (type === "rating" && props.display !== void 0 && props.display !== "numbers" && props.display !== "stars") {
|
|
2232
|
-
issue(`${path}.display`, "Must be numbers or stars.");
|
|
2233
|
-
}
|
|
2234
|
-
if (type === "number") {
|
|
2235
|
-
for (const k of ["min", "max", "step"]) {
|
|
2236
|
-
if (props[k] !== void 0 && !isFiniteNumber(props[k])) issue(`${path}.${k}`, "Must be a number.");
|
|
2237
|
-
}
|
|
2238
|
-
if (isFiniteNumber(props.step) && props.step <= 0) issue(`${path}.step`, "Must be above 0.");
|
|
2239
|
-
if (isFiniteNumber(props.min) && isFiniteNumber(props.max) && props.min > props.max) {
|
|
2240
|
-
issue(`${path}.min`, "Can't be more than the largest number.");
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
if (type === "date") {
|
|
2244
|
-
for (const k of ["min", "max"]) {
|
|
2245
|
-
if (props[k] !== void 0 && !(typeof props[k] === "string" && isIsoDate(props[k]))) {
|
|
2246
|
-
issue(`${path}.${k}`, "Must be a date like 2026-10-22.");
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
if (typeof props.min === "string" && typeof props.max === "string" && isIsoDate(props.min) && isIsoDate(props.max) && props.min > props.max) {
|
|
2250
|
-
issue(`${path}.min`, "Can't be after the latest date.");
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
if (type === "consent") optionalText(props.text, `${path}.text`);
|
|
2254
|
-
if (type === PHOTO_TYPE) {
|
|
2255
|
-
if (props.maxFiles !== void 0 && !(isInt(props.maxFiles) && props.maxFiles >= 1 && props.maxFiles <= MAX_PHOTO_FILES)) {
|
|
2256
|
-
issue(`${path}.maxFiles`, `Must be a whole number from 1 to ${MAX_PHOTO_FILES}.`);
|
|
2257
|
-
}
|
|
2258
|
-
if (props.maxSizeMb !== void 0 && !(isInt(props.maxSizeMb) && props.maxSizeMb >= 1 && props.maxSizeMb <= MAX_PHOTO_SIZE_MB)) {
|
|
2259
|
-
issue(`${path}.maxSizeMb`, `Must be a whole number from 1 to ${MAX_PHOTO_SIZE_MB}.`);
|
|
2260
|
-
}
|
|
2261
|
-
if (props.formats !== void 0) {
|
|
2262
|
-
const formats = props.formats;
|
|
2263
|
-
if (!Array.isArray(formats) || formats.length === 0 || !formats.every(isPhotoFormat)) {
|
|
2264
|
-
issue(`${path}.formats`, "Must be a list of one or more of jpeg, png, heic and webp.");
|
|
2265
|
-
} else if (new Set(formats).size !== formats.length) {
|
|
2266
|
-
issue(`${path}.formats`, "Name each format once.");
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2359
|
+
const kindProps = (check, props, path) => {
|
|
2360
|
+
if (!check) return;
|
|
2361
|
+
try {
|
|
2362
|
+
check(props, propsChecker(path, issue));
|
|
2363
|
+
} catch {
|
|
2364
|
+
issue(path, "These settings couldn't be checked.");
|
|
2269
2365
|
}
|
|
2270
2366
|
};
|
|
2271
2367
|
if (!isObject2(input)) {
|
|
@@ -2304,7 +2400,7 @@ function parseForm(input) {
|
|
|
2304
2400
|
const order = /* @__PURE__ */ new Map();
|
|
2305
2401
|
const tableColumnKeys = /* @__PURE__ */ new Map();
|
|
2306
2402
|
const choices = /* @__PURE__ */ new Map();
|
|
2307
|
-
const
|
|
2403
|
+
const literals = /* @__PURE__ */ new Map();
|
|
2308
2404
|
input.blocks.forEach((block, i) => {
|
|
2309
2405
|
const path = `blocks[${i}]`;
|
|
2310
2406
|
if (!isObject2(block) || typeof block.id !== "string" || !block.id) {
|
|
@@ -2332,7 +2428,7 @@ function parseForm(input) {
|
|
|
2332
2428
|
if (from !== void 0) {
|
|
2333
2429
|
if (!isObject2(from) || from.source !== "host" || typeof from.key !== "string" || !from.key) {
|
|
2334
2430
|
issue(`${path}.props.optionsFrom`, 'Must be { source: "host", key } with a non-empty key.');
|
|
2335
|
-
} else if (typeof block.type === "string" && !
|
|
2431
|
+
} else if (typeof block.type === "string" && !choiceKind(block.type)) {
|
|
2336
2432
|
warnings.push({ path: `${path}.props.optionsFrom`, message: "Only single choice, multiple choice and dropdown questions take options from the app." });
|
|
2337
2433
|
}
|
|
2338
2434
|
const filter = isObject2(from) ? from.filter : void 0;
|
|
@@ -2354,7 +2450,7 @@ function parseForm(input) {
|
|
|
2354
2450
|
);
|
|
2355
2451
|
} else {
|
|
2356
2452
|
optionalBoolean(prefill.hideWhenPrefilled, `${path}.props.prefill.hideWhenPrefilled`);
|
|
2357
|
-
if (typeof block.type === "string" && !
|
|
2453
|
+
if (typeof block.type === "string" && !choiceKind(block.type)) {
|
|
2358
2454
|
warnings.push({ path: `${path}.props.prefill`, message: "Only single choice, multiple choice and dropdown questions are filled in from a link." });
|
|
2359
2455
|
}
|
|
2360
2456
|
}
|
|
@@ -2385,7 +2481,7 @@ function parseForm(input) {
|
|
|
2385
2481
|
if (other !== void 0) {
|
|
2386
2482
|
if (other !== true && other !== false && !(isObject2(other) && (other.label === void 0 || typeof other.label === "string"))) {
|
|
2387
2483
|
issue(`${path}.props.other`, "Must be true, false or { label }.");
|
|
2388
|
-
} else if (other !== false && typeof block.type === "string" && !
|
|
2484
|
+
} else if (other !== false && typeof block.type === "string" && !choiceKind(block.type)) {
|
|
2389
2485
|
warnings.push({ path: `${path}.props.other`, message: 'Only single choice, multiple choice and dropdown questions take an "Other" answer.' });
|
|
2390
2486
|
}
|
|
2391
2487
|
}
|
|
@@ -2398,19 +2494,14 @@ function parseForm(input) {
|
|
|
2398
2494
|
}
|
|
2399
2495
|
}
|
|
2400
2496
|
if (typeof block.type === "string" && (block.props === void 0 || isObject2(block.props))) {
|
|
2401
|
-
|
|
2402
|
-
fieldProps(block.type, block.props ?? {}, `${path}.props`);
|
|
2497
|
+
kindProps(getFieldType(block.type)?.checkProps, block.props ?? {}, `${path}.props`);
|
|
2403
2498
|
}
|
|
2404
2499
|
if (validKey) {
|
|
2405
2500
|
if (keys.has(block.key)) issue(`${path}.key`, `Duplicate key "${String(block.key)}".`);
|
|
2406
2501
|
keys.set(block.key, block.id);
|
|
2407
2502
|
}
|
|
2408
2503
|
fieldTypes.set(block.id, typeof block.type === "string" ? block.type : "");
|
|
2409
|
-
|
|
2410
|
-
const format = isObject2(block.props) ? block.props.format : void 0;
|
|
2411
|
-
if (format === "text") calculatedLiteral.set(block.id, "string");
|
|
2412
|
-
else if (format !== "number" && format !== "percent") calculatedLiteral.set(block.id, void 0);
|
|
2413
|
-
}
|
|
2504
|
+
literals.set(block.id, typeof block.type === "string" ? literalType(block.type, isObject2(block.props) ? block.props : {}) : void 0);
|
|
2414
2505
|
if (block.type === "table" && isObject2(block.props) && Array.isArray(block.props.columns)) {
|
|
2415
2506
|
tableColumnKeys.set(
|
|
2416
2507
|
block.id,
|
|
@@ -2418,8 +2509,13 @@ function parseForm(input) {
|
|
|
2418
2509
|
);
|
|
2419
2510
|
}
|
|
2420
2511
|
} else if (block.kind === "content") {
|
|
2421
|
-
|
|
2512
|
+
const content = typeof block.type === "string" ? getContentType(block.type) : void 0;
|
|
2513
|
+
if (!content) issue(`${path}.type`, `Must be ${contentKinds()}.`);
|
|
2422
2514
|
text(block.text, `${path}.text`);
|
|
2515
|
+
if (content?.checkProps) {
|
|
2516
|
+
if (block.props === void 0 || isObject2(block.props) && isJson(block.props)) kindProps(content.checkProps, block.props ?? {}, `${path}.props`);
|
|
2517
|
+
else issue(`${path}.props`, "Must be a JSON object.");
|
|
2518
|
+
}
|
|
2423
2519
|
} else if (block.kind === "page-break") {
|
|
2424
2520
|
optionalText(block.title, `${path}.title`);
|
|
2425
2521
|
} else {
|
|
@@ -2549,8 +2645,8 @@ function parseForm(input) {
|
|
|
2549
2645
|
if (typeof left.field === "string" && fieldTypes.has(left.field)) {
|
|
2550
2646
|
const fieldType = fieldTypes.get(left.field);
|
|
2551
2647
|
what = `"${keyOf(left.field)}"`;
|
|
2552
|
-
type =
|
|
2553
|
-
if (
|
|
2648
|
+
type = literals.get(left.field);
|
|
2649
|
+
if (choiceKind(fieldType)) options = choices.get(left.field);
|
|
2554
2650
|
} else if (typeof left.hidden === "string" && hiddenTypes.has(left.hidden)) {
|
|
2555
2651
|
what = `hidden input "${left.hidden}"`;
|
|
2556
2652
|
type = hiddenTypes.get(left.hidden);
|