@10x-media/form-builder 0.1.0-beta.24 → 0.1.0-beta.26

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -21
  3. package/dist/actions/body/serializeBody.d.ts +16 -14
  4. package/dist/actions/body/serializeBody.js +5 -8
  5. package/dist/actions/body/serializeBody.js.map +1 -1
  6. package/dist/actions/builtin/emailAction.d.ts +10 -1
  7. package/dist/actions/builtin/emailAction.js +22 -7
  8. package/dist/actions/builtin/emailAction.js.map +1 -1
  9. package/dist/actions/defineAction.d.ts +2 -4
  10. package/dist/actions/defineAction.js.map +1 -1
  11. package/dist/actions/emailRender.d.ts +27 -0
  12. package/dist/actions/fromAddresses.d.ts +3 -1
  13. package/dist/actions/fromAddresses.js.map +1 -1
  14. package/dist/actions/recipientSources.d.ts +6 -17
  15. package/dist/actions/recipientSources.js.map +1 -1
  16. package/dist/actions/runActions.js +9 -7
  17. package/dist/actions/runActions.js.map +1 -1
  18. package/dist/actions/submissionContext.d.ts +34 -0
  19. package/dist/actions/task.js +10 -13
  20. package/dist/actions/task.js.map +1 -1
  21. package/dist/aggregation/aggregateResponses.js +1 -1
  22. package/dist/aggregation/resolveResultsRequest.js +38 -24
  23. package/dist/aggregation/resolveResultsRequest.js.map +1 -1
  24. package/dist/client/EndpointOptionsSelect.d.ts +1 -0
  25. package/dist/client/EndpointOptionsSelect.js +1 -0
  26. package/dist/client/EndpointOptionsSelect.js.map +1 -1
  27. package/dist/client/FieldNameSelect.d.ts +1 -0
  28. package/dist/client/FieldNameSelect.js +1 -0
  29. package/dist/client/FieldNameSelect.js.map +1 -1
  30. package/dist/client/RecipientsSelect.d.ts +1 -0
  31. package/dist/client/RecipientsSelect.js +1 -0
  32. package/dist/client/RecipientsSelect.js.map +1 -1
  33. package/dist/collections/formSubmissions.js +3 -3
  34. package/dist/collections/forms.js +2 -2
  35. package/dist/exports/rsc.d.ts +1 -1
  36. package/dist/exports/rsc.js +3 -3
  37. package/dist/form/findFormAtLocale.d.ts +30 -0
  38. package/dist/form/findFormAtLocale.js +88 -0
  39. package/dist/form/findFormAtLocale.js.map +1 -0
  40. package/dist/index.d.ts +5 -2
  41. package/dist/index.js +17 -13
  42. package/dist/index.js.map +1 -1
  43. package/dist/options.d.ts +36 -3
  44. package/dist/plugin/registerCollections.js +2 -2
  45. package/dist/poll/resolvePollOutcome.js +1 -1
  46. package/dist/poll/votes/recountPollVotes.js +2 -2
  47. package/dist/poll/votes/voteTallyHook.js +2 -2
  48. package/dist/react/Form.d.ts +11 -2
  49. package/dist/react/Form.js +8 -6
  50. package/dist/react/Form.js.map +1 -1
  51. package/dist/react/Poll.js +4 -2
  52. package/dist/react/Poll.js.map +1 -1
  53. package/dist/react/fetchResults.d.ts +2 -1
  54. package/dist/react/fetchResults.js +5 -2
  55. package/dist/react/fetchResults.js.map +1 -1
  56. package/dist/react/submitForm.d.ts +15 -3
  57. package/dist/react/submitForm.js +5 -3
  58. package/dist/react/submitForm.js.map +1 -1
  59. package/dist/submissions/createSubmission.d.ts +6 -0
  60. package/dist/submissions/createSubmission.js +7 -2
  61. package/dist/submissions/createSubmission.js.map +1 -1
  62. package/dist/submissions/submissionLocale.js +39 -0
  63. package/dist/submissions/submissionLocale.js.map +1 -0
  64. package/dist/submissions/validateSubmission.js +108 -99
  65. package/dist/submissions/validateSubmission.js.map +1 -1
  66. package/dist/submissions/voteChangeEndpoint.js +1 -1
  67. package/package.json +5 -5
@@ -0,0 +1,30 @@
1
+ import { SubmissionForm } from "../actions/submissionContext.js";
2
+ import { Payload, PayloadRequest } from "payload";
3
+
4
+ //#region src/form/findFormAtLocale.d.ts
5
+ /**
6
+ * What a `fallbackLocale` resolver returns: a locale code, an ordered list of codes, `false` for no
7
+ * fallback, or `undefined` to keep the default.
8
+ */
9
+ type FormFallbackLocaleResult = string | string[] | false | undefined;
10
+ type FormFallbackLocaleArgs = {
11
+ /**
12
+ * The form as first read at `locale` with the config's own fallback, at depth 0: a non-localized
13
+ * owner relationship (a multi-tenant host's `form.tenant`) is already on it, so no read of your own
14
+ * is needed. Decide from non-localized fields: a localized one may be empty at `locale`.
15
+ */
16
+ form: SubmissionForm; /** The locale the form is being read at (the submission's, clamped). */
17
+ locale: string;
18
+ payload: Payload;
19
+ req?: PayloadRequest;
20
+ };
21
+ /**
22
+ * Host seam choosing the fallback locale per form (plugin option `fallbackLocale`), for hosts where
23
+ * the right fallback depends on the form's owner rather than on the config, e.g. a tenant whose own
24
+ * default locale is not the config-wide one. Runs before the plugin acts on a form server-side
25
+ * (validating a submission, running its actions, serving poll results).
26
+ */
27
+ type FormFallbackLocale = (args: FormFallbackLocaleArgs) => Promise<FormFallbackLocaleResult> | FormFallbackLocaleResult;
28
+ //#endregion
29
+ export { FormFallbackLocale, FormFallbackLocaleArgs, FormFallbackLocaleResult };
30
+ //# sourceMappingURL=findFormAtLocale.d.ts.map
@@ -0,0 +1,88 @@
1
+ import { customStateOf, stashCustomState } from "../plugin/customState.js";
2
+ import { FORMS_SLUG } from "../collections/forms.js";
3
+ import { sanitizeFallbackLocale } from "payload";
4
+ //#region src/form/findFormAtLocale.ts
5
+ const stashFallbackLocale = (custom, fallbackLocale) => stashCustomState(custom, { fallbackLocale });
6
+ /**
7
+ * A host `fallbackLocale` resolver threw. Kept distinct so callers that read a failed form load as a
8
+ * deleted form (and skip quietly) let this one through instead: a transient failure in host I/O must
9
+ * fail the action run (and retry it on the queued path), never drop it.
10
+ */
11
+ var FallbackLocaleError = class extends Error {
12
+ constructor(cause) {
13
+ super(`@10x-media/form-builder: fallbackLocale resolver failed: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
14
+ this.name = "FallbackLocaleError";
15
+ }
16
+ };
17
+ /**
18
+ * `.catch` handler for callers that treat a form that cannot be read as missing: `null` for a read
19
+ * failure, rethrowing a `FallbackLocaleError`.
20
+ */
21
+ const missingFormOnReadError = (error) => {
22
+ if (error instanceof FallbackLocaleError) throw error;
23
+ return null;
24
+ };
25
+ /**
26
+ * Loads a form (depth 0) at `locale` for server work done on a visitor's behalf: validating a
27
+ * submission, running its actions, serving poll results. The read falls back exactly like any other
28
+ * Payload read, so with `localization.fallback: false` content the author never filled in at
29
+ * `locale` stays empty, matching what the host's own page renders.
30
+ *
31
+ * With the plugin's `fallbackLocale` resolver set, it gets the form as read and may choose another
32
+ * fallback (the way a host forces one); the form is read again only when that differs from the one
33
+ * already applied.
34
+ *
35
+ * Payload's local API writes `locale` and `fallbackLocale` onto the `req` it is handed. Both are
36
+ * restored afterwards, so a host (or job runner) request passed in comes back unchanged. The restore
37
+ * assumes one operation per `req` at a time: concurrent reads sharing a `req` (a `Promise.all` of
38
+ * creates at different locales) can interleave it, so give each its own request.
39
+ */
40
+ const findFormAtLocale = async ({ payload, id, locale, req, overrideAccess }) => {
41
+ const { localization } = payload.config;
42
+ const read = (fallbackLocale) => payload.findByID({
43
+ collection: FORMS_SLUG,
44
+ id,
45
+ depth: 0,
46
+ locale,
47
+ ...fallbackLocale === void 0 ? {} : { fallbackLocale },
48
+ ...overrideAccess === void 0 ? {} : { overrideAccess },
49
+ req
50
+ });
51
+ const previous = req ? {
52
+ locale: req.locale,
53
+ fallbackLocale: req.fallbackLocale
54
+ } : void 0;
55
+ try {
56
+ if (!localization) return await read(void 0);
57
+ const form = await read(void 0);
58
+ const resolver = customStateOf(payload).fallbackLocale;
59
+ if (!resolver) return form;
60
+ let chosen;
61
+ try {
62
+ chosen = await resolver({
63
+ form,
64
+ locale,
65
+ payload,
66
+ req
67
+ });
68
+ } catch (error) {
69
+ throw new FallbackLocaleError(error);
70
+ }
71
+ const applied = sanitizeFallbackLocale({
72
+ fallbackLocale: void 0,
73
+ locale,
74
+ localization
75
+ });
76
+ if (chosen === void 0 || chosen === locale || chosen === applied) return form;
77
+ return await read(chosen);
78
+ } finally {
79
+ if (req && previous) {
80
+ req.locale = previous.locale;
81
+ req.fallbackLocale = previous.fallbackLocale;
82
+ }
83
+ }
84
+ };
85
+ //#endregion
86
+ export { findFormAtLocale, missingFormOnReadError, stashFallbackLocale };
87
+
88
+ //# sourceMappingURL=findFormAtLocale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findFormAtLocale.js","names":[],"sources":["../../src/form/findFormAtLocale.ts"],"sourcesContent":["import {\n\ttype Payload,\n\ttype PayloadRequest,\n\tsanitizeFallbackLocale,\n\ttype TypedFallbackLocale,\n\ttype TypedLocale,\n} from 'payload'\nimport type { SubmissionForm } from '../actions/submissionContext'\nimport { FORMS_SLUG } from '../collections/forms'\nimport { customStateOf, stashCustomState } from '../plugin/customState'\n\n/**\n * What a `fallbackLocale` resolver returns: a locale code, an ordered list of codes, `false` for no\n * fallback, or `undefined` to keep the default.\n */\nexport type FormFallbackLocaleResult = string | string[] | false | undefined\n\nexport type FormFallbackLocaleArgs = {\n\t/**\n\t * The form as first read at `locale` with the config's own fallback, at depth 0: a non-localized\n\t * owner relationship (a multi-tenant host's `form.tenant`) is already on it, so no read of your own\n\t * is needed. Decide from non-localized fields: a localized one may be empty at `locale`.\n\t */\n\tform: SubmissionForm\n\t/** The locale the form is being read at (the submission's, clamped). */\n\tlocale: string\n\tpayload: Payload\n\treq?: PayloadRequest\n}\n\n/**\n * Host seam choosing the fallback locale per form (plugin option `fallbackLocale`), for hosts where\n * the right fallback depends on the form's owner rather than on the config, e.g. a tenant whose own\n * default locale is not the config-wide one. Runs before the plugin acts on a form server-side\n * (validating a submission, running its actions, serving poll results).\n */\nexport type FormFallbackLocale = (\n\targs: FormFallbackLocaleArgs\n) => Promise<FormFallbackLocaleResult> | FormFallbackLocaleResult\n\ntype FallbackState = { fallbackLocale?: FormFallbackLocale }\n\nexport const stashFallbackLocale = (\n\tcustom: Record<string, unknown> | undefined,\n\tfallbackLocale: FormFallbackLocale\n): Record<string, unknown> => stashCustomState<FallbackState>(custom, { fallbackLocale })\n\nexport type FindFormAtLocaleArgs = {\n\tpayload: Payload\n\tid: number | string\n\t/** A clamped locale (see `resolveSubmissionLocale`), never raw visitor input. */\n\tlocale: string\n\treq?: PayloadRequest\n\toverrideAccess?: boolean\n}\n\n/**\n * A host `fallbackLocale` resolver threw. Kept distinct so callers that read a failed form load as a\n * deleted form (and skip quietly) let this one through instead: a transient failure in host I/O must\n * fail the action run (and retry it on the queued path), never drop it.\n */\nexport class FallbackLocaleError extends Error {\n\tconstructor(cause: unknown) {\n\t\tsuper(\n\t\t\t`@10x-media/form-builder: fallbackLocale resolver failed: ${\n\t\t\t\tcause instanceof Error ? cause.message : String(cause)\n\t\t\t}`,\n\t\t\t{ cause }\n\t\t)\n\t\tthis.name = 'FallbackLocaleError'\n\t}\n}\n\n/**\n * `.catch` handler for callers that treat a form that cannot be read as missing: `null` for a read\n * failure, rethrowing a `FallbackLocaleError`.\n */\nexport const missingFormOnReadError = (error: unknown): null => {\n\tif (error instanceof FallbackLocaleError) {\n\t\tthrow error\n\t}\n\treturn null\n}\n\n/**\n * Loads a form (depth 0) at `locale` for server work done on a visitor's behalf: validating a\n * submission, running its actions, serving poll results. The read falls back exactly like any other\n * Payload read, so with `localization.fallback: false` content the author never filled in at\n * `locale` stays empty, matching what the host's own page renders.\n *\n * With the plugin's `fallbackLocale` resolver set, it gets the form as read and may choose another\n * fallback (the way a host forces one); the form is read again only when that differs from the one\n * already applied.\n *\n * Payload's local API writes `locale` and `fallbackLocale` onto the `req` it is handed. Both are\n * restored afterwards, so a host (or job runner) request passed in comes back unchanged. The restore\n * assumes one operation per `req` at a time: concurrent reads sharing a `req` (a `Promise.all` of\n * creates at different locales) can interleave it, so give each its own request.\n */\nexport const findFormAtLocale = async ({\n\tpayload,\n\tid,\n\tlocale,\n\treq,\n\toverrideAccess,\n}: FindFormAtLocaleArgs) => {\n\tconst { localization } = payload.config\n\tconst read = (fallbackLocale: string | string[] | false | undefined) =>\n\t\tpayload.findByID({\n\t\t\tcollection: FORMS_SLUG,\n\t\t\tid,\n\t\t\tdepth: 0,\n\t\t\t// Cast: a clamped locale is one of the host's own codes, but its concrete `TypedLocale` union\n\t\t\t// is unknowable from the plugin.\n\t\t\tlocale: locale as TypedLocale,\n\t\t\t...(fallbackLocale === undefined ? {} : { fallbackLocale: fallbackLocale as TypedLocale }),\n\t\t\t...(overrideAccess === undefined ? {} : { overrideAccess }),\n\t\t\treq,\n\t\t})\n\tconst previous = req ? { locale: req.locale, fallbackLocale: req.fallbackLocale } : undefined\n\ttry {\n\t\tif (!localization) {\n\t\t\treturn await read(undefined)\n\t\t}\n\t\tconst form = await read(undefined)\n\t\tconst resolver = customStateOf<FallbackState>(payload).fallbackLocale\n\t\tif (!resolver) {\n\t\t\treturn form\n\t\t}\n\t\tlet chosen: FormFallbackLocaleResult\n\t\ttry {\n\t\t\tchosen = await resolver({\n\t\t\t\t// Double cast: a host's generated Form interface has no index signature.\n\t\t\t\tform: form as unknown as SubmissionForm,\n\t\t\t\tlocale,\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tthrow new FallbackLocaleError(error)\n\t\t}\n\t\t// Falling back to the read's own locale, or to the fallback Payload already applied (resolved by\n\t\t// Payload's own sanitizer, so the two cannot drift), changes nothing.\n\t\tconst applied = sanitizeFallbackLocale({\n\t\t\tfallbackLocale: undefined as unknown as TypedFallbackLocale,\n\t\t\tlocale,\n\t\t\tlocalization,\n\t\t})\n\t\tif (chosen === undefined || chosen === locale || chosen === applied) {\n\t\t\treturn form\n\t\t}\n\t\treturn await read(chosen)\n\t} finally {\n\t\tif (req && previous) {\n\t\t\treq.locale = previous.locale\n\t\t\treq.fallbackLocale = previous.fallbackLocale\n\t\t}\n\t}\n}\n"],"mappings":";;;;AA0CA,MAAa,uBACZ,QACA,mBAC6B,iBAAgC,QAAQ,EAAE,eAAe,CAAC;;;;;;AAgBxF,IAAa,sBAAb,cAAyC,MAAM;CAC9C,YAAY,OAAgB;EAC3B,MACC,4DACC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAEtD,EAAE,MAAM,CACT;EACA,KAAK,OAAO;CACb;AACD;;;;;AAMA,MAAa,0BAA0B,UAAyB;CAC/D,IAAI,iBAAiB,qBACpB,MAAM;CAEP,OAAO;AACR;;;;;;;;;;;;;;;;AAiBA,MAAa,mBAAmB,OAAO,EACtC,SACA,IACA,QACA,KACA,qBAC2B;CAC3B,MAAM,EAAE,iBAAiB,QAAQ;CACjC,MAAM,QAAQ,mBACb,QAAQ,SAAS;EAChB,YAAY;EACZ;EACA,OAAO;EAGC;EACR,GAAI,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAkB,eAA8B;EACxF,GAAI,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,eAAe;EACzD;CACD,CAAC;CACF,MAAM,WAAW,MAAM;EAAE,QAAQ,IAAI;EAAQ,gBAAgB,IAAI;CAAe,IAAI,KAAA;CACpF,IAAI;EACH,IAAI,CAAC,cACJ,OAAO,MAAM,KAAK,KAAA,CAAS;EAE5B,MAAM,OAAO,MAAM,KAAK,KAAA,CAAS;EACjC,MAAM,WAAW,cAA6B,OAAO,EAAE;EACvD,IAAI,CAAC,UACJ,OAAO;EAER,IAAI;EACJ,IAAI;GACH,SAAS,MAAM,SAAS;IAEjB;IACN;IACA;IACA;GACD,CAAC;EACF,SAAS,OAAO;GACf,MAAM,IAAI,oBAAoB,KAAK;EACpC;EAGA,MAAM,UAAU,uBAAuB;GACtC,gBAAgB,KAAA;GAChB;GACA;EACD,CAAC;EACD,IAAI,WAAW,KAAA,KAAa,WAAW,UAAU,WAAW,SAC3D,OAAO;EAER,OAAO,MAAM,KAAK,MAAM;CACzB,UAAU;EACT,IAAI,OAAO,UAAU;GACpB,IAAI,SAAS,SAAS;GACtB,IAAI,iBAAiB,SAAS;EAC/B;CACD;AACD"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ import { FileFieldConfig, FileRef, FileRefError } from "./uploads/types.js";
4
4
  import { AnyFormFieldDefinition, FormFieldDefinition, FormFieldFormat, FormFieldValidate, FormFieldValueKind, OmittableSharedField, ResolveFieldOptionsArgs } from "./fields/types.js";
5
5
  import { CalcAllowed, normalizeCalc } from "./calc/normalizeCalc.js";
6
6
  import { BodyConverter, BodyConverterArgs, BodyRender, defaultBodyConverters, sanitizeUrl } from "./actions/body/converters.js";
7
+ import { FormContextReference, signFormContext, verifyFormContext } from "./context/formContext.js";
8
+ import { SubmissionContextArgs, SubmissionForm } from "./actions/submissionContext.js";
7
9
  import { BodyContext, RichTextBodyOption, SerializeBodyArgs, serializeBody } from "./actions/body/serializeBody.js";
8
10
  import { textOfBody } from "./actions/body/textOfBody.js";
9
11
  import { AggregationBucket, AggregationRow, FieldAggregation, FieldMeta, SubmissionStatusFilter } from "./aggregation/types.js";
@@ -26,7 +28,6 @@ import { VotedSubmission, resolveVotedSubmission } from "./submissions/resolveVo
26
28
  import { interpolate } from "./recall/interpolate.js";
27
29
  import { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from "./spam/constants.js";
28
30
  import { formatBytes } from "./uploads/formatBytes.js";
29
- import { FormContextReference, signFormContext, verifyFormContext } from "./context/formContext.js";
30
31
  import { isPollClosed } from "./form/pollState.js";
31
32
  import { PollOptionSourceOption, PollOptionSourceRegistry, PollOptionSourcesConfig, resolvePollOptionSources } from "./poll/registry.js";
32
33
  import { ResolveEffectivePollOptionsArgs, resolveEffectivePollOptions } from "./poll/effectivePollOptions.js";
@@ -48,9 +49,11 @@ import { ResolveConsentEntriesArgs, resolveConsentEntries } from "./consent/reso
48
49
  import { ConsentSourceOption, ResolveConsentSourcesRequestArgs, ResolveConsentSourcesRequestResult, resolveConsentSourcesRequest } from "./consent/resolveConsentSourcesRequest.js";
49
50
  import { ResolvePublishedVersionRefArgs, resolvePublishedVersionRef } from "./consent/resolvePublishedVersionRef.js";
50
51
  import { DepartmentEmailsResolver, DepartmentOption, ResolveDepartmentOptionsArgs, ResolveDepartmentsRequestArgs, ResolveDepartmentsRequestResult, resolveDepartmentOptions } from "./email/departments.js";
52
+ import { EmailActionType, EmailRender, EmailRenderArgs } from "./actions/emailRender.js";
51
53
  import { CalcFunction, CalcSource, CalcSourceResolveArgs, CalcWeightResolveArgs, defineCalcFunction, defineCalcSource } from "./calc/registry.js";
52
54
  import { RedirectFieldsOverride, RedirectOption, ResponseOption } from "./collections/redirectFields.js";
53
55
  import { DefaultSettingsFields, SettingsFieldsOverride, SettingsOption, buildDefaultSettingsFields } from "./collections/settingsFields.js";
56
+ import { FormFallbackLocale, FormFallbackLocaleArgs, FormFallbackLocaleResult } from "./form/findFormAtLocale.js";
54
57
  import { CaptchaProvider, CaptchaVerifyArgs, IdentifyFn, RateLimitCheckArgs, RateLimitConfig, RateLimitResult, RateLimiter, SpamConfig, SpamMetadataConfig, SpamOption } from "./spam/types.js";
55
58
  import { UploadsOption } from "./plugin/uploadsCollection.js";
56
59
  import { DefaultOutcomeFields, OutcomeFieldsOverride, buildDefaultOutcomeFields, buildResolvedAtField, buildWinningValuesField } from "./poll/outcomeFields.js";
@@ -96,5 +99,5 @@ import { FieldTargetParamOptions, fieldTargetParam } from "./validation/fieldTar
96
99
  //#region src/index.d.ts
97
100
  declare const formBuilder: (options: FormBuilderPluginOptions) => import("payload").Plugin;
98
101
  //#endregion
99
- export { type ActionDefinition, ActionError, type ActionOption, type ActionRegistry, type ActionResult, type ActionRunArgs, type ActionValidateArgs, type ActionsConfig, type AggregateFieldResponsesArgs, type AggregateFormResponsesArgs, type AggregationBucket, type AggregationRow, type AnyActionDefinition, type AnyFormFieldDefinition, type AnyPollOptionSource, type AnyValidationRuleDefinition, type BodyContext, type BodyConverter, type BodyConverterArgs, type BodyRender, type ButtonFieldsOverride, type ButtonsOption, CAPTCHA_TOKEN_KEY, type CalcAllowed, type CalcDisplayConfig, type CalcExpression, type CalcFunction, type CalcResolved, type CalcSource, type CalcSourceResolveArgs, type CalcWeightResolveArgs, type CaptchaProvider, type CaptchaVerifyArgs, type ConsentProof, type ConsentSnapshotMode, type ConsentSourceEntry, type ConsentSourceOption, type ConsentSourcePage, type ConsentSourcesFieldOptions, type ConsentSourcesResolver, type ConsentStatement, type ConsentStatements, type CreateSubmissionArgs, type CreatedSubmission, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, type DefaultButtonFields, type DefaultOutcomeFields, type DefaultSettingsFields, type DepartmentEmailsResolver, type DepartmentOption, type DepartmentsFieldOptions, type FieldAggregation, type FieldCondition, type FieldMeta, type FieldTargetParamOptions, type FieldTypeOption, type FieldTypeRegistry, type FieldTypesConfig, type FileFieldConfig, type FileRef, type FileRefError, type FormBuilderPluginOptions, type FormButtonSettings, type FormContextReference, type FormDocument, type FormFieldDefinition, type FormFieldFormat, type FormFieldValidate, type FormFieldValueKind, type FormPollSettings, type FormResponseSettings, type FormResultsAccess, type FormResultsAccessArgs, type FromAddressOption, type FromAddressSource, type FromAddressSourceRegistry, type FromAddressesResolver, type HcaptchaProviderOptions, INLINE_DISPATCH_DEADLINE_MS, type IdentifyFn, type OmittableSharedField, type OutcomeFieldsOverride, POLL_CLOSE_TASK_SLUG, POLL_VOTES_SLUG, type FormBuilderPluginOptions as PluginOptions, type PollCloseTaskInput, type PollOption, type PollOptionResolveArgs, type PollOptionSource, type PollOptionSourceOption, type PollOptionSourceRegistry, type PollOptionSourcesConfig, type PollOutcomeStrategy, type PollOutcomeStrategyArgs, type PollTypeRegistry, type PollTypesConfig, type PrefillOptions, type PresentationDensity, type PresentationDescriptor, type PresentationSurface, RESPONDENTS_VALUE, type RateLimitCheckArgs, type RateLimitConfig, type RateLimitResult, type RateLimiter, type RecallResolver, type RecaptchaProviderOptions, type RecipientResolveArgs, type RecipientSource, type RecipientSourceRegistry, type RedirectFieldsOverride, type RedirectOption, type ResolveCalcContextArgs, type ResolveConsentEntriesArgs, type ResolveConsentSourcesRequestArgs, type ResolveConsentSourcesRequestResult, type ResolveConsentStatementsArgs, type ResolveDepartmentOptionsArgs, type ResolveDepartmentsRequestArgs, type ResolveDepartmentsRequestResult, type ResolveEffectivePollOptionsArgs, type ResolveFieldOptionsArgs, type ResolvePollOptionsArgs, type ResolvePollOutcomeArgs, type ResolvePublishedVersionRefArgs, type ResolveResultsRequestArgs, type ResolveResultsRequestResult, type ResponseOption, type RichTextBodyOption, SIGNATURE_HEADER, type SerializeBodyArgs, type SettingsFieldsOverride, type SettingsOption, type SpamConfig, type SpamMetadataConfig, type SpamOption, type SubmissionStatusFilter, type ToFormDocumentOptions, type TurnstileProviderOptions, type UploadsOption, VOTE_SHARDS, type ValidationRuleDefinition, type ValidationRuleOption, type ValidationRuleRegistry, type ValidationRuleResult, type ValidationRulesConfig, type VotedSubmission, aggregateFieldResponses, aggregateFormResponses, aggregateFromVotes, aggregateRowForField, aggregateRowsForFields, applyConsentStatements, buildDefaultActionDefinitions, buildDefaultButtonFields, buildDefaultFieldDefinitions, buildDefaultOutcomeFields, buildDefaultSettingsFields, buildNextLabelField, buildPollCloseTask, buildPrevLabelField, buildRecallResolver, buildResolvedAtField, buildSubmitLabelField, buildWinningValuesField, calcExpressionOf, calcUsesSources, calcWeightKey, captureConsent, captureFileRef, computeCalcFields, consentSourcesField, countryField, createKvRateLimiter, createSubmission, defaultActionDefinitions, defaultBodyConverters, defaultFieldDefinitions, defaultFieldDefinitionsByType, defaultIdentify, defaultPresentationDescriptors, defaultValidationRules, defaultValidationRulesByType, defineAction, defineCalcFunction, defineCalcSource, defineCaptchaProvider, defineFormField, definePollOptionSource, definePollType, defineValidationRule, departmentsField, enqueuePollClose, escapeHtml, evaluateCalc, evaluateCondition, fieldHasOptions, fieldKey, fieldTargetParam, fileMimeTypeOptions, formBuilder, formatBytes, formatCalc, formatCalcValue, hasVotedCookie, hcaptchaProvider, interpolate, isPollClosed, localizedIf, manualStrategy, mostVotedStrategy, normalizeCalc, optionLabelsFor, pollTypesOf, recaptchaProvider, recountPollVotes, registerPollCloseTask, renderAllValues, renderAllValuesTable, resolveActions, resolveCalcContext, resolveConsentEntries, resolveConsentSourcesRequest, resolveConsentStatements, resolveDepartmentOptions, resolveEffectivePollOptions, resolveFileRef, resolveFormResultsRequest, resolvePollOptionSources, resolvePollOptions, resolvePollOutcome, resolvePollTypes, resolvePublishedVersionRef, resolveSpamConfig, resolveVotedSubmission, runPollClose, sanitizeUrl, serializeBody, shouldAutoResolvePoll, signFormContext, signPayload, sourceStrategy, stashPollTypes, stateField, textOfBody, toFormDocument, topBucketValues, turnstileProvider, valuesFromSearchParams, verifyFormContext, votedCookieName };
102
+ export { type ActionDefinition, ActionError, type ActionOption, type ActionRegistry, type ActionResult, type ActionRunArgs, type ActionValidateArgs, type ActionsConfig, type AggregateFieldResponsesArgs, type AggregateFormResponsesArgs, type AggregationBucket, type AggregationRow, type AnyActionDefinition, type AnyFormFieldDefinition, type AnyPollOptionSource, type AnyValidationRuleDefinition, type BodyContext, type BodyConverter, type BodyConverterArgs, type BodyRender, type ButtonFieldsOverride, type ButtonsOption, CAPTCHA_TOKEN_KEY, type CalcAllowed, type CalcDisplayConfig, type CalcExpression, type CalcFunction, type CalcResolved, type CalcSource, type CalcSourceResolveArgs, type CalcWeightResolveArgs, type CaptchaProvider, type CaptchaVerifyArgs, type ConsentProof, type ConsentSnapshotMode, type ConsentSourceEntry, type ConsentSourceOption, type ConsentSourcePage, type ConsentSourcesFieldOptions, type ConsentSourcesResolver, type ConsentStatement, type ConsentStatements, type CreateSubmissionArgs, type CreatedSubmission, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, type DefaultButtonFields, type DefaultOutcomeFields, type DefaultSettingsFields, type DepartmentEmailsResolver, type DepartmentOption, type DepartmentsFieldOptions, type EmailActionType, type EmailRender, type EmailRenderArgs, type FieldAggregation, type FieldCondition, type FieldMeta, type FieldTargetParamOptions, type FieldTypeOption, type FieldTypeRegistry, type FieldTypesConfig, type FileFieldConfig, type FileRef, type FileRefError, type FormBuilderPluginOptions, type FormButtonSettings, type FormContextReference, type FormDocument, type FormFallbackLocale, type FormFallbackLocaleArgs, type FormFallbackLocaleResult, type FormFieldDefinition, type FormFieldFormat, type FormFieldValidate, type FormFieldValueKind, type FormPollSettings, type FormResponseSettings, type FormResultsAccess, type FormResultsAccessArgs, type FromAddressOption, type FromAddressSource, type FromAddressSourceRegistry, type FromAddressesResolver, type HcaptchaProviderOptions, INLINE_DISPATCH_DEADLINE_MS, type IdentifyFn, type OmittableSharedField, type OutcomeFieldsOverride, POLL_CLOSE_TASK_SLUG, POLL_VOTES_SLUG, type FormBuilderPluginOptions as PluginOptions, type PollCloseTaskInput, type PollOption, type PollOptionResolveArgs, type PollOptionSource, type PollOptionSourceOption, type PollOptionSourceRegistry, type PollOptionSourcesConfig, type PollOutcomeStrategy, type PollOutcomeStrategyArgs, type PollTypeRegistry, type PollTypesConfig, type PrefillOptions, type PresentationDensity, type PresentationDescriptor, type PresentationSurface, RESPONDENTS_VALUE, type RateLimitCheckArgs, type RateLimitConfig, type RateLimitResult, type RateLimiter, type RecallResolver, type RecaptchaProviderOptions, type RecipientResolveArgs, type RecipientSource, type RecipientSourceRegistry, type RedirectFieldsOverride, type RedirectOption, type ResolveCalcContextArgs, type ResolveConsentEntriesArgs, type ResolveConsentSourcesRequestArgs, type ResolveConsentSourcesRequestResult, type ResolveConsentStatementsArgs, type ResolveDepartmentOptionsArgs, type ResolveDepartmentsRequestArgs, type ResolveDepartmentsRequestResult, type ResolveEffectivePollOptionsArgs, type ResolveFieldOptionsArgs, type ResolvePollOptionsArgs, type ResolvePollOutcomeArgs, type ResolvePublishedVersionRefArgs, type ResolveResultsRequestArgs, type ResolveResultsRequestResult, type ResponseOption, type RichTextBodyOption, SIGNATURE_HEADER, type SerializeBodyArgs, type SettingsFieldsOverride, type SettingsOption, type SpamConfig, type SpamMetadataConfig, type SpamOption, type SubmissionContextArgs, type SubmissionForm, type SubmissionStatusFilter, type ToFormDocumentOptions, type TurnstileProviderOptions, type UploadsOption, VOTE_SHARDS, type ValidationRuleDefinition, type ValidationRuleOption, type ValidationRuleRegistry, type ValidationRuleResult, type ValidationRulesConfig, type VotedSubmission, aggregateFieldResponses, aggregateFormResponses, aggregateFromVotes, aggregateRowForField, aggregateRowsForFields, applyConsentStatements, buildDefaultActionDefinitions, buildDefaultButtonFields, buildDefaultFieldDefinitions, buildDefaultOutcomeFields, buildDefaultSettingsFields, buildNextLabelField, buildPollCloseTask, buildPrevLabelField, buildRecallResolver, buildResolvedAtField, buildSubmitLabelField, buildWinningValuesField, calcExpressionOf, calcUsesSources, calcWeightKey, captureConsent, captureFileRef, computeCalcFields, consentSourcesField, countryField, createKvRateLimiter, createSubmission, defaultActionDefinitions, defaultBodyConverters, defaultFieldDefinitions, defaultFieldDefinitionsByType, defaultIdentify, defaultPresentationDescriptors, defaultValidationRules, defaultValidationRulesByType, defineAction, defineCalcFunction, defineCalcSource, defineCaptchaProvider, defineFormField, definePollOptionSource, definePollType, defineValidationRule, departmentsField, enqueuePollClose, escapeHtml, evaluateCalc, evaluateCondition, fieldHasOptions, fieldKey, fieldTargetParam, fileMimeTypeOptions, formBuilder, formatBytes, formatCalc, formatCalcValue, hasVotedCookie, hcaptchaProvider, interpolate, isPollClosed, localizedIf, manualStrategy, mostVotedStrategy, normalizeCalc, optionLabelsFor, pollTypesOf, recaptchaProvider, recountPollVotes, registerPollCloseTask, renderAllValues, renderAllValuesTable, resolveActions, resolveCalcContext, resolveConsentEntries, resolveConsentSourcesRequest, resolveConsentStatements, resolveDepartmentOptions, resolveEffectivePollOptions, resolveFileRef, resolveFormResultsRequest, resolvePollOptionSources, resolvePollOptions, resolvePollOutcome, resolvePollTypes, resolvePublishedVersionRef, resolveSpamConfig, resolveVotedSubmission, runPollClose, sanitizeUrl, serializeBody, shouldAutoResolvePoll, signFormContext, signPayload, sourceStrategy, stashPollTypes, stateField, textOfBody, toFormDocument, topBucketValues, turnstileProvider, valuesFromSearchParams, verifyFormContext, votedCookieName };
100
103
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -18,22 +18,29 @@ import { resolveFileRef } from "./uploads/resolveFileRef.js";
18
18
  import { fileMimeTypeOptions } from "./fields/builtin/file.js";
19
19
  import { buildDefaultFieldDefinitions, defaultFieldDefinitions, defaultFieldDefinitionsByType } from "./fields/builtin/index.js";
20
20
  import { resolveFieldTypes, stashFieldTypes } from "./fields/registry.js";
21
- import { INLINE_DISPATCH_DEADLINE_MS } from "./actions/dispatch.js";
22
- import { isPollClosed } from "./form/pollState.js";
23
21
  import { fieldKey } from "./fields/fieldKey.js";
24
22
  import { calcWeightKey, evaluateCalc } from "./calc/evaluate.js";
25
23
  import { calcExpressionOf, computeCalcFields } from "./calc/computeCalcFields.js";
26
24
  import { calcUsesSources, resolveCalcContext } from "./calc/resolveCalcContext.js";
27
25
  import { textOfBody } from "./actions/body/textOfBody.js";
28
26
  import { resolveConsentStatements } from "./consent/resolveConsentStatements.js";
29
- import { aggregateRowForField, aggregateRowsForFields } from "./aggregation/aggregateRows.js";
30
- import { aggregateFieldResponses, aggregateFormResponses, fieldHasOptions } from "./aggregation/aggregateResponses.js";
27
+ import { isPollClosed } from "./form/pollState.js";
28
+ import { INLINE_DISPATCH_DEADLINE_MS } from "./actions/dispatch.js";
29
+ import { POLL_VOTES_SLUG, RESPONDENTS_VALUE, VOTE_SHARDS } from "./poll/votes/votesCollection.js";
30
+ import { signFormContext, verifyFormContext } from "./context/formContext.js";
31
+ import { hasVotedCookie, votedCookieName } from "./submissions/votedCookie.js";
32
+ import { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from "./spam/constants.js";
31
33
  import { resolvePollOptions, stashPollOptionSources } from "./poll/resolvePollOptions.js";
32
34
  import { resolveEffectivePollOptions } from "./poll/effectivePollOptions.js";
35
+ import { evaluateCondition } from "./conditions/evaluate.js";
36
+ import { resolvePublishedVersionRef } from "./consent/resolvePublishedVersionRef.js";
37
+ import { captureConsent } from "./consent/captureConsent.js";
38
+ import { captureFileRef } from "./uploads/captureFileRef.js";
39
+ import { aggregateRowForField, aggregateRowsForFields } from "./aggregation/aggregateRows.js";
40
+ import { aggregateFieldResponses, aggregateFormResponses, fieldHasOptions } from "./aggregation/aggregateResponses.js";
33
41
  import { definePollType } from "./poll/definePollType.js";
34
42
  import { mostVotedStrategy, topBucketValues } from "./poll/mostVoted.js";
35
43
  import { manualStrategy, pollTypesOf, resolvePollTypes, sourceStrategy, stashPollTypes } from "./poll/pollTypeRegistry.js";
36
- import { POLL_VOTES_SLUG, RESPONDENTS_VALUE, VOTE_SHARDS } from "./poll/votes/votesCollection.js";
37
44
  import { aggregateFromVotes } from "./poll/votes/aggregateFromVotes.js";
38
45
  import { resolvePollOutcome } from "./poll/resolvePollOutcome.js";
39
46
  import { POLL_CLOSE_TASK_SLUG, buildPollCloseTask, enqueuePollClose, registerPollCloseTask, runPollClose, shouldAutoResolvePoll } from "./poll/closeJob.js";
@@ -43,13 +50,7 @@ import { resolveFormResultsRequest } from "./aggregation/resolveResultsRequest.j
43
50
  import { resolveConsentSourcesRequest } from "./consent/resolveConsentSourcesRequest.js";
44
51
  import { resolveDepartmentOptions } from "./email/departments.js";
45
52
  import { buildDefaultSettingsFields } from "./collections/settingsFields.js";
46
- import { signFormContext, verifyFormContext } from "./context/formContext.js";
47
- import { hasVotedCookie, votedCookieName } from "./submissions/votedCookie.js";
48
- import { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from "./spam/constants.js";
49
- import { evaluateCondition } from "./conditions/evaluate.js";
50
- import { resolvePublishedVersionRef } from "./consent/resolvePublishedVersionRef.js";
51
- import { captureConsent } from "./consent/captureConsent.js";
52
- import { captureFileRef } from "./uploads/captureFileRef.js";
53
+ import { stashFallbackLocale } from "./form/findFormAtLocale.js";
53
54
  import { readUploadCollectionMimeTypes } from "./plugin/uploadsCollection.js";
54
55
  import { registerCollections } from "./plugin/registerCollections.js";
55
56
  import { registerTranslations } from "./plugin/registerTranslations.js";
@@ -116,8 +117,10 @@ const formBuilder = definePlugin({
116
117
  fromAddresses,
117
118
  fromSources,
118
119
  departments,
120
+ localizeRecipients: options.email?.localizeRecipients,
119
121
  recipients: options.email?.recipients,
120
- recipientSources: options.email?.recipientSources
122
+ recipientSources: options.email?.recipientSources,
123
+ render: options.email?.render
121
124
  }), options.actions);
122
125
  assertNoActionBlockCollision(config, actionRegistry);
123
126
  const spam = resolveSpamConfig(options.spam);
@@ -127,6 +130,7 @@ const formBuilder = definePlugin({
127
130
  config.custom = stashPollTypes(config.custom, pollTypeRegistry);
128
131
  config.custom = stashFieldTypes(config.custom, registry);
129
132
  if (consentSources) config.custom = stashConsentSources(config.custom, consentSources);
133
+ if (options.fallbackLocale) config.custom = stashFallbackLocale(config.custom, options.fallbackLocale);
130
134
  registerTranslations(config, options.translations);
131
135
  registerCollections({
132
136
  config,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type Config, definePlugin } from 'payload'\nimport { assertNoActionBlockCollision } from './actions/assertNoBlockCollision'\nimport { buildDefaultActionDefinitions } from './actions/builtin'\nimport { resolveActions } from './actions/registry'\nimport { assertNoCalcFunctionCollision, assertValidCalcSourceKeys } from './calc/registry'\nimport { stashConsentSources } from './consent/resolveConsentEntries'\nimport { buildDefaultFieldDefinitions } from './fields/builtin'\nimport { resolveFieldTypes, stashFieldTypes } from './fields/registry'\nimport type { FormBuilderPluginOptions } from './options'\nimport { registerCollections } from './plugin/registerCollections'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport { readUploadCollectionMimeTypes } from './plugin/uploadsCollection'\nimport { resolvePollTypes, stashPollTypes } from './poll/pollTypeRegistry'\nimport { resolvePollOptionSources } from './poll/registry'\nimport { stashPollOptionSources } from './poll/resolvePollOptions'\nimport { resolveSpamConfig } from './spam/resolveSpam'\nimport { defaultValidationRules } from './validation/builtin'\nimport { resolveValidationRules } from './validation/registry'\n\nexport const formBuilder = definePlugin<FormBuilderPluginOptions>({\n\tslug: '@10x-media/form-builder',\n\torder: 50,\n\tplugin: ({ config, plugins, ...options }): Config => {\n\t\tif (options.disabled === true) {\n\t\t\treturn config\n\t\t}\n\t\tconst localizeContent = options.localizeContent !== false\n\t\tconst uploads = options.uploads ?? false\n\t\tconst calcSources = options.calc?.sources ?? {}\n\t\tconst calcFunctions = options.calc?.functions ?? {}\n\t\t// Fail fast: the evaluator resolves built-ins first, so a colliding custom function could never\n\t\t// run; a source key with a space would make the weight-map key (`calcWeightKey`) ambiguous.\n\t\tassertNoCalcFunctionCollision(calcFunctions)\n\t\tassertValidCalcSourceKeys(calcSources)\n\t\t// The allowed extension names, threaded into every normalizeCalc gate (the calculation field's\n\t\t// validate and the forms beforeValidate) so a stored expression can only ever reference a\n\t\t// registered source or function.\n\t\tconst calcAllowed = {\n\t\t\tsources: new Set(Object.keys(calcSources)),\n\t\t\tfunctions: new Set(Object.keys(calcFunctions)),\n\t\t}\n\t\t// Serializable source metadata for the builder UI: key, label, and implemented modes. The\n\t\t// resolver functions themselves never leave the server.\n\t\tconst calcSourceMeta = Object.entries(calcSources).map(([key, source]) => ({\n\t\t\tkey,\n\t\t\tlabel: source.label,\n\t\t\tscalar: typeof source.resolve === 'function',\n\t\t\tweights: typeof source.resolveWeights === 'function',\n\t\t}))\n\t\t// The file field's MIME picker is constrained to what the host upload collection accepts: the\n\t\t// explicit `uploads.mimeTypes` override, else the collection's own `upload.mimeTypes`. Read here,\n\t\t// before the field registry freezes below (attachUploadsCollection runs too late).\n\t\tconst uploadMimeTypes =\n\t\t\tuploads === false\n\t\t\t\t? undefined\n\t\t\t\t: (uploads.mimeTypes ?? readUploadCollectionMimeTypes(config, uploads.collection))\n\t\tconst consentSources = options.consent?.sources\n\t\t// A built-in with nowhere to point never enters the registry, so an author is never offered a\n\t\t// field that cannot work: file without an uploads collection has nowhere to store anything,\n\t\t// consent without sources has no statement to reference. An explicit `fields.file` /\n\t\t// `fields.consent` definition remains a developer choice.\n\t\tconst defaultFieldDefinitions = buildDefaultFieldDefinitions(\n\t\t\tlocalizeContent,\n\t\t\toptions.richText?.editor,\n\t\t\tuploadMimeTypes,\n\t\t\tcalcAllowed,\n\t\t\tcalcSourceMeta\n\t\t).filter(\n\t\t\t(definition) =>\n\t\t\t\t(uploads !== false || definition.type !== 'file') &&\n\t\t\t\t(consentSources !== undefined || definition.type !== 'consent')\n\t\t)\n\t\tconst registry = resolveFieldTypes(defaultFieldDefinitions, options.fields)\n\t\tconst ruleRegistry = resolveValidationRules(defaultValidationRules, options.rules)\n\t\tconst fromAddresses = options.email?.fromAddresses\n\t\tconst fromSources = options.email?.fromSources\n\t\tconst departments = options.email?.departments\n\t\tconst actionRegistry = resolveActions(\n\t\t\tbuildDefaultActionDefinitions({\n\t\t\t\tlocalize: localizeContent,\n\t\t\t\teditor: options.richText?.bodyEditor ?? options.richText?.editor,\n\t\t\t\tfromAddresses,\n\t\t\t\tfromSources,\n\t\t\t\tdepartments,\n\t\t\t\trecipients: options.email?.recipients,\n\t\t\t\trecipientSources: options.email?.recipientSources,\n\t\t\t}),\n\t\t\toptions.actions\n\t\t)\n\t\t// Fail fast if a custom action type collides with a host block slug (Payload resolves blocks\n\t\t// globally by slug, which would silently merge the content block's fields into saved actions).\n\t\tassertNoActionBlockCollision(config, actionRegistry)\n\t\tconst spam = resolveSpamConfig(options.spam)\n\t\tconst pollSourceRegistry = resolvePollOptionSources(options.poll?.sources)\n\t\tconst pollTypeRegistry = resolvePollTypes(options.poll?.types)\n\t\t// Stashed on config.custom so the root-level resolvePollOptions, resolveEffectivePollOptions,\n\t\t// resolvePollOutcome, and resolveConsentStatements helpers can reach these through\n\t\t// `payload.config` at request time, without threading plugin state through the host's own\n\t\t// server code. The field-type registry rides along so resolveEffectivePollOptions can look up a\n\t\t// results field's definition (and its `resolveOptions`) from a bare `payload` instance.\n\t\tconfig.custom = stashPollOptionSources(config.custom, pollSourceRegistry)\n\t\tconfig.custom = stashPollTypes(config.custom, pollTypeRegistry)\n\t\tconfig.custom = stashFieldTypes(config.custom, registry)\n\t\tif (consentSources) {\n\t\t\tconfig.custom = stashConsentSources(config.custom, consentSources)\n\t\t}\n\t\tregisterTranslations(config, options.translations)\n\t\tregisterCollections({\n\t\t\tconfig,\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tcalcAllowed,\n\t\t\tcalcSources,\n\t\t\tcalcFunctions,\n\t\t\tconsentSources,\n\t\t\tconsentSnapshot: options.consent?.snapshot ?? 'both',\n\t\t\tconsentResolveOnRead: options.consent?.resolveOnRead,\n\t\t\tactionRegistry,\n\t\t\trichText: options.richText,\n\t\t\thasJobsPlugin: Boolean(plugins['@10x-media/jobs']),\n\t\t\tdispatchDeadlineMs: options.dispatch?.deadlineMs,\n\t\t\tevents: options.events,\n\t\t\tuploads,\n\t\t\tspam,\n\t\t\tshowSubmissionRawFields: options.showSubmissionRawFields ?? false,\n\t\t\tlocalizeContent,\n\t\t\tresultsAccess: options.results?.access,\n\t\t\tvotedCookie: options.poll?.votedCookie === true,\n\t\t\tpollSourceRegistry,\n\t\t\tpollTypeRegistry,\n\t\t\toutcomeFields: options.poll?.outcomeFields,\n\t\t\tpollVotes: options.poll?.votes === false ? false : (options.poll?.votes ?? {}),\n\t\t\tbuttons: options.buttons,\n\t\t\tsettings: options.settings,\n\t\t\tresponse: options.response,\n\t\t\tfromAddresses,\n\t\t\tfromSources,\n\t\t\tdepartments,\n\t\t\tredirectRelationships: options.redirectRelationships,\n\t\t\toverrides: options.overrides,\n\t\t})\n\t\treturn config\n\t},\n})\n\nexport type {\n\tBodyConverter,\n\tBodyConverterArgs,\n\tBodyRender,\n} from './actions/body/converters'\nexport { defaultBodyConverters, sanitizeUrl } from './actions/body/converters'\nexport { escapeHtml } from './actions/body/escapeHtml'\nexport type {\n\tBodyContext,\n\tRichTextBodyOption,\n\tSerializeBodyArgs,\n} from './actions/body/serializeBody'\nexport { serializeBody } from './actions/body/serializeBody'\nexport { textOfBody } from './actions/body/textOfBody'\nexport { renderAllValues, renderAllValuesTable } from './actions/body/wildcards'\nexport { buildDefaultActionDefinitions, defaultActionDefinitions } from './actions/builtin'\nexport type {\n\tActionDefinition,\n\tActionRunArgs,\n\tActionValidateArgs,\n\tAnyActionDefinition,\n} from './actions/defineAction'\nexport { ActionError, defineAction } from './actions/defineAction'\nexport { INLINE_DISPATCH_DEADLINE_MS } from './actions/dispatch'\nexport type {\n\tFromAddressesResolver,\n\tFromAddressOption,\n\tFromAddressSource,\n\tFromAddressSourceRegistry,\n} from './actions/fromAddresses'\nexport type {\n\tRecipientResolveArgs,\n\tRecipientSource,\n\tRecipientSourceRegistry,\n} from './actions/recipientSources'\nexport type { ActionOption, ActionRegistry, ActionsConfig } from './actions/registry'\nexport { resolveActions } from './actions/registry'\nexport type { ActionResult } from './actions/runActions'\nexport { SIGNATURE_HEADER, signPayload } from './actions/sign'\nexport type {\n\tAggregateFieldResponsesArgs,\n\tAggregateFormResponsesArgs,\n} from './aggregation/aggregateResponses'\nexport {\n\taggregateFieldResponses,\n\taggregateFormResponses,\n\tfieldHasOptions,\n} from './aggregation/aggregateResponses'\nexport { aggregateRowForField, aggregateRowsForFields } from './aggregation/aggregateRows'\nexport type {\n\tFormResultsAccess,\n\tFormResultsAccessArgs,\n\tResolveResultsRequestArgs,\n\tResolveResultsRequestResult,\n} from './aggregation/resolveResultsRequest'\nexport { resolveFormResultsRequest } from './aggregation/resolveResultsRequest'\nexport type {\n\tAggregationBucket,\n\tAggregationRow,\n\tFieldAggregation,\n\tFieldMeta,\n\tSubmissionStatusFilter,\n} from './aggregation/types'\nexport { calcExpressionOf, computeCalcFields } from './calc/computeCalcFields'\nexport type { CalcResolved } from './calc/evaluate'\nexport { calcWeightKey, evaluateCalc } from './calc/evaluate'\nexport { formatCalc } from './calc/formatCalc'\nexport type { CalcDisplayConfig } from './calc/formatCalcValue'\nexport { formatCalcValue } from './calc/formatCalcValue'\nexport type { CalcAllowed } from './calc/normalizeCalc'\nexport { normalizeCalc } from './calc/normalizeCalc'\nexport type {\n\tCalcFunction,\n\tCalcSource,\n\tCalcSourceResolveArgs,\n\tCalcWeightResolveArgs,\n} from './calc/registry'\nexport { defineCalcFunction, defineCalcSource } from './calc/registry'\nexport type { ResolveCalcContextArgs } from './calc/resolveCalcContext'\nexport { calcUsesSources, resolveCalcContext } from './calc/resolveCalcContext'\nexport type { CalcExpression } from './calc/types'\nexport type {\n\tButtonFieldsOverride,\n\tButtonsOption,\n\tDefaultButtonFields,\n} from './collections/buttonFields'\nexport {\n\tbuildDefaultButtonFields,\n\tbuildNextLabelField,\n\tbuildPrevLabelField,\n\tbuildSubmitLabelField,\n} from './collections/buttonFields'\nexport type {\n\tRedirectFieldsOverride,\n\tRedirectOption,\n\tResponseOption,\n} from './collections/redirectFields'\nexport type {\n\tDefaultSettingsFields,\n\tSettingsFieldsOverride,\n\tSettingsOption,\n} from './collections/settingsFields'\nexport { buildDefaultSettingsFields } from './collections/settingsFields'\nexport { evaluateCondition } from './conditions/evaluate'\nexport type { FieldCondition } from './conditions/types'\nexport { applyConsentStatements } from './consent/applyConsentStatements'\nexport type { ConsentProof, ConsentSnapshotMode } from './consent/captureConsent'\nexport { captureConsent } from './consent/captureConsent'\nexport type { ConsentSourcesFieldOptions } from './consent/consentSourcesField'\nexport { consentSourcesField } from './consent/consentSourcesField'\nexport type { ResolveConsentEntriesArgs } from './consent/resolveConsentEntries'\nexport { resolveConsentEntries } from './consent/resolveConsentEntries'\nexport type {\n\tConsentSourceOption,\n\tResolveConsentSourcesRequestArgs,\n\tResolveConsentSourcesRequestResult,\n} from './consent/resolveConsentSourcesRequest'\nexport { resolveConsentSourcesRequest } from './consent/resolveConsentSourcesRequest'\nexport type {\n\tConsentStatement,\n\tConsentStatements,\n\tResolveConsentStatementsArgs,\n} from './consent/resolveConsentStatements'\nexport { resolveConsentStatements } from './consent/resolveConsentStatements'\nexport type { ResolvePublishedVersionRefArgs } from './consent/resolvePublishedVersionRef'\nexport { resolvePublishedVersionRef } from './consent/resolvePublishedVersionRef'\nexport type {\n\tConsentSourceEntry,\n\tConsentSourcePage,\n\tConsentSourcesResolver,\n} from './consent/types'\nexport type { FormContextReference } from './context/formContext'\nexport { signFormContext, verifyFormContext } from './context/formContext'\nexport type {\n\tDepartmentEmailsResolver,\n\tDepartmentOption,\n\tResolveDepartmentOptionsArgs,\n\tResolveDepartmentsRequestArgs,\n\tResolveDepartmentsRequestResult,\n} from './email/departments'\nexport { resolveDepartmentOptions } from './email/departments'\nexport type { DepartmentsFieldOptions } from './email/departmentsField'\nexport { departmentsField } from './email/departmentsField'\nexport {\n\tbuildDefaultFieldDefinitions,\n\tdefaultFieldDefinitions,\n\tdefaultFieldDefinitionsByType,\n} from './fields/builtin'\nexport { countryField } from './fields/builtin/country'\nexport { fileMimeTypeOptions } from './fields/builtin/file'\nexport { stateField } from './fields/builtin/state'\nexport { defineFormField } from './fields/defineFormField'\nexport { fieldKey } from './fields/fieldKey'\nexport { localizedIf } from './fields/localizedIf'\nexport type { FieldTypeOption, FieldTypeRegistry, FieldTypesConfig } from './fields/registry'\nexport type {\n\tAnyFormFieldDefinition,\n\tFormFieldDefinition,\n\tFormFieldFormat,\n\tFormFieldValidate,\n\tFormFieldValueKind,\n\tOmittableSharedField,\n\tResolveFieldOptionsArgs,\n} from './fields/types'\nexport { isPollClosed } from './form/pollState'\nexport type { ToFormDocumentOptions } from './form/toFormDocument'\nexport { toFormDocument } from './form/toFormDocument'\nexport type {\n\tFormButtonSettings,\n\tFormDocument,\n\tFormPollSettings,\n\tFormResponseSettings,\n} from './form/types'\nexport type {\n\tFormBuilderPluginOptions,\n\tFormBuilderPluginOptions as PluginOptions,\n} from './options'\nexport type { UploadsOption } from './plugin/uploadsCollection'\nexport type { PollCloseTaskInput } from './poll/closeJob'\nexport {\n\tbuildPollCloseTask,\n\tenqueuePollClose,\n\tPOLL_CLOSE_TASK_SLUG,\n\tregisterPollCloseTask,\n\trunPollClose,\n\tshouldAutoResolvePoll,\n} from './poll/closeJob'\nexport type {\n\tAnyPollOptionSource,\n\tPollOption,\n\tPollOptionResolveArgs,\n\tPollOptionSource,\n} from './poll/definePollOptionSource'\nexport { definePollOptionSource } from './poll/definePollOptionSource'\nexport type { PollOutcomeStrategy, PollOutcomeStrategyArgs } from './poll/definePollType'\nexport { definePollType } from './poll/definePollType'\nexport type { ResolveEffectivePollOptionsArgs } from './poll/effectivePollOptions'\nexport { resolveEffectivePollOptions } from './poll/effectivePollOptions'\nexport { mostVotedStrategy, topBucketValues } from './poll/mostVoted'\nexport type { DefaultOutcomeFields, OutcomeFieldsOverride } from './poll/outcomeFields'\nexport {\n\tbuildDefaultOutcomeFields,\n\tbuildResolvedAtField,\n\tbuildWinningValuesField,\n} from './poll/outcomeFields'\nexport type { PollTypeRegistry, PollTypesConfig } from './poll/pollTypeRegistry'\nexport {\n\tmanualStrategy,\n\tpollTypesOf,\n\tresolvePollTypes,\n\tsourceStrategy,\n\tstashPollTypes,\n} from './poll/pollTypeRegistry'\nexport type {\n\tPollOptionSourceOption,\n\tPollOptionSourceRegistry,\n\tPollOptionSourcesConfig,\n} from './poll/registry'\nexport { resolvePollOptionSources } from './poll/registry'\nexport type { ResolvePollOptionsArgs } from './poll/resolvePollOptions'\nexport { resolvePollOptions } from './poll/resolvePollOptions'\nexport type { ResolvePollOutcomeArgs } from './poll/resolvePollOutcome'\nexport { resolvePollOutcome } from './poll/resolvePollOutcome'\nexport { aggregateFromVotes } from './poll/votes/aggregateFromVotes'\nexport { recountPollVotes } from './poll/votes/recountPollVotes'\nexport { POLL_VOTES_SLUG, RESPONDENTS_VALUE, VOTE_SHARDS } from './poll/votes/votesCollection'\nexport type { PrefillOptions } from './prefill/valuesFromSearchParams'\nexport { valuesFromSearchParams } from './prefill/valuesFromSearchParams'\nexport { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from './presentations/defaults'\nexport type {\n\tPresentationDensity,\n\tPresentationDescriptor,\n\tPresentationSurface,\n} from './presentations/types'\nexport { interpolate } from './recall/interpolate'\nexport type { RecallResolver } from './recall/resolver'\nexport { buildRecallResolver, optionLabelsFor } from './recall/resolver'\nexport { defineCaptchaProvider } from './spam/captcha'\nexport { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from './spam/constants'\nexport { defaultIdentify } from './spam/identify'\nexport type { HcaptchaProviderOptions } from './spam/providers/hcaptcha'\nexport { hcaptchaProvider } from './spam/providers/hcaptcha'\nexport type { RecaptchaProviderOptions } from './spam/providers/recaptcha'\nexport { recaptchaProvider } from './spam/providers/recaptcha'\nexport type { TurnstileProviderOptions } from './spam/providers/turnstile'\nexport { turnstileProvider } from './spam/providers/turnstile'\nexport { createKvRateLimiter } from './spam/rateLimiter'\nexport { resolveSpamConfig } from './spam/resolveSpam'\nexport type {\n\tCaptchaProvider,\n\tCaptchaVerifyArgs,\n\tIdentifyFn,\n\tRateLimitCheckArgs,\n\tRateLimitConfig,\n\tRateLimiter,\n\tRateLimitResult,\n\tSpamConfig,\n\tSpamMetadataConfig,\n\tSpamOption,\n} from './spam/types'\nexport type { CreatedSubmission, CreateSubmissionArgs } from './submissions/createSubmission'\nexport { createSubmission } from './submissions/createSubmission'\nexport type { VotedSubmission } from './submissions/resolveVotedSubmission'\nexport { resolveVotedSubmission } from './submissions/resolveVotedSubmission'\nexport { hasVotedCookie, votedCookieName } from './submissions/votedCookie'\nexport { captureFileRef } from './uploads/captureFileRef'\nexport { formatBytes } from './uploads/formatBytes'\nexport { resolveFileRef } from './uploads/resolveFileRef'\nexport type { FileFieldConfig, FileRef, FileRefError } from './uploads/types'\nexport { defaultValidationRules, defaultValidationRulesByType } from './validation/builtin'\nexport { defineValidationRule } from './validation/defineValidationRule'\nexport type { FieldTargetParamOptions } from './validation/fieldTargetParam'\nexport { fieldTargetParam } from './validation/fieldTargetParam'\nexport type {\n\tValidationRuleOption,\n\tValidationRuleRegistry,\n\tValidationRulesConfig,\n} from './validation/registry'\nexport type {\n\tAnyValidationRuleDefinition,\n\tValidationRuleDefinition,\n\tValidationRuleResult,\n} from './validation/types'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,MAAa,cAAc,aAAuC;CACjE,MAAM;CACN,OAAO;CACP,SAAS,EAAE,QAAQ,SAAS,GAAG,cAAsB;EACpD,IAAI,QAAQ,aAAa,MACxB,OAAO;EAER,MAAM,kBAAkB,QAAQ,oBAAoB;EACpD,MAAM,UAAU,QAAQ,WAAW;EACnC,MAAM,cAAc,QAAQ,MAAM,WAAW,CAAC;EAC9C,MAAM,gBAAgB,QAAQ,MAAM,aAAa,CAAC;EAGlD,8BAA8B,aAAa;EAC3C,0BAA0B,WAAW;EAIrC,MAAM,cAAc;GACnB,SAAS,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;GACzC,WAAW,IAAI,IAAI,OAAO,KAAK,aAAa,CAAC;EAC9C;EAGA,MAAM,iBAAiB,OAAO,QAAQ,WAAW,EAAE,KAAK,CAAC,KAAK,aAAa;GAC1E;GACA,OAAO,OAAO;GACd,QAAQ,OAAO,OAAO,YAAY;GAClC,SAAS,OAAO,OAAO,mBAAmB;EAC3C,EAAE;EAIF,MAAM,kBACL,YAAY,QACT,KAAA,IACC,QAAQ,aAAa,8BAA8B,QAAQ,QAAQ,UAAU;EAClF,MAAM,iBAAiB,QAAQ,SAAS;EAgBxC,MAAM,WAAW,kBAXe,6BAC/B,iBACA,QAAQ,UAAU,QAClB,iBACA,aACA,cACD,EAAE,QACA,gBACC,YAAY,SAAS,WAAW,SAAS,YACzC,mBAAmB,KAAA,KAAa,WAAW,SAAS,UAEE,GAAG,QAAQ,MAAM;EAC1E,MAAM,eAAe,uBAAuB,wBAAwB,QAAQ,KAAK;EACjF,MAAM,gBAAgB,QAAQ,OAAO;EACrC,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,iBAAiB,eACtB,8BAA8B;GAC7B,UAAU;GACV,QAAQ,QAAQ,UAAU,cAAc,QAAQ,UAAU;GAC1D;GACA;GACA;GACA,YAAY,QAAQ,OAAO;GAC3B,kBAAkB,QAAQ,OAAO;EAClC,CAAC,GACD,QAAQ,OACT;EAGA,6BAA6B,QAAQ,cAAc;EACnD,MAAM,OAAO,kBAAkB,QAAQ,IAAI;EAC3C,MAAM,qBAAqB,yBAAyB,QAAQ,MAAM,OAAO;EACzE,MAAM,mBAAmB,iBAAiB,QAAQ,MAAM,KAAK;EAM7D,OAAO,SAAS,uBAAuB,OAAO,QAAQ,kBAAkB;EACxE,OAAO,SAAS,eAAe,OAAO,QAAQ,gBAAgB;EAC9D,OAAO,SAAS,gBAAgB,OAAO,QAAQ,QAAQ;EACvD,IAAI,gBACH,OAAO,SAAS,oBAAoB,OAAO,QAAQ,cAAc;EAElE,qBAAqB,QAAQ,QAAQ,YAAY;EACjD,oBAAoB;GACnB;GACA;GACA;GACA;GACA;GACA;GACA;GACA,iBAAiB,QAAQ,SAAS,YAAY;GAC9C,sBAAsB,QAAQ,SAAS;GACvC;GACA,UAAU,QAAQ;GAClB,eAAe,QAAQ,QAAQ,kBAAkB;GACjD,oBAAoB,QAAQ,UAAU;GACtC,QAAQ,QAAQ;GAChB;GACA;GACA,yBAAyB,QAAQ,2BAA2B;GAC5D;GACA,eAAe,QAAQ,SAAS;GAChC,aAAa,QAAQ,MAAM,gBAAgB;GAC3C;GACA;GACA,eAAe,QAAQ,MAAM;GAC7B,WAAW,QAAQ,MAAM,UAAU,QAAQ,QAAS,QAAQ,MAAM,SAAS,CAAC;GAC5E,SAAS,QAAQ;GACjB,UAAU,QAAQ;GAClB,UAAU,QAAQ;GAClB;GACA;GACA;GACA,uBAAuB,QAAQ;GAC/B,WAAW,QAAQ;EACpB,CAAC;EACD,OAAO;CACR;AACD,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type Config, definePlugin } from 'payload'\nimport { assertNoActionBlockCollision } from './actions/assertNoBlockCollision'\nimport { buildDefaultActionDefinitions } from './actions/builtin'\nimport { resolveActions } from './actions/registry'\nimport { assertNoCalcFunctionCollision, assertValidCalcSourceKeys } from './calc/registry'\nimport { stashConsentSources } from './consent/resolveConsentEntries'\nimport { buildDefaultFieldDefinitions } from './fields/builtin'\nimport { resolveFieldTypes, stashFieldTypes } from './fields/registry'\nimport { stashFallbackLocale } from './form/findFormAtLocale'\nimport type { FormBuilderPluginOptions } from './options'\nimport { registerCollections } from './plugin/registerCollections'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport { readUploadCollectionMimeTypes } from './plugin/uploadsCollection'\nimport { resolvePollTypes, stashPollTypes } from './poll/pollTypeRegistry'\nimport { resolvePollOptionSources } from './poll/registry'\nimport { stashPollOptionSources } from './poll/resolvePollOptions'\nimport { resolveSpamConfig } from './spam/resolveSpam'\nimport { defaultValidationRules } from './validation/builtin'\nimport { resolveValidationRules } from './validation/registry'\n\nexport const formBuilder = definePlugin<FormBuilderPluginOptions>({\n\tslug: '@10x-media/form-builder',\n\torder: 50,\n\tplugin: ({ config, plugins, ...options }): Config => {\n\t\tif (options.disabled === true) {\n\t\t\treturn config\n\t\t}\n\t\tconst localizeContent = options.localizeContent !== false\n\t\tconst uploads = options.uploads ?? false\n\t\tconst calcSources = options.calc?.sources ?? {}\n\t\tconst calcFunctions = options.calc?.functions ?? {}\n\t\t// Fail fast: the evaluator resolves built-ins first, so a colliding custom function could never\n\t\t// run; a source key with a space would make the weight-map key (`calcWeightKey`) ambiguous.\n\t\tassertNoCalcFunctionCollision(calcFunctions)\n\t\tassertValidCalcSourceKeys(calcSources)\n\t\t// The allowed extension names, threaded into every normalizeCalc gate (the calculation field's\n\t\t// validate and the forms beforeValidate) so a stored expression can only ever reference a\n\t\t// registered source or function.\n\t\tconst calcAllowed = {\n\t\t\tsources: new Set(Object.keys(calcSources)),\n\t\t\tfunctions: new Set(Object.keys(calcFunctions)),\n\t\t}\n\t\t// Serializable source metadata for the builder UI: key, label, and implemented modes. The\n\t\t// resolver functions themselves never leave the server.\n\t\tconst calcSourceMeta = Object.entries(calcSources).map(([key, source]) => ({\n\t\t\tkey,\n\t\t\tlabel: source.label,\n\t\t\tscalar: typeof source.resolve === 'function',\n\t\t\tweights: typeof source.resolveWeights === 'function',\n\t\t}))\n\t\t// The file field's MIME picker is constrained to what the host upload collection accepts: the\n\t\t// explicit `uploads.mimeTypes` override, else the collection's own `upload.mimeTypes`. Read here,\n\t\t// before the field registry freezes below (attachUploadsCollection runs too late).\n\t\tconst uploadMimeTypes =\n\t\t\tuploads === false\n\t\t\t\t? undefined\n\t\t\t\t: (uploads.mimeTypes ?? readUploadCollectionMimeTypes(config, uploads.collection))\n\t\tconst consentSources = options.consent?.sources\n\t\t// A built-in with nowhere to point never enters the registry, so an author is never offered a\n\t\t// field that cannot work: file without an uploads collection has nowhere to store anything,\n\t\t// consent without sources has no statement to reference. An explicit `fields.file` /\n\t\t// `fields.consent` definition remains a developer choice.\n\t\tconst defaultFieldDefinitions = buildDefaultFieldDefinitions(\n\t\t\tlocalizeContent,\n\t\t\toptions.richText?.editor,\n\t\t\tuploadMimeTypes,\n\t\t\tcalcAllowed,\n\t\t\tcalcSourceMeta\n\t\t).filter(\n\t\t\t(definition) =>\n\t\t\t\t(uploads !== false || definition.type !== 'file') &&\n\t\t\t\t(consentSources !== undefined || definition.type !== 'consent')\n\t\t)\n\t\tconst registry = resolveFieldTypes(defaultFieldDefinitions, options.fields)\n\t\tconst ruleRegistry = resolveValidationRules(defaultValidationRules, options.rules)\n\t\tconst fromAddresses = options.email?.fromAddresses\n\t\tconst fromSources = options.email?.fromSources\n\t\tconst departments = options.email?.departments\n\t\tconst actionRegistry = resolveActions(\n\t\t\tbuildDefaultActionDefinitions({\n\t\t\t\tlocalize: localizeContent,\n\t\t\t\teditor: options.richText?.bodyEditor ?? options.richText?.editor,\n\t\t\t\tfromAddresses,\n\t\t\t\tfromSources,\n\t\t\t\tdepartments,\n\t\t\t\tlocalizeRecipients: options.email?.localizeRecipients,\n\t\t\t\trecipients: options.email?.recipients,\n\t\t\t\trecipientSources: options.email?.recipientSources,\n\t\t\t\trender: options.email?.render,\n\t\t\t}),\n\t\t\toptions.actions\n\t\t)\n\t\t// Fail fast if a custom action type collides with a host block slug (Payload resolves blocks\n\t\t// globally by slug, which would silently merge the content block's fields into saved actions).\n\t\tassertNoActionBlockCollision(config, actionRegistry)\n\t\tconst spam = resolveSpamConfig(options.spam)\n\t\tconst pollSourceRegistry = resolvePollOptionSources(options.poll?.sources)\n\t\tconst pollTypeRegistry = resolvePollTypes(options.poll?.types)\n\t\t// Stashed on config.custom so the root-level resolvePollOptions, resolveEffectivePollOptions,\n\t\t// resolvePollOutcome, and resolveConsentStatements helpers can reach these through\n\t\t// `payload.config` at request time, without threading plugin state through the host's own\n\t\t// server code. The field-type registry rides along so resolveEffectivePollOptions can look up a\n\t\t// results field's definition (and its `resolveOptions`) from a bare `payload` instance.\n\t\tconfig.custom = stashPollOptionSources(config.custom, pollSourceRegistry)\n\t\tconfig.custom = stashPollTypes(config.custom, pollTypeRegistry)\n\t\tconfig.custom = stashFieldTypes(config.custom, registry)\n\t\tif (consentSources) {\n\t\t\tconfig.custom = stashConsentSources(config.custom, consentSources)\n\t\t}\n\t\tif (options.fallbackLocale) {\n\t\t\tconfig.custom = stashFallbackLocale(config.custom, options.fallbackLocale)\n\t\t}\n\t\tregisterTranslations(config, options.translations)\n\t\tregisterCollections({\n\t\t\tconfig,\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tcalcAllowed,\n\t\t\tcalcSources,\n\t\t\tcalcFunctions,\n\t\t\tconsentSources,\n\t\t\tconsentSnapshot: options.consent?.snapshot ?? 'both',\n\t\t\tconsentResolveOnRead: options.consent?.resolveOnRead,\n\t\t\tactionRegistry,\n\t\t\trichText: options.richText,\n\t\t\thasJobsPlugin: Boolean(plugins['@10x-media/jobs']),\n\t\t\tdispatchDeadlineMs: options.dispatch?.deadlineMs,\n\t\t\tevents: options.events,\n\t\t\tuploads,\n\t\t\tspam,\n\t\t\tshowSubmissionRawFields: options.showSubmissionRawFields ?? false,\n\t\t\tlocalizeContent,\n\t\t\tresultsAccess: options.results?.access,\n\t\t\tvotedCookie: options.poll?.votedCookie === true,\n\t\t\tpollSourceRegistry,\n\t\t\tpollTypeRegistry,\n\t\t\toutcomeFields: options.poll?.outcomeFields,\n\t\t\tpollVotes: options.poll?.votes === false ? false : (options.poll?.votes ?? {}),\n\t\t\tbuttons: options.buttons,\n\t\t\tsettings: options.settings,\n\t\t\tresponse: options.response,\n\t\t\tfromAddresses,\n\t\t\tfromSources,\n\t\t\tdepartments,\n\t\t\tredirectRelationships: options.redirectRelationships,\n\t\t\toverrides: options.overrides,\n\t\t})\n\t\treturn config\n\t},\n})\n\nexport type {\n\tBodyConverter,\n\tBodyConverterArgs,\n\tBodyRender,\n} from './actions/body/converters'\nexport { defaultBodyConverters, sanitizeUrl } from './actions/body/converters'\nexport { escapeHtml } from './actions/body/escapeHtml'\nexport type {\n\tBodyContext,\n\tRichTextBodyOption,\n\tSerializeBodyArgs,\n} from './actions/body/serializeBody'\nexport { serializeBody } from './actions/body/serializeBody'\nexport { textOfBody } from './actions/body/textOfBody'\nexport { renderAllValues, renderAllValuesTable } from './actions/body/wildcards'\nexport { buildDefaultActionDefinitions, defaultActionDefinitions } from './actions/builtin'\nexport type {\n\tActionDefinition,\n\tActionRunArgs,\n\tActionValidateArgs,\n\tAnyActionDefinition,\n} from './actions/defineAction'\nexport { ActionError, defineAction } from './actions/defineAction'\nexport { INLINE_DISPATCH_DEADLINE_MS } from './actions/dispatch'\nexport type { EmailActionType, EmailRender, EmailRenderArgs } from './actions/emailRender'\nexport type {\n\tFromAddressesResolver,\n\tFromAddressOption,\n\tFromAddressSource,\n\tFromAddressSourceRegistry,\n} from './actions/fromAddresses'\nexport type {\n\tRecipientResolveArgs,\n\tRecipientSource,\n\tRecipientSourceRegistry,\n} from './actions/recipientSources'\nexport type { ActionOption, ActionRegistry, ActionsConfig } from './actions/registry'\nexport { resolveActions } from './actions/registry'\nexport type { ActionResult } from './actions/runActions'\nexport { SIGNATURE_HEADER, signPayload } from './actions/sign'\nexport type { SubmissionContextArgs, SubmissionForm } from './actions/submissionContext'\nexport type {\n\tAggregateFieldResponsesArgs,\n\tAggregateFormResponsesArgs,\n} from './aggregation/aggregateResponses'\nexport {\n\taggregateFieldResponses,\n\taggregateFormResponses,\n\tfieldHasOptions,\n} from './aggregation/aggregateResponses'\nexport { aggregateRowForField, aggregateRowsForFields } from './aggregation/aggregateRows'\nexport type {\n\tFormResultsAccess,\n\tFormResultsAccessArgs,\n\tResolveResultsRequestArgs,\n\tResolveResultsRequestResult,\n} from './aggregation/resolveResultsRequest'\nexport { resolveFormResultsRequest } from './aggregation/resolveResultsRequest'\nexport type {\n\tAggregationBucket,\n\tAggregationRow,\n\tFieldAggregation,\n\tFieldMeta,\n\tSubmissionStatusFilter,\n} from './aggregation/types'\nexport { calcExpressionOf, computeCalcFields } from './calc/computeCalcFields'\nexport type { CalcResolved } from './calc/evaluate'\nexport { calcWeightKey, evaluateCalc } from './calc/evaluate'\nexport { formatCalc } from './calc/formatCalc'\nexport type { CalcDisplayConfig } from './calc/formatCalcValue'\nexport { formatCalcValue } from './calc/formatCalcValue'\nexport type { CalcAllowed } from './calc/normalizeCalc'\nexport { normalizeCalc } from './calc/normalizeCalc'\nexport type {\n\tCalcFunction,\n\tCalcSource,\n\tCalcSourceResolveArgs,\n\tCalcWeightResolveArgs,\n} from './calc/registry'\nexport { defineCalcFunction, defineCalcSource } from './calc/registry'\nexport type { ResolveCalcContextArgs } from './calc/resolveCalcContext'\nexport { calcUsesSources, resolveCalcContext } from './calc/resolveCalcContext'\nexport type { CalcExpression } from './calc/types'\nexport type {\n\tButtonFieldsOverride,\n\tButtonsOption,\n\tDefaultButtonFields,\n} from './collections/buttonFields'\nexport {\n\tbuildDefaultButtonFields,\n\tbuildNextLabelField,\n\tbuildPrevLabelField,\n\tbuildSubmitLabelField,\n} from './collections/buttonFields'\nexport type {\n\tRedirectFieldsOverride,\n\tRedirectOption,\n\tResponseOption,\n} from './collections/redirectFields'\nexport type {\n\tDefaultSettingsFields,\n\tSettingsFieldsOverride,\n\tSettingsOption,\n} from './collections/settingsFields'\nexport { buildDefaultSettingsFields } from './collections/settingsFields'\nexport { evaluateCondition } from './conditions/evaluate'\nexport type { FieldCondition } from './conditions/types'\nexport { applyConsentStatements } from './consent/applyConsentStatements'\nexport type { ConsentProof, ConsentSnapshotMode } from './consent/captureConsent'\nexport { captureConsent } from './consent/captureConsent'\nexport type { ConsentSourcesFieldOptions } from './consent/consentSourcesField'\nexport { consentSourcesField } from './consent/consentSourcesField'\nexport type { ResolveConsentEntriesArgs } from './consent/resolveConsentEntries'\nexport { resolveConsentEntries } from './consent/resolveConsentEntries'\nexport type {\n\tConsentSourceOption,\n\tResolveConsentSourcesRequestArgs,\n\tResolveConsentSourcesRequestResult,\n} from './consent/resolveConsentSourcesRequest'\nexport { resolveConsentSourcesRequest } from './consent/resolveConsentSourcesRequest'\nexport type {\n\tConsentStatement,\n\tConsentStatements,\n\tResolveConsentStatementsArgs,\n} from './consent/resolveConsentStatements'\nexport { resolveConsentStatements } from './consent/resolveConsentStatements'\nexport type { ResolvePublishedVersionRefArgs } from './consent/resolvePublishedVersionRef'\nexport { resolvePublishedVersionRef } from './consent/resolvePublishedVersionRef'\nexport type {\n\tConsentSourceEntry,\n\tConsentSourcePage,\n\tConsentSourcesResolver,\n} from './consent/types'\nexport type { FormContextReference } from './context/formContext'\nexport { signFormContext, verifyFormContext } from './context/formContext'\nexport type {\n\tDepartmentEmailsResolver,\n\tDepartmentOption,\n\tResolveDepartmentOptionsArgs,\n\tResolveDepartmentsRequestArgs,\n\tResolveDepartmentsRequestResult,\n} from './email/departments'\nexport { resolveDepartmentOptions } from './email/departments'\nexport type { DepartmentsFieldOptions } from './email/departmentsField'\nexport { departmentsField } from './email/departmentsField'\nexport {\n\tbuildDefaultFieldDefinitions,\n\tdefaultFieldDefinitions,\n\tdefaultFieldDefinitionsByType,\n} from './fields/builtin'\nexport { countryField } from './fields/builtin/country'\nexport { fileMimeTypeOptions } from './fields/builtin/file'\nexport { stateField } from './fields/builtin/state'\nexport { defineFormField } from './fields/defineFormField'\nexport { fieldKey } from './fields/fieldKey'\nexport { localizedIf } from './fields/localizedIf'\nexport type { FieldTypeOption, FieldTypeRegistry, FieldTypesConfig } from './fields/registry'\nexport type {\n\tAnyFormFieldDefinition,\n\tFormFieldDefinition,\n\tFormFieldFormat,\n\tFormFieldValidate,\n\tFormFieldValueKind,\n\tOmittableSharedField,\n\tResolveFieldOptionsArgs,\n} from './fields/types'\nexport type {\n\tFormFallbackLocale,\n\tFormFallbackLocaleArgs,\n\tFormFallbackLocaleResult,\n} from './form/findFormAtLocale'\nexport { isPollClosed } from './form/pollState'\nexport type { ToFormDocumentOptions } from './form/toFormDocument'\nexport { toFormDocument } from './form/toFormDocument'\nexport type {\n\tFormButtonSettings,\n\tFormDocument,\n\tFormPollSettings,\n\tFormResponseSettings,\n} from './form/types'\nexport type {\n\tFormBuilderPluginOptions,\n\tFormBuilderPluginOptions as PluginOptions,\n} from './options'\nexport type { UploadsOption } from './plugin/uploadsCollection'\nexport type { PollCloseTaskInput } from './poll/closeJob'\nexport {\n\tbuildPollCloseTask,\n\tenqueuePollClose,\n\tPOLL_CLOSE_TASK_SLUG,\n\tregisterPollCloseTask,\n\trunPollClose,\n\tshouldAutoResolvePoll,\n} from './poll/closeJob'\nexport type {\n\tAnyPollOptionSource,\n\tPollOption,\n\tPollOptionResolveArgs,\n\tPollOptionSource,\n} from './poll/definePollOptionSource'\nexport { definePollOptionSource } from './poll/definePollOptionSource'\nexport type { PollOutcomeStrategy, PollOutcomeStrategyArgs } from './poll/definePollType'\nexport { definePollType } from './poll/definePollType'\nexport type { ResolveEffectivePollOptionsArgs } from './poll/effectivePollOptions'\nexport { resolveEffectivePollOptions } from './poll/effectivePollOptions'\nexport { mostVotedStrategy, topBucketValues } from './poll/mostVoted'\nexport type { DefaultOutcomeFields, OutcomeFieldsOverride } from './poll/outcomeFields'\nexport {\n\tbuildDefaultOutcomeFields,\n\tbuildResolvedAtField,\n\tbuildWinningValuesField,\n} from './poll/outcomeFields'\nexport type { PollTypeRegistry, PollTypesConfig } from './poll/pollTypeRegistry'\nexport {\n\tmanualStrategy,\n\tpollTypesOf,\n\tresolvePollTypes,\n\tsourceStrategy,\n\tstashPollTypes,\n} from './poll/pollTypeRegistry'\nexport type {\n\tPollOptionSourceOption,\n\tPollOptionSourceRegistry,\n\tPollOptionSourcesConfig,\n} from './poll/registry'\nexport { resolvePollOptionSources } from './poll/registry'\nexport type { ResolvePollOptionsArgs } from './poll/resolvePollOptions'\nexport { resolvePollOptions } from './poll/resolvePollOptions'\nexport type { ResolvePollOutcomeArgs } from './poll/resolvePollOutcome'\nexport { resolvePollOutcome } from './poll/resolvePollOutcome'\nexport { aggregateFromVotes } from './poll/votes/aggregateFromVotes'\nexport { recountPollVotes } from './poll/votes/recountPollVotes'\nexport { POLL_VOTES_SLUG, RESPONDENTS_VALUE, VOTE_SHARDS } from './poll/votes/votesCollection'\nexport type { PrefillOptions } from './prefill/valuesFromSearchParams'\nexport { valuesFromSearchParams } from './prefill/valuesFromSearchParams'\nexport { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from './presentations/defaults'\nexport type {\n\tPresentationDensity,\n\tPresentationDescriptor,\n\tPresentationSurface,\n} from './presentations/types'\nexport { interpolate } from './recall/interpolate'\nexport type { RecallResolver } from './recall/resolver'\nexport { buildRecallResolver, optionLabelsFor } from './recall/resolver'\nexport { defineCaptchaProvider } from './spam/captcha'\nexport { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from './spam/constants'\nexport { defaultIdentify } from './spam/identify'\nexport type { HcaptchaProviderOptions } from './spam/providers/hcaptcha'\nexport { hcaptchaProvider } from './spam/providers/hcaptcha'\nexport type { RecaptchaProviderOptions } from './spam/providers/recaptcha'\nexport { recaptchaProvider } from './spam/providers/recaptcha'\nexport type { TurnstileProviderOptions } from './spam/providers/turnstile'\nexport { turnstileProvider } from './spam/providers/turnstile'\nexport { createKvRateLimiter } from './spam/rateLimiter'\nexport { resolveSpamConfig } from './spam/resolveSpam'\nexport type {\n\tCaptchaProvider,\n\tCaptchaVerifyArgs,\n\tIdentifyFn,\n\tRateLimitCheckArgs,\n\tRateLimitConfig,\n\tRateLimiter,\n\tRateLimitResult,\n\tSpamConfig,\n\tSpamMetadataConfig,\n\tSpamOption,\n} from './spam/types'\nexport type { CreatedSubmission, CreateSubmissionArgs } from './submissions/createSubmission'\nexport { createSubmission } from './submissions/createSubmission'\nexport type { VotedSubmission } from './submissions/resolveVotedSubmission'\nexport { resolveVotedSubmission } from './submissions/resolveVotedSubmission'\nexport { hasVotedCookie, votedCookieName } from './submissions/votedCookie'\nexport { captureFileRef } from './uploads/captureFileRef'\nexport { formatBytes } from './uploads/formatBytes'\nexport { resolveFileRef } from './uploads/resolveFileRef'\nexport type { FileFieldConfig, FileRef, FileRefError } from './uploads/types'\nexport { defaultValidationRules, defaultValidationRulesByType } from './validation/builtin'\nexport { defineValidationRule } from './validation/defineValidationRule'\nexport type { FieldTargetParamOptions } from './validation/fieldTargetParam'\nexport { fieldTargetParam } from './validation/fieldTargetParam'\nexport type {\n\tValidationRuleOption,\n\tValidationRuleRegistry,\n\tValidationRulesConfig,\n} from './validation/registry'\nexport type {\n\tAnyValidationRuleDefinition,\n\tValidationRuleDefinition,\n\tValidationRuleResult,\n} from './validation/types'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,cAAc,aAAuC;CACjE,MAAM;CACN,OAAO;CACP,SAAS,EAAE,QAAQ,SAAS,GAAG,cAAsB;EACpD,IAAI,QAAQ,aAAa,MACxB,OAAO;EAER,MAAM,kBAAkB,QAAQ,oBAAoB;EACpD,MAAM,UAAU,QAAQ,WAAW;EACnC,MAAM,cAAc,QAAQ,MAAM,WAAW,CAAC;EAC9C,MAAM,gBAAgB,QAAQ,MAAM,aAAa,CAAC;EAGlD,8BAA8B,aAAa;EAC3C,0BAA0B,WAAW;EAIrC,MAAM,cAAc;GACnB,SAAS,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;GACzC,WAAW,IAAI,IAAI,OAAO,KAAK,aAAa,CAAC;EAC9C;EAGA,MAAM,iBAAiB,OAAO,QAAQ,WAAW,EAAE,KAAK,CAAC,KAAK,aAAa;GAC1E;GACA,OAAO,OAAO;GACd,QAAQ,OAAO,OAAO,YAAY;GAClC,SAAS,OAAO,OAAO,mBAAmB;EAC3C,EAAE;EAIF,MAAM,kBACL,YAAY,QACT,KAAA,IACC,QAAQ,aAAa,8BAA8B,QAAQ,QAAQ,UAAU;EAClF,MAAM,iBAAiB,QAAQ,SAAS;EAgBxC,MAAM,WAAW,kBAXe,6BAC/B,iBACA,QAAQ,UAAU,QAClB,iBACA,aACA,cACD,EAAE,QACA,gBACC,YAAY,SAAS,WAAW,SAAS,YACzC,mBAAmB,KAAA,KAAa,WAAW,SAAS,UAEE,GAAG,QAAQ,MAAM;EAC1E,MAAM,eAAe,uBAAuB,wBAAwB,QAAQ,KAAK;EACjF,MAAM,gBAAgB,QAAQ,OAAO;EACrC,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,iBAAiB,eACtB,8BAA8B;GAC7B,UAAU;GACV,QAAQ,QAAQ,UAAU,cAAc,QAAQ,UAAU;GAC1D;GACA;GACA;GACA,oBAAoB,QAAQ,OAAO;GACnC,YAAY,QAAQ,OAAO;GAC3B,kBAAkB,QAAQ,OAAO;GACjC,QAAQ,QAAQ,OAAO;EACxB,CAAC,GACD,QAAQ,OACT;EAGA,6BAA6B,QAAQ,cAAc;EACnD,MAAM,OAAO,kBAAkB,QAAQ,IAAI;EAC3C,MAAM,qBAAqB,yBAAyB,QAAQ,MAAM,OAAO;EACzE,MAAM,mBAAmB,iBAAiB,QAAQ,MAAM,KAAK;EAM7D,OAAO,SAAS,uBAAuB,OAAO,QAAQ,kBAAkB;EACxE,OAAO,SAAS,eAAe,OAAO,QAAQ,gBAAgB;EAC9D,OAAO,SAAS,gBAAgB,OAAO,QAAQ,QAAQ;EACvD,IAAI,gBACH,OAAO,SAAS,oBAAoB,OAAO,QAAQ,cAAc;EAElE,IAAI,QAAQ,gBACX,OAAO,SAAS,oBAAoB,OAAO,QAAQ,QAAQ,cAAc;EAE1E,qBAAqB,QAAQ,QAAQ,YAAY;EACjD,oBAAoB;GACnB;GACA;GACA;GACA;GACA;GACA;GACA;GACA,iBAAiB,QAAQ,SAAS,YAAY;GAC9C,sBAAsB,QAAQ,SAAS;GACvC;GACA,UAAU,QAAQ;GAClB,eAAe,QAAQ,QAAQ,kBAAkB;GACjD,oBAAoB,QAAQ,UAAU;GACtC,QAAQ,QAAQ;GAChB;GACA;GACA,yBAAyB,QAAQ,2BAA2B;GAC5D;GACA,eAAe,QAAQ,SAAS;GAChC,aAAa,QAAQ,MAAM,gBAAgB;GAC3C;GACA;GACA,eAAe,QAAQ,MAAM;GAC7B,WAAW,QAAQ,MAAM,UAAU,QAAQ,QAAS,QAAQ,MAAM,SAAS,CAAC;GAC5E,SAAS,QAAQ;GACjB,UAAU,QAAQ;GAClB,UAAU,QAAQ;GAClB;GACA;GACA;GACA,uBAAuB,QAAQ;GAC/B,WAAW,QAAQ;EACpB,CAAC;EACD,OAAO;CACR;AACD,CAAC"}
package/dist/options.d.ts CHANGED
@@ -12,9 +12,11 @@ import { ButtonsOption } from "./collections/buttonFields.js";
12
12
  import { ConsentSnapshotMode } from "./consent/captureConsent.js";
13
13
  import { DepartmentEmailsResolver } from "./email/departments.js";
14
14
  import { RecipientsConfig } from "./actions/emailRecipients.js";
15
+ import { EmailRender } from "./actions/emailRender.js";
15
16
  import { CalcFunction, CalcSource } from "./calc/registry.js";
16
17
  import { ResponseOption } from "./collections/redirectFields.js";
17
18
  import { SettingsOption } from "./collections/settingsFields.js";
19
+ import { FormFallbackLocale } from "./form/findFormAtLocale.js";
18
20
  import { CollectionOverrides } from "./plugin/collectionOverrides.js";
19
21
  import { SpamOption } from "./spam/types.js";
20
22
  import { UploadsOption } from "./plugin/uploadsCollection.js";
@@ -45,7 +47,20 @@ type FormBuilderPluginOptions = {
45
47
  * Consent statements are unaffected either way: they live on the host's own
46
48
  * `consentSourcesField()`, which carries its own `localized` option.
47
49
  */
48
- localizeContent?: boolean; /** Add, override, or remove field types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
50
+ localizeContent?: boolean;
51
+ /**
52
+ * Chooses the fallback locale per form for the plugin's server-side reads of it (validating a
53
+ * submission, running its actions, serving poll results), for hosts where the right fallback
54
+ * depends on the form's owner rather than on the config, e.g. a tenant whose default locale is not
55
+ * the config-wide one, or to force one on a host with `localization.fallback: false`. It receives
56
+ * the form as read with the config's own fallback (depth 0, so a
57
+ * non-localized owner relationship such as `form.tenant` is on it) and returns a locale code, an
58
+ * ordered list of codes, `false` for no fallback, or `undefined` to keep the default. The form is
59
+ * read again only when the result differs from the fallback already applied. Absent, a form falls
60
+ * back like any Payload read. Render the form on your page with the same fallback so what the
61
+ * visitor sees matches what the server validates and sends. See {@link FormFallbackLocale}.
62
+ */
63
+ fallbackLocale?: FormFallbackLocale; /** Add, override, or remove field types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
49
64
  fields?: FieldTypesConfig; /** Add, override, or remove validation rule types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
50
65
  rules?: ValidationRulesConfig; /** Add, override, or remove post-submit action types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
51
66
  actions?: ActionsConfig;
@@ -84,8 +99,9 @@ type FormBuilderPluginOptions = {
84
99
  * back to `editor`. `converters` spread over the default
85
100
  * Lexical node converters; `serialize` replaces the whole action-body pipeline (e.g. to
86
101
  * target chat or plain-text channels instead of email HTML). A custom `serialize` receives
87
- * the submitted `form` (id/title) and `req`, enabling per-tenant lookups or handing the raw
88
- * body off to a renderer like react-email.
102
+ * the submitted `form` (id/title), `req`, the submission `locale`, and the rendering
103
+ * `actionType`, enabling per-tenant lookups or handing the raw body off to a renderer like
104
+ * react-email. To only wrap emails in a layout, use `email.render` instead.
89
105
  */
90
106
  richText?: RichTextBodyOption;
91
107
  /**
@@ -125,12 +141,29 @@ type FormBuilderPluginOptions = {
125
141
  fromSources?: FromAddressSourceRegistry;
126
142
  departments?: DepartmentEmailsResolver; /** Narrows the recipient fields' behavior (free-typed emails, field tokens). See {@link RecipientsConfig}. */
127
143
  recipients?: RecipientsConfig;
144
+ /**
145
+ * Localize the email actions' recipient lists (`to`, `cc`, `bcc`, `replyTo`), so each locale
146
+ * stores and routes to its own addresses or departments. Off by default: routing is usually the
147
+ * same in every locale, and with per-locale lists a locale the editor never filled in has no
148
+ * recipients unless it falls back (mind `localization.fallback: false`; see `fallbackLocale`).
149
+ * Ignored with `localizeContent: false`. Switching it on or off changes how existing
150
+ * forms store these fields, so it needs a data migration.
151
+ */
152
+ localizeRecipients?: boolean;
128
153
  /**
129
154
  * Server-resolved recipients, offered in every recipient field as their own option group and
130
155
  * resolved to addresses at send time. Each source's `value` is a namespaced string (so it cannot
131
156
  * collide with an address); its `resolve` receives the verified form context. See {@link RecipientSource}.
132
157
  */
133
158
  recipientSources?: RecipientSourceRegistry;
159
+ /**
160
+ * Produces the final `html` of every `emailTeam` and `confirmation` email from the already
161
+ * serialized body, e.g. to wrap it in a branded layout localized by the submission's `locale`
162
+ * and varied by `actionType`. Runs after `richText.serialize` (when set), so the two compose:
163
+ * `serialize` replaces how an action body is rendered for any channel, `render` only wraps
164
+ * email. Absent, the serialized body is sent as is. See {@link EmailRender}.
165
+ */
166
+ render?: EmailRender;
134
167
  };
135
168
  /**
136
169
  * Where the consent statements a form can reference come from. Absent (the default): no sources,
@@ -1,8 +1,8 @@
1
+ import { registerActionsTask } from "../actions/task.js";
1
2
  import { buildPollVotesCollection } from "../poll/votes/votesCollection.js";
3
+ import { buildSubmissionsCollection } from "../collections/formSubmissions.js";
2
4
  import { registerPollCloseTask } from "../poll/closeJob.js";
3
5
  import { buildFormsCollection } from "../collections/forms.js";
4
- import { buildSubmissionsCollection } from "../collections/formSubmissions.js";
5
- import { registerActionsTask } from "../actions/task.js";
6
6
  import { attachUploadsCollection } from "./uploadsCollection.js";
7
7
  //#region src/plugin/registerCollections.ts
8
8
  const registerCollections = ({ config, registry, ruleRegistry, calcAllowed, calcSources, calcFunctions, consentSources, consentSnapshot, consentResolveOnRead, actionRegistry, richText, hasJobsPlugin, dispatchDeadlineMs, events, uploads, spam, showSubmissionRawFields, localizeContent, resultsAccess, votedCookie, pollSourceRegistry, pollTypeRegistry, outcomeFields, pollVotes, buttons, settings, response, fromAddresses, fromSources, departments, redirectRelationships, overrides }) => {
@@ -1,6 +1,6 @@
1
1
  import { pollConfigOf } from "../form/pollState.js";
2
- import { aggregateFieldResponses } from "../aggregation/aggregateResponses.js";
3
2
  import { resolveEffectivePollOptions } from "./effectivePollOptions.js";
3
+ import { aggregateFieldResponses } from "../aggregation/aggregateResponses.js";
4
4
  import { pollTypesOf } from "./pollTypeRegistry.js";
5
5
  import { aggregateFromVotes } from "./votes/aggregateFromVotes.js";
6
6
  import { FORMS_SLUG } from "../collections/forms.js";
@@ -1,9 +1,9 @@
1
1
  import { pollConfigOf } from "../../form/pollState.js";
2
- import { aggregateFieldResponses } from "../../aggregation/aggregateResponses.js";
3
2
  import { POLL_VOTES_SLUG } from "./votesCollection.js";
4
- import { FORMS_SLUG } from "../../collections/forms.js";
5
3
  import { bumpPollVote } from "./bumpPollVote.js";
6
4
  import { transactionIDOf } from "./voteTallyHook.js";
5
+ import { aggregateFieldResponses } from "../../aggregation/aggregateResponses.js";
6
+ import { FORMS_SLUG } from "../../collections/forms.js";
7
7
  //#region src/poll/votes/recountPollVotes.ts
8
8
  const RECOUNT_MAX_SUBMISSIONS = 1e5;
9
9
  /**
@@ -1,9 +1,9 @@
1
1
  import { pollConfigOf } from "../../form/pollState.js";
2
- import "./votesCollection.js";
3
- import { FORMS_SLUG } from "../../collections/forms.js";
4
2
  import { formIdOf } from "../../submissions/formIdOf.js";
5
3
  import { answerValues } from "./answerValues.js";
4
+ import "./votesCollection.js";
6
5
  import { bumpPollVote } from "./bumpPollVote.js";
6
+ import { FORMS_SLUG } from "../../collections/forms.js";
7
7
  //#region src/poll/votes/voteTallyHook.ts
8
8
  const isComplete = (doc) => doc.status == null || doc.status === "complete";
9
9
  /** Narrow a request's loosely typed `transactionID` to the value `bumpPollVote` accepts. */
@@ -62,8 +62,16 @@ type FormProps = {
62
62
  */
63
63
  calcFunctions?: Record<string, (args: number[]) => number>;
64
64
  events?: FormEventSink;
65
- t?: RendererTranslate;
65
+ t?: RendererTranslate; /** The visitor's locale for renderer strings and value formatting, e.g. `en-US` (`'en'` when absent). */
66
66
  locale?: string;
67
+ /**
68
+ * The Payload content locale to submit under, one of the host's `localization` codes (`en`, `de`),
69
+ * sent as `?locale=` so the server stores it on the submission and the post-submit actions
70
+ * (confirmation emails included) render in it. A `<Poll>` also reads its results in it. Separate
71
+ * from `locale` because a formatting tag like `en-US` is usually not a content locale code.
72
+ * Absent, the submission takes the host's default locale.
73
+ */
74
+ submissionLocale?: string;
67
75
  layout?: boolean; /** Submit button label. Precedence: this prop, then the form's `buttons.submitLabel`, then the translated default. */
68
76
  submitLabel?: string; /** "Next" button label for multi-step forms. Precedence: this prop, then the form's `buttons.nextLabel`, then the translated default. */
69
77
  nextLabel?: string; /** "Back" button label for multi-step forms. Precedence: this prop, then the form's `buttons.prevLabel`, then the translated default. */
@@ -121,7 +129,8 @@ declare const Form: ({
121
129
  calcFunctions,
122
130
  events,
123
131
  t,
124
- locale,
132
+ locale: localeProp,
133
+ submissionLocale,
125
134
  layout,
126
135
  submitLabel,
127
136
  nextLabel,
@@ -2,9 +2,9 @@
2
2
  import { keys } from "../translations/keys.js";
3
3
  import { interpolate } from "../recall/interpolate.js";
4
4
  import { serializeBody } from "../actions/body/serializeBody.js";
5
- import { noopEventSink } from "../events/noopSink.js";
6
5
  import { fieldKey, isNamedField } from "../fields/fieldKey.js";
7
6
  import { calcExpressionOf, computeCalcFields } from "../calc/computeCalcFields.js";
7
+ import { noopEventSink } from "../events/noopSink.js";
8
8
  import { CAPTCHA_TOKEN_KEY, CONTEXT_KEY, HONEYPOT_VALUE_KEY } from "../spam/constants.js";
9
9
  import { evaluateCondition } from "../conditions/evaluate.js";
10
10
  import { resolveMessage } from "../validation/message.js";
@@ -61,7 +61,8 @@ const focusFirstIn = (root, selector) => {
61
61
  /** A stored button label counts only when it is a non-empty string; anything else falls through. */
62
62
  const storedLabel = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
63
63
  /** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */
64
- const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, converters, successBehavior = "replace", calcFunctions, events, t, locale = "en", layout, submitLabel, nextLabel, prevLabel, closeLabel, successMessage, presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, context, children, header, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName, adapters }) => {
64
+ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, converters, successBehavior = "replace", calcFunctions, events, t, locale: localeProp, submissionLocale, layout, submitLabel, nextLabel, prevLabel, closeLabel, successMessage, presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, context, children, header, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName, adapters }) => {
65
+ const locale = localeProp ?? "en";
65
66
  const honeypotName = honeypot === false ? null : honeypot?.name ?? "website";
66
67
  const honeypotRef = useRef(null);
67
68
  const registry = useMemo(() => buildFieldTypeRegistry(fieldTypes), [fieldTypes]);
@@ -438,12 +439,13 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
438
439
  field: CONTEXT_KEY,
439
440
  value: context
440
441
  });
441
- const result = onSubmit ? await onSubmit({
442
- formId: form.id,
443
- values
444
- }) : await submitForm({
442
+ const input = {
445
443
  formId: form.id,
446
444
  values,
445
+ ...submissionLocale ? { locale: submissionLocale } : {}
446
+ };
447
+ const result = onSubmit ? await onSubmit(input) : await submitForm({
448
+ ...input,
447
449
  apiRoute
448
450
  });
449
451
  submittingRef.current = false;