@10x-media/form-builder 0.1.0-beta.1 → 0.1.0-beta.3
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 +34 -0
- package/README.md +4 -2
- package/dist/actions/body/converters.d.ts +20 -0
- package/dist/actions/body/converters.js +57 -0
- package/dist/actions/body/converters.js.map +1 -0
- package/dist/actions/body/escapeHtml.d.ts +6 -0
- package/dist/actions/body/escapeHtml.js +14 -0
- package/dist/actions/body/escapeHtml.js.map +1 -0
- package/dist/actions/body/serializeBody.d.ts +35 -0
- package/dist/actions/body/serializeBody.js +76 -0
- package/dist/actions/body/serializeBody.js.map +1 -0
- package/dist/actions/body/serializeSlate.js +31 -0
- package/dist/actions/body/serializeSlate.js.map +1 -0
- package/dist/actions/body/wildcards.d.ts +10 -0
- package/dist/actions/body/wildcards.js +33 -0
- package/dist/actions/body/wildcards.js.map +1 -0
- package/dist/actions/builtin/confirmation.js +28 -12
- package/dist/actions/builtin/confirmation.js.map +1 -1
- package/dist/actions/builtin/emailTeam.js +7 -9
- package/dist/actions/builtin/emailTeam.js.map +1 -1
- package/dist/actions/builtin/signedWebhook.js +21 -3
- package/dist/actions/builtin/signedWebhook.js.map +1 -1
- package/dist/actions/defineAction.d.ts +2 -1
- package/dist/actions/defineAction.js.map +1 -1
- package/dist/actions/dispatch.js +2 -1
- package/dist/actions/dispatch.js.map +1 -1
- package/dist/actions/runActions.js +8 -1
- package/dist/actions/runActions.js.map +1 -1
- package/dist/actions/task.js +7 -5
- package/dist/actions/task.js.map +1 -1
- package/dist/calc/normalizeCalc.js +5 -17
- package/dist/calc/normalizeCalc.js.map +1 -1
- package/dist/calc/types.d.ts +8 -2
- package/dist/calc/types.js +22 -0
- package/dist/calc/types.js.map +1 -0
- package/dist/client/ByteSizeField.d.ts +21 -0
- package/dist/client/ByteSizeField.js +56 -0
- package/dist/client/ByteSizeField.js.map +1 -0
- package/dist/client/ConditionBuilder.js +2 -1
- package/dist/client/ConditionBuilder.js.map +1 -1
- package/dist/client/FieldNameSelect.d.ts +20 -0
- package/dist/client/FieldNameSelect.js +58 -0
- package/dist/client/FieldNameSelect.js.map +1 -0
- package/dist/client/FlowBuilder.d.ts +0 -1
- package/dist/client/FlowBuilder.js +4 -6
- package/dist/client/FlowBuilder.js.map +1 -1
- package/dist/client/FormConditionField.js +1 -4
- package/dist/client/FormConditionField.js.map +1 -1
- package/dist/client/admin.css +192 -0
- package/dist/client/toStaticLabel.js +13 -0
- package/dist/client/toStaticLabel.js.map +1 -0
- package/dist/collections/formSubmissions.js +60 -39
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +143 -115
- 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/client.d.ts +3 -1
- package/dist/exports/client.js +3 -1
- package/dist/exports/react.d.ts +7 -2
- package/dist/exports/react.js +6 -2
- package/dist/exports/types.d.ts +2 -1
- package/dist/fields/buildFieldBlocks.js +22 -12
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/calculation.js +115 -2
- package/dist/fields/builtin/calculation.js.map +1 -1
- package/dist/fields/builtin/date.js +29 -0
- package/dist/fields/builtin/date.js.map +1 -0
- package/dist/fields/builtin/file.d.ts +15 -0
- package/dist/fields/builtin/file.js +79 -3
- package/dist/fields/builtin/file.js.map +1 -1
- package/dist/fields/builtin/index.d.ts +14 -0
- package/dist/fields/builtin/index.js +13 -2
- 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/fieldNamesOfType.js +25 -0
- package/dist/fields/fieldNamesOfType.js.map +1 -0
- package/dist/fields/sharedConfig.js +30 -18
- package/dist/fields/sharedConfig.js.map +1 -1
- package/dist/fields/types.d.ts +2 -1
- package/dist/index.d.ts +38 -2
- package/dist/index.js +17 -5
- package/dist/index.js.map +1 -1
- package/dist/plugin/collectionOverrides.d.ts +22 -0
- package/dist/plugin/registerCollections.js +39 -10
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/react/Form.d.ts +32 -3
- package/dist/react/Form.js +50 -8
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormContext.js.map +1 -1
- package/dist/react/Poll.js +1 -1
- package/dist/react/captcha/HcaptchaCaptcha.d.ts +28 -0
- package/dist/react/captcha/HcaptchaCaptcha.js +53 -0
- package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/RecaptchaCaptcha.d.ts +41 -0
- package/dist/react/captcha/RecaptchaCaptcha.js +93 -0
- package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/TurnstileCaptcha.d.ts +28 -0
- package/dist/react/captcha/TurnstileCaptcha.js +53 -0
- package/dist/react/captcha/TurnstileCaptcha.js.map +1 -0
- package/dist/react/captcha/types.d.ts +8 -0
- package/dist/react/captcha/useCaptchaScript.js +45 -0
- package/dist/react/captcha/useCaptchaScript.js.map +1 -0
- package/dist/react/captcha/vendors.js +8 -0
- package/dist/react/captcha/vendors.js.map +1 -0
- package/dist/react/primitives/Input.d.ts +1 -1
- package/dist/react/primitives/Input.js.map +1 -1
- package/dist/react/renderers/calculation.js +1 -1
- package/dist/react/renderers/checkbox.js +1 -1
- package/dist/react/renderers/consent.js +1 -1
- package/dist/react/renderers/date.js +37 -0
- package/dist/react/renderers/date.js.map +1 -0
- package/dist/react/renderers/email.js +1 -1
- package/dist/react/renderers/file.js +20 -7
- package/dist/react/renderers/file.js.map +1 -1
- package/dist/react/renderers/index.js +4 -0
- package/dist/react/renderers/index.js.map +1 -1
- package/dist/react/renderers/number.js +1 -1
- package/dist/react/renderers/repeater.js +113 -0
- package/dist/react/renderers/repeater.js.map +1 -0
- package/dist/react/renderers/select.js +1 -1
- package/dist/react/renderers/text.js +1 -1
- package/dist/react/renderers/textarea.js +1 -1
- package/dist/spam/captcha.d.ts +3 -2
- package/dist/spam/captcha.js +3 -2
- package/dist/spam/captcha.js.map +1 -1
- package/dist/spam/clientIp.js +9 -0
- package/dist/spam/clientIp.js.map +1 -0
- package/dist/spam/identify.js +3 -6
- package/dist/spam/identify.js.map +1 -1
- package/dist/spam/providers/hcaptcha.d.ts +17 -0
- package/dist/spam/providers/hcaptcha.js +18 -0
- package/dist/spam/providers/hcaptcha.js.map +1 -0
- package/dist/spam/providers/recaptcha.d.ts +23 -0
- package/dist/spam/providers/recaptcha.js +23 -0
- package/dist/spam/providers/recaptcha.js.map +1 -0
- package/dist/spam/providers/siteverify.js +34 -0
- package/dist/spam/providers/siteverify.js.map +1 -0
- package/dist/spam/providers/turnstile.d.ts +17 -0
- package/dist/spam/providers/turnstile.js +18 -0
- package/dist/spam/providers/turnstile.js.map +1 -0
- package/dist/spam/spamGuard.js +2 -5
- package/dist/spam/spamGuard.js.map +1 -1
- package/dist/spam/types.d.ts +2 -2
- 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 +52 -5
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/keys.d.ts +51 -4
- package/dist/translations/keys.js +52 -5
- package/dist/translations/keys.js.map +1 -1
- package/dist/uploads/formatBytes.d.ts +9 -0
- package/dist/uploads/formatBytes.js +25 -0
- package/dist/uploads/formatBytes.js.map +1 -0
- package/dist/uploads/resolveFileRef.js +1 -1
- package/dist/uploads/resolveFileRef.js.map +1 -1
- package/dist/validation/builtin/index.d.ts +14 -0
- package/dist/validation/builtin/index.js +12 -1
- package/dist/validation/builtin/index.js.map +1 -1
- package/dist/validation/builtin/maxDate.js +23 -0
- package/dist/validation/builtin/maxDate.js.map +1 -0
- package/dist/validation/builtin/minDate.js +23 -0
- package/dist/validation/builtin/minDate.js.map +1 -0
- package/dist/validation/builtin/validateDateBound.js +18 -0
- package/dist/validation/builtin/validateDateBound.js.map +1 -0
- package/dist/validation/runValidation.js +1 -1
- package/dist/validation/runValidation.js.map +1 -1
- package/package.json +4 -3
- package/styles/form-builder.css +0 -190
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/uploads/formatBytes.ts
|
|
2
|
+
const units = [
|
|
3
|
+
"B",
|
|
4
|
+
"KB",
|
|
5
|
+
"MB",
|
|
6
|
+
"GB"
|
|
7
|
+
];
|
|
8
|
+
/**
|
|
9
|
+
* Format a byte count as a human-readable size (1024-based, B/KB/MB/GB) with at most one decimal.
|
|
10
|
+
* Trailing `.0` is dropped: `2621440` -> `'2.5 MB'`, `1024` -> `'1 KB'`, `500` -> `'500 B'`.
|
|
11
|
+
*/
|
|
12
|
+
const formatBytes = (n) => {
|
|
13
|
+
if (!Number.isFinite(n) || n <= 0) return "0 B";
|
|
14
|
+
let value = n;
|
|
15
|
+
let unit = 0;
|
|
16
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
17
|
+
value /= 1024;
|
|
18
|
+
unit += 1;
|
|
19
|
+
}
|
|
20
|
+
return `${Math.round(value * 10) / 10} ${units[unit]}`;
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { formatBytes };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=formatBytes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatBytes.js","names":[],"sources":["../../src/uploads/formatBytes.ts"],"sourcesContent":["const units = ['B', 'KB', 'MB', 'GB'] as const\n\n/**\n * Format a byte count as a human-readable size (1024-based, B/KB/MB/GB) with at most one decimal.\n * Trailing `.0` is dropped: `2621440` -> `'2.5 MB'`, `1024` -> `'1 KB'`, `500` -> `'500 B'`.\n */\nexport const formatBytes = (n: number): string => {\n\tif (!Number.isFinite(n) || n <= 0) {\n\t\treturn '0 B'\n\t}\n\tlet value = n\n\tlet unit = 0\n\twhile (value >= 1024 && unit < units.length - 1) {\n\t\tvalue /= 1024\n\t\tunit += 1\n\t}\n\treturn `${Math.round(value * 10) / 10} ${units[unit]}`\n}\n"],"mappings":";AAAA,MAAM,QAAQ;CAAC;CAAK;CAAM;CAAM;AAAI;;;;;AAMpC,MAAa,eAAe,MAAsB;CACjD,IAAI,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,GAC/B,OAAO;CAER,IAAI,QAAQ;CACZ,IAAI,OAAO;CACX,OAAO,SAAS,QAAQ,OAAO,MAAM,SAAS,GAAG;EAChD,SAAS;EACT,QAAQ;CACT;CACA,OAAO,GAAG,KAAK,MAAM,QAAQ,EAAE,IAAI,GAAG,GAAG,MAAM;AAChD"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const mimeAllowed = (mimeType, allow) => {
|
|
3
3
|
if (!allow || allow.length === 0) return true;
|
|
4
4
|
return allow.some((pattern) => {
|
|
5
|
-
if (pattern.endsWith("
|
|
5
|
+
if (pattern.endsWith("*")) return mimeType.startsWith(pattern.slice(0, -1));
|
|
6
6
|
return mimeType === pattern;
|
|
7
7
|
});
|
|
8
8
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveFileRef.js","names":[],"sources":["../../src/uploads/resolveFileRef.ts"],"sourcesContent":["import type { FileFieldConfig, FileRef, FileRefError } from './types'\n\ntype UploadDoc = {\n\tid?: string | number\n\tfilename?: unknown\n\tmimeType?: unknown\n\tfilesize?: unknown\n\turl?: unknown\n}\n\nexport type ResolveFileRefResult = { ok: true; ref: FileRef } | { ok: false; code: FileRefError }\n\nconst mimeAllowed = (mimeType: string, allow: string[] | undefined): boolean => {\n\tif (!allow || allow.length === 0) {\n\t\treturn true\n\t}\n\treturn allow.some((pattern) => {\n\t\tif (pattern.endsWith('
|
|
1
|
+
{"version":3,"file":"resolveFileRef.js","names":[],"sources":["../../src/uploads/resolveFileRef.ts"],"sourcesContent":["import type { FileFieldConfig, FileRef, FileRefError } from './types'\n\ntype UploadDoc = {\n\tid?: string | number\n\tfilename?: unknown\n\tmimeType?: unknown\n\tfilesize?: unknown\n\turl?: unknown\n}\n\nexport type ResolveFileRefResult = { ok: true; ref: FileRef } | { ok: false; code: FileRefError }\n\nconst mimeAllowed = (mimeType: string, allow: string[] | undefined): boolean => {\n\tif (!allow || allow.length === 0) {\n\t\treturn true\n\t}\n\treturn allow.some((pattern) => {\n\t\tif (pattern.endsWith('*')) {\n\t\t\treturn mimeType.startsWith(pattern.slice(0, -1))\n\t\t}\n\t\treturn mimeType === pattern\n\t})\n}\n\n/**\n * Pure server trust-boundary check for an uploaded file: given the loaded upload doc (or null) and the\n * field's constraints, either return an authoritative `FileRef` snapshot or a rejection code. Never trusts\n * client metadata; the caller passes the doc it loaded from the upload collection.\n */\nexport const resolveFileRef = (\n\tdoc: UploadDoc | null | undefined,\n\tconfig: FileFieldConfig\n): ResolveFileRefResult => {\n\tif (doc == null || doc.id == null) {\n\t\treturn { ok: false, code: 'missing' }\n\t}\n\tconst mimeType = typeof doc.mimeType === 'string' ? doc.mimeType : ''\n\tconst filesize = typeof doc.filesize === 'number' ? doc.filesize : 0\n\tif (!mimeAllowed(mimeType, config.mimeTypes)) {\n\t\treturn { ok: false, code: 'mimeType' }\n\t}\n\tif (typeof config.maxSize === 'number' && filesize > config.maxSize) {\n\t\treturn { ok: false, code: 'tooLarge' }\n\t}\n\tconst ref: FileRef = {\n\t\tid: doc.id,\n\t\tfilename: typeof doc.filename === 'string' ? doc.filename : '',\n\t\tmimeType,\n\t\tfilesize,\n\t}\n\tif (typeof doc.url === 'string' && doc.url.length > 0) {\n\t\tref.url = doc.url\n\t}\n\treturn { ok: true, ref }\n}\n"],"mappings":";AAYA,MAAM,eAAe,UAAkB,UAAyC;CAC/E,IAAI,CAAC,SAAS,MAAM,WAAW,GAC9B,OAAO;CAER,OAAO,MAAM,MAAM,YAAY;EAC9B,IAAI,QAAQ,SAAS,GAAG,GACvB,OAAO,SAAS,WAAW,QAAQ,MAAM,GAAG,EAAE,CAAC;EAEhD,OAAO,aAAa;CACrB,CAAC;AACF;;;;;;AAOA,MAAa,kBACZ,KACA,WAC0B;CAC1B,IAAI,OAAO,QAAQ,IAAI,MAAM,MAC5B,OAAO;EAAE,IAAI;EAAO,MAAM;CAAU;CAErC,MAAM,WAAW,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;CACnE,MAAM,WAAW,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;CACnE,IAAI,CAAC,YAAY,UAAU,OAAO,SAAS,GAC1C,OAAO;EAAE,IAAI;EAAO,MAAM;CAAW;CAEtC,IAAI,OAAO,OAAO,YAAY,YAAY,WAAW,OAAO,SAC3D,OAAO;EAAE,IAAI;EAAO,MAAM;CAAW;CAEtC,MAAM,MAAe;EACpB,IAAI,IAAI;EACR,UAAU,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;EAC5D;EACA;CACD;CACA,IAAI,OAAO,IAAI,QAAQ,YAAY,IAAI,IAAI,SAAS,GACnD,IAAI,MAAM,IAAI;CAEf,OAAO;EAAE,IAAI;EAAM;CAAI;AACxB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AnyValidationRuleDefinition } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/validation/builtin/index.d.ts
|
|
4
|
+
declare const defaultValidationRules: AnyValidationRuleDefinition[];
|
|
5
|
+
/**
|
|
6
|
+
* `defaultValidationRules` indexed by `type`, so a single built-in can be spread and tweaked
|
|
7
|
+
* without hand-rolling a `.find()`, e.g.
|
|
8
|
+
* `rules: { minLength: { ...defaultValidationRulesByType.minLength, message: myMessage } }`. The
|
|
9
|
+
* `rules` plugin option replaces a built-in of the same `type`; `false` removes it, `true` keeps it.
|
|
10
|
+
*/
|
|
11
|
+
declare const defaultValidationRulesByType: Record<string, AnyValidationRuleDefinition>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { defaultValidationRules, defaultValidationRulesByType };
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { emailRule } from "./email.js";
|
|
2
2
|
import { matchesFieldRule } from "./matchesField.js";
|
|
3
3
|
import { maxRule } from "./max.js";
|
|
4
|
+
import { maxDateRule } from "./maxDate.js";
|
|
4
5
|
import { maxLengthRule } from "./maxLength.js";
|
|
5
6
|
import { minRule } from "./min.js";
|
|
7
|
+
import { minDateRule } from "./minDate.js";
|
|
6
8
|
import { minLengthRule } from "./minLength.js";
|
|
7
9
|
import { notAlreadySubmittedRule } from "./notAlreadySubmitted.js";
|
|
8
10
|
import { oneOfRule } from "./oneOf.js";
|
|
@@ -14,6 +16,8 @@ const defaultValidationRules = [
|
|
|
14
16
|
maxLengthRule,
|
|
15
17
|
minRule,
|
|
16
18
|
maxRule,
|
|
19
|
+
minDateRule,
|
|
20
|
+
maxDateRule,
|
|
17
21
|
patternRule,
|
|
18
22
|
emailRule,
|
|
19
23
|
urlRule,
|
|
@@ -21,7 +25,14 @@ const defaultValidationRules = [
|
|
|
21
25
|
matchesFieldRule,
|
|
22
26
|
notAlreadySubmittedRule
|
|
23
27
|
];
|
|
28
|
+
/**
|
|
29
|
+
* `defaultValidationRules` indexed by `type`, so a single built-in can be spread and tweaked
|
|
30
|
+
* without hand-rolling a `.find()`, e.g.
|
|
31
|
+
* `rules: { minLength: { ...defaultValidationRulesByType.minLength, message: myMessage } }`. The
|
|
32
|
+
* `rules` plugin option replaces a built-in of the same `type`; `false` removes it, `true` keeps it.
|
|
33
|
+
*/
|
|
34
|
+
const defaultValidationRulesByType = Object.fromEntries(defaultValidationRules.map((rule) => [rule.type, rule]));
|
|
24
35
|
//#endregion
|
|
25
|
-
export { defaultValidationRules };
|
|
36
|
+
export { defaultValidationRules, defaultValidationRulesByType };
|
|
26
37
|
|
|
27
38
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/validation/builtin/index.ts"],"sourcesContent":["import type { AnyValidationRuleDefinition } from '../types'\nimport { emailRule } from './email'\nimport { matchesFieldRule } from './matchesField'\nimport { maxRule } from './max'\nimport { maxLengthRule } from './maxLength'\nimport { minRule } from './min'\nimport { minLengthRule } from './minLength'\nimport { notAlreadySubmittedRule } from './notAlreadySubmitted'\nimport { oneOfRule } from './oneOf'\nimport { patternRule } from './pattern'\nimport { urlRule } from './url'\n\n// Rules are authored with precise param/value generics; the registry stores them erased (params\n// re-narrow per matched rule at execution). One cast per rule, no `any`.\nexport const defaultValidationRules: AnyValidationRuleDefinition[] = [\n\tminLengthRule as AnyValidationRuleDefinition,\n\tmaxLengthRule as AnyValidationRuleDefinition,\n\tminRule as AnyValidationRuleDefinition,\n\tmaxRule as AnyValidationRuleDefinition,\n\tpatternRule as AnyValidationRuleDefinition,\n\temailRule as AnyValidationRuleDefinition,\n\turlRule as AnyValidationRuleDefinition,\n\toneOfRule as AnyValidationRuleDefinition,\n\tmatchesFieldRule as AnyValidationRuleDefinition,\n\tnotAlreadySubmittedRule as AnyValidationRuleDefinition,\n]\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/validation/builtin/index.ts"],"sourcesContent":["import type { AnyValidationRuleDefinition } from '../types'\nimport { emailRule } from './email'\nimport { matchesFieldRule } from './matchesField'\nimport { maxRule } from './max'\nimport { maxDateRule } from './maxDate'\nimport { maxLengthRule } from './maxLength'\nimport { minRule } from './min'\nimport { minDateRule } from './minDate'\nimport { minLengthRule } from './minLength'\nimport { notAlreadySubmittedRule } from './notAlreadySubmitted'\nimport { oneOfRule } from './oneOf'\nimport { patternRule } from './pattern'\nimport { urlRule } from './url'\n\n// Rules are authored with precise param/value generics; the registry stores them erased (params\n// re-narrow per matched rule at execution). One cast per rule, no `any`.\nexport const defaultValidationRules: AnyValidationRuleDefinition[] = [\n\tminLengthRule as AnyValidationRuleDefinition,\n\tmaxLengthRule as AnyValidationRuleDefinition,\n\tminRule as AnyValidationRuleDefinition,\n\tmaxRule as AnyValidationRuleDefinition,\n\tminDateRule as AnyValidationRuleDefinition,\n\tmaxDateRule as AnyValidationRuleDefinition,\n\tpatternRule as AnyValidationRuleDefinition,\n\temailRule as AnyValidationRuleDefinition,\n\turlRule as AnyValidationRuleDefinition,\n\toneOfRule as AnyValidationRuleDefinition,\n\tmatchesFieldRule as AnyValidationRuleDefinition,\n\tnotAlreadySubmittedRule as AnyValidationRuleDefinition,\n]\n\n/**\n * `defaultValidationRules` indexed by `type`, so a single built-in can be spread and tweaked\n * without hand-rolling a `.find()`, e.g.\n * `rules: { minLength: { ...defaultValidationRulesByType.minLength, message: myMessage } }`. The\n * `rules` plugin option replaces a built-in of the same `type`; `false` removes it, `true` keeps it.\n */\nexport const defaultValidationRulesByType: Record<string, AnyValidationRuleDefinition> =\n\tObject.fromEntries(defaultValidationRules.map((rule) => [rule.type, rule]))\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAa,yBAAwD;CACpE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;;;AAQA,MAAa,+BACZ,OAAO,YAAY,uBAAuB,KAAK,SAAS,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { keys } from "../../translations/keys.js";
|
|
2
|
+
import { labelFor } from "../../translations/server.js";
|
|
3
|
+
import { defineValidationRule } from "../defineValidationRule.js";
|
|
4
|
+
import { validateDateBound } from "./validateDateBound.js";
|
|
5
|
+
//#region src/validation/builtin/maxDate.ts
|
|
6
|
+
const maxDateRule = defineValidationRule({
|
|
7
|
+
type: "maxDate",
|
|
8
|
+
label: keys.ruleMaxDate,
|
|
9
|
+
appliesTo: ["date"],
|
|
10
|
+
params: [{
|
|
11
|
+
name: "max",
|
|
12
|
+
type: "text",
|
|
13
|
+
required: true,
|
|
14
|
+
label: labelFor(keys.ruleParamMaxDate),
|
|
15
|
+
validate: validateDateBound
|
|
16
|
+
}],
|
|
17
|
+
defaultMessage: keys.ruleMaxDateMessage,
|
|
18
|
+
validate: ({ value, params, message }) => value == null || value === "" || value <= params.max ? true : message({ max: params.max })
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
export { maxDateRule };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=maxDate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maxDate.js","names":[],"sources":["../../../src/validation/builtin/maxDate.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineValidationRule } from '../defineValidationRule'\nimport { validateDateBound } from './validateDateBound'\n\nexport const maxDateRule = defineValidationRule<{ max: string }, string>({\n\ttype: 'maxDate',\n\tlabel: keys.ruleMaxDate,\n\tappliesTo: ['date'],\n\tparams: [\n\t\t{\n\t\t\tname: 'max',\n\t\t\ttype: 'text',\n\t\t\trequired: true,\n\t\t\tlabel: labelFor(keys.ruleParamMaxDate),\n\t\t\tvalidate: validateDateBound,\n\t\t},\n\t],\n\tdefaultMessage: keys.ruleMaxDateMessage,\n\tvalidate: ({ value, params, message }) =>\n\t\tvalue == null || value === '' || value <= params.max ? true : message({ max: params.max }),\n})\n"],"mappings":";;;;;AAKA,MAAa,cAAc,qBAA8C;CACxE,MAAM;CACN,OAAO,KAAK;CACZ,WAAW,CAAC,MAAM;CAClB,QAAQ,CACP;EACC,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO,SAAS,KAAK,gBAAgB;EACrC,UAAU;CACX,CACD;CACA,gBAAgB,KAAK;CACrB,WAAW,EAAE,OAAO,QAAQ,cAC3B,SAAS,QAAQ,UAAU,MAAM,SAAS,OAAO,MAAM,OAAO,QAAQ,EAAE,KAAK,OAAO,IAAI,CAAC;AAC3F,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { keys } from "../../translations/keys.js";
|
|
2
|
+
import { labelFor } from "../../translations/server.js";
|
|
3
|
+
import { defineValidationRule } from "../defineValidationRule.js";
|
|
4
|
+
import { validateDateBound } from "./validateDateBound.js";
|
|
5
|
+
//#region src/validation/builtin/minDate.ts
|
|
6
|
+
const minDateRule = defineValidationRule({
|
|
7
|
+
type: "minDate",
|
|
8
|
+
label: keys.ruleMinDate,
|
|
9
|
+
appliesTo: ["date"],
|
|
10
|
+
params: [{
|
|
11
|
+
name: "min",
|
|
12
|
+
type: "text",
|
|
13
|
+
required: true,
|
|
14
|
+
label: labelFor(keys.ruleParamMinDate),
|
|
15
|
+
validate: validateDateBound
|
|
16
|
+
}],
|
|
17
|
+
defaultMessage: keys.ruleMinDateMessage,
|
|
18
|
+
validate: ({ value, params, message }) => value == null || value === "" || value >= params.min ? true : message({ min: params.min })
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
export { minDateRule };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=minDate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minDate.js","names":[],"sources":["../../../src/validation/builtin/minDate.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineValidationRule } from '../defineValidationRule'\nimport { validateDateBound } from './validateDateBound'\n\nexport const minDateRule = defineValidationRule<{ min: string }, string>({\n\ttype: 'minDate',\n\tlabel: keys.ruleMinDate,\n\tappliesTo: ['date'],\n\tparams: [\n\t\t{\n\t\t\tname: 'min',\n\t\t\ttype: 'text',\n\t\t\trequired: true,\n\t\t\tlabel: labelFor(keys.ruleParamMinDate),\n\t\t\tvalidate: validateDateBound,\n\t\t},\n\t],\n\tdefaultMessage: keys.ruleMinDateMessage,\n\tvalidate: ({ value, params, message }) =>\n\t\tvalue == null || value === '' || value >= params.min ? true : message({ min: params.min }),\n})\n"],"mappings":";;;;;AAKA,MAAa,cAAc,qBAA8C;CACxE,MAAM;CACN,OAAO,KAAK;CACZ,WAAW,CAAC,MAAM;CAClB,QAAQ,CACP;EACC,MAAM;EACN,MAAM;EACN,UAAU;EACV,OAAO,SAAS,KAAK,gBAAgB;EACrC,UAAU;CACX,CACD;CACA,gBAAgB,KAAK;CACrB,WAAW,EAAE,OAAO,QAAQ,cAC3B,SAAS,QAAQ,UAAU,MAAM,SAAS,OAAO,MAAM,OAAO,QAAQ,EAAE,KAAK,OAAO,IAAI,CAAC;AAC3F,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { keys } from "../../translations/keys.js";
|
|
2
|
+
import { asTranslate } from "../../translations/server.js";
|
|
3
|
+
import { isValidCalendarDate } from "../../fields/builtin/date.js";
|
|
4
|
+
//#region src/validation/builtin/validateDateBound.ts
|
|
5
|
+
/**
|
|
6
|
+
* Field-level `validate` for a minDate/maxDate rule's bound param: an unset bound is tolerated
|
|
7
|
+
* (the rule itself already treats a missing bound as pass-through), otherwise it must be a real
|
|
8
|
+
* `YYYY-MM-DD` calendar date so the rule's string comparison against submitted values is meaningful.
|
|
9
|
+
* Exported for unit testing.
|
|
10
|
+
*/
|
|
11
|
+
const validateDateBound = (value, { req }) => {
|
|
12
|
+
if (typeof value !== "string" || value === "") return true;
|
|
13
|
+
return isValidCalendarDate(value) ? true : asTranslate(req.t)(keys.validationDate);
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { validateDateBound };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=validateDateBound.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateDateBound.js","names":[],"sources":["../../../src/validation/builtin/validateDateBound.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\nimport { isValidCalendarDate } from '../../fields/builtin/date'\nimport { keys } from '../../translations/keys'\nimport { asTranslate } from '../../translations/server'\n\n/**\n * Field-level `validate` for a minDate/maxDate rule's bound param: an unset bound is tolerated\n * (the rule itself already treats a missing bound as pass-through), otherwise it must be a real\n * `YYYY-MM-DD` calendar date so the rule's string comparison against submitted values is meaningful.\n * Exported for unit testing.\n */\nexport const validateDateBound = (\n\tvalue: unknown,\n\t{ req }: { data?: unknown; req: PayloadRequest }\n): string | true => {\n\tif (typeof value !== 'string' || value === '') {\n\t\treturn true\n\t}\n\treturn isValidCalendarDate(value) ? true : asTranslate(req.t)(keys.validationDate)\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAa,qBACZ,OACA,EAAE,UACiB;CACnB,IAAI,OAAO,UAAU,YAAY,UAAU,IAC1C,OAAO;CAER,OAAO,oBAAoB,KAAK,IAAI,OAAO,YAAY,IAAI,CAAC,EAAE,KAAK,cAAc;AAClF"}
|
|
@@ -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.3",
|
|
4
4
|
"description": "End-to-end forms platform for Payload: author, validate, render, collect, aggregate, and act.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"@types/react-dom": "19.2.3",
|
|
84
84
|
"axe-core": "^4.12.1",
|
|
85
85
|
"jsdom": "^25.0.1",
|
|
86
|
+
"msw": "^2.14.6",
|
|
86
87
|
"payload": "3.85.0",
|
|
87
88
|
"playwright": "1.60.0",
|
|
88
89
|
"react": "19.2.6",
|
|
@@ -91,10 +92,10 @@
|
|
|
91
92
|
"tsdown": "0.22.1",
|
|
92
93
|
"typescript": "5.9.3",
|
|
93
94
|
"vitest": "4.1.7",
|
|
94
|
-
"@10x-media/tsconfig": "0.0.0",
|
|
95
95
|
"@10x-media/payload-test-harness": "0.0.0",
|
|
96
96
|
"@10x-media/vitest-config": "0.0.0",
|
|
97
|
-
"@10x-media/tsdown-config": "0.0.0"
|
|
97
|
+
"@10x-media/tsdown-config": "0.0.0",
|
|
98
|
+
"@10x-media/tsconfig": "0.0.0"
|
|
98
99
|
},
|
|
99
100
|
"publishConfig": {
|
|
100
101
|
"access": "public"
|
package/styles/form-builder.css
CHANGED
|
@@ -106,193 +106,3 @@
|
|
|
106
106
|
opacity: 0.7;
|
|
107
107
|
font-size: 0.875em;
|
|
108
108
|
}
|
|
109
|
-
|
|
110
|
-
/* Flow builder (admin UI only, requires Payload theme variables). */
|
|
111
|
-
.fb-flow-builder {
|
|
112
|
-
display: flex;
|
|
113
|
-
flex-direction: column;
|
|
114
|
-
gap: 0.5rem;
|
|
115
|
-
margin-block-end: 1rem;
|
|
116
|
-
}
|
|
117
|
-
.fb-flow-builder__hint {
|
|
118
|
-
color: var(--theme-elevation-400);
|
|
119
|
-
font-size: 0.875rem;
|
|
120
|
-
margin: 0;
|
|
121
|
-
}
|
|
122
|
-
.fb-flow-builder__steps {
|
|
123
|
-
display: flex;
|
|
124
|
-
flex-direction: column;
|
|
125
|
-
gap: 0.5rem;
|
|
126
|
-
}
|
|
127
|
-
.fb-flow-builder__actions {
|
|
128
|
-
margin-block-start: 0.25rem;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.fb-flow-step__header-content {
|
|
132
|
-
display: flex;
|
|
133
|
-
align-items: center;
|
|
134
|
-
gap: 0.5rem;
|
|
135
|
-
overflow: hidden;
|
|
136
|
-
}
|
|
137
|
-
.fb-flow-step__index {
|
|
138
|
-
font-size: 0.6875rem;
|
|
139
|
-
font-weight: 600;
|
|
140
|
-
text-transform: uppercase;
|
|
141
|
-
letter-spacing: 0.04em;
|
|
142
|
-
color: var(--theme-elevation-400);
|
|
143
|
-
flex-shrink: 0;
|
|
144
|
-
}
|
|
145
|
-
.fb-flow-step__id-preview {
|
|
146
|
-
font-size: 0.875rem;
|
|
147
|
-
overflow: hidden;
|
|
148
|
-
text-overflow: ellipsis;
|
|
149
|
-
white-space: nowrap;
|
|
150
|
-
}
|
|
151
|
-
.fb-flow-step__id-preview em {
|
|
152
|
-
color: var(--theme-elevation-400);
|
|
153
|
-
font-style: italic;
|
|
154
|
-
}
|
|
155
|
-
.fb-flow-step__body {
|
|
156
|
-
padding: 0.75rem;
|
|
157
|
-
display: flex;
|
|
158
|
-
flex-direction: column;
|
|
159
|
-
gap: 0.25rem;
|
|
160
|
-
}
|
|
161
|
-
.fb-flow-step__row {
|
|
162
|
-
display: flex;
|
|
163
|
-
flex-direction: column;
|
|
164
|
-
gap: 0.25rem;
|
|
165
|
-
}
|
|
166
|
-
.fb-flow-step__hint {
|
|
167
|
-
font-size: 0.75rem;
|
|
168
|
-
color: var(--theme-elevation-400);
|
|
169
|
-
margin: 0;
|
|
170
|
-
}
|
|
171
|
-
.fb-flow-step__field-picker {
|
|
172
|
-
display: flex;
|
|
173
|
-
flex-wrap: wrap;
|
|
174
|
-
gap: 0.25rem 1.25rem;
|
|
175
|
-
padding-block: 0.25rem;
|
|
176
|
-
}
|
|
177
|
-
.fb-flow-step__transitions {
|
|
178
|
-
display: flex;
|
|
179
|
-
flex-direction: column;
|
|
180
|
-
gap: 0.5rem;
|
|
181
|
-
padding-block-start: 0.5rem;
|
|
182
|
-
}
|
|
183
|
-
.fb-flow-step__transitions-header {
|
|
184
|
-
display: flex;
|
|
185
|
-
align-items: baseline;
|
|
186
|
-
gap: 0.5rem;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.fb-flow-transition {
|
|
190
|
-
border: 1px solid var(--theme-border-color);
|
|
191
|
-
border-radius: var(--style-radius-s);
|
|
192
|
-
padding: 0.5rem 0.75rem;
|
|
193
|
-
display: flex;
|
|
194
|
-
flex-direction: column;
|
|
195
|
-
gap: 0.5rem;
|
|
196
|
-
}
|
|
197
|
-
.fb-flow-transition__header {
|
|
198
|
-
display: flex;
|
|
199
|
-
align-items: center;
|
|
200
|
-
gap: 0.5rem;
|
|
201
|
-
}
|
|
202
|
-
.fb-flow-transition__label {
|
|
203
|
-
font-size: 0.6875rem;
|
|
204
|
-
font-weight: 600;
|
|
205
|
-
text-transform: uppercase;
|
|
206
|
-
letter-spacing: 0.04em;
|
|
207
|
-
color: var(--theme-elevation-400);
|
|
208
|
-
flex-shrink: 0;
|
|
209
|
-
}
|
|
210
|
-
.fb-flow-transition__goto {
|
|
211
|
-
flex: 1;
|
|
212
|
-
min-width: 0;
|
|
213
|
-
}
|
|
214
|
-
.fb-flow-transition__actions {
|
|
215
|
-
display: flex;
|
|
216
|
-
align-items: center;
|
|
217
|
-
gap: 0.25rem;
|
|
218
|
-
flex-shrink: 0;
|
|
219
|
-
}
|
|
220
|
-
.fb-flow-transition__when-label {
|
|
221
|
-
font-size: 0.6875rem;
|
|
222
|
-
font-weight: 600;
|
|
223
|
-
text-transform: uppercase;
|
|
224
|
-
letter-spacing: 0.04em;
|
|
225
|
-
color: var(--theme-elevation-400);
|
|
226
|
-
}
|
|
227
|
-
.fb-flow-transition__condition {
|
|
228
|
-
padding-inline-start: 0.25rem;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/* Condition builder (admin UI only, requires Payload theme variables). */
|
|
232
|
-
.fb-condition-builder {
|
|
233
|
-
display: flex;
|
|
234
|
-
flex-direction: column;
|
|
235
|
-
gap: 0.5rem;
|
|
236
|
-
}
|
|
237
|
-
.fb-condition-builder__hint {
|
|
238
|
-
color: var(--theme-elevation-400);
|
|
239
|
-
font-size: 0.875rem;
|
|
240
|
-
margin: 0;
|
|
241
|
-
}
|
|
242
|
-
.fb-condition-builder__group {
|
|
243
|
-
display: flex;
|
|
244
|
-
flex-direction: column;
|
|
245
|
-
gap: 0.375rem;
|
|
246
|
-
padding: 0.75rem;
|
|
247
|
-
border: 1px solid var(--theme-border-color);
|
|
248
|
-
border-radius: var(--style-radius-m);
|
|
249
|
-
}
|
|
250
|
-
.fb-condition-builder__or-label {
|
|
251
|
-
display: inline-block;
|
|
252
|
-
font-size: 11px;
|
|
253
|
-
font-weight: 500;
|
|
254
|
-
text-transform: uppercase;
|
|
255
|
-
margin-block: 0.125rem;
|
|
256
|
-
}
|
|
257
|
-
.fb-condition-builder__and-row {
|
|
258
|
-
display: flex;
|
|
259
|
-
flex-direction: column;
|
|
260
|
-
gap: 0.25rem;
|
|
261
|
-
}
|
|
262
|
-
.fb-condition-builder__and-label {
|
|
263
|
-
font-size: 10px;
|
|
264
|
-
font-weight: 500;
|
|
265
|
-
text-transform: uppercase;
|
|
266
|
-
padding-inline-start: 0.125rem;
|
|
267
|
-
}
|
|
268
|
-
.fb-condition-builder__add-condition {
|
|
269
|
-
align-self: flex-start;
|
|
270
|
-
margin-block-start: 0.25rem;
|
|
271
|
-
}
|
|
272
|
-
.fb-condition-builder__actions {
|
|
273
|
-
display: flex;
|
|
274
|
-
gap: 0.5rem;
|
|
275
|
-
margin-top: 0.5rem;
|
|
276
|
-
}
|
|
277
|
-
.fb-condition-row {
|
|
278
|
-
display: flex;
|
|
279
|
-
align-items: flex-start;
|
|
280
|
-
gap: 0.5rem;
|
|
281
|
-
flex-wrap: nowrap;
|
|
282
|
-
}
|
|
283
|
-
.fb-condition-row__field {
|
|
284
|
-
flex: 1 1 9rem;
|
|
285
|
-
min-width: 0;
|
|
286
|
-
}
|
|
287
|
-
.fb-condition-row__operator {
|
|
288
|
-
flex: 0 1 9rem;
|
|
289
|
-
min-width: 0;
|
|
290
|
-
}
|
|
291
|
-
.fb-condition-row__value {
|
|
292
|
-
flex: 1 1 9rem;
|
|
293
|
-
min-width: 0;
|
|
294
|
-
}
|
|
295
|
-
.fb-condition-row__remove {
|
|
296
|
-
flex-shrink: 0;
|
|
297
|
-
align-self: center;
|
|
298
|
-
}
|