@10x-media/form-builder 0.1.0-beta.1 → 0.1.0-beta.2
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/CHANGELOG.md +16 -0
- package/README.md +3 -1
- package/dist/collections/formSubmissions.js +57 -38
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +125 -114
- package/dist/collections/forms.js.map +1 -1
- package/dist/collections/uploads.d.ts +2 -1
- package/dist/collections/uploads.js +22 -13
- package/dist/collections/uploads.js.map +1 -1
- package/dist/exports/react.d.ts +2 -2
- package/dist/exports/react.js +1 -1
- package/dist/exports/types.d.ts +2 -1
- package/dist/fields/buildFieldBlocks.js +14 -0
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/index.js +3 -1
- package/dist/fields/builtin/index.js.map +1 -1
- package/dist/fields/builtin/repeater.js +44 -0
- package/dist/fields/builtin/repeater.js.map +1 -0
- package/dist/fields/types.d.ts +2 -1
- package/dist/index.d.ts +19 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin/collectionOverrides.d.ts +22 -0
- package/dist/plugin/registerCollections.js +37 -9
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/react/Form.d.ts +32 -3
- package/dist/react/Form.js +47 -5
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormContext.js.map +1 -1
- package/dist/react/renderers/index.js +2 -0
- package/dist/react/renderers/index.js.map +1 -1
- package/dist/react/renderers/repeater.js +113 -0
- package/dist/react/renderers/repeater.js.map +1 -0
- package/dist/submissions/SubmissionAnswers.d.ts +2 -4
- package/dist/submissions/SubmissionAnswers.js +110 -34
- package/dist/submissions/SubmissionAnswers.js.map +1 -1
- package/dist/submissions/SubmissionAnswersClient.js +142 -0
- package/dist/submissions/SubmissionAnswersClient.js.map +1 -0
- package/dist/submissions/runSubmission.js +54 -2
- package/dist/submissions/runSubmission.js.map +1 -1
- package/dist/submissions/types.d.ts +2 -1
- package/dist/translations/en.js +26 -1
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/keys.d.ts +25 -0
- package/dist/translations/keys.js +26 -1
- package/dist/translations/keys.js.map +1 -1
- package/dist/validation/runValidation.js +1 -1
- package/dist/validation/runValidation.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { FieldLabel, TextInput } from "@payloadcms/ui";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/submissions/SubmissionAnswersClient.tsx
|
|
5
|
+
const noop = (_e) => {};
|
|
6
|
+
const pillStyle = (agreed) => ({
|
|
7
|
+
display: "inline-block",
|
|
8
|
+
padding: "2px 8px",
|
|
9
|
+
borderRadius: "9999px",
|
|
10
|
+
fontSize: "0.75rem",
|
|
11
|
+
fontWeight: 600,
|
|
12
|
+
background: agreed ? "var(--theme-success-500)" : "var(--theme-error-500)",
|
|
13
|
+
color: "#fff"
|
|
14
|
+
});
|
|
15
|
+
const rowHeading = {
|
|
16
|
+
fontSize: "0.75rem",
|
|
17
|
+
fontWeight: 600,
|
|
18
|
+
color: "var(--theme-elevation-500)",
|
|
19
|
+
margin: "0 0 4px"
|
|
20
|
+
};
|
|
21
|
+
const SubmissionAnswersClient = ({ answers, repeaters, consent, meta, labels }) => {
|
|
22
|
+
if (!(answers.length > 0 || repeaters.length > 0 || consent.length > 0 || meta.length > 0)) return /* @__PURE__ */ jsx("p", {
|
|
23
|
+
style: { color: "var(--theme-elevation-500)" },
|
|
24
|
+
children: labels.empty
|
|
25
|
+
});
|
|
26
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
27
|
+
className: "field-type",
|
|
28
|
+
style: {
|
|
29
|
+
display: "flex",
|
|
30
|
+
flexDirection: "column",
|
|
31
|
+
gap: "32px"
|
|
32
|
+
},
|
|
33
|
+
children: [
|
|
34
|
+
(answers.length > 0 || repeaters.length > 0) && /* @__PURE__ */ jsxs("section", { children: [
|
|
35
|
+
/* @__PURE__ */ jsx(FieldLabel, { label: labels.answers }),
|
|
36
|
+
answers.map(({ field, label, value, href }) => href ? /* @__PURE__ */ jsxs("div", {
|
|
37
|
+
className: "field-type",
|
|
38
|
+
children: [/* @__PURE__ */ jsx(FieldLabel, { label }), /* @__PURE__ */ jsx("a", {
|
|
39
|
+
href,
|
|
40
|
+
target: "_blank",
|
|
41
|
+
rel: "noreferrer",
|
|
42
|
+
style: {
|
|
43
|
+
display: "block",
|
|
44
|
+
padding: "8px 0",
|
|
45
|
+
color: "var(--theme-text)",
|
|
46
|
+
textDecoration: "underline"
|
|
47
|
+
},
|
|
48
|
+
children: value
|
|
49
|
+
})]
|
|
50
|
+
}, field) : /* @__PURE__ */ jsx(TextInput, {
|
|
51
|
+
path: `sa-${field}`,
|
|
52
|
+
label,
|
|
53
|
+
value,
|
|
54
|
+
readOnly: true,
|
|
55
|
+
onChange: noop
|
|
56
|
+
}, field)),
|
|
57
|
+
repeaters.map(({ field, label, rows }) => /* @__PURE__ */ jsxs("div", {
|
|
58
|
+
className: "field-type",
|
|
59
|
+
children: [/* @__PURE__ */ jsx(FieldLabel, { label }), rows.length === 0 ? /* @__PURE__ */ jsx(TextInput, {
|
|
60
|
+
path: `sa-${field}-empty`,
|
|
61
|
+
label: "",
|
|
62
|
+
value: "—",
|
|
63
|
+
readOnly: true,
|
|
64
|
+
onChange: noop
|
|
65
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
66
|
+
style: {
|
|
67
|
+
display: "flex",
|
|
68
|
+
flexDirection: "column",
|
|
69
|
+
gap: "8px",
|
|
70
|
+
marginTop: "4px"
|
|
71
|
+
},
|
|
72
|
+
children: rows.map((row) => /* @__PURE__ */ jsxs("div", {
|
|
73
|
+
style: {
|
|
74
|
+
border: "1px solid var(--theme-elevation-150)",
|
|
75
|
+
borderRadius: "4px",
|
|
76
|
+
padding: "4px 12px 0"
|
|
77
|
+
},
|
|
78
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
79
|
+
style: rowHeading,
|
|
80
|
+
children: labels.row.replace("{n}", String(Number(row.id) + 1))
|
|
81
|
+
}), row.subFields.map((subField) => /* @__PURE__ */ jsx(TextInput, {
|
|
82
|
+
path: `sa-${field}-${row.id}-${subField.label}`,
|
|
83
|
+
label: subField.label,
|
|
84
|
+
value: subField.value,
|
|
85
|
+
readOnly: true,
|
|
86
|
+
onChange: noop
|
|
87
|
+
}, subField.label))]
|
|
88
|
+
}, row.id))
|
|
89
|
+
})]
|
|
90
|
+
}, field))
|
|
91
|
+
] }),
|
|
92
|
+
consent.length > 0 && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx(FieldLabel, { label: labels.consent }), consent.map((entry) => /* @__PURE__ */ jsxs("div", {
|
|
93
|
+
className: "field-type",
|
|
94
|
+
children: [/* @__PURE__ */ jsx(FieldLabel, { label: entry.field }), /* @__PURE__ */ jsxs("div", {
|
|
95
|
+
style: {
|
|
96
|
+
display: "flex",
|
|
97
|
+
flexDirection: "column",
|
|
98
|
+
gap: "4px"
|
|
99
|
+
},
|
|
100
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
101
|
+
style: {
|
|
102
|
+
display: "flex",
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
gap: "8px",
|
|
105
|
+
flexWrap: "wrap"
|
|
106
|
+
},
|
|
107
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
108
|
+
style: pillStyle(entry.agreed),
|
|
109
|
+
children: entry.agreed ? labels.agreed : labels.declined
|
|
110
|
+
}), entry.ref ? /* @__PURE__ */ jsxs("a", {
|
|
111
|
+
href: entry.ref,
|
|
112
|
+
target: "_blank",
|
|
113
|
+
rel: "noreferrer",
|
|
114
|
+
style: {
|
|
115
|
+
fontSize: "0.8rem",
|
|
116
|
+
color: "var(--theme-text)"
|
|
117
|
+
},
|
|
118
|
+
children: [entry.ref, entry.versionRef ? ` (v${entry.versionRef})` : ""]
|
|
119
|
+
}) : null]
|
|
120
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
121
|
+
style: {
|
|
122
|
+
fontSize: "0.75rem",
|
|
123
|
+
color: "var(--theme-elevation-500)"
|
|
124
|
+
},
|
|
125
|
+
children: entry.at
|
|
126
|
+
})]
|
|
127
|
+
})]
|
|
128
|
+
}, entry.field))] }),
|
|
129
|
+
meta.length > 0 && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx(FieldLabel, { label: labels.submissionDetails }), meta.map((item) => /* @__PURE__ */ jsx(TextInput, {
|
|
130
|
+
path: `sa-meta-${item.label}`,
|
|
131
|
+
label: item.label,
|
|
132
|
+
value: item.value,
|
|
133
|
+
readOnly: true,
|
|
134
|
+
onChange: noop
|
|
135
|
+
}, item.label))] })
|
|
136
|
+
]
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
//#endregion
|
|
140
|
+
export { SubmissionAnswersClient };
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=SubmissionAnswersClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubmissionAnswersClient.js","names":[],"sources":["../../src/submissions/SubmissionAnswersClient.tsx"],"sourcesContent":["'use client'\n\nimport { FieldLabel, TextInput } from '@payloadcms/ui'\nimport type { ChangeEvent } from 'react'\n\nexport type AnswerItem = {\n\tfield: string\n\tlabel: string\n\tvalue: string\n\thref?: string\n\tmultiline?: boolean\n}\n\nexport type RepeaterRow = {\n\tid: string\n\tsubFields: Array<{ label: string; value: string }>\n}\n\nexport type RepeaterItem = {\n\tfield: string\n\tlabel: string\n\trows: RepeaterRow[]\n}\n\nexport type ConsentItem = {\n\tfield: string\n\tagreed: boolean\n\tref?: string\n\tversionRef?: string\n\tat: string\n}\n\nexport type MetaItem = {\n\tlabel: string\n\tvalue: string\n}\n\nexport type SubmissionAnswersLabels = {\n\tanswers: string\n\tconsent: string\n\tsubmissionDetails: string\n\tagreed: string\n\tdeclined: string\n\t/** Row heading template containing `{n}`, the 1-based row number. */\n\trow: string\n\tempty: string\n}\n\ntype Props = {\n\tanswers: AnswerItem[]\n\trepeaters: RepeaterItem[]\n\tconsent: ConsentItem[]\n\tmeta: MetaItem[]\n\tlabels: SubmissionAnswersLabels\n}\n\nconst noop = (_e: ChangeEvent<HTMLInputElement>) => {}\n\nconst pillStyle = (agreed: boolean): React.CSSProperties => ({\n\tdisplay: 'inline-block',\n\tpadding: '2px 8px',\n\tborderRadius: '9999px',\n\tfontSize: '0.75rem',\n\tfontWeight: 600,\n\tbackground: agreed ? 'var(--theme-success-500)' : 'var(--theme-error-500)',\n\tcolor: '#fff',\n})\n\nconst rowHeading: React.CSSProperties = {\n\tfontSize: '0.75rem',\n\tfontWeight: 600,\n\tcolor: 'var(--theme-elevation-500)',\n\tmargin: '0 0 4px',\n}\n\nexport const SubmissionAnswersClient = ({ answers, repeaters, consent, meta, labels }: Props) => {\n\tconst hasContent =\n\t\tanswers.length > 0 || repeaters.length > 0 || consent.length > 0 || meta.length > 0\n\n\tif (!hasContent) {\n\t\treturn <p style={{ color: 'var(--theme-elevation-500)' }}>{labels.empty}</p>\n\t}\n\n\treturn (\n\t\t<div className=\"field-type\" style={{ display: 'flex', flexDirection: 'column', gap: '32px' }}>\n\t\t\t{(answers.length > 0 || repeaters.length > 0) && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.answers} />\n\t\t\t\t\t{answers.map(({ field, label, value, href }) =>\n\t\t\t\t\t\thref ? (\n\t\t\t\t\t\t\t<div key={field} className=\"field-type\">\n\t\t\t\t\t\t\t\t<FieldLabel label={label} />\n\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\thref={href}\n\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tdisplay: 'block',\n\t\t\t\t\t\t\t\t\t\tpadding: '8px 0',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--theme-text)',\n\t\t\t\t\t\t\t\t\t\ttextDecoration: 'underline',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{value}\n\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\t\tkey={field}\n\t\t\t\t\t\t\t\tpath={`sa-${field}`}\n\t\t\t\t\t\t\t\tlabel={label}\n\t\t\t\t\t\t\t\tvalue={value}\n\t\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t)}\n\n\t\t\t\t\t{repeaters.map(({ field, label, rows }) => (\n\t\t\t\t\t\t<div key={field} className=\"field-type\">\n\t\t\t\t\t\t\t<FieldLabel label={label} />\n\t\t\t\t\t\t\t{rows.length === 0 ? (\n\t\t\t\t\t\t\t\t<TextInput path={`sa-${field}-empty`} label=\"\" value=\"—\" readOnly onChange={noop} />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle={{ display: 'flex', flexDirection: 'column', gap: '8px', marginTop: '4px' }}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{rows.map((row) => (\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tkey={row.id}\n\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\tborder: '1px solid var(--theme-elevation-150)',\n\t\t\t\t\t\t\t\t\t\t\t\tborderRadius: '4px',\n\t\t\t\t\t\t\t\t\t\t\t\tpadding: '4px 12px 0',\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<p style={rowHeading}>\n\t\t\t\t\t\t\t\t\t\t\t\t{labels.row.replace('{n}', String(Number(row.id) + 1))}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{row.subFields.map((subField) => (\n\t\t\t\t\t\t\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={subField.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\tpath={`sa-${field}-${row.id}-${subField.label}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\tlabel={subField.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={subField.value}\n\t\t\t\t\t\t\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</section>\n\t\t\t)}\n\n\t\t\t{consent.length > 0 && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.consent} />\n\t\t\t\t\t{consent.map((entry) => (\n\t\t\t\t\t\t<div key={entry.field} className=\"field-type\">\n\t\t\t\t\t\t\t<FieldLabel label={entry.field} />\n\t\t\t\t\t\t\t<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap' }}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span style={pillStyle(entry.agreed)}>\n\t\t\t\t\t\t\t\t\t\t{entry.agreed ? labels.agreed : labels.declined}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{entry.ref ? (\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\thref={entry.ref}\n\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{ fontSize: '0.8rem', color: 'var(--theme-text)' }}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{entry.ref}\n\t\t\t\t\t\t\t\t\t\t\t{entry.versionRef ? ` (v${entry.versionRef})` : ''}\n\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<span style={{ fontSize: '0.75rem', color: 'var(--theme-elevation-500)' }}>\n\t\t\t\t\t\t\t\t\t{entry.at}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</section>\n\t\t\t)}\n\n\t\t\t{meta.length > 0 && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.submissionDetails} />\n\t\t\t\t\t{meta.map((item) => (\n\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\tkey={item.label}\n\t\t\t\t\t\t\tpath={`sa-meta-${item.label}`}\n\t\t\t\t\t\t\tlabel={item.label}\n\t\t\t\t\t\t\tvalue={item.value}\n\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</section>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n"],"mappings":"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"}
|
|
@@ -66,8 +66,8 @@ const runSubmission = async (input) => {
|
|
|
66
66
|
const definition = registry.get(instance.blockType);
|
|
67
67
|
const raw = incoming.get(instance.name);
|
|
68
68
|
if (!definition || calcExpressionOf(instance)) continue;
|
|
69
|
-
const effectiveRaw = instance.blockType === "consent" && isEmpty(raw) ? false : raw;
|
|
70
|
-
if (isEmpty(effectiveRaw)) continue;
|
|
69
|
+
const effectiveRaw = instance.blockType === "consent" && isEmpty(raw) ? false : instance.blockType === "repeater" && isEmpty(raw) ? [] : raw;
|
|
70
|
+
if (isEmpty(effectiveRaw) && instance.blockType !== "repeater") continue;
|
|
71
71
|
const value = coerce(definition.value, effectiveRaw);
|
|
72
72
|
coercedAnswers[instance.name] = value;
|
|
73
73
|
coercedByName.set(instance.name, value);
|
|
@@ -179,6 +179,58 @@ const runSubmission = async (input) => {
|
|
|
179
179
|
});
|
|
180
180
|
continue;
|
|
181
181
|
}
|
|
182
|
+
if (instance.blockType === "repeater") {
|
|
183
|
+
const rows = Array.isArray(value) ? value : [];
|
|
184
|
+
const subFields = Array.isArray(instance.subFields) ? instance.subFields : [];
|
|
185
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
186
|
+
const row = rows[rowIndex] ?? {};
|
|
187
|
+
for (const subField of subFields) {
|
|
188
|
+
const subDef = registry.get(subField.blockType);
|
|
189
|
+
if (!subDef) continue;
|
|
190
|
+
if (!evaluateCondition(subField.visibleWhen, row)) continue;
|
|
191
|
+
if (!evaluateCondition(subField.validateWhen, row)) continue;
|
|
192
|
+
const subValue = row[subField.name];
|
|
193
|
+
const { errors: subErrors } = await runValidation({
|
|
194
|
+
field: subField,
|
|
195
|
+
fieldDefinition: subDef,
|
|
196
|
+
value: subValue,
|
|
197
|
+
fieldType: subField.blockType,
|
|
198
|
+
ruleRegistry,
|
|
199
|
+
answers: row,
|
|
200
|
+
locale,
|
|
201
|
+
t,
|
|
202
|
+
operation,
|
|
203
|
+
event: "submit",
|
|
204
|
+
mode: "server",
|
|
205
|
+
req,
|
|
206
|
+
payload,
|
|
207
|
+
formId
|
|
208
|
+
});
|
|
209
|
+
for (const issue of subErrors.filter((e) => e.severity === "error")) errors.push({
|
|
210
|
+
path: `${instance.name}[${rowIndex}].${subField.name}`,
|
|
211
|
+
message: issue.message
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (errors.length > 0) continue;
|
|
216
|
+
const subFieldDescriptors = subFields.map((sf) => ({
|
|
217
|
+
field: sf.name,
|
|
218
|
+
label: sf.label ?? sf.name,
|
|
219
|
+
fieldType: sf.blockType,
|
|
220
|
+
...sf.options ? { optionLabels: optionLabelsFor(sf) ?? void 0 } : {}
|
|
221
|
+
}));
|
|
222
|
+
outValues.push({
|
|
223
|
+
field: instance.name,
|
|
224
|
+
value: rows
|
|
225
|
+
});
|
|
226
|
+
descriptors.push({
|
|
227
|
+
field: instance.name,
|
|
228
|
+
label: instance.label ?? instance.name,
|
|
229
|
+
fieldType: instance.blockType,
|
|
230
|
+
...subFieldDescriptors.length > 0 ? { subFieldDescriptors } : {}
|
|
231
|
+
});
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
182
234
|
if (isEmpty(raw)) continue;
|
|
183
235
|
outValues.push({
|
|
184
236
|
field: instance.name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runSubmission.js","names":[],"sources":["../../src/submissions/runSubmission.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { calcExpressionOf, computeCalcFields } from '../calc/computeCalcFields'\nimport { evaluateCondition } from '../conditions/evaluate'\nimport type { ConsentProof } from '../consent/captureConsent'\nimport { captureConsent } from '../consent/captureConsent'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { Translate } from '../fields/types'\nimport { keys } from '../translations/keys'\nimport { captureFileRef } from '../uploads/captureFileRef'\nimport type { FileFieldConfig, FileRefError } from '../uploads/types'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport { runValidation } from '../validation/runValidation'\nimport type {\n\tFormFieldInstance,\n\tSubmissionDescriptor,\n\tSubmissionFieldError,\n\tSubmissionValue,\n} from './types'\n\nconst errorKeyFor = (code: FileRefError): string => {\n\tif (code === 'mimeType') {\n\t\treturn keys.validationFileMimeType\n\t}\n\tif (code === 'tooLarge') {\n\t\treturn keys.validationFileTooLarge\n\t}\n\treturn keys.validationFileMissing\n}\n\n/** Normalize the authored `mimeTypes` (a `hasMany` text field) to a `string[]`. */\nconst mimeTypesOf = (raw: unknown): string[] | undefined => {\n\tif (!Array.isArray(raw)) {\n\t\treturn undefined\n\t}\n\tconst out = (raw as unknown[]).filter((entry): entry is string => typeof entry === 'string')\n\treturn out.length > 0 ? out : undefined\n}\n\nconst fileFieldConfigOf = (instance: FormFieldInstance): FileFieldConfig => ({\n\trelationTo: typeof instance.relationTo === 'string' ? instance.relationTo : undefined,\n\tmimeTypes: mimeTypesOf(instance.mimeTypes),\n\tmaxSize: typeof instance.maxSize === 'number' ? instance.maxSize : undefined,\n})\n\nconst isEmpty = (value: unknown): boolean =>\n\tvalue == null || value === '' || (Array.isArray(value) && value.length === 0)\n\nconst coerce = (kind: string, value: unknown): unknown => {\n\tif (value == null) {\n\t\treturn value\n\t}\n\tif (kind === 'number') {\n\t\tconst next = typeof value === 'number' ? value : Number(value)\n\t\treturn Number.isNaN(next) ? value : next\n\t}\n\tif (kind === 'boolean') {\n\t\t// A genuine boolean from the renderer passes through; a raw client string is parsed strictly so that\n\t\t// \"false\"/\"0\"/\"off\"/\"no\"/\"\" are not silently truthy (this matters for the required-consent check).\n\t\tif (typeof value === 'string') {\n\t\t\tconst normalized = value.trim().toLowerCase()\n\t\t\treturn !(\n\t\t\t\tnormalized === '' ||\n\t\t\t\tnormalized === 'false' ||\n\t\t\t\tnormalized === '0' ||\n\t\t\t\tnormalized === 'off' ||\n\t\t\t\tnormalized === 'no'\n\t\t\t)\n\t\t}\n\t\treturn Boolean(value)\n\t}\n\tif (kind === 'text') {\n\t\treturn typeof value === 'string' ? value : String(value)\n\t}\n\treturn value\n}\n\nconst optionLabelsFor = (instance: FormFieldInstance): Record<string, string> | undefined => {\n\tconst options = instance.options\n\tif (!Array.isArray(options)) {\n\t\treturn undefined\n\t}\n\tconst map: Record<string, string> = {}\n\tfor (const option of options as Array<{ label?: string; value?: string }>) {\n\t\tif (typeof option?.value === 'string') {\n\t\t\tmap[option.value] = option.label ?? option.value\n\t\t}\n\t}\n\treturn Object.keys(map).length > 0 ? map : undefined\n}\n\nexport type ConsentProofEntry = { field: string } & ConsentProof\n\nexport type RunSubmissionInput = {\n\tfields: FormFieldInstance[]\n\tvalues: SubmissionValue[]\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tlocale: string\n\tt: Translate\n\toperation: 'create' | 'update'\n\treq?: PayloadRequest\n\tpayload?: Payload\n\tformId?: number | string\n\t/** Upload collection slug for file fields without an explicit `relationTo`. Defaults to `form-uploads`. */\n\tuploadSlug?: string\n\t/** Resolved request identity, verified against an upload's `owner` stamp when a file field is captured. */\n\texpectedOwner?: string\n}\n\nexport type RunSubmissionResult = {\n\terrors: SubmissionFieldError[]\n\tvalues: SubmissionValue[]\n\tdescriptors: SubmissionDescriptor[]\n\tconsent: ConsentProofEntry[]\n}\n\n/**\n * Pure submission core, two-pass: first coerce every answered field to its typed kind (so cross-field\n * rules see coerced siblings), then validate each field through `runValidation` (required, intrinsic\n * facet, declarative rules), snapshotting a localized descriptor per answered field. Calc fields are\n * the trust boundary: their client-sent values are never seeded, their values are derived from their\n * expressions over the coerced answers, and those derived values are authoritative everywhere downstream\n * (conditions, validation, storage). Conditions gate the second pass against these effective answers: a\n * field whose `visibleWhen` is false is skipped entirely (never validated, never stored, so a client-sent\n * value for it is ignored), and a visible field whose `validateWhen` is false stores its value but skips\n * validation. A visible calc field stores its derived value and is never validated. Only `error` severity\n * blocks; warnings are computed but not surfaced server-side (the renderer surfaces them).\n */\nexport const runSubmission = async (input: RunSubmissionInput): Promise<RunSubmissionResult> => {\n\tconst {\n\t\tfields,\n\t\tvalues,\n\t\tregistry,\n\t\truleRegistry,\n\t\tconsentRegistry,\n\t\tlocale,\n\t\tt,\n\t\toperation,\n\t\treq,\n\t\tpayload,\n\t\tformId,\n\t\tuploadSlug,\n\t\texpectedOwner,\n\t} = input\n\tconst incoming = new Map(values.map((entry) => [entry.field, entry.value]))\n\n\tconst coercedAnswers: Record<string, unknown> = {}\n\tconst coercedByName = new Map<string, unknown>()\n\tfor (const instance of fields) {\n\t\tconst definition = registry.get(instance.blockType)\n\t\tconst raw = incoming.get(instance.name)\n\t\t// Never seed a calc field's client value: its value is derived below, so the client cannot influence it (even for a self-referencing expression).\n\t\tif (!definition || calcExpressionOf(instance)) {\n\t\t\tcontinue\n\t\t}\n\t\t// A consent field's \"not agreed\" state is semantically meaningful: treat a missing value as\n\t\t// `false` so the intrinsic validate can enforce required-agreement (not optional = must be true).\n\t\tconst effectiveRaw = instance.blockType === 'consent' && isEmpty(raw) ? false : raw\n\t\tif (isEmpty(effectiveRaw)) {\n\t\t\tcontinue\n\t\t}\n\t\tconst value = coerce(definition.value, effectiveRaw)\n\t\tcoercedAnswers[instance.name] = value\n\t\tcoercedByName.set(instance.name, value)\n\t}\n\n\t// Calc values are authoritative everywhere downstream (conditions, validation, storage), never the client-sent value.\n\tconst effective = computeCalcFields(fields, coercedAnswers)\n\n\tconst errors: SubmissionFieldError[] = []\n\tconst outValues: SubmissionValue[] = []\n\tconst descriptors: SubmissionDescriptor[] = []\n\tconst consentProofs: ConsentProofEntry[] = []\n\tconst now = new Date().toISOString()\n\n\tfor (const instance of fields) {\n\t\tconst definition = registry.get(instance.blockType)\n\t\tif (!definition) {\n\t\t\tcontinue\n\t\t}\n\t\tconst raw = incoming.get(instance.name)\n\t\tconst value = coercedByName.has(instance.name) ? coercedByName.get(instance.name) : raw\n\n\t\tif (!evaluateCondition(instance.visibleWhen, effective)) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// A calc field's value is server-derived and always valid: skip validation and the client value entirely, storing the computed result.\n\t\tif (calcExpressionOf(instance)) {\n\t\t\toutValues.push({ field: instance.name, value: effective[instance.name] })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (evaluateCondition(instance.validateWhen, effective)) {\n\t\t\tconst { errors: issues } = await runValidation({\n\t\t\t\tfield: instance,\n\t\t\t\tfieldDefinition: definition,\n\t\t\t\tvalue,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\truleRegistry,\n\t\t\t\tanswers: effective,\n\t\t\t\tlocale,\n\t\t\t\tt,\n\t\t\t\toperation,\n\t\t\t\tevent: 'submit',\n\t\t\t\tmode: 'server',\n\t\t\t\treq,\n\t\t\t\tpayload,\n\t\t\t\tformId,\n\t\t\t})\n\t\t\tconst blocking = issues.filter((issue) => issue.severity === 'error')\n\t\t\tif (blocking.length > 0) {\n\t\t\t\tfor (const issue of blocking) {\n\t\t\t\t\terrors.push({ path: instance.name, message: issue.message })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif (instance.blockType === 'file') {\n\t\t\tif (isEmpty(value)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (payload) {\n\t\t\t\tconst fileConfig = fileFieldConfigOf(instance)\n\t\t\t\tconst slug = fileConfig.relationTo ?? uploadSlug ?? 'form-uploads'\n\t\t\t\tconst captured = await captureFileRef({\n\t\t\t\t\tpayload,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t\tuploadId: value as string | number,\n\t\t\t\t\tconfig: fileConfig,\n\t\t\t\t\treq,\n\t\t\t\t\texpectedOwner,\n\t\t\t\t})\n\t\t\t\tif (!captured.ok) {\n\t\t\t\t\terrors.push({ path: instance.name, message: t(errorKeyFor(captured.code)) })\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toutValues.push({ field: instance.name, value: captured.ref })\n\t\t\t\tdescriptors.push({\n\t\t\t\t\tfield: instance.name,\n\t\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\toutValues.push({ field: instance.name, value })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (instance.blockType === 'consent' && payload) {\n\t\t\tconst proof = await captureConsent({\n\t\t\t\tfield: instance,\n\t\t\t\tagreed: value === true,\n\t\t\t\tregistry: consentRegistry,\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\tlocale,\n\t\t\t\tnow,\n\t\t\t})\n\t\t\tconsentProofs.push({ field: instance.name, ...proof })\n\t\t\tcontinue\n\t\t}\n\n\t\tif (isEmpty(raw)) {\n\t\t\tcontinue\n\t\t}\n\n\t\toutValues.push({ field: instance.name, value })\n\t\tconst optionLabels = optionLabelsFor(instance)\n\t\tdescriptors.push({\n\t\t\tfield: instance.name,\n\t\t\tlabel: instance.label ?? instance.name,\n\t\t\tfieldType: instance.blockType,\n\t\t\t...(optionLabels ? { optionLabels } : {}),\n\t\t})\n\t}\n\n\treturn { errors, values: outValues, descriptors, consent: consentProofs }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runSubmission.js","names":[],"sources":["../../src/submissions/runSubmission.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { calcExpressionOf, computeCalcFields } from '../calc/computeCalcFields'\nimport { evaluateCondition } from '../conditions/evaluate'\nimport type { ConsentProof } from '../consent/captureConsent'\nimport { captureConsent } from '../consent/captureConsent'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { Translate } from '../fields/types'\nimport { keys } from '../translations/keys'\nimport { captureFileRef } from '../uploads/captureFileRef'\nimport type { FileFieldConfig, FileRefError } from '../uploads/types'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport { runValidation } from '../validation/runValidation'\nimport type {\n\tFormFieldInstance,\n\tSubmissionDescriptor,\n\tSubmissionFieldError,\n\tSubmissionValue,\n} from './types'\n\nconst errorKeyFor = (code: FileRefError): string => {\n\tif (code === 'mimeType') {\n\t\treturn keys.validationFileMimeType\n\t}\n\tif (code === 'tooLarge') {\n\t\treturn keys.validationFileTooLarge\n\t}\n\treturn keys.validationFileMissing\n}\n\n/** Normalize the authored `mimeTypes` (a `hasMany` text field) to a `string[]`. */\nconst mimeTypesOf = (raw: unknown): string[] | undefined => {\n\tif (!Array.isArray(raw)) {\n\t\treturn undefined\n\t}\n\tconst out = (raw as unknown[]).filter((entry): entry is string => typeof entry === 'string')\n\treturn out.length > 0 ? out : undefined\n}\n\nconst fileFieldConfigOf = (instance: FormFieldInstance): FileFieldConfig => ({\n\trelationTo: typeof instance.relationTo === 'string' ? instance.relationTo : undefined,\n\tmimeTypes: mimeTypesOf(instance.mimeTypes),\n\tmaxSize: typeof instance.maxSize === 'number' ? instance.maxSize : undefined,\n})\n\nconst isEmpty = (value: unknown): boolean =>\n\tvalue == null || value === '' || (Array.isArray(value) && value.length === 0)\n\nconst coerce = (kind: string, value: unknown): unknown => {\n\tif (value == null) {\n\t\treturn value\n\t}\n\tif (kind === 'number') {\n\t\tconst next = typeof value === 'number' ? value : Number(value)\n\t\treturn Number.isNaN(next) ? value : next\n\t}\n\tif (kind === 'boolean') {\n\t\t// A genuine boolean from the renderer passes through; a raw client string is parsed strictly so that\n\t\t// \"false\"/\"0\"/\"off\"/\"no\"/\"\" are not silently truthy (this matters for the required-consent check).\n\t\tif (typeof value === 'string') {\n\t\t\tconst normalized = value.trim().toLowerCase()\n\t\t\treturn !(\n\t\t\t\tnormalized === '' ||\n\t\t\t\tnormalized === 'false' ||\n\t\t\t\tnormalized === '0' ||\n\t\t\t\tnormalized === 'off' ||\n\t\t\t\tnormalized === 'no'\n\t\t\t)\n\t\t}\n\t\treturn Boolean(value)\n\t}\n\tif (kind === 'text') {\n\t\treturn typeof value === 'string' ? value : String(value)\n\t}\n\treturn value\n}\n\nconst optionLabelsFor = (instance: FormFieldInstance): Record<string, string> | undefined => {\n\tconst options = instance.options\n\tif (!Array.isArray(options)) {\n\t\treturn undefined\n\t}\n\tconst map: Record<string, string> = {}\n\tfor (const option of options as Array<{ label?: string; value?: string }>) {\n\t\tif (typeof option?.value === 'string') {\n\t\t\tmap[option.value] = option.label ?? option.value\n\t\t}\n\t}\n\treturn Object.keys(map).length > 0 ? map : undefined\n}\n\nexport type ConsentProofEntry = { field: string } & ConsentProof\n\nexport type RunSubmissionInput = {\n\tfields: FormFieldInstance[]\n\tvalues: SubmissionValue[]\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tlocale: string\n\tt: Translate\n\toperation: 'create' | 'update'\n\treq?: PayloadRequest\n\tpayload?: Payload\n\tformId?: number | string\n\t/** Upload collection slug for file fields without an explicit `relationTo`. Defaults to `form-uploads`. */\n\tuploadSlug?: string\n\t/** Resolved request identity, verified against an upload's `owner` stamp when a file field is captured. */\n\texpectedOwner?: string\n}\n\nexport type RunSubmissionResult = {\n\terrors: SubmissionFieldError[]\n\tvalues: SubmissionValue[]\n\tdescriptors: SubmissionDescriptor[]\n\tconsent: ConsentProofEntry[]\n}\n\n/**\n * Pure submission core, two-pass: first coerce every answered field to its typed kind (so cross-field\n * rules see coerced siblings), then validate each field through `runValidation` (required, intrinsic\n * facet, declarative rules), snapshotting a localized descriptor per answered field. Calc fields are\n * the trust boundary: their client-sent values are never seeded, their values are derived from their\n * expressions over the coerced answers, and those derived values are authoritative everywhere downstream\n * (conditions, validation, storage). Conditions gate the second pass against these effective answers: a\n * field whose `visibleWhen` is false is skipped entirely (never validated, never stored, so a client-sent\n * value for it is ignored), and a visible field whose `validateWhen` is false stores its value but skips\n * validation. A visible calc field stores its derived value and is never validated. Only `error` severity\n * blocks; warnings are computed but not surfaced server-side (the renderer surfaces them).\n */\nexport const runSubmission = async (input: RunSubmissionInput): Promise<RunSubmissionResult> => {\n\tconst {\n\t\tfields,\n\t\tvalues,\n\t\tregistry,\n\t\truleRegistry,\n\t\tconsentRegistry,\n\t\tlocale,\n\t\tt,\n\t\toperation,\n\t\treq,\n\t\tpayload,\n\t\tformId,\n\t\tuploadSlug,\n\t\texpectedOwner,\n\t} = input\n\tconst incoming = new Map(values.map((entry) => [entry.field, entry.value]))\n\n\tconst coercedAnswers: Record<string, unknown> = {}\n\tconst coercedByName = new Map<string, unknown>()\n\tfor (const instance of fields) {\n\t\tconst definition = registry.get(instance.blockType)\n\t\tconst raw = incoming.get(instance.name)\n\t\t// Never seed a calc field's client value: its value is derived below, so the client cannot influence it (even for a self-referencing expression).\n\t\tif (!definition || calcExpressionOf(instance)) {\n\t\t\tcontinue\n\t\t}\n\t\t// A consent field's \"not agreed\" state is semantically meaningful: treat a missing value as\n\t\t// `false` so the intrinsic validate can enforce required-agreement (not optional = must be true).\n\t\t// A repeater with no rows is coerced to [] so validate() can check minRows. The empty-guard\n\t\t// below must not skip repeaters: isEmpty([]) is true, but [] still needs to reach validate()\n\t\t// so a minRows > 0 constraint correctly rejects a zero-row submission.\n\t\tconst effectiveRaw =\n\t\t\tinstance.blockType === 'consent' && isEmpty(raw)\n\t\t\t\t? false\n\t\t\t\t: instance.blockType === 'repeater' && isEmpty(raw)\n\t\t\t\t\t? []\n\t\t\t\t\t: raw\n\t\tif (isEmpty(effectiveRaw) && instance.blockType !== 'repeater') {\n\t\t\tcontinue\n\t\t}\n\t\tconst value = coerce(definition.value, effectiveRaw)\n\t\tcoercedAnswers[instance.name] = value\n\t\tcoercedByName.set(instance.name, value)\n\t}\n\n\t// Calc values are authoritative everywhere downstream (conditions, validation, storage), never the client-sent value.\n\tconst effective = computeCalcFields(fields, coercedAnswers)\n\n\tconst errors: SubmissionFieldError[] = []\n\tconst outValues: SubmissionValue[] = []\n\tconst descriptors: SubmissionDescriptor[] = []\n\tconst consentProofs: ConsentProofEntry[] = []\n\tconst now = new Date().toISOString()\n\n\tfor (const instance of fields) {\n\t\tconst definition = registry.get(instance.blockType)\n\t\tif (!definition) {\n\t\t\tcontinue\n\t\t}\n\t\tconst raw = incoming.get(instance.name)\n\t\tconst value = coercedByName.has(instance.name) ? coercedByName.get(instance.name) : raw\n\n\t\tif (!evaluateCondition(instance.visibleWhen, effective)) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// A calc field's value is server-derived and always valid: skip validation and the client value entirely, storing the computed result.\n\t\tif (calcExpressionOf(instance)) {\n\t\t\toutValues.push({ field: instance.name, value: effective[instance.name] })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (evaluateCondition(instance.validateWhen, effective)) {\n\t\t\tconst { errors: issues } = await runValidation({\n\t\t\t\tfield: instance,\n\t\t\t\tfieldDefinition: definition,\n\t\t\t\tvalue,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\truleRegistry,\n\t\t\t\tanswers: effective,\n\t\t\t\tlocale,\n\t\t\t\tt,\n\t\t\t\toperation,\n\t\t\t\tevent: 'submit',\n\t\t\t\tmode: 'server',\n\t\t\t\treq,\n\t\t\t\tpayload,\n\t\t\t\tformId,\n\t\t\t})\n\t\t\tconst blocking = issues.filter((issue) => issue.severity === 'error')\n\t\t\tif (blocking.length > 0) {\n\t\t\t\tfor (const issue of blocking) {\n\t\t\t\t\terrors.push({ path: instance.name, message: issue.message })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif (instance.blockType === 'file') {\n\t\t\tif (isEmpty(value)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (payload) {\n\t\t\t\tconst fileConfig = fileFieldConfigOf(instance)\n\t\t\t\tconst slug = fileConfig.relationTo ?? uploadSlug ?? 'form-uploads'\n\t\t\t\tconst captured = await captureFileRef({\n\t\t\t\t\tpayload,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t\tuploadId: value as string | number,\n\t\t\t\t\tconfig: fileConfig,\n\t\t\t\t\treq,\n\t\t\t\t\texpectedOwner,\n\t\t\t\t})\n\t\t\t\tif (!captured.ok) {\n\t\t\t\t\terrors.push({ path: instance.name, message: t(errorKeyFor(captured.code)) })\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toutValues.push({ field: instance.name, value: captured.ref })\n\t\t\t\tdescriptors.push({\n\t\t\t\t\tfield: instance.name,\n\t\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\toutValues.push({ field: instance.name, value })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (instance.blockType === 'consent' && payload) {\n\t\t\tconst proof = await captureConsent({\n\t\t\t\tfield: instance,\n\t\t\t\tagreed: value === true,\n\t\t\t\tregistry: consentRegistry,\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\tlocale,\n\t\t\t\tnow,\n\t\t\t})\n\t\t\tconsentProofs.push({ field: instance.name, ...proof })\n\t\t\tcontinue\n\t\t}\n\n\t\tif (instance.blockType === 'repeater') {\n\t\t\tconst rows = Array.isArray(value) ? (value as Array<Record<string, unknown>>) : []\n\t\t\tconst subFields = Array.isArray(instance.subFields)\n\t\t\t\t? (instance.subFields as FormFieldInstance[])\n\t\t\t\t: []\n\n\t\t\t// Per-row sub-field validation: evaluate each sub-field's visibleWhen against the\n\t\t\t// row's own values, then run its validation rules. Errors are reported with path\n\t\t\t// fieldName[rowIndex].subFieldName so the client can map them to the right input.\n\t\t\tfor (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n\t\t\t\tconst row = rows[rowIndex] ?? {}\n\t\t\t\tfor (const subField of subFields) {\n\t\t\t\t\tconst subDef = registry.get(subField.blockType)\n\t\t\t\t\tif (!subDef) continue\n\t\t\t\t\tif (!evaluateCondition(subField.visibleWhen, row)) continue\n\t\t\t\t\tif (!evaluateCondition(subField.validateWhen, row)) continue\n\t\t\t\t\tconst subValue = row[subField.name]\n\t\t\t\t\tconst { errors: subErrors } = await runValidation({\n\t\t\t\t\t\tfield: subField,\n\t\t\t\t\t\tfieldDefinition: subDef,\n\t\t\t\t\t\tvalue: subValue,\n\t\t\t\t\t\tfieldType: subField.blockType,\n\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\tanswers: row,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t\tt,\n\t\t\t\t\t\toperation,\n\t\t\t\t\t\tevent: 'submit',\n\t\t\t\t\t\tmode: 'server',\n\t\t\t\t\t\treq,\n\t\t\t\t\t\tpayload,\n\t\t\t\t\t\tformId,\n\t\t\t\t\t})\n\t\t\t\t\tfor (const issue of subErrors.filter((e) => e.severity === 'error')) {\n\t\t\t\t\t\terrors.push({\n\t\t\t\t\t\t\tpath: `${instance.name}[${rowIndex}].${subField.name}`,\n\t\t\t\t\t\t\tmessage: issue.message,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errors.length > 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst subFieldDescriptors: SubmissionDescriptor[] = subFields.map((sf) => ({\n\t\t\t\tfield: sf.name,\n\t\t\t\tlabel: sf.label ?? sf.name,\n\t\t\t\tfieldType: sf.blockType,\n\t\t\t\t...((sf.options as unknown) ? { optionLabels: optionLabelsFor(sf) ?? undefined } : {}),\n\t\t\t}))\n\n\t\t\toutValues.push({ field: instance.name, value: rows })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t...(subFieldDescriptors.length > 0 ? { subFieldDescriptors } : {}),\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (isEmpty(raw)) {\n\t\t\tcontinue\n\t\t}\n\n\t\toutValues.push({ field: instance.name, value })\n\t\tconst optionLabels = optionLabelsFor(instance)\n\t\tdescriptors.push({\n\t\t\tfield: instance.name,\n\t\t\tlabel: instance.label ?? instance.name,\n\t\t\tfieldType: instance.blockType,\n\t\t\t...(optionLabels ? { optionLabels } : {}),\n\t\t})\n\t}\n\n\treturn { errors, values: outValues, descriptors, consent: consentProofs }\n}\n"],"mappings":";;;;;;;AAoBA,MAAM,eAAe,SAA+B;CACnD,IAAI,SAAS,YACZ,OAAO,KAAK;CAEb,IAAI,SAAS,YACZ,OAAO,KAAK;CAEb,OAAO,KAAK;AACb;;AAGA,MAAM,eAAe,QAAuC;CAC3D,IAAI,CAAC,MAAM,QAAQ,GAAG,GACrB;CAED,MAAM,MAAO,IAAkB,QAAQ,UAA2B,OAAO,UAAU,QAAQ;CAC3F,OAAO,IAAI,SAAS,IAAI,MAAM,KAAA;AAC/B;AAEA,MAAM,qBAAqB,cAAkD;CAC5E,YAAY,OAAO,SAAS,eAAe,WAAW,SAAS,aAAa,KAAA;CAC5E,WAAW,YAAY,SAAS,SAAS;CACzC,SAAS,OAAO,SAAS,YAAY,WAAW,SAAS,UAAU,KAAA;AACpE;AAEA,MAAM,WAAW,UAChB,SAAS,QAAQ,UAAU,MAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAE5E,MAAM,UAAU,MAAc,UAA4B;CACzD,IAAI,SAAS,MACZ,OAAO;CAER,IAAI,SAAS,UAAU;EACtB,MAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;EAC7D,OAAO,OAAO,MAAM,IAAI,IAAI,QAAQ;CACrC;CACA,IAAI,SAAS,WAAW;EAGvB,IAAI,OAAO,UAAU,UAAU;GAC9B,MAAM,aAAa,MAAM,KAAK,EAAE,YAAY;GAC5C,OAAO,EACN,eAAe,MACf,eAAe,WACf,eAAe,OACf,eAAe,SACf,eAAe;EAEjB;EACA,OAAO,QAAQ,KAAK;CACrB;CACA,IAAI,SAAS,QACZ,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;CAExD,OAAO;AACR;AAEA,MAAM,mBAAmB,aAAoE;CAC5F,MAAM,UAAU,SAAS;CACzB,IAAI,CAAC,MAAM,QAAQ,OAAO,GACzB;CAED,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,UAAU,SACpB,IAAI,OAAO,QAAQ,UAAU,UAC5B,IAAI,OAAO,SAAS,OAAO,SAAS,OAAO;CAG7C,OAAO,OAAO,KAAK,GAAG,EAAE,SAAS,IAAI,MAAM,KAAA;AAC5C;;;;;;;;;;;;;AAyCA,MAAa,gBAAgB,OAAO,UAA4D;CAC/F,MAAM,EACL,QACA,QACA,UACA,cACA,iBACA,QACA,GACA,WACA,KACA,SACA,QACA,YACA,kBACG;CACJ,MAAM,WAAW,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,MAAM,OAAO,MAAM,KAAK,CAAC,CAAC;CAE1E,MAAM,iBAA0C,CAAC;CACjD,MAAM,gCAAgB,IAAI,IAAqB;CAC/C,KAAK,MAAM,YAAY,QAAQ;EAC9B,MAAM,aAAa,SAAS,IAAI,SAAS,SAAS;EAClD,MAAM,MAAM,SAAS,IAAI,SAAS,IAAI;EAEtC,IAAI,CAAC,cAAc,iBAAiB,QAAQ,GAC3C;EAOD,MAAM,eACL,SAAS,cAAc,aAAa,QAAQ,GAAG,IAC5C,QACA,SAAS,cAAc,cAAc,QAAQ,GAAG,IAC/C,CAAC,IACD;EACL,IAAI,QAAQ,YAAY,KAAK,SAAS,cAAc,YACnD;EAED,MAAM,QAAQ,OAAO,WAAW,OAAO,YAAY;EACnD,eAAe,SAAS,QAAQ;EAChC,cAAc,IAAI,SAAS,MAAM,KAAK;CACvC;CAGA,MAAM,YAAY,kBAAkB,QAAQ,cAAc;CAE1D,MAAM,SAAiC,CAAC;CACxC,MAAM,YAA+B,CAAC;CACtC,MAAM,cAAsC,CAAC;CAC7C,MAAM,gBAAqC,CAAC;CAC5C,MAAM,uBAAM,IAAI,KAAK,GAAE,YAAY;CAEnC,KAAK,MAAM,YAAY,QAAQ;EAC9B,MAAM,aAAa,SAAS,IAAI,SAAS,SAAS;EAClD,IAAI,CAAC,YACJ;EAED,MAAM,MAAM,SAAS,IAAI,SAAS,IAAI;EACtC,MAAM,QAAQ,cAAc,IAAI,SAAS,IAAI,IAAI,cAAc,IAAI,SAAS,IAAI,IAAI;EAEpF,IAAI,CAAC,kBAAkB,SAAS,aAAa,SAAS,GACrD;EAID,IAAI,iBAAiB,QAAQ,GAAG;GAC/B,UAAU,KAAK;IAAE,OAAO,SAAS;IAAM,OAAO,UAAU,SAAS;GAAM,CAAC;GACxE,YAAY,KAAK;IAChB,OAAO,SAAS;IAChB,OAAO,SAAS,SAAS,SAAS;IAClC,WAAW,SAAS;GACrB,CAAC;GACD;EACD;EAEA,IAAI,kBAAkB,SAAS,cAAc,SAAS,GAAG;GACxD,MAAM,EAAE,QAAQ,WAAW,MAAM,cAAc;IAC9C,OAAO;IACP,iBAAiB;IACjB;IACA,WAAW,SAAS;IACpB;IACA,SAAS;IACT;IACA;IACA;IACA,OAAO;IACP,MAAM;IACN;IACA;IACA;GACD,CAAC;GACD,MAAM,WAAW,OAAO,QAAQ,UAAU,MAAM,aAAa,OAAO;GACpE,IAAI,SAAS,SAAS,GAAG;IACxB,KAAK,MAAM,SAAS,UACnB,OAAO,KAAK;KAAE,MAAM,SAAS;KAAM,SAAS,MAAM;IAAQ,CAAC;IAE5D;GACD;EACD;EAEA,IAAI,SAAS,cAAc,QAAQ;GAClC,IAAI,QAAQ,KAAK,GAChB;GAED,IAAI,SAAS;IACZ,MAAM,aAAa,kBAAkB,QAAQ;IAE7C,MAAM,WAAW,MAAM,eAAe;KACrC;KACA,gBAHY,WAAW,cAAc,cAAc;KAInD,UAAU;KACV,QAAQ;KACR;KACA;IACD,CAAC;IACD,IAAI,CAAC,SAAS,IAAI;KACjB,OAAO,KAAK;MAAE,MAAM,SAAS;MAAM,SAAS,EAAE,YAAY,SAAS,IAAI,CAAC;KAAE,CAAC;KAC3E;IACD;IACA,UAAU,KAAK;KAAE,OAAO,SAAS;KAAM,OAAO,SAAS;IAAI,CAAC;IAC5D,YAAY,KAAK;KAChB,OAAO,SAAS;KAChB,OAAO,SAAS,SAAS,SAAS;KAClC,WAAW,SAAS;IACrB,CAAC;IACD;GACD;GACA,UAAU,KAAK;IAAE,OAAO,SAAS;IAAM;GAAM,CAAC;GAC9C,YAAY,KAAK;IAChB,OAAO,SAAS;IAChB,OAAO,SAAS,SAAS,SAAS;IAClC,WAAW,SAAS;GACrB,CAAC;GACD;EACD;EAEA,IAAI,SAAS,cAAc,aAAa,SAAS;GAChD,MAAM,QAAQ,MAAM,eAAe;IAClC,OAAO;IACP,QAAQ,UAAU;IAClB,UAAU;IACV;IACA;IACA;IACA;GACD,CAAC;GACD,cAAc,KAAK;IAAE,OAAO,SAAS;IAAM,GAAG;GAAM,CAAC;GACrD;EACD;EAEA,IAAI,SAAS,cAAc,YAAY;GACtC,MAAM,OAAO,MAAM,QAAQ,KAAK,IAAK,QAA2C,CAAC;GACjF,MAAM,YAAY,MAAM,QAAQ,SAAS,SAAS,IAC9C,SAAS,YACV,CAAC;GAKJ,KAAK,IAAI,WAAW,GAAG,WAAW,KAAK,QAAQ,YAAY;IAC1D,MAAM,MAAM,KAAK,aAAa,CAAC;IAC/B,KAAK,MAAM,YAAY,WAAW;KACjC,MAAM,SAAS,SAAS,IAAI,SAAS,SAAS;KAC9C,IAAI,CAAC,QAAQ;KACb,IAAI,CAAC,kBAAkB,SAAS,aAAa,GAAG,GAAG;KACnD,IAAI,CAAC,kBAAkB,SAAS,cAAc,GAAG,GAAG;KACpD,MAAM,WAAW,IAAI,SAAS;KAC9B,MAAM,EAAE,QAAQ,cAAc,MAAM,cAAc;MACjD,OAAO;MACP,iBAAiB;MACjB,OAAO;MACP,WAAW,SAAS;MACpB;MACA,SAAS;MACT;MACA;MACA;MACA,OAAO;MACP,MAAM;MACN;MACA;MACA;KACD,CAAC;KACD,KAAK,MAAM,SAAS,UAAU,QAAQ,MAAM,EAAE,aAAa,OAAO,GACjE,OAAO,KAAK;MACX,MAAM,GAAG,SAAS,KAAK,GAAG,SAAS,IAAI,SAAS;MAChD,SAAS,MAAM;KAChB,CAAC;IAEH;GACD;GAEA,IAAI,OAAO,SAAS,GACnB;GAGD,MAAM,sBAA8C,UAAU,KAAK,QAAQ;IAC1E,OAAO,GAAG;IACV,OAAO,GAAG,SAAS,GAAG;IACtB,WAAW,GAAG;IACd,GAAK,GAAG,UAAsB,EAAE,cAAc,gBAAgB,EAAE,KAAK,KAAA,EAAU,IAAI,CAAC;GACrF,EAAE;GAEF,UAAU,KAAK;IAAE,OAAO,SAAS;IAAM,OAAO;GAAK,CAAC;GACpD,YAAY,KAAK;IAChB,OAAO,SAAS;IAChB,OAAO,SAAS,SAAS,SAAS;IAClC,WAAW,SAAS;IACpB,GAAI,oBAAoB,SAAS,IAAI,EAAE,oBAAoB,IAAI,CAAC;GACjE,CAAC;GACD;EACD;EAEA,IAAI,QAAQ,GAAG,GACd;EAGD,UAAU,KAAK;GAAE,OAAO,SAAS;GAAM;EAAM,CAAC;EAC9C,MAAM,eAAe,gBAAgB,QAAQ;EAC7C,YAAY,KAAK;GAChB,OAAO,SAAS;GAChB,OAAO,SAAS,SAAS,SAAS;GAClC,WAAW,SAAS;GACpB,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;EACxC,CAAC;CACF;CAEA,OAAO;EAAE;EAAQ,QAAQ;EAAW;EAAa,SAAS;CAAc;AACzE"}
|
|
@@ -11,7 +11,8 @@ type SubmissionDescriptor = {
|
|
|
11
11
|
field: string;
|
|
12
12
|
label: string;
|
|
13
13
|
fieldType: string;
|
|
14
|
-
optionLabels?: Record<string, string>;
|
|
14
|
+
optionLabels?: Record<string, string>; /** For repeater fields: one descriptor per sub-field, shared across all rows. */
|
|
15
|
+
subFieldDescriptors?: SubmissionDescriptor[];
|
|
15
16
|
};
|
|
16
17
|
/** A per-field validation failure (`path` is the field name, for renderer error mapping). */
|
|
17
18
|
type SubmissionFieldError = {
|
package/dist/translations/en.js
CHANGED
|
@@ -125,7 +125,32 @@ const en = {
|
|
|
125
125
|
[keys.fileConfigMaxSize]: "Maximum size (bytes)",
|
|
126
126
|
[keys.spamRateLimited]: "You have sent too many requests. Please try again later.",
|
|
127
127
|
[keys.spamRejected]: "Your submission could not be processed.",
|
|
128
|
-
[keys.spamCaptchaFailed]: "Captcha verification failed. Please try again."
|
|
128
|
+
[keys.spamCaptchaFailed]: "Captcha verification failed. Please try again.",
|
|
129
|
+
[keys.collectionFormSingular]: "Form",
|
|
130
|
+
[keys.collectionFormPlural]: "Forms",
|
|
131
|
+
[keys.collectionSubmissionSingular]: "Submission",
|
|
132
|
+
[keys.collectionSubmissionPlural]: "Submissions",
|
|
133
|
+
[keys.statusComplete]: "Complete",
|
|
134
|
+
[keys.statusPartial]: "Partial",
|
|
135
|
+
[keys.fieldTypeRepeater]: "Repeater",
|
|
136
|
+
[keys.configMinRows]: "Minimum rows",
|
|
137
|
+
[keys.configMaxRows]: "Maximum rows",
|
|
138
|
+
[keys.configAddLabel]: "Add button label",
|
|
139
|
+
[keys.configSubFields]: "Sub-fields",
|
|
140
|
+
[keys.validationRepeaterMin]: "Add at least {min} row(s)",
|
|
141
|
+
[keys.validationRepeaterMax]: "Remove rows to stay within {max}",
|
|
142
|
+
[keys.repeaterAddRow]: "Add row",
|
|
143
|
+
[keys.repeaterRemoveRow]: "Remove",
|
|
144
|
+
[keys.repeaterRow]: "Row {n}",
|
|
145
|
+
[keys.repeaterRowCount]: "{count} row(s)",
|
|
146
|
+
[keys.submissionConsent]: "Consent",
|
|
147
|
+
[keys.submissionDetails]: "Submission details",
|
|
148
|
+
[keys.submissionConsentAgreed]: "Agreed",
|
|
149
|
+
[keys.submissionConsentDeclined]: "Declined",
|
|
150
|
+
[keys.submissionMetaReceivedAt]: "Received at",
|
|
151
|
+
[keys.submissionMetaIp]: "IP address",
|
|
152
|
+
[keys.submissionMetaUserAgent]: "User agent",
|
|
153
|
+
[keys.submissionMetaCaptcha]: "Captcha"
|
|
129
154
|
};
|
|
130
155
|
//#endregion
|
|
131
156
|
export { en };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.js","names":[],"sources":["../../src/translations/en.ts"],"sourcesContent":["import { keys, type TranslationKey } from './keys'\n\n/**\n * English values, keyed by the typed constants in `keys.ts` so the two stay in\n * lockstep. The `Record<TranslationKey, string>` annotation makes a missing or\n * unknown key a type error. `translations/index.ts` nests these for Payload.\n */\nexport const en: Record<TranslationKey, string> = {\n\t[keys.pluginName]: 'Form Builder',\n\t[keys.fieldTitle]: 'Title',\n\t[keys.fieldTypeText]: 'Text',\n\t[keys.fieldTypeTextarea]: 'Long text',\n\t[keys.fieldTypeEmail]: 'Email',\n\t[keys.fieldTypeNumber]: 'Number',\n\t[keys.fieldTypeSelect]: 'Select',\n\t[keys.fieldTypeCheckbox]: 'Checkbox',\n\t[keys.configOptions]: 'Options',\n\t[keys.configOption]: 'Option',\n\t[keys.configOptionLabel]: 'Option label',\n\t[keys.configOptionValue]: 'Option value',\n\t[keys.validationRequired]: 'This field is required',\n\t[keys.validationEmail]: 'Enter a valid email address',\n\t[keys.validationNumber]: 'Enter a valid number',\n\t[keys.validationSelect]: 'Choose a valid option',\n\t[keys.formatYes]: 'Yes',\n\t[keys.formatNo]: 'No',\n\t[keys.configName]: 'Name',\n\t[keys.configLabel]: 'Label',\n\t[keys.configRequired]: 'Required',\n\t[keys.configWidth]: 'Width',\n\t[keys.configPlaceholder]: 'Placeholder',\n\t[keys.configDescription]: 'Description',\n\t[keys.configVisibleWhen]: 'Show this field when',\n\t[keys.configValidateWhen]: 'Validate this field only when',\n\t[keys.submissionAnswers]: 'Answers',\n\t[keys.submissionNoAnswers]: 'No answers',\n\t[keys.ruleMinLength]: 'Minimum length',\n\t[keys.ruleMaxLength]: 'Maximum length',\n\t[keys.ruleMin]: 'Minimum',\n\t[keys.ruleMax]: 'Maximum',\n\t[keys.rulePattern]: 'Pattern',\n\t[keys.ruleEmail]: 'Email',\n\t[keys.ruleUrl]: 'URL',\n\t[keys.ruleOneOf]: 'One of',\n\t[keys.ruleMatchesField]: 'Matches field',\n\t[keys.ruleNotAlreadySubmitted]: 'Not already submitted',\n\t[keys.ruleMinLengthMessage]: 'Must be at least {min} characters',\n\t[keys.ruleMaxLengthMessage]: 'Must be at most {max} characters',\n\t[keys.ruleMinMessage]: 'Must be at least {min}',\n\t[keys.ruleMaxMessage]: 'Must be at most {max}',\n\t[keys.rulePatternMessage]: 'Invalid format',\n\t[keys.ruleEmailMessage]: 'Enter a valid email address',\n\t[keys.ruleUrlMessage]: 'Enter a valid URL',\n\t[keys.ruleOneOfMessage]: 'Choose an allowed value',\n\t[keys.ruleMatchesFieldMessage]: 'Does not match',\n\t[keys.ruleNotAlreadySubmittedMessage]: 'This value was already submitted',\n\t[keys.ruleParamMin]: 'Minimum',\n\t[keys.ruleParamMax]: 'Maximum',\n\t[keys.ruleParamPattern]: 'Pattern',\n\t[keys.ruleParamFlags]: 'Flags',\n\t[keys.ruleParamValues]: 'Allowed values',\n\t[keys.ruleParamField]: 'Field name',\n\t[keys.validationsLabel]: 'Validation rules',\n\t[keys.validationMessageLabel]: 'Custom message',\n\t[keys.validationSeverityLabel]: 'Severity',\n\t[keys.validationSeverityError]: 'Error',\n\t[keys.validationSeverityWarning]: 'Warning',\n\t[keys.conditionAddCondition]: 'Add condition',\n\t[keys.conditionAddOr]: 'Add \"or\" group',\n\t[keys.conditionAnd]: 'And',\n\t[keys.conditionOr]: 'Or',\n\t[keys.conditionRemove]: 'Remove',\n\t[keys.conditionNoFields]: 'Add named fields to this form to build a condition.',\n\t[keys.conditionEmpty]: 'No conditions. This field is always shown.',\n\t[keys.conditionSelectField]: 'Select a field',\n\t[keys.conditionValuePlaceholder]: 'Value',\n\t[keys.conditionTrue]: 'True',\n\t[keys.conditionFalse]: 'False',\n\t[keys.configAdvanced]: 'Advanced',\n\t[keys.configHidden]: 'Hidden (capture without showing)',\n\t[keys.fieldTypeCalculation]: 'Calculation',\n\t[keys.configExpression]: 'Expression (JSON)',\n\t[keys.configCalcDisplay]: 'Show computed value',\n\t[keys.configDefaultPresentation]: 'Presentation',\n\t[keys.presentationPage]: 'Page',\n\t[keys.presentationModal]: 'Modal',\n\t[keys.presentationDrawer]: 'Drawer',\n\t[keys.presentationInline]: 'Inline',\n\t[keys.actionEmailTeam]: 'Email team',\n\t[keys.actionConfirmation]: 'Confirmation email',\n\t[keys.actionSignedWebhook]: 'Signed webhook',\n\t[keys.actionConfigTo]: 'To',\n\t[keys.actionConfigSubject]: 'Subject',\n\t[keys.actionConfigBody]: 'Body',\n\t[keys.actionConfigToField]: 'Email field name',\n\t[keys.actionConfigUrl]: 'URL',\n\t[keys.actionConfigSecret]: 'Secret',\n\t[keys.configActions]: 'Actions',\n\t[keys.consentSourceStatic]: 'Static text',\n\t[keys.consentSourcePageReference]: 'Page reference',\n\t[keys.consentConfigLabel]: 'Label',\n\t[keys.consentConfigUrl]: 'URL',\n\t[keys.consentConfigVersion]: 'Version',\n\t[keys.consentConfigRelationTo]: 'Collection',\n\t[keys.consentConfigDocId]: 'Document ID',\n\t[keys.consentConfigUrlField]: 'URL field',\n\t[keys.consentConfigCaptureVersion]: 'Capture published version',\n\t[keys.fieldTypeConsent]: 'Consent',\n\t[keys.consentConfigStatement]: 'Statement',\n\t[keys.consentConfigSource]: 'Source',\n\t[keys.consentConfigSourceConfig]: 'Source settings',\n\t[keys.consentConfigOptional]: 'Optional (e.g. marketing)',\n\t[keys.resultsTitle]: 'Results',\n\t[keys.resultsResponses]: 'responses',\n\t[keys.resultsNoResponses]: 'No responses yet',\n\t[keys.resultsTruncated]: 'Showing a sample of responses',\n\t[keys.configShowResults]: 'Show results publicly',\n\t[keys.configResultsField]: 'Results field (machine name)',\n\t[keys.validationFileMissing]: 'Upload a file',\n\t[keys.validationFileMimeType]: 'File type not allowed',\n\t[keys.validationFileTooLarge]: 'File is too large',\n\t[keys.fieldTypeFile]: 'File upload',\n\t[keys.fileConfigRelationTo]: 'Upload collection',\n\t[keys.fileConfigMimeTypes]: 'Allowed file types',\n\t[keys.fileConfigMaxSize]: 'Maximum size (bytes)',\n\t[keys.spamRateLimited]: 'You have sent too many requests. Please try again later.',\n\t[keys.spamRejected]: 'Your submission could not be processed.',\n\t[keys.spamCaptchaFailed]: 'Captcha verification failed. Please try again.',\n}\n"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"en.js","names":[],"sources":["../../src/translations/en.ts"],"sourcesContent":["import { keys, type TranslationKey } from './keys'\n\n/**\n * English values, keyed by the typed constants in `keys.ts` so the two stay in\n * lockstep. The `Record<TranslationKey, string>` annotation makes a missing or\n * unknown key a type error. `translations/index.ts` nests these for Payload.\n */\nexport const en: Record<TranslationKey, string> = {\n\t[keys.pluginName]: 'Form Builder',\n\t[keys.fieldTitle]: 'Title',\n\t[keys.fieldTypeText]: 'Text',\n\t[keys.fieldTypeTextarea]: 'Long text',\n\t[keys.fieldTypeEmail]: 'Email',\n\t[keys.fieldTypeNumber]: 'Number',\n\t[keys.fieldTypeSelect]: 'Select',\n\t[keys.fieldTypeCheckbox]: 'Checkbox',\n\t[keys.configOptions]: 'Options',\n\t[keys.configOption]: 'Option',\n\t[keys.configOptionLabel]: 'Option label',\n\t[keys.configOptionValue]: 'Option value',\n\t[keys.validationRequired]: 'This field is required',\n\t[keys.validationEmail]: 'Enter a valid email address',\n\t[keys.validationNumber]: 'Enter a valid number',\n\t[keys.validationSelect]: 'Choose a valid option',\n\t[keys.formatYes]: 'Yes',\n\t[keys.formatNo]: 'No',\n\t[keys.configName]: 'Name',\n\t[keys.configLabel]: 'Label',\n\t[keys.configRequired]: 'Required',\n\t[keys.configWidth]: 'Width',\n\t[keys.configPlaceholder]: 'Placeholder',\n\t[keys.configDescription]: 'Description',\n\t[keys.configVisibleWhen]: 'Show this field when',\n\t[keys.configValidateWhen]: 'Validate this field only when',\n\t[keys.submissionAnswers]: 'Answers',\n\t[keys.submissionNoAnswers]: 'No answers',\n\t[keys.ruleMinLength]: 'Minimum length',\n\t[keys.ruleMaxLength]: 'Maximum length',\n\t[keys.ruleMin]: 'Minimum',\n\t[keys.ruleMax]: 'Maximum',\n\t[keys.rulePattern]: 'Pattern',\n\t[keys.ruleEmail]: 'Email',\n\t[keys.ruleUrl]: 'URL',\n\t[keys.ruleOneOf]: 'One of',\n\t[keys.ruleMatchesField]: 'Matches field',\n\t[keys.ruleNotAlreadySubmitted]: 'Not already submitted',\n\t[keys.ruleMinLengthMessage]: 'Must be at least {min} characters',\n\t[keys.ruleMaxLengthMessage]: 'Must be at most {max} characters',\n\t[keys.ruleMinMessage]: 'Must be at least {min}',\n\t[keys.ruleMaxMessage]: 'Must be at most {max}',\n\t[keys.rulePatternMessage]: 'Invalid format',\n\t[keys.ruleEmailMessage]: 'Enter a valid email address',\n\t[keys.ruleUrlMessage]: 'Enter a valid URL',\n\t[keys.ruleOneOfMessage]: 'Choose an allowed value',\n\t[keys.ruleMatchesFieldMessage]: 'Does not match',\n\t[keys.ruleNotAlreadySubmittedMessage]: 'This value was already submitted',\n\t[keys.ruleParamMin]: 'Minimum',\n\t[keys.ruleParamMax]: 'Maximum',\n\t[keys.ruleParamPattern]: 'Pattern',\n\t[keys.ruleParamFlags]: 'Flags',\n\t[keys.ruleParamValues]: 'Allowed values',\n\t[keys.ruleParamField]: 'Field name',\n\t[keys.validationsLabel]: 'Validation rules',\n\t[keys.validationMessageLabel]: 'Custom message',\n\t[keys.validationSeverityLabel]: 'Severity',\n\t[keys.validationSeverityError]: 'Error',\n\t[keys.validationSeverityWarning]: 'Warning',\n\t[keys.conditionAddCondition]: 'Add condition',\n\t[keys.conditionAddOr]: 'Add \"or\" group',\n\t[keys.conditionAnd]: 'And',\n\t[keys.conditionOr]: 'Or',\n\t[keys.conditionRemove]: 'Remove',\n\t[keys.conditionNoFields]: 'Add named fields to this form to build a condition.',\n\t[keys.conditionEmpty]: 'No conditions. This field is always shown.',\n\t[keys.conditionSelectField]: 'Select a field',\n\t[keys.conditionValuePlaceholder]: 'Value',\n\t[keys.conditionTrue]: 'True',\n\t[keys.conditionFalse]: 'False',\n\t[keys.configAdvanced]: 'Advanced',\n\t[keys.configHidden]: 'Hidden (capture without showing)',\n\t[keys.fieldTypeCalculation]: 'Calculation',\n\t[keys.configExpression]: 'Expression (JSON)',\n\t[keys.configCalcDisplay]: 'Show computed value',\n\t[keys.configDefaultPresentation]: 'Presentation',\n\t[keys.presentationPage]: 'Page',\n\t[keys.presentationModal]: 'Modal',\n\t[keys.presentationDrawer]: 'Drawer',\n\t[keys.presentationInline]: 'Inline',\n\t[keys.actionEmailTeam]: 'Email team',\n\t[keys.actionConfirmation]: 'Confirmation email',\n\t[keys.actionSignedWebhook]: 'Signed webhook',\n\t[keys.actionConfigTo]: 'To',\n\t[keys.actionConfigSubject]: 'Subject',\n\t[keys.actionConfigBody]: 'Body',\n\t[keys.actionConfigToField]: 'Email field name',\n\t[keys.actionConfigUrl]: 'URL',\n\t[keys.actionConfigSecret]: 'Secret',\n\t[keys.configActions]: 'Actions',\n\t[keys.consentSourceStatic]: 'Static text',\n\t[keys.consentSourcePageReference]: 'Page reference',\n\t[keys.consentConfigLabel]: 'Label',\n\t[keys.consentConfigUrl]: 'URL',\n\t[keys.consentConfigVersion]: 'Version',\n\t[keys.consentConfigRelationTo]: 'Collection',\n\t[keys.consentConfigDocId]: 'Document ID',\n\t[keys.consentConfigUrlField]: 'URL field',\n\t[keys.consentConfigCaptureVersion]: 'Capture published version',\n\t[keys.fieldTypeConsent]: 'Consent',\n\t[keys.consentConfigStatement]: 'Statement',\n\t[keys.consentConfigSource]: 'Source',\n\t[keys.consentConfigSourceConfig]: 'Source settings',\n\t[keys.consentConfigOptional]: 'Optional (e.g. marketing)',\n\t[keys.resultsTitle]: 'Results',\n\t[keys.resultsResponses]: 'responses',\n\t[keys.resultsNoResponses]: 'No responses yet',\n\t[keys.resultsTruncated]: 'Showing a sample of responses',\n\t[keys.configShowResults]: 'Show results publicly',\n\t[keys.configResultsField]: 'Results field (machine name)',\n\t[keys.validationFileMissing]: 'Upload a file',\n\t[keys.validationFileMimeType]: 'File type not allowed',\n\t[keys.validationFileTooLarge]: 'File is too large',\n\t[keys.fieldTypeFile]: 'File upload',\n\t[keys.fileConfigRelationTo]: 'Upload collection',\n\t[keys.fileConfigMimeTypes]: 'Allowed file types',\n\t[keys.fileConfigMaxSize]: 'Maximum size (bytes)',\n\t[keys.spamRateLimited]: 'You have sent too many requests. Please try again later.',\n\t[keys.spamRejected]: 'Your submission could not be processed.',\n\t[keys.spamCaptchaFailed]: 'Captcha verification failed. Please try again.',\n\t[keys.collectionFormSingular]: 'Form',\n\t[keys.collectionFormPlural]: 'Forms',\n\t[keys.collectionSubmissionSingular]: 'Submission',\n\t[keys.collectionSubmissionPlural]: 'Submissions',\n\t[keys.statusComplete]: 'Complete',\n\t[keys.statusPartial]: 'Partial',\n\t[keys.fieldTypeRepeater]: 'Repeater',\n\t[keys.configMinRows]: 'Minimum rows',\n\t[keys.configMaxRows]: 'Maximum rows',\n\t[keys.configAddLabel]: 'Add button label',\n\t[keys.configSubFields]: 'Sub-fields',\n\t[keys.validationRepeaterMin]: 'Add at least {min} row(s)',\n\t[keys.validationRepeaterMax]: 'Remove rows to stay within {max}',\n\t[keys.repeaterAddRow]: 'Add row',\n\t[keys.repeaterRemoveRow]: 'Remove',\n\t[keys.repeaterRow]: 'Row {n}',\n\t[keys.repeaterRowCount]: '{count} row(s)',\n\t[keys.submissionConsent]: 'Consent',\n\t[keys.submissionDetails]: 'Submission details',\n\t[keys.submissionConsentAgreed]: 'Agreed',\n\t[keys.submissionConsentDeclined]: 'Declined',\n\t[keys.submissionMetaReceivedAt]: 'Received at',\n\t[keys.submissionMetaIp]: 'IP address',\n\t[keys.submissionMetaUserAgent]: 'User agent',\n\t[keys.submissionMetaCaptcha]: 'Captcha',\n}\n"],"mappings":"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"}
|
|
@@ -125,6 +125,31 @@ declare const keys: {
|
|
|
125
125
|
readonly spamRateLimited: "formBuilder:spam.rateLimited";
|
|
126
126
|
readonly spamRejected: "formBuilder:spam.rejected";
|
|
127
127
|
readonly spamCaptchaFailed: "formBuilder:spam.captchaFailed";
|
|
128
|
+
readonly collectionFormSingular: "formBuilder:collection.form.singular";
|
|
129
|
+
readonly collectionFormPlural: "formBuilder:collection.form.plural";
|
|
130
|
+
readonly collectionSubmissionSingular: "formBuilder:collection.submission.singular";
|
|
131
|
+
readonly collectionSubmissionPlural: "formBuilder:collection.submission.plural";
|
|
132
|
+
readonly statusComplete: "formBuilder:status.complete";
|
|
133
|
+
readonly statusPartial: "formBuilder:status.partial";
|
|
134
|
+
readonly fieldTypeRepeater: "formBuilder:fieldType.repeater";
|
|
135
|
+
readonly configMinRows: "formBuilder:config.minRows";
|
|
136
|
+
readonly configMaxRows: "formBuilder:config.maxRows";
|
|
137
|
+
readonly configAddLabel: "formBuilder:config.addLabel";
|
|
138
|
+
readonly configSubFields: "formBuilder:config.subFields";
|
|
139
|
+
readonly validationRepeaterMin: "formBuilder:validation.repeaterMin";
|
|
140
|
+
readonly validationRepeaterMax: "formBuilder:validation.repeaterMax";
|
|
141
|
+
readonly repeaterAddRow: "formBuilder:repeater.addRow";
|
|
142
|
+
readonly repeaterRemoveRow: "formBuilder:repeater.removeRow";
|
|
143
|
+
readonly repeaterRow: "formBuilder:repeater.row";
|
|
144
|
+
readonly repeaterRowCount: "formBuilder:repeater.rowCount";
|
|
145
|
+
readonly submissionConsent: "formBuilder:submission.consent";
|
|
146
|
+
readonly submissionDetails: "formBuilder:submission.details";
|
|
147
|
+
readonly submissionConsentAgreed: "formBuilder:submission.consentAgreed";
|
|
148
|
+
readonly submissionConsentDeclined: "formBuilder:submission.consentDeclined";
|
|
149
|
+
readonly submissionMetaReceivedAt: "formBuilder:submission.meta.receivedAt";
|
|
150
|
+
readonly submissionMetaIp: "formBuilder:submission.meta.ip";
|
|
151
|
+
readonly submissionMetaUserAgent: "formBuilder:submission.meta.userAgent";
|
|
152
|
+
readonly submissionMetaCaptcha: "formBuilder:submission.meta.captcha";
|
|
128
153
|
};
|
|
129
154
|
type TranslationKey = (typeof keys)[keyof typeof keys];
|
|
130
155
|
//#endregion
|
|
@@ -124,7 +124,32 @@ const keys = {
|
|
|
124
124
|
fileConfigMaxSize: "formBuilder:file.config.maxSize",
|
|
125
125
|
spamRateLimited: "formBuilder:spam.rateLimited",
|
|
126
126
|
spamRejected: "formBuilder:spam.rejected",
|
|
127
|
-
spamCaptchaFailed: "formBuilder:spam.captchaFailed"
|
|
127
|
+
spamCaptchaFailed: "formBuilder:spam.captchaFailed",
|
|
128
|
+
collectionFormSingular: "formBuilder:collection.form.singular",
|
|
129
|
+
collectionFormPlural: "formBuilder:collection.form.plural",
|
|
130
|
+
collectionSubmissionSingular: "formBuilder:collection.submission.singular",
|
|
131
|
+
collectionSubmissionPlural: "formBuilder:collection.submission.plural",
|
|
132
|
+
statusComplete: "formBuilder:status.complete",
|
|
133
|
+
statusPartial: "formBuilder:status.partial",
|
|
134
|
+
fieldTypeRepeater: "formBuilder:fieldType.repeater",
|
|
135
|
+
configMinRows: "formBuilder:config.minRows",
|
|
136
|
+
configMaxRows: "formBuilder:config.maxRows",
|
|
137
|
+
configAddLabel: "formBuilder:config.addLabel",
|
|
138
|
+
configSubFields: "formBuilder:config.subFields",
|
|
139
|
+
validationRepeaterMin: "formBuilder:validation.repeaterMin",
|
|
140
|
+
validationRepeaterMax: "formBuilder:validation.repeaterMax",
|
|
141
|
+
repeaterAddRow: "formBuilder:repeater.addRow",
|
|
142
|
+
repeaterRemoveRow: "formBuilder:repeater.removeRow",
|
|
143
|
+
repeaterRow: "formBuilder:repeater.row",
|
|
144
|
+
repeaterRowCount: "formBuilder:repeater.rowCount",
|
|
145
|
+
submissionConsent: "formBuilder:submission.consent",
|
|
146
|
+
submissionDetails: "formBuilder:submission.details",
|
|
147
|
+
submissionConsentAgreed: "formBuilder:submission.consentAgreed",
|
|
148
|
+
submissionConsentDeclined: "formBuilder:submission.consentDeclined",
|
|
149
|
+
submissionMetaReceivedAt: "formBuilder:submission.meta.receivedAt",
|
|
150
|
+
submissionMetaIp: "formBuilder:submission.meta.ip",
|
|
151
|
+
submissionMetaUserAgent: "formBuilder:submission.meta.userAgent",
|
|
152
|
+
submissionMetaCaptcha: "formBuilder:submission.meta.captcha"
|
|
128
153
|
};
|
|
129
154
|
//#endregion
|
|
130
155
|
export { keys };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","names":[],"sources":["../../src/translations/keys.ts"],"sourcesContent":["/**\n * Typed translation keys. Lookups must go through these constants, not string\n * literals (enforced by requireI18nKeysTyped.grit). Every key here must have a\n * value in every locale (`en.ts`), or it is a type error.\n */\nexport const keys = {\n\tpluginName: 'formBuilder:pluginName',\n\tfieldTitle: 'formBuilder:fieldTitle',\n\tfieldTypeText: 'formBuilder:fieldType.text',\n\tfieldTypeTextarea: 'formBuilder:fieldType.textarea',\n\tfieldTypeEmail: 'formBuilder:fieldType.email',\n\tfieldTypeNumber: 'formBuilder:fieldType.number',\n\tfieldTypeSelect: 'formBuilder:fieldType.select',\n\tfieldTypeCheckbox: 'formBuilder:fieldType.checkbox',\n\tconfigOptions: 'formBuilder:config.options',\n\tconfigOption: 'formBuilder:config.option',\n\tconfigOptionLabel: 'formBuilder:config.optionLabel',\n\tconfigOptionValue: 'formBuilder:config.optionValue',\n\tvalidationRequired: 'formBuilder:validation.required',\n\tvalidationEmail: 'formBuilder:validation.email',\n\tvalidationNumber: 'formBuilder:validation.number',\n\tvalidationSelect: 'formBuilder:validation.select',\n\tformatYes: 'formBuilder:format.yes',\n\tformatNo: 'formBuilder:format.no',\n\tconfigName: 'formBuilder:config.name',\n\tconfigLabel: 'formBuilder:config.label',\n\tconfigRequired: 'formBuilder:config.required',\n\tconfigWidth: 'formBuilder:config.width',\n\tconfigPlaceholder: 'formBuilder:config.placeholder',\n\tconfigDescription: 'formBuilder:config.description',\n\tconfigVisibleWhen: 'formBuilder:config.visibleWhen',\n\tconfigValidateWhen: 'formBuilder:config.validateWhen',\n\tsubmissionAnswers: 'formBuilder:submission.answers',\n\tsubmissionNoAnswers: 'formBuilder:submission.noAnswers',\n\truleMinLength: 'formBuilder:rule.minLength.label',\n\truleMaxLength: 'formBuilder:rule.maxLength.label',\n\truleMin: 'formBuilder:rule.min.label',\n\truleMax: 'formBuilder:rule.max.label',\n\trulePattern: 'formBuilder:rule.pattern.label',\n\truleEmail: 'formBuilder:rule.email.label',\n\truleUrl: 'formBuilder:rule.url.label',\n\truleOneOf: 'formBuilder:rule.oneOf.label',\n\truleMatchesField: 'formBuilder:rule.matchesField.label',\n\truleNotAlreadySubmitted: 'formBuilder:rule.notAlreadySubmitted.label',\n\truleMinLengthMessage: 'formBuilder:rule.minLength.message',\n\truleMaxLengthMessage: 'formBuilder:rule.maxLength.message',\n\truleMinMessage: 'formBuilder:rule.min.message',\n\truleMaxMessage: 'formBuilder:rule.max.message',\n\trulePatternMessage: 'formBuilder:rule.pattern.message',\n\truleEmailMessage: 'formBuilder:rule.email.message',\n\truleUrlMessage: 'formBuilder:rule.url.message',\n\truleOneOfMessage: 'formBuilder:rule.oneOf.message',\n\truleMatchesFieldMessage: 'formBuilder:rule.matchesField.message',\n\truleNotAlreadySubmittedMessage: 'formBuilder:rule.notAlreadySubmitted.message',\n\truleParamMin: 'formBuilder:rule.param.min',\n\truleParamMax: 'formBuilder:rule.param.max',\n\truleParamPattern: 'formBuilder:rule.param.pattern',\n\truleParamFlags: 'formBuilder:rule.param.flags',\n\truleParamValues: 'formBuilder:rule.param.values',\n\truleParamField: 'formBuilder:rule.param.field',\n\tvalidationsLabel: 'formBuilder:validations.label',\n\tvalidationMessageLabel: 'formBuilder:validations.message',\n\tvalidationSeverityLabel: 'formBuilder:validations.severity',\n\tvalidationSeverityError: 'formBuilder:validations.severity.error',\n\tvalidationSeverityWarning: 'formBuilder:validations.severity.warning',\n\tconditionAddCondition: 'formBuilder:condition.addCondition',\n\tconditionAddOr: 'formBuilder:condition.addOr',\n\tconditionAnd: 'formBuilder:condition.and',\n\tconditionOr: 'formBuilder:condition.or',\n\tconditionRemove: 'formBuilder:condition.remove',\n\tconditionNoFields: 'formBuilder:condition.noFields',\n\tconditionEmpty: 'formBuilder:condition.empty',\n\tconditionSelectField: 'formBuilder:condition.selectField',\n\tconditionValuePlaceholder: 'formBuilder:condition.value',\n\tconditionTrue: 'formBuilder:condition.true',\n\tconditionFalse: 'formBuilder:condition.false',\n\tconfigAdvanced: 'formBuilder:config.advanced',\n\tconfigHidden: 'formBuilder:config.hidden',\n\tfieldTypeCalculation: 'formBuilder:fieldType.calculation',\n\tconfigExpression: 'formBuilder:config.expression',\n\tconfigCalcDisplay: 'formBuilder:config.calcDisplay',\n\tconfigDefaultPresentation: 'formBuilder:config.defaultPresentation',\n\tpresentationPage: 'formBuilder:presentation.page',\n\tpresentationModal: 'formBuilder:presentation.modal',\n\tpresentationDrawer: 'formBuilder:presentation.drawer',\n\tpresentationInline: 'formBuilder:presentation.inline',\n\tactionEmailTeam: 'formBuilder:action.emailTeam',\n\tactionConfirmation: 'formBuilder:action.confirmation',\n\tactionSignedWebhook: 'formBuilder:action.signedWebhook',\n\tactionConfigTo: 'formBuilder:action.config.to',\n\tactionConfigSubject: 'formBuilder:action.config.subject',\n\tactionConfigBody: 'formBuilder:action.config.body',\n\tactionConfigToField: 'formBuilder:action.config.toField',\n\tactionConfigUrl: 'formBuilder:action.config.url',\n\tactionConfigSecret: 'formBuilder:action.config.secret',\n\tconfigActions: 'formBuilder:config.actions',\n\tconsentSourceStatic: 'formBuilder:consentSource.static',\n\tconsentSourcePageReference: 'formBuilder:consentSource.pageReference',\n\tconsentConfigLabel: 'formBuilder:consent.config.label',\n\tconsentConfigUrl: 'formBuilder:consent.config.url',\n\tconsentConfigVersion: 'formBuilder:consent.config.version',\n\tconsentConfigRelationTo: 'formBuilder:consent.config.relationTo',\n\tconsentConfigDocId: 'formBuilder:consent.config.docId',\n\tconsentConfigUrlField: 'formBuilder:consent.config.urlField',\n\tconsentConfigCaptureVersion: 'formBuilder:consent.config.captureVersion',\n\tfieldTypeConsent: 'formBuilder:fieldType.consent',\n\tconsentConfigStatement: 'formBuilder:consent.config.statement',\n\tconsentConfigSource: 'formBuilder:consent.config.source',\n\tconsentConfigSourceConfig: 'formBuilder:consent.config.sourceConfig',\n\tconsentConfigOptional: 'formBuilder:consent.config.optional',\n\tresultsTitle: 'formBuilder:results.title',\n\tresultsResponses: 'formBuilder:results.responses',\n\tresultsNoResponses: 'formBuilder:results.noResponses',\n\tresultsTruncated: 'formBuilder:results.truncated',\n\tconfigShowResults: 'formBuilder:config.showResults',\n\tconfigResultsField: 'formBuilder:config.resultsField',\n\tvalidationFileMissing: 'formBuilder:validation.file.missing',\n\tvalidationFileMimeType: 'formBuilder:validation.file.mimeType',\n\tvalidationFileTooLarge: 'formBuilder:validation.file.tooLarge',\n\tfieldTypeFile: 'formBuilder:fieldType.file',\n\tfileConfigRelationTo: 'formBuilder:file.config.relationTo',\n\tfileConfigMimeTypes: 'formBuilder:file.config.mimeTypes',\n\tfileConfigMaxSize: 'formBuilder:file.config.maxSize',\n\tspamRateLimited: 'formBuilder:spam.rateLimited',\n\tspamRejected: 'formBuilder:spam.rejected',\n\tspamCaptchaFailed: 'formBuilder:spam.captchaFailed',\n} as const\n\nexport type TranslationKey = (typeof keys)[keyof typeof keys]\n"],"mappings":";;;;;;AAKA,MAAa,OAAO;CACnB,YAAY;CACZ,YAAY;CACZ,eAAe;CACf,mBAAmB;CACnB,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,mBAAmB;CACnB,eAAe;CACf,cAAc;CACd,mBAAmB;CACnB,mBAAmB;CACnB,oBAAoB;CACpB,iBAAiB;CACjB,kBAAkB;CAClB,kBAAkB;CAClB,WAAW;CACX,UAAU;CACV,YAAY;CACZ,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,mBAAmB;CACnB,mBAAmB;CACnB,mBAAmB;CACnB,oBAAoB;CACpB,mBAAmB;CACnB,qBAAqB;CACrB,eAAe;CACf,eAAe;CACf,SAAS;CACT,SAAS;CACT,aAAa;CACb,WAAW;CACX,SAAS;CACT,WAAW;CACX,kBAAkB;CAClB,yBAAyB;CACzB,sBAAsB;CACtB,sBAAsB;CACtB,gBAAgB;CAChB,gBAAgB;CAChB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,kBAAkB;CAClB,yBAAyB;CACzB,gCAAgC;CAChC,cAAc;CACd,cAAc;CACd,kBAAkB;CAClB,gBAAgB;CAChB,iBAAiB;CACjB,gBAAgB;CAChB,kBAAkB;CAClB,wBAAwB;CACxB,yBAAyB;CACzB,yBAAyB;CACzB,2BAA2B;CAC3B,uBAAuB;CACvB,gBAAgB;CAChB,cAAc;CACd,aAAa;CACb,iBAAiB;CACjB,mBAAmB;CACnB,gBAAgB;CAChB,sBAAsB;CACtB,2BAA2B;CAC3B,eAAe;CACf,gBAAgB;CAChB,gBAAgB;CAChB,cAAc;CACd,sBAAsB;CACtB,kBAAkB;CAClB,mBAAmB;CACnB,2BAA2B;CAC3B,kBAAkB;CAClB,mBAAmB;CACnB,oBAAoB;CACpB,oBAAoB;CACpB,iBAAiB;CACjB,oBAAoB;CACpB,qBAAqB;CACrB,gBAAgB;CAChB,qBAAqB;CACrB,kBAAkB;CAClB,qBAAqB;CACrB,iBAAiB;CACjB,oBAAoB;CACpB,eAAe;CACf,qBAAqB;CACrB,4BAA4B;CAC5B,oBAAoB;CACpB,kBAAkB;CAClB,sBAAsB;CACtB,yBAAyB;CACzB,oBAAoB;CACpB,uBAAuB;CACvB,6BAA6B;CAC7B,kBAAkB;CAClB,wBAAwB;CACxB,qBAAqB;CACrB,2BAA2B;CAC3B,uBAAuB;CACvB,cAAc;CACd,kBAAkB;CAClB,oBAAoB;CACpB,kBAAkB;CAClB,mBAAmB;CACnB,oBAAoB;CACpB,uBAAuB;CACvB,wBAAwB;CACxB,wBAAwB;CACxB,eAAe;CACf,sBAAsB;CACtB,qBAAqB;CACrB,mBAAmB;CACnB,iBAAiB;CACjB,cAAc;CACd,mBAAmB;AACpB"}
|
|
1
|
+
{"version":3,"file":"keys.js","names":[],"sources":["../../src/translations/keys.ts"],"sourcesContent":["/**\n * Typed translation keys. Lookups must go through these constants, not string\n * literals (enforced by requireI18nKeysTyped.grit). Every key here must have a\n * value in every locale (`en.ts`), or it is a type error.\n */\nexport const keys = {\n\tpluginName: 'formBuilder:pluginName',\n\tfieldTitle: 'formBuilder:fieldTitle',\n\tfieldTypeText: 'formBuilder:fieldType.text',\n\tfieldTypeTextarea: 'formBuilder:fieldType.textarea',\n\tfieldTypeEmail: 'formBuilder:fieldType.email',\n\tfieldTypeNumber: 'formBuilder:fieldType.number',\n\tfieldTypeSelect: 'formBuilder:fieldType.select',\n\tfieldTypeCheckbox: 'formBuilder:fieldType.checkbox',\n\tconfigOptions: 'formBuilder:config.options',\n\tconfigOption: 'formBuilder:config.option',\n\tconfigOptionLabel: 'formBuilder:config.optionLabel',\n\tconfigOptionValue: 'formBuilder:config.optionValue',\n\tvalidationRequired: 'formBuilder:validation.required',\n\tvalidationEmail: 'formBuilder:validation.email',\n\tvalidationNumber: 'formBuilder:validation.number',\n\tvalidationSelect: 'formBuilder:validation.select',\n\tformatYes: 'formBuilder:format.yes',\n\tformatNo: 'formBuilder:format.no',\n\tconfigName: 'formBuilder:config.name',\n\tconfigLabel: 'formBuilder:config.label',\n\tconfigRequired: 'formBuilder:config.required',\n\tconfigWidth: 'formBuilder:config.width',\n\tconfigPlaceholder: 'formBuilder:config.placeholder',\n\tconfigDescription: 'formBuilder:config.description',\n\tconfigVisibleWhen: 'formBuilder:config.visibleWhen',\n\tconfigValidateWhen: 'formBuilder:config.validateWhen',\n\tsubmissionAnswers: 'formBuilder:submission.answers',\n\tsubmissionNoAnswers: 'formBuilder:submission.noAnswers',\n\truleMinLength: 'formBuilder:rule.minLength.label',\n\truleMaxLength: 'formBuilder:rule.maxLength.label',\n\truleMin: 'formBuilder:rule.min.label',\n\truleMax: 'formBuilder:rule.max.label',\n\trulePattern: 'formBuilder:rule.pattern.label',\n\truleEmail: 'formBuilder:rule.email.label',\n\truleUrl: 'formBuilder:rule.url.label',\n\truleOneOf: 'formBuilder:rule.oneOf.label',\n\truleMatchesField: 'formBuilder:rule.matchesField.label',\n\truleNotAlreadySubmitted: 'formBuilder:rule.notAlreadySubmitted.label',\n\truleMinLengthMessage: 'formBuilder:rule.minLength.message',\n\truleMaxLengthMessage: 'formBuilder:rule.maxLength.message',\n\truleMinMessage: 'formBuilder:rule.min.message',\n\truleMaxMessage: 'formBuilder:rule.max.message',\n\trulePatternMessage: 'formBuilder:rule.pattern.message',\n\truleEmailMessage: 'formBuilder:rule.email.message',\n\truleUrlMessage: 'formBuilder:rule.url.message',\n\truleOneOfMessage: 'formBuilder:rule.oneOf.message',\n\truleMatchesFieldMessage: 'formBuilder:rule.matchesField.message',\n\truleNotAlreadySubmittedMessage: 'formBuilder:rule.notAlreadySubmitted.message',\n\truleParamMin: 'formBuilder:rule.param.min',\n\truleParamMax: 'formBuilder:rule.param.max',\n\truleParamPattern: 'formBuilder:rule.param.pattern',\n\truleParamFlags: 'formBuilder:rule.param.flags',\n\truleParamValues: 'formBuilder:rule.param.values',\n\truleParamField: 'formBuilder:rule.param.field',\n\tvalidationsLabel: 'formBuilder:validations.label',\n\tvalidationMessageLabel: 'formBuilder:validations.message',\n\tvalidationSeverityLabel: 'formBuilder:validations.severity',\n\tvalidationSeverityError: 'formBuilder:validations.severity.error',\n\tvalidationSeverityWarning: 'formBuilder:validations.severity.warning',\n\tconditionAddCondition: 'formBuilder:condition.addCondition',\n\tconditionAddOr: 'formBuilder:condition.addOr',\n\tconditionAnd: 'formBuilder:condition.and',\n\tconditionOr: 'formBuilder:condition.or',\n\tconditionRemove: 'formBuilder:condition.remove',\n\tconditionNoFields: 'formBuilder:condition.noFields',\n\tconditionEmpty: 'formBuilder:condition.empty',\n\tconditionSelectField: 'formBuilder:condition.selectField',\n\tconditionValuePlaceholder: 'formBuilder:condition.value',\n\tconditionTrue: 'formBuilder:condition.true',\n\tconditionFalse: 'formBuilder:condition.false',\n\tconfigAdvanced: 'formBuilder:config.advanced',\n\tconfigHidden: 'formBuilder:config.hidden',\n\tfieldTypeCalculation: 'formBuilder:fieldType.calculation',\n\tconfigExpression: 'formBuilder:config.expression',\n\tconfigCalcDisplay: 'formBuilder:config.calcDisplay',\n\tconfigDefaultPresentation: 'formBuilder:config.defaultPresentation',\n\tpresentationPage: 'formBuilder:presentation.page',\n\tpresentationModal: 'formBuilder:presentation.modal',\n\tpresentationDrawer: 'formBuilder:presentation.drawer',\n\tpresentationInline: 'formBuilder:presentation.inline',\n\tactionEmailTeam: 'formBuilder:action.emailTeam',\n\tactionConfirmation: 'formBuilder:action.confirmation',\n\tactionSignedWebhook: 'formBuilder:action.signedWebhook',\n\tactionConfigTo: 'formBuilder:action.config.to',\n\tactionConfigSubject: 'formBuilder:action.config.subject',\n\tactionConfigBody: 'formBuilder:action.config.body',\n\tactionConfigToField: 'formBuilder:action.config.toField',\n\tactionConfigUrl: 'formBuilder:action.config.url',\n\tactionConfigSecret: 'formBuilder:action.config.secret',\n\tconfigActions: 'formBuilder:config.actions',\n\tconsentSourceStatic: 'formBuilder:consentSource.static',\n\tconsentSourcePageReference: 'formBuilder:consentSource.pageReference',\n\tconsentConfigLabel: 'formBuilder:consent.config.label',\n\tconsentConfigUrl: 'formBuilder:consent.config.url',\n\tconsentConfigVersion: 'formBuilder:consent.config.version',\n\tconsentConfigRelationTo: 'formBuilder:consent.config.relationTo',\n\tconsentConfigDocId: 'formBuilder:consent.config.docId',\n\tconsentConfigUrlField: 'formBuilder:consent.config.urlField',\n\tconsentConfigCaptureVersion: 'formBuilder:consent.config.captureVersion',\n\tfieldTypeConsent: 'formBuilder:fieldType.consent',\n\tconsentConfigStatement: 'formBuilder:consent.config.statement',\n\tconsentConfigSource: 'formBuilder:consent.config.source',\n\tconsentConfigSourceConfig: 'formBuilder:consent.config.sourceConfig',\n\tconsentConfigOptional: 'formBuilder:consent.config.optional',\n\tresultsTitle: 'formBuilder:results.title',\n\tresultsResponses: 'formBuilder:results.responses',\n\tresultsNoResponses: 'formBuilder:results.noResponses',\n\tresultsTruncated: 'formBuilder:results.truncated',\n\tconfigShowResults: 'formBuilder:config.showResults',\n\tconfigResultsField: 'formBuilder:config.resultsField',\n\tvalidationFileMissing: 'formBuilder:validation.file.missing',\n\tvalidationFileMimeType: 'formBuilder:validation.file.mimeType',\n\tvalidationFileTooLarge: 'formBuilder:validation.file.tooLarge',\n\tfieldTypeFile: 'formBuilder:fieldType.file',\n\tfileConfigRelationTo: 'formBuilder:file.config.relationTo',\n\tfileConfigMimeTypes: 'formBuilder:file.config.mimeTypes',\n\tfileConfigMaxSize: 'formBuilder:file.config.maxSize',\n\tspamRateLimited: 'formBuilder:spam.rateLimited',\n\tspamRejected: 'formBuilder:spam.rejected',\n\tspamCaptchaFailed: 'formBuilder:spam.captchaFailed',\n\tcollectionFormSingular: 'formBuilder:collection.form.singular',\n\tcollectionFormPlural: 'formBuilder:collection.form.plural',\n\tcollectionSubmissionSingular: 'formBuilder:collection.submission.singular',\n\tcollectionSubmissionPlural: 'formBuilder:collection.submission.plural',\n\tstatusComplete: 'formBuilder:status.complete',\n\tstatusPartial: 'formBuilder:status.partial',\n\tfieldTypeRepeater: 'formBuilder:fieldType.repeater',\n\tconfigMinRows: 'formBuilder:config.minRows',\n\tconfigMaxRows: 'formBuilder:config.maxRows',\n\tconfigAddLabel: 'formBuilder:config.addLabel',\n\tconfigSubFields: 'formBuilder:config.subFields',\n\tvalidationRepeaterMin: 'formBuilder:validation.repeaterMin',\n\tvalidationRepeaterMax: 'formBuilder:validation.repeaterMax',\n\trepeaterAddRow: 'formBuilder:repeater.addRow',\n\trepeaterRemoveRow: 'formBuilder:repeater.removeRow',\n\trepeaterRow: 'formBuilder:repeater.row',\n\trepeaterRowCount: 'formBuilder:repeater.rowCount',\n\tsubmissionConsent: 'formBuilder:submission.consent',\n\tsubmissionDetails: 'formBuilder:submission.details',\n\tsubmissionConsentAgreed: 'formBuilder:submission.consentAgreed',\n\tsubmissionConsentDeclined: 'formBuilder:submission.consentDeclined',\n\tsubmissionMetaReceivedAt: 'formBuilder:submission.meta.receivedAt',\n\tsubmissionMetaIp: 'formBuilder:submission.meta.ip',\n\tsubmissionMetaUserAgent: 'formBuilder:submission.meta.userAgent',\n\tsubmissionMetaCaptcha: 'formBuilder:submission.meta.captcha',\n} as const\n\nexport type TranslationKey = (typeof keys)[keyof typeof keys]\n"],"mappings":";;;;;;AAKA,MAAa,OAAO;CACnB,YAAY;CACZ,YAAY;CACZ,eAAe;CACf,mBAAmB;CACnB,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,mBAAmB;CACnB,eAAe;CACf,cAAc;CACd,mBAAmB;CACnB,mBAAmB;CACnB,oBAAoB;CACpB,iBAAiB;CACjB,kBAAkB;CAClB,kBAAkB;CAClB,WAAW;CACX,UAAU;CACV,YAAY;CACZ,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,mBAAmB;CACnB,mBAAmB;CACnB,mBAAmB;CACnB,oBAAoB;CACpB,mBAAmB;CACnB,qBAAqB;CACrB,eAAe;CACf,eAAe;CACf,SAAS;CACT,SAAS;CACT,aAAa;CACb,WAAW;CACX,SAAS;CACT,WAAW;CACX,kBAAkB;CAClB,yBAAyB;CACzB,sBAAsB;CACtB,sBAAsB;CACtB,gBAAgB;CAChB,gBAAgB;CAChB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,kBAAkB;CAClB,yBAAyB;CACzB,gCAAgC;CAChC,cAAc;CACd,cAAc;CACd,kBAAkB;CAClB,gBAAgB;CAChB,iBAAiB;CACjB,gBAAgB;CAChB,kBAAkB;CAClB,wBAAwB;CACxB,yBAAyB;CACzB,yBAAyB;CACzB,2BAA2B;CAC3B,uBAAuB;CACvB,gBAAgB;CAChB,cAAc;CACd,aAAa;CACb,iBAAiB;CACjB,mBAAmB;CACnB,gBAAgB;CAChB,sBAAsB;CACtB,2BAA2B;CAC3B,eAAe;CACf,gBAAgB;CAChB,gBAAgB;CAChB,cAAc;CACd,sBAAsB;CACtB,kBAAkB;CAClB,mBAAmB;CACnB,2BAA2B;CAC3B,kBAAkB;CAClB,mBAAmB;CACnB,oBAAoB;CACpB,oBAAoB;CACpB,iBAAiB;CACjB,oBAAoB;CACpB,qBAAqB;CACrB,gBAAgB;CAChB,qBAAqB;CACrB,kBAAkB;CAClB,qBAAqB;CACrB,iBAAiB;CACjB,oBAAoB;CACpB,eAAe;CACf,qBAAqB;CACrB,4BAA4B;CAC5B,oBAAoB;CACpB,kBAAkB;CAClB,sBAAsB;CACtB,yBAAyB;CACzB,oBAAoB;CACpB,uBAAuB;CACvB,6BAA6B;CAC7B,kBAAkB;CAClB,wBAAwB;CACxB,qBAAqB;CACrB,2BAA2B;CAC3B,uBAAuB;CACvB,cAAc;CACd,kBAAkB;CAClB,oBAAoB;CACpB,kBAAkB;CAClB,mBAAmB;CACnB,oBAAoB;CACpB,uBAAuB;CACvB,wBAAwB;CACxB,wBAAwB;CACxB,eAAe;CACf,sBAAsB;CACtB,qBAAqB;CACrB,mBAAmB;CACnB,iBAAiB;CACjB,cAAc;CACd,mBAAmB;CACnB,wBAAwB;CACxB,sBAAsB;CACtB,8BAA8B;CAC9B,4BAA4B;CAC5B,gBAAgB;CAChB,eAAe;CACf,mBAAmB;CACnB,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,iBAAiB;CACjB,uBAAuB;CACvB,uBAAuB;CACvB,gBAAgB;CAChB,mBAAmB;CACnB,aAAa;CACb,kBAAkB;CAClB,mBAAmB;CACnB,mBAAmB;CACnB,yBAAyB;CACzB,2BAA2B;CAC3B,0BAA0B;CAC1B,kBAAkB;CAClB,yBAAyB;CACzB,uBAAuB;AACxB"}
|
|
@@ -38,7 +38,7 @@ const runValidation = async (input) => {
|
|
|
38
38
|
message: t(keys.validationRequired),
|
|
39
39
|
severity: "error"
|
|
40
40
|
}] };
|
|
41
|
-
if (isEmpty(value)) return { errors: [] };
|
|
41
|
+
if (isEmpty(value) && field.blockType !== "repeater") return { errors: [] };
|
|
42
42
|
if (fieldDefinition?.validate) try {
|
|
43
43
|
const ran = fieldDefinition.validate({
|
|
44
44
|
value,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runValidation.js","names":[],"sources":["../../src/validation/runValidation.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport type { AnyFormFieldDefinition, Translate } from '../fields/types'\nimport type { FormFieldInstance } from '../submissions/types'\nimport { keys } from '../translations/keys'\nimport { resolveMessage, withTimeout } from './message'\nimport type { ValidationRuleRegistry } from './registry'\nimport type { MessageFn, ValidationRuleResult, ValidationSeverity } from './types'\n\nconst RULE_TIMEOUT_MS = 5000\n\nexport type ValidationIssue = { message: string; severity: ValidationSeverity }\n\nexport type RunValidationInput = {\n\tfield: FormFieldInstance\n\tfieldDefinition?: AnyFormFieldDefinition\n\tvalue: unknown\n\tfieldType: string\n\truleRegistry: ValidationRuleRegistry\n\tanswers: Record<string, unknown>\n\tlocale: string\n\tt: Translate\n\toperation: 'create' | 'update'\n\tevent: 'onChange' | 'submit'\n\tmode: 'client' | 'server'\n\treq?: PayloadRequest\n\tpayload?: Payload\n\tformId?: number | string\n\t/** Override the per-rule timeout in ms. Defaults to RULE_TIMEOUT_MS; primarily for tests. */\n\ttimeoutMs?: number\n}\n\nexport type RunValidationResult = { errors: ValidationIssue[] }\n\nconst isEmpty = (value: unknown): boolean =>\n\tvalue == null || value === '' || (Array.isArray(value) && value.length === 0)\n\ntype RuleInstance = { blockType: string; message?: string; severity?: ValidationSeverity } & Record<\n\tstring,\n\tunknown\n>\n\n/** Build the per-instance message resolver: custom override (a literal template) or the localized default. */\nconst makeMessage = (\n\tinstance: RuleInstance,\n\tdefaultMessageKey: string,\n\tt: Translate\n): MessageFn => {\n\tconst { blockType: _blockType, message: _message, severity: _severity, ...params } = instance\n\tconst template =\n\t\ttypeof instance.message === 'string' && instance.message.length > 0\n\t\t\t? instance.message\n\t\t\t: t(defaultMessageKey)\n\treturn (vars = {}) => resolveMessage(template, { ...params, ...vars })\n}\n\nconst toIssue = (\n\tresult: ValidationRuleResult,\n\tfallbackSeverity: ValidationSeverity\n): ValidationIssue | null => {\n\tif (result === true) {\n\t\treturn null\n\t}\n\tif (typeof result === 'string') {\n\t\treturn { message: result, severity: fallbackSeverity }\n\t}\n\treturn { message: result.message, severity: result.severity ?? fallbackSeverity }\n}\n\n/**\n * The one validation engine, run per field on client and server. Order: required, the field type's\n * intrinsic validator, then each declarative rule instance. Server-only rules are skipped in client\n * mode; every rule is wrapped in try/catch + a timeout so a bad rule cannot crash or stall the submit\n * path (a thrown or timed-out rule fails open, logged on the server). Only `error`-severity issues\n * block submission; the caller filters severity.\n */\nexport const runValidation = async (input: RunValidationInput): Promise<RunValidationResult> => {\n\tconst {\n\t\tfield,\n\t\tfieldDefinition,\n\t\tvalue,\n\t\tfieldType,\n\t\truleRegistry,\n\t\tanswers,\n\t\tlocale,\n\t\tt,\n\t\toperation,\n\t\tevent,\n\t\tmode,\n\t\treq,\n\t\tpayload,\n\t\tformId,\n\t\ttimeoutMs,\n\t} = input\n\tconst timeout = timeoutMs ?? RULE_TIMEOUT_MS\n\tconst errors: ValidationIssue[] = []\n\n\tif (field.required && isEmpty(value)) {\n\t\treturn { errors: [{ message: t(keys.validationRequired), severity: 'error' }] }\n\t}\n\tif (isEmpty(value)) {\n\t\treturn { errors: [] }\n\t}\n\n\tif (fieldDefinition?.validate) {\n\t\ttry {\n\t\t\tconst ran = fieldDefinition.validate({\n\t\t\t\tvalue,\n\t\t\t\tconfig: field,\n\t\t\t\tsiblingData: answers,\n\t\t\t\tdata: answers,\n\t\t\t\tlocale,\n\t\t\t\tt,\n\t\t\t})\n\t\t\tconst result = ran instanceof Promise ? await withTimeout(ran, timeout, true) : ran\n\t\t\tif (result !== true) {\n\t\t\t\terrors.push({ message: result, severity: 'error' })\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treq?.payload?.logger?.error?.(\n\t\t\t\t{ err: error, field: field.name },\n\t\t\t\t'form-builder intrinsic field validator threw'\n\t\t\t)\n\t\t}\n\t}\n\n\tif (fieldDefinition?.schema) {\n\t\ttry {\n\t\t\tconst outcome = await fieldDefinition.schema['~standard'].validate(value)\n\t\t\tif (outcome.issues) {\n\t\t\t\tfor (const issue of outcome.issues) {\n\t\t\t\t\terrors.push({ message: issue.message, severity: 'error' })\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treq?.payload?.logger?.error?.(\n\t\t\t\t{ err: error, field: field.name },\n\t\t\t\t'form-builder field schema threw'\n\t\t\t)\n\t\t}\n\t}\n\n\tconst instances = (Array.isArray(field.validations) ? field.validations : []) as RuleInstance[]\n\tfor (const instance of instances) {\n\t\tconst rule = ruleRegistry.get(instance.blockType)\n\t\tif (!rule) {\n\t\t\tcontinue\n\t\t}\n\t\tif (mode === 'client' && rule.client === false) {\n\t\t\tcontinue\n\t\t}\n\t\tconst message = makeMessage(instance, rule.defaultMessage, t)\n\t\tconst fallbackSeverity: ValidationSeverity = instance.severity ?? rule.severity ?? 'error'\n\t\ttry {\n\t\t\tconst ran = rule.validate({\n\t\t\t\tvalue,\n\t\t\t\tparams: instance,\n\t\t\t\tsiblingData: answers,\n\t\t\t\tdata: answers,\n\t\t\t\tfield,\n\t\t\t\tfieldType,\n\t\t\t\toperation,\n\t\t\t\tevent,\n\t\t\t\tlocale,\n\t\t\t\tmessage,\n\t\t\t\treq: mode === 'server' ? req : undefined,\n\t\t\t\tpayload: mode === 'server' ? payload : undefined,\n\t\t\t\tformId: mode === 'server' ? formId : undefined,\n\t\t\t})\n\t\t\tconst result = ran instanceof Promise ? await withTimeout(ran, timeout, true) : ran\n\t\t\tconst issue = toIssue(result, fallbackSeverity)\n\t\t\tif (issue) {\n\t\t\t\terrors.push(issue)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treq?.payload?.logger?.error?.(\n\t\t\t\t{ err: error, rule: rule.type },\n\t\t\t\t'form-builder validation rule threw'\n\t\t\t)\n\t\t}\n\t}\n\n\treturn { errors }\n}\n"],"mappings":";;;AAQA,MAAM,kBAAkB;AAyBxB,MAAM,WAAW,UAChB,SAAS,QAAQ,UAAU,MAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;;AAQ5E,MAAM,eACL,UACA,mBACA,MACe;CACf,MAAM,EAAE,WAAW,YAAY,SAAS,UAAU,UAAU,WAAW,GAAG,WAAW;CACrF,MAAM,WACL,OAAO,SAAS,YAAY,YAAY,SAAS,QAAQ,SAAS,IAC/D,SAAS,UACT,EAAE,iBAAiB;CACvB,QAAQ,OAAO,CAAC,MAAM,eAAe,UAAU;EAAE,GAAG;EAAQ,GAAG;CAAK,CAAC;AACtE;AAEA,MAAM,WACL,QACA,qBAC4B;CAC5B,IAAI,WAAW,MACd,OAAO;CAER,IAAI,OAAO,WAAW,UACrB,OAAO;EAAE,SAAS;EAAQ,UAAU;CAAiB;CAEtD,OAAO;EAAE,SAAS,OAAO;EAAS,UAAU,OAAO,YAAY;CAAiB;AACjF;;;;;;;;AASA,MAAa,gBAAgB,OAAO,UAA4D;CAC/F,MAAM,EACL,OACA,iBACA,OACA,WACA,cACA,SACA,QACA,GACA,WACA,OACA,MACA,KACA,SACA,QACA,cACG;CACJ,MAAM,UAAU,aAAa;CAC7B,MAAM,SAA4B,CAAC;CAEnC,IAAI,MAAM,YAAY,QAAQ,KAAK,GAClC,OAAO,EAAE,QAAQ,CAAC;EAAE,SAAS,EAAE,KAAK,kBAAkB;EAAG,UAAU;CAAQ,CAAC,EAAE;
|
|
1
|
+
{"version":3,"file":"runValidation.js","names":[],"sources":["../../src/validation/runValidation.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport type { AnyFormFieldDefinition, Translate } from '../fields/types'\nimport type { FormFieldInstance } from '../submissions/types'\nimport { keys } from '../translations/keys'\nimport { resolveMessage, withTimeout } from './message'\nimport type { ValidationRuleRegistry } from './registry'\nimport type { MessageFn, ValidationRuleResult, ValidationSeverity } from './types'\n\nconst RULE_TIMEOUT_MS = 5000\n\nexport type ValidationIssue = { message: string; severity: ValidationSeverity }\n\nexport type RunValidationInput = {\n\tfield: FormFieldInstance\n\tfieldDefinition?: AnyFormFieldDefinition\n\tvalue: unknown\n\tfieldType: string\n\truleRegistry: ValidationRuleRegistry\n\tanswers: Record<string, unknown>\n\tlocale: string\n\tt: Translate\n\toperation: 'create' | 'update'\n\tevent: 'onChange' | 'submit'\n\tmode: 'client' | 'server'\n\treq?: PayloadRequest\n\tpayload?: Payload\n\tformId?: number | string\n\t/** Override the per-rule timeout in ms. Defaults to RULE_TIMEOUT_MS; primarily for tests. */\n\ttimeoutMs?: number\n}\n\nexport type RunValidationResult = { errors: ValidationIssue[] }\n\nconst isEmpty = (value: unknown): boolean =>\n\tvalue == null || value === '' || (Array.isArray(value) && value.length === 0)\n\ntype RuleInstance = { blockType: string; message?: string; severity?: ValidationSeverity } & Record<\n\tstring,\n\tunknown\n>\n\n/** Build the per-instance message resolver: custom override (a literal template) or the localized default. */\nconst makeMessage = (\n\tinstance: RuleInstance,\n\tdefaultMessageKey: string,\n\tt: Translate\n): MessageFn => {\n\tconst { blockType: _blockType, message: _message, severity: _severity, ...params } = instance\n\tconst template =\n\t\ttypeof instance.message === 'string' && instance.message.length > 0\n\t\t\t? instance.message\n\t\t\t: t(defaultMessageKey)\n\treturn (vars = {}) => resolveMessage(template, { ...params, ...vars })\n}\n\nconst toIssue = (\n\tresult: ValidationRuleResult,\n\tfallbackSeverity: ValidationSeverity\n): ValidationIssue | null => {\n\tif (result === true) {\n\t\treturn null\n\t}\n\tif (typeof result === 'string') {\n\t\treturn { message: result, severity: fallbackSeverity }\n\t}\n\treturn { message: result.message, severity: result.severity ?? fallbackSeverity }\n}\n\n/**\n * The one validation engine, run per field on client and server. Order: required, the field type's\n * intrinsic validator, then each declarative rule instance. Server-only rules are skipped in client\n * mode; every rule is wrapped in try/catch + a timeout so a bad rule cannot crash or stall the submit\n * path (a thrown or timed-out rule fails open, logged on the server). Only `error`-severity issues\n * block submission; the caller filters severity.\n */\nexport const runValidation = async (input: RunValidationInput): Promise<RunValidationResult> => {\n\tconst {\n\t\tfield,\n\t\tfieldDefinition,\n\t\tvalue,\n\t\tfieldType,\n\t\truleRegistry,\n\t\tanswers,\n\t\tlocale,\n\t\tt,\n\t\toperation,\n\t\tevent,\n\t\tmode,\n\t\treq,\n\t\tpayload,\n\t\tformId,\n\t\ttimeoutMs,\n\t} = input\n\tconst timeout = timeoutMs ?? RULE_TIMEOUT_MS\n\tconst errors: ValidationIssue[] = []\n\n\tif (field.required && isEmpty(value)) {\n\t\treturn { errors: [{ message: t(keys.validationRequired), severity: 'error' }] }\n\t}\n\t// Repeaters coerced to [] must reach fieldDefinition.validate so minRows is enforced;\n\t// all other empty fields have no intrinsic rules to run.\n\tif (isEmpty(value) && field.blockType !== 'repeater') {\n\t\treturn { errors: [] }\n\t}\n\n\tif (fieldDefinition?.validate) {\n\t\ttry {\n\t\t\tconst ran = fieldDefinition.validate({\n\t\t\t\tvalue,\n\t\t\t\tconfig: field,\n\t\t\t\tsiblingData: answers,\n\t\t\t\tdata: answers,\n\t\t\t\tlocale,\n\t\t\t\tt,\n\t\t\t})\n\t\t\tconst result = ran instanceof Promise ? await withTimeout(ran, timeout, true) : ran\n\t\t\tif (result !== true) {\n\t\t\t\terrors.push({ message: result, severity: 'error' })\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treq?.payload?.logger?.error?.(\n\t\t\t\t{ err: error, field: field.name },\n\t\t\t\t'form-builder intrinsic field validator threw'\n\t\t\t)\n\t\t}\n\t}\n\n\tif (fieldDefinition?.schema) {\n\t\ttry {\n\t\t\tconst outcome = await fieldDefinition.schema['~standard'].validate(value)\n\t\t\tif (outcome.issues) {\n\t\t\t\tfor (const issue of outcome.issues) {\n\t\t\t\t\terrors.push({ message: issue.message, severity: 'error' })\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treq?.payload?.logger?.error?.(\n\t\t\t\t{ err: error, field: field.name },\n\t\t\t\t'form-builder field schema threw'\n\t\t\t)\n\t\t}\n\t}\n\n\tconst instances = (Array.isArray(field.validations) ? field.validations : []) as RuleInstance[]\n\tfor (const instance of instances) {\n\t\tconst rule = ruleRegistry.get(instance.blockType)\n\t\tif (!rule) {\n\t\t\tcontinue\n\t\t}\n\t\tif (mode === 'client' && rule.client === false) {\n\t\t\tcontinue\n\t\t}\n\t\tconst message = makeMessage(instance, rule.defaultMessage, t)\n\t\tconst fallbackSeverity: ValidationSeverity = instance.severity ?? rule.severity ?? 'error'\n\t\ttry {\n\t\t\tconst ran = rule.validate({\n\t\t\t\tvalue,\n\t\t\t\tparams: instance,\n\t\t\t\tsiblingData: answers,\n\t\t\t\tdata: answers,\n\t\t\t\tfield,\n\t\t\t\tfieldType,\n\t\t\t\toperation,\n\t\t\t\tevent,\n\t\t\t\tlocale,\n\t\t\t\tmessage,\n\t\t\t\treq: mode === 'server' ? req : undefined,\n\t\t\t\tpayload: mode === 'server' ? payload : undefined,\n\t\t\t\tformId: mode === 'server' ? formId : undefined,\n\t\t\t})\n\t\t\tconst result = ran instanceof Promise ? await withTimeout(ran, timeout, true) : ran\n\t\t\tconst issue = toIssue(result, fallbackSeverity)\n\t\t\tif (issue) {\n\t\t\t\terrors.push(issue)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treq?.payload?.logger?.error?.(\n\t\t\t\t{ err: error, rule: rule.type },\n\t\t\t\t'form-builder validation rule threw'\n\t\t\t)\n\t\t}\n\t}\n\n\treturn { errors }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@10x-media/form-builder",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.2",
|
|
4
4
|
"description": "End-to-end forms platform for Payload: author, validate, render, collect, aggregate, and act.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -91,10 +91,10 @@
|
|
|
91
91
|
"tsdown": "0.22.1",
|
|
92
92
|
"typescript": "5.9.3",
|
|
93
93
|
"vitest": "4.1.7",
|
|
94
|
-
"@10x-media/tsconfig": "0.0.0",
|
|
95
94
|
"@10x-media/payload-test-harness": "0.0.0",
|
|
95
|
+
"@10x-media/tsdown-config": "0.0.0",
|
|
96
96
|
"@10x-media/vitest-config": "0.0.0",
|
|
97
|
-
"@10x-media/
|
|
97
|
+
"@10x-media/tsconfig": "0.0.0"
|
|
98
98
|
},
|
|
99
99
|
"publishConfig": {
|
|
100
100
|
"access": "public"
|