@10x-media/form-builder 0.1.0-beta.5 → 0.1.0-beta.7

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 (213) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/dist/actions/buildActionBlocks.js +12 -9
  3. package/dist/actions/buildActionBlocks.js.map +1 -1
  4. package/dist/actions/builtin/confirmation.js +27 -79
  5. package/dist/actions/builtin/confirmation.js.map +1 -1
  6. package/dist/actions/builtin/emailAction.d.ts +17 -0
  7. package/dist/actions/builtin/emailAction.js +83 -0
  8. package/dist/actions/builtin/emailAction.js.map +1 -0
  9. package/dist/actions/builtin/emailTeam.js +14 -84
  10. package/dist/actions/builtin/emailTeam.js.map +1 -1
  11. package/dist/actions/builtin/index.d.ts +9 -11
  12. package/dist/actions/builtin/index.js +11 -11
  13. package/dist/actions/builtin/index.js.map +1 -1
  14. package/dist/actions/defineAction.d.ts +2 -2
  15. package/dist/actions/defineAction.js.map +1 -1
  16. package/dist/actions/emailRecipients.d.ts +12 -0
  17. package/dist/actions/emailRecipients.js +116 -0
  18. package/dist/actions/emailRecipients.js.map +1 -0
  19. package/dist/actions/registry.d.ts +2 -1
  20. package/dist/actions/registry.js +2 -6
  21. package/dist/actions/registry.js.map +1 -1
  22. package/dist/actions/task.js +5 -3
  23. package/dist/actions/task.js.map +1 -1
  24. package/dist/calc/evaluate.js +1 -2
  25. package/dist/calc/evaluate.js.map +1 -1
  26. package/dist/calc/normalizeCalc.js +1 -2
  27. package/dist/calc/normalizeCalc.js.map +1 -1
  28. package/dist/calc/types.js.map +1 -1
  29. package/dist/client/ConsentBlockLabel.d.ts +15 -0
  30. package/dist/client/ConsentBlockLabel.js +71 -0
  31. package/dist/client/ConsentBlockLabel.js.map +1 -0
  32. package/dist/client/ConsentSourceRowLabel.d.ts +9 -0
  33. package/dist/client/ConsentSourceRowLabel.js +21 -0
  34. package/dist/client/ConsentSourceRowLabel.js.map +1 -0
  35. package/dist/client/EndpointOptionsSelect.d.ts +1 -0
  36. package/dist/client/EndpointOptionsSelect.js +4 -1
  37. package/dist/client/EndpointOptionsSelect.js.map +1 -1
  38. package/dist/client/FieldNameSelect.d.ts +1 -0
  39. package/dist/client/FieldNameSelect.js +4 -1
  40. package/dist/client/FieldNameSelect.js.map +1 -1
  41. package/dist/client/RecipientsSelect.d.ts +31 -0
  42. package/dist/client/RecipientsSelect.js +174 -0
  43. package/dist/client/RecipientsSelect.js.map +1 -0
  44. package/dist/client/admin.css +5 -0
  45. package/dist/collections/buttonFields.js +1 -1
  46. package/dist/collections/formSubmissions.js +4 -2
  47. package/dist/collections/formSubmissions.js.map +1 -1
  48. package/dist/collections/forms.js +46 -99
  49. package/dist/collections/forms.js.map +1 -1
  50. package/dist/collections/formsEndpoints.js +101 -0
  51. package/dist/collections/formsEndpoints.js.map +1 -0
  52. package/dist/conditions/evaluate.js +1 -1
  53. package/dist/conditions/evaluate.js.map +1 -1
  54. package/dist/conditions/fieldTypes.d.ts +3 -1
  55. package/dist/conditions/fieldTypes.js +4 -1
  56. package/dist/conditions/fieldTypes.js.map +1 -1
  57. package/dist/conditions/normalizeConditions.js +18 -5
  58. package/dist/conditions/normalizeConditions.js.map +1 -1
  59. package/dist/consent/captureConsent.d.ts +21 -5
  60. package/dist/consent/captureConsent.js +31 -7
  61. package/dist/consent/captureConsent.js.map +1 -1
  62. package/dist/consent/consentSourcesField.d.ts +8 -0
  63. package/dist/consent/consentSourcesField.js +13 -2
  64. package/dist/consent/consentSourcesField.js.map +1 -1
  65. package/dist/consent/resolveConsentEntries.d.ts +1 -1
  66. package/dist/consent/resolveConsentEntries.js +1 -4
  67. package/dist/consent/resolveConsentEntries.js.map +1 -1
  68. package/dist/consent/resolveConsentSourcesRequest.js +0 -1
  69. package/dist/consent/resolveConsentSourcesRequest.js.map +1 -1
  70. package/dist/consent/resolveConsentStatements.d.ts +1 -1
  71. package/dist/consent/resolveConsentStatements.js.map +1 -1
  72. package/dist/consent/types.d.ts +13 -1
  73. package/dist/email/departments.d.ts +13 -9
  74. package/dist/email/departments.js +7 -50
  75. package/dist/email/departments.js.map +1 -1
  76. package/dist/email/departmentsField.d.ts +4 -0
  77. package/dist/email/departmentsField.js +5 -1
  78. package/dist/email/departmentsField.js.map +1 -1
  79. package/dist/exports/client.d.ts +4 -1
  80. package/dist/exports/client.js +4 -1
  81. package/dist/exports/react.d.ts +3 -2
  82. package/dist/exports/react.js +4 -3
  83. package/dist/exports/rsc.js +1 -1
  84. package/dist/exports/types.d.ts +1 -1
  85. package/dist/fields/buildFieldBlocks.js +4 -4
  86. package/dist/fields/buildFieldBlocks.js.map +1 -1
  87. package/dist/fields/builtin/consent.js +6 -1
  88. package/dist/fields/builtin/consent.js.map +1 -1
  89. package/dist/fields/builtin/email.js +1 -1
  90. package/dist/fields/builtin/email.js.map +1 -1
  91. package/dist/fields/builtin/file.js +9 -3
  92. package/dist/fields/builtin/file.js.map +1 -1
  93. package/dist/fields/builtin/index.d.ts +1 -1
  94. package/dist/fields/builtin/index.js +3 -3
  95. package/dist/fields/builtin/index.js.map +1 -1
  96. package/dist/fields/builtin/message.js +1 -1
  97. package/dist/fields/builtin/repeater.js +1 -1
  98. package/dist/fields/builtin/select.js +1 -1
  99. package/dist/fields/registry.d.ts +2 -1
  100. package/dist/fields/registry.js +2 -6
  101. package/dist/fields/registry.js.map +1 -1
  102. package/dist/fields/sharedConfig.js +1 -1
  103. package/dist/fields/types.d.ts +9 -2
  104. package/dist/flow/normalizeFlow.js +7 -3
  105. package/dist/flow/normalizeFlow.js.map +1 -1
  106. package/dist/form/toFormDocument.d.ts +20 -3
  107. package/dist/form/toFormDocument.js +16 -4
  108. package/dist/form/toFormDocument.js.map +1 -1
  109. package/dist/form/types.d.ts +5 -5
  110. package/dist/index.d.ts +5 -193
  111. package/dist/index.js +18 -8
  112. package/dist/index.js.map +1 -1
  113. package/dist/options.d.ts +228 -0
  114. package/dist/plugin/applyRegistryConfig.d.ts +11 -0
  115. package/dist/plugin/applyRegistryConfig.js +20 -0
  116. package/dist/plugin/applyRegistryConfig.js.map +1 -0
  117. package/dist/plugin/registerCollections.js +3 -1
  118. package/dist/plugin/registerCollections.js.map +1 -1
  119. package/dist/plugin/uploadsCollection.d.ts +3 -0
  120. package/dist/plugin/uploadsCollection.js +10 -1
  121. package/dist/plugin/uploadsCollection.js.map +1 -1
  122. package/dist/poll/buildPollOptionSourceFields.js +2 -2
  123. package/dist/poll/buildPollOptionSourceFields.js.map +1 -1
  124. package/dist/poll/definePollOptionSource.d.ts +2 -2
  125. package/dist/poll/definePollOptionSource.js.map +1 -1
  126. package/dist/poll/pollTypeRegistry.js +2 -4
  127. package/dist/poll/pollTypeRegistry.js.map +1 -1
  128. package/dist/poll/registry.d.ts +2 -1
  129. package/dist/poll/registry.js +2 -6
  130. package/dist/poll/registry.js.map +1 -1
  131. package/dist/poll/resolvePollOptionsRequest.js +0 -1
  132. package/dist/poll/resolvePollOptionsRequest.js.map +1 -1
  133. package/dist/prefill/valuesFromSearchParams.js +2 -7
  134. package/dist/prefill/valuesFromSearchParams.js.map +1 -1
  135. package/dist/react/Form.d.ts +3 -1
  136. package/dist/react/Form.js +93 -155
  137. package/dist/react/Form.js.map +1 -1
  138. package/dist/react/FormContext.d.ts +5 -1
  139. package/dist/react/FormContext.js.map +1 -1
  140. package/dist/react/FormFields.d.ts +18 -0
  141. package/dist/react/FormFields.js +114 -0
  142. package/dist/react/FormFields.js.map +1 -0
  143. package/dist/react/FormLayout.d.ts +4 -2
  144. package/dist/react/FormLayout.js +3 -2
  145. package/dist/react/FormLayout.js.map +1 -1
  146. package/dist/react/Poll.js +13 -3
  147. package/dist/react/Poll.js.map +1 -1
  148. package/dist/react/presentation/DialogSurface.js +1 -4
  149. package/dist/react/presentation/DialogSurface.js.map +1 -1
  150. package/dist/react/renderers/calculation.js +1 -3
  151. package/dist/react/renderers/calculation.js.map +1 -1
  152. package/dist/react/renderers/checkbox.js +1 -3
  153. package/dist/react/renderers/checkbox.js.map +1 -1
  154. package/dist/react/renderers/consent.js +2 -3
  155. package/dist/react/renderers/consent.js.map +1 -1
  156. package/dist/react/renderers/date.js +1 -3
  157. package/dist/react/renderers/date.js.map +1 -1
  158. package/dist/react/renderers/email.js +1 -3
  159. package/dist/react/renderers/email.js.map +1 -1
  160. package/dist/react/renderers/file.js +6 -7
  161. package/dist/react/renderers/file.js.map +1 -1
  162. package/dist/react/renderers/number.js +1 -3
  163. package/dist/react/renderers/number.js.map +1 -1
  164. package/dist/react/renderers/region.js +1 -3
  165. package/dist/react/renderers/region.js.map +1 -1
  166. package/dist/react/renderers/repeater.js +20 -4
  167. package/dist/react/renderers/repeater.js.map +1 -1
  168. package/dist/react/renderers/select.js +1 -3
  169. package/dist/react/renderers/select.js.map +1 -1
  170. package/dist/react/renderers/text.js +1 -3
  171. package/dist/react/renderers/text.js.map +1 -1
  172. package/dist/react/renderers/textarea.js +1 -3
  173. package/dist/react/renderers/textarea.js.map +1 -1
  174. package/dist/spam/constants.js +7 -1
  175. package/dist/spam/constants.js.map +1 -1
  176. package/dist/spam/resolveSpam.js +2 -1
  177. package/dist/spam/resolveSpam.js.map +1 -1
  178. package/dist/spam/spamGuard.js +22 -6
  179. package/dist/spam/spamGuard.js.map +1 -1
  180. package/dist/spam/types.d.ts +17 -2
  181. package/dist/submissions/SubmissionAnswers.js.map +1 -1
  182. package/dist/submissions/SubmissionAnswersClient.js +33 -26
  183. package/dist/submissions/SubmissionAnswersClient.js.map +1 -1
  184. package/dist/submissions/coerceBoolean.js +14 -0
  185. package/dist/submissions/coerceBoolean.js.map +1 -0
  186. package/dist/submissions/runSubmission.js +9 -8
  187. package/dist/submissions/runSubmission.js.map +1 -1
  188. package/dist/submissions/validateSubmission.js +4 -2
  189. package/dist/submissions/validateSubmission.js.map +1 -1
  190. package/dist/translations/de.js +14 -2
  191. package/dist/translations/de.js.map +1 -1
  192. package/dist/translations/en.js +14 -2
  193. package/dist/translations/en.js.map +1 -1
  194. package/dist/translations/keys.d.ts +14 -2
  195. package/dist/translations/keys.js +14 -2
  196. package/dist/translations/keys.js.map +1 -1
  197. package/dist/translations/server.js +8 -1
  198. package/dist/translations/server.js.map +1 -1
  199. package/dist/uploads/captureFileRef.d.ts +10 -3
  200. package/dist/uploads/captureFileRef.js +18 -9
  201. package/dist/uploads/captureFileRef.js.map +1 -1
  202. package/dist/uploads/resolveFileRef.js +2 -1
  203. package/dist/uploads/resolveFileRef.js.map +1 -1
  204. package/dist/validation/builtin/email.js +1 -1
  205. package/dist/validation/builtin/email.js.map +1 -1
  206. package/dist/validation/builtin/notAlreadySubmitted.js +1 -0
  207. package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
  208. package/dist/validation/emailPattern.js +10 -0
  209. package/dist/validation/emailPattern.js.map +1 -0
  210. package/dist/validation/registry.d.ts +2 -1
  211. package/dist/validation/registry.js +2 -9
  212. package/dist/validation/registry.js.map +1 -1
  213. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -5,17 +5,18 @@ import { defaultBodyConverters, sanitizeUrl } from "./actions/body/converters.js
5
5
  import { renderAllValues, renderAllValuesTable } from "./actions/body/wildcards.js";
6
6
  import { serializeBody } from "./actions/body/serializeBody.js";
7
7
  import { defineAction } from "./actions/defineAction.js";
8
- import { resolveDepartmentOptions } from "./email/departments.js";
9
8
  import { SIGNATURE_HEADER, signPayload } from "./actions/sign.js";
10
9
  import { buildDefaultActionDefinitions, defaultActionDefinitions } from "./actions/builtin/index.js";
11
10
  import { resolveActions } from "./actions/registry.js";
12
11
  import { resolveConsentEntries, stashConsentSources } from "./consent/resolveConsentEntries.js";
13
12
  import { normalizeCalc } from "./calc/normalizeCalc.js";
14
13
  import { defineFormField } from "./fields/defineFormField.js";
14
+ import { resolveFileRef } from "./uploads/resolveFileRef.js";
15
15
  import { fileMimeTypeOptions } from "./fields/builtin/file.js";
16
16
  import { buildDefaultFieldDefinitions, defaultFieldDefinitions, defaultFieldDefinitionsByType } from "./fields/builtin/index.js";
17
17
  import { resolveFieldTypes, stashFieldTypes } from "./fields/registry.js";
18
18
  import { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from "./spam/constants.js";
19
+ import { resolveConsentStatements } from "./consent/resolveConsentStatements.js";
19
20
  import { isPollClosed } from "./form/pollState.js";
20
21
  import { fieldKey } from "./fields/fieldKey.js";
21
22
  import { aggregateRowForField, aggregateRowsForFields } from "./aggregation/aggregateRows.js";
@@ -27,18 +28,20 @@ import { manualStrategy, pollTypesOf, resolvePollTypes, sourceStrategy, stashPol
27
28
  import { resolvePollOutcome } from "./poll/resolvePollOutcome.js";
28
29
  import { POLL_CLOSE_TASK_SLUG, buildPollCloseTask, enqueuePollClose, registerPollCloseTask, runPollClose, shouldAutoResolvePoll } from "./poll/closeJob.js";
29
30
  import { resolveEffectivePollOptions } from "./poll/effectivePollOptions.js";
30
- import { resolveFormResultsRequest } from "./aggregation/resolveResultsRequest.js";
31
- import { resolveConsentSourcesRequest } from "./consent/resolveConsentSourcesRequest.js";
32
31
  import { buildDefaultOutcomeFields, buildResolvedAtField, buildWinningValuesField } from "./poll/outcomeFields.js";
33
32
  import { buildDefaultButtonFields, buildNextLabelField, buildPrevLabelField, buildSubmitLabelField } from "./collections/buttonFields.js";
33
+ import { resolveFormResultsRequest } from "./aggregation/resolveResultsRequest.js";
34
+ import { resolveConsentSourcesRequest } from "./consent/resolveConsentSourcesRequest.js";
35
+ import { resolveDepartmentOptions } from "./email/departments.js";
34
36
  import { evaluateCalc } from "./calc/evaluate.js";
35
37
  import { calcExpressionOf, computeCalcFields } from "./calc/computeCalcFields.js";
36
38
  import { evaluateCondition } from "./conditions/evaluate.js";
39
+ import { textOfBody } from "./actions/body/textOfBody.js";
37
40
  import { resolvePublishedVersionRef } from "./consent/resolvePublishedVersionRef.js";
38
41
  import { captureConsent } from "./consent/captureConsent.js";
39
- import { resolveFileRef } from "./uploads/resolveFileRef.js";
40
42
  import { captureFileRef } from "./uploads/captureFileRef.js";
41
43
  import { hasVotedCookie, votedCookieName } from "./submissions/votedCookie.js";
44
+ import { readUploadCollectionMimeTypes } from "./plugin/uploadsCollection.js";
42
45
  import { registerCollections } from "./plugin/registerCollections.js";
43
46
  import { registerTranslations } from "./plugin/registerTranslations.js";
44
47
  import { resolvePollOptionSources } from "./poll/registry.js";
@@ -49,10 +52,8 @@ import { defineValidationRule } from "./validation/defineValidationRule.js";
49
52
  import { fieldTargetParam } from "./validation/fieldTargetParam.js";
50
53
  import { defaultValidationRules, defaultValidationRulesByType } from "./validation/builtin/index.js";
51
54
  import { resolveValidationRules } from "./validation/registry.js";
52
- import { textOfBody } from "./actions/body/textOfBody.js";
53
55
  import { applyConsentStatements } from "./consent/applyConsentStatements.js";
54
56
  import { consentSourcesField } from "./consent/consentSourcesField.js";
55
- import { resolveConsentStatements } from "./consent/resolveConsentStatements.js";
56
57
  import { departmentsField } from "./email/departmentsField.js";
57
58
  import { countryField } from "./fields/builtin/country.js";
58
59
  import { stateField } from "./fields/builtin/state.js";
@@ -76,12 +77,19 @@ const formBuilder = definePlugin({
76
77
  if (options.disabled === true) return config;
77
78
  const localizeContent = options.localizeContent !== false;
78
79
  const uploads = options.uploads ?? false;
80
+ const uploadMimeTypes = uploads === false ? void 0 : uploads.mimeTypes ?? readUploadCollectionMimeTypes(config, uploads.collection);
79
81
  const consentSources = options.consent?.sources;
80
- const registry = resolveFieldTypes(buildDefaultFieldDefinitions(localizeContent, options.richText?.editor).filter((definition) => (uploads !== false || definition.type !== "file") && (consentSources !== void 0 || definition.type !== "consent")), options.fields);
82
+ const registry = resolveFieldTypes(buildDefaultFieldDefinitions(localizeContent, options.richText?.editor, uploadMimeTypes).filter((definition) => (uploads !== false || definition.type !== "file") && (consentSources !== void 0 || definition.type !== "consent")), options.fields);
81
83
  const ruleRegistry = resolveValidationRules(defaultValidationRules, options.rules);
82
84
  const fromAddresses = options.email?.fromAddresses;
83
85
  const departments = options.email?.departments;
84
- const actionRegistry = resolveActions(buildDefaultActionDefinitions(localizeContent, options.richText?.bodyEditor ?? options.richText?.editor, fromAddresses, departments), options.actions);
86
+ const actionRegistry = resolveActions(buildDefaultActionDefinitions({
87
+ localize: localizeContent,
88
+ editor: options.richText?.bodyEditor ?? options.richText?.editor,
89
+ fromAddresses,
90
+ departments,
91
+ recipients: options.email?.recipients
92
+ }), options.actions);
85
93
  const spam = resolveSpamConfig(options.spam);
86
94
  const pollSourceRegistry = resolvePollOptionSources(options.poll?.sources);
87
95
  const pollTypeRegistry = resolvePollTypes(options.poll?.types);
@@ -95,6 +103,8 @@ const formBuilder = definePlugin({
95
103
  registry,
96
104
  ruleRegistry,
97
105
  consentSources,
106
+ consentSnapshot: options.consent?.snapshot ?? "both",
107
+ consentResolveOnRead: options.consent?.resolveOnRead,
98
108
  actionRegistry,
99
109
  richText: options.richText,
100
110
  hasJobsPlugin: Boolean(plugins["@10x-media/jobs"]),
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type CollectionSlug, type Config, definePlugin } from 'payload'\nimport type { RichTextBodyOption } from './actions/body/serializeBody'\nimport { buildDefaultActionDefinitions } from './actions/builtin'\nimport type { FromAddressesResolver } from './actions/fromAddresses'\nimport type { ActionsConfig } from './actions/registry'\nimport { resolveActions } from './actions/registry'\nimport type { FormResultsAccess } from './aggregation/resolveResultsRequest'\nimport type { ButtonsOption } from './collections/buttonFields'\nimport type { ResponseOption } from './collections/redirectFields'\nimport { stashConsentSources } from './consent/resolveConsentEntries'\nimport type { ConsentSourcesResolver } from './consent/types'\nimport type { DepartmentEmailsResolver } from './email/departments'\nimport type { FormEventSink } from './events/types'\nimport { buildDefaultFieldDefinitions } from './fields/builtin'\nimport { type FieldTypesConfig, resolveFieldTypes, stashFieldTypes } from './fields/registry'\nimport type { CollectionOverrides } from './plugin/collectionOverrides'\nimport { registerCollections } from './plugin/registerCollections'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport type { UploadsOption } from './plugin/uploadsCollection'\nimport type { OutcomeFieldsOverride } from './poll/outcomeFields'\nimport type { PollTypesConfig } from './poll/pollTypeRegistry'\nimport { resolvePollTypes, stashPollTypes } from './poll/pollTypeRegistry'\nimport type { PollOptionSourcesConfig } from './poll/registry'\nimport { resolvePollOptionSources } from './poll/registry'\nimport { stashPollOptionSources } from './poll/resolvePollOptions'\nimport { resolveSpamConfig } from './spam/resolveSpam'\nimport type { SpamOption } from './spam/types'\nimport type { TranslationsOption } from './translations'\nimport { defaultValidationRules } from './validation/builtin'\nimport { resolveValidationRules, type ValidationRulesConfig } from './validation/registry'\n\nexport type FormBuilderPluginOptions = {\n\tdisabled?: boolean\n\t/**\n\t * Per-locale overrides for this plugin's UI strings, keyed by the typed\n\t * translation keys exported from `@10x-media/form-builder/i18n`. Values win\n\t * over the built-in locales key-by-key; locales the plugin does not ship are\n\t * added whole. App-level `i18n.translations` still wins over both.\n\t */\n\ttranslations?: TranslationsOption\n\t/** Pluggable sink for form lifecycle events. Defaults to a no-op; analytics adapters or a future analytics plugin subscribe here. */\n\tevents?: FormEventSink\n\t/**\n\t * Content-bearing author fields (labels, placeholders, option labels, action subjects and\n\t * bodies) are localized by default. Payload strips the `localized` flag on hosts without\n\t * `localization` configured, so the default is safe everywhere. Set `false` to keep form\n\t * content single-locale even on localized hosts. Spread-overrides of the prebuilt default\n\t * exports (`defaultFieldDefinitionsByType`, `defaultActionDefinitions`) carry `localized`\n\t * flags from the default-true set; when opting out, derive overrides from\n\t * `buildDefaultFieldDefinitions(false)` / `buildDefaultActionDefinitions(false)` instead.\n\t * Consent statements are unaffected either way: they live on the host's own\n\t * `consentSourcesField()`, which carries its own `localized` option.\n\t */\n\tlocalizeContent?: boolean\n\t/** Add, override, or remove field types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */\n\tfields?: FieldTypesConfig\n\t/** Add, override, or remove validation rule types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */\n\trules?: ValidationRulesConfig\n\t/** Add, override, or remove post-submit action types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */\n\tactions?: ActionsConfig\n\t/**\n\t * Customize how the plugin's rich text is authored and rendered. `editor` is the default\n\t * Lexical/richText editor for every plugin richText field (message content, consent\n\t * statement, response message, action bodies); `bodyEditor` overrides the action body\n\t * fields specifically, falling back to `editor`. `converters` spread over the default\n\t * Lexical node converters; `serialize` replaces the whole action-body pipeline (e.g. to\n\t * target chat or plain-text channels instead of email HTML). A custom `serialize` receives\n\t * the submitted `form` (id/title) and `req`, enabling per-tenant lookups or handing the raw\n\t * body off to a renderer like react-email.\n\t */\n\trichText?: RichTextBodyOption\n\t/**\n\t * Email routing for the `emailTeam` and `confirmation` actions. Both sub-options are opt-in by the\n\t * presence of a resolver (not a `false`/`true`/object flag) and share a shape: a `req`-scoped\n\t * resolver, evaluated per request via an endpoint, whose choice is validated at save time only and\n\t * never re-checked when the action sends (the config is admin-authored, not visitor-controlled).\n\t *\n\t * `fromAddresses` gives both actions a `from` select whose options come from the resolver; absent,\n\t * neither action has a `from` field and every send uses the email adapter's default sender. The\n\t * intended use is multi-tenant hosts where each tenant may only send from particular addresses\n\t * (derive the tenant from `req`, return its allowed senders). Values are the literal string\n\t * `payload.sendEmail` accepts as `from` (e.g. `'Name <addr@x.com>'` or a plain address).\n\t *\n\t * `departments` turns the `emailTeam` `to` into a select whose options come from the resolver\n\t * (a `/:id/departments` endpoint); the intended use is to place `departmentsField()` on a document\n\t * you own and read it back with `resolveDepartmentOptions`, which resolves each department's address\n\t * for the requesting locale. Because `to` is localized, each admin locale stores its own resolved\n\t * address and a submission's locale selects the address it routes to (Payload-native, no per-send\n\t * lookup). Storing the resolved address, rather than a department id resolved live at send (consent's\n\t * model), is deliberate: the routing target a form was saved with stays audit-stable even if the\n\t * resolver's data later changes, so do not \"fix\" it into a live lookup. Multi-tenant hosts scope\n\t * which document they read by the tenant derived from `req`; absent, `to` stays a plain localized\n\t * text field.\n\t */\n\temail?: { fromAddresses?: FromAddressesResolver; departments?: DepartmentEmailsResolver }\n\t/**\n\t * Where the consent statements a form can reference come from. Absent (the default): no sources,\n\t * so the built-in `consent` field type is not registered at all and authors cannot add a consent\n\t * field with nothing to reference (a developer-registered custom `consent` type via `fields`\n\t * still wins).\n\t *\n\t * `sources` is an async `req`-scoped resolver returning the sources available to this request.\n\t * The intended shape: place `consentSourcesField()` on a collection or global you own and read it\n\t * back here. Multi-tenant hosts scope that read by the tenant derived from `req`, so a tenant's\n\t * authors only ever see, and their visitors only ever agree to, their own statements. A form\n\t * stores only a source's row `id`; the statement is resolved live per request (see\n\t * `resolveConsentStatements`) and the proof is rebuilt from the source at submit, so neither is\n\t * ever a copy the client could stale or forge.\n\t */\n\tconsent?: { sources: ConsentSourcesResolver }\n\t/**\n\t * Form-level button labels: `submitLabel` at the bottom of the Fields tab, `prevLabel` and\n\t * `nextLabel` in a row at the bottom of the Flow tab (shown once the flow has a step). The\n\t * rendered chrome resolves each label as `<Form>` prop, then the stored value, then the\n\t * translated default. `fields` composes them: it receives the three default fields as a\n\t * `{ submit, prev, next }` map with the content localization flag already applied and returns\n\t * the map, so wrapping a default in a row with a host field (e.g. an icon select) or replacing\n\t * one is explicit. `FormDocument.buttons` reassembles only those three known labels, so a\n\t * host-added sibling field is still stored but is read off the raw document, not `doc.buttons`.\n\t */\n\tbuttons?: ButtonsOption\n\t/**\n\t * The success-response group. `redirect.fields` composes the fields inside the `response.redirect`\n\t * group, mirroring `buttons.fields`: it receives the default fields (the `url` text field, plus the\n\t * polymorphic `reference` relationship when `redirectRelationships` is set) and returns the group's\n\t * final field array, so a host can prepend a custom link field (the pattern most projects already\n\t * have), swap `url` for their own picker, reorder, or filter. Omit it and the group stays the\n\t * built-in `url` (+ optional `reference`), unchanged.\n\t *\n\t * The plugin's built-in redirect handling reads `redirect.url` (and `redirect.reference` when\n\t * configured); `toFormDocument` passes the whole `response` group through, so a host that replaces\n\t * `url` with their own link field owns resolving it to a destination in their frontend, the same as\n\t * the internal-reference case.\n\t */\n\tresponse?: ResponseOption\n\t/**\n\t * Collections whose documents `response.redirect` can reference (`response.redirect.reference`),\n\t * letting an author redirect a visitor to an internal document instead of a URL after a\n\t * successful submit. Absent (the default): no `reference` field exists at all, matching today's\n\t * URL-only redirect. Always polymorphic, even for a single slug (a `CollectionSlug[]` array, not\n\t * a bare `CollectionSlug`), so a host adding a second collection later never changes the stored\n\t * shape, the same precedent as `consentSourcesField()`'s `page` picker. The plugin never resolves\n\t * the reference to a URL itself (it has no notion of the host's routing); `toFormDocument` passes\n\t * the raw `{ relationTo, value }` pair through on `FormDocument.response.redirect.reference` for\n\t * the host to resolve. Mirrors the `redirectRelationships` option of Payload's native\n\t * `plugin-form-builder`.\n\t */\n\tredirectRelationships?: CollectionSlug[]\n\t/**\n\t * File uploads are bring-your-own. Default `false`: no upload collection is involved and the\n\t * built-in `file` field type is removed from the registry, so form authors cannot add a field\n\t * with nowhere to land (a developer-registered custom `file` type via `fields` still wins).\n\t * `{ collection: 'slug' }` points at a host-owned upload collection (created by the app with\n\t * its storage adapter); the plugin validates it at boot, appends its hidden `owner` field when\n\t * absent, and prepends the spam upload hooks.\n\t */\n\tuploads?: UploadsOption\n\t/** Honeypot + rate-limiting (on by default) + a captcha adapter seam + upload-ownership scoping. `false` disables the whole subsystem. */\n\tspam?: SpamOption\n\t/**\n\t * Aggregate-results endpoint options. `access` gates anonymous reads after the form is loaded\n\t * and before anything is served; absent keeps the plugin-default gating (poll opt-in +\n\t * visibility + enumerable-field guard). Multi-tenant hosts should compare `form.tenant` against\n\t * the tenant derived from `req` so one tenant's poll counts are never readable under another\n\t * tenant's id. Authenticated callers bypass this seam.\n\t */\n\tresults?: { access?: FormResultsAccess }\n\t/**\n\t * Poll behavior. `votedCookie: true` sets an httpOnly `fb-voted-{formId}=1` cookie on each\n\t * successful submission to a poll-enabled form, letting SSR hosts read the voted state via\n\t * `hasVotedCookie` and pass it to `<Poll hasVoted>`. Default `false`.\n\t * `sources` registers poll option sources (`definePollOptionSource`), letting authors populate\n\t * a poll's choices from host domain data with stable values; there are no built-ins. With at\n\t * least one source registered, the forms poll group gains an `optionSource` select plus its\n\t * per-source `sourceConfig`, and submissions to a sourced poll only accept resolved values.\n\t * `outcomeFields` composes the poll `outcome` group: it receives the two default fields\n\t * (`winningValues`, `resolvedAt`) and returns the group's final field array verbatim, so a host\n\t * can swap `winningValues` for its own component (e.g. a relationship picker over the voteable\n\t * records). Membership validation runs server-side regardless, so a swap cannot bypass it.\n\t * `types` registers outcome strategies (`definePollType`) shown in the poll `type` select. The\n\t * built-ins `manual` (default, hand-picked), `mostVoted` (auto-resolves to the top choice(s) on\n\t * close), and `source` (delegates to the option source) are always registered; a host entry keyed\n\t * by (or carrying) a built-in slug replaces it. A closed poll whose strategy is not `manual`\n\t * auto-resolves via a scheduled job when a runner is present, or on the next results read otherwise.\n\t */\n\tpoll?: {\n\t\tvotedCookie?: boolean\n\t\tsources?: PollOptionSourcesConfig\n\t\ttypes?: PollTypesConfig\n\t\toutcomeFields?: OutcomeFieldsOverride\n\t}\n\t/**\n\t * When `true`, the raw `values`, `descriptors`, and `consent` JSON fields are visible in the\n\t * submission admin view. Default `false`, because the `SubmissionAnswers` UI component already\n\t * represents those fields fully and they are noisy shown alongside it.\n\t */\n\tshowSubmissionRawFields?: boolean\n\t/**\n\t * Override individual plugin-managed collections using explicit spreads. Each key accepts a\n\t * `CollectionOverrides` object: top-level keys are spread with the plugin's defaults (spread\n\t * order per key determines who wins), hooks are appended after the plugin's own hooks, and\n\t * `fields` is a function that receives the default fields and returns the final array so\n\t * additions/removals are always intentional.\n\t */\n\toverrides?: {\n\t\tforms?: CollectionOverrides\n\t\tformSubmissions?: CollectionOverrides\n\t}\n}\n\ndeclare module 'payload' {\n\tinterface RegisteredPlugins {\n\t\t'@10x-media/form-builder': FormBuilderPluginOptions\n\t}\n}\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 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).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 departments = options.email?.departments\n\t\tconst actionRegistry = resolveActions(\n\t\t\tbuildDefaultActionDefinitions(\n\t\t\t\tlocalizeContent,\n\t\t\t\toptions.richText?.bodyEditor ?? options.richText?.editor,\n\t\t\t\tfromAddresses,\n\t\t\t\tdepartments\n\t\t\t),\n\t\t\toptions.actions\n\t\t)\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\tconsentSources,\n\t\t\tactionRegistry,\n\t\t\trichText: options.richText,\n\t\t\thasJobsPlugin: Boolean(plugins['@10x-media/jobs']),\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\tbuttons: options.buttons,\n\t\t\tresponse: options.response,\n\t\t\tfromAddresses,\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 { ActionDefinition, ActionRunArgs, AnyActionDefinition } from './actions/defineAction'\nexport { defineAction } from './actions/defineAction'\nexport type { FromAddressesResolver, FromAddressOption } from './actions/fromAddresses'\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 { evaluateCalc } from './calc/evaluate'\nexport { normalizeCalc } from './calc/normalizeCalc'\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 { evaluateCondition } from './conditions/evaluate'\nexport type { FieldCondition } from './conditions/types'\nexport { applyConsentStatements } from './consent/applyConsentStatements'\nexport type { ConsentProof } 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 {\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 { 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 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 { 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'\nexport type { FormBuilderPluginOptions as PluginOptions }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwNA,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,iBAAiB,QAAQ,SAAS;EAaxC,MAAM,WAAW,kBARe,6BAC/B,iBACA,QAAQ,UAAU,MACnB,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,iBAAiB,eACtB,8BACC,iBACA,QAAQ,UAAU,cAAc,QAAQ,UAAU,QAClD,eACA,WACD,GACA,QAAQ,OACT;EACA,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,UAAU,QAAQ;GAClB,eAAe,QAAQ,QAAQ,kBAAkB;GACjD,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,SAAS,QAAQ;GACjB,UAAU,QAAQ;GAClB;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 { buildDefaultActionDefinitions } from './actions/builtin'\nimport { resolveActions } from './actions/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\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).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 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\tdepartments,\n\t\t\t\trecipients: options.email?.recipients,\n\t\t\t}),\n\t\t\toptions.actions\n\t\t)\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\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\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\tbuttons: options.buttons,\n\t\t\tresponse: options.response,\n\t\t\tfromAddresses,\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 { ActionDefinition, ActionRunArgs, AnyActionDefinition } from './actions/defineAction'\nexport { defineAction } from './actions/defineAction'\nexport type { FromAddressesResolver, FromAddressOption } from './actions/fromAddresses'\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 { evaluateCalc } from './calc/evaluate'\nexport { normalizeCalc } from './calc/normalizeCalc'\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 { 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 {\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 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 { 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA,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;EAInC,MAAM,kBACL,YAAY,QACT,KAAA,IACC,QAAQ,aAAa,8BAA8B,QAAQ,QAAQ,UAAU;EAClF,MAAM,iBAAiB,QAAQ,SAAS;EAcxC,MAAM,WAAW,kBATe,6BAC/B,iBACA,QAAQ,UAAU,QAClB,eACD,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,iBAAiB,eACtB,8BAA8B;GAC7B,UAAU;GACV,QAAQ,QAAQ,UAAU,cAAc,QAAQ,UAAU;GAC1D;GACA;GACA,YAAY,QAAQ,OAAO;EAC5B,CAAC,GACD,QAAQ,OACT;EACA,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,iBAAiB,QAAQ,SAAS,YAAY;GAC9C,sBAAsB,QAAQ,SAAS;GACvC;GACA,UAAU,QAAQ;GAClB,eAAe,QAAQ,QAAQ,kBAAkB;GACjD,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,SAAS,QAAQ;GACjB,UAAU,QAAQ;GAClB;GACA;GACA,uBAAuB,QAAQ;GAC/B,WAAW,QAAQ;EACpB,CAAC;EACD,OAAO;CACR;AACD,CAAC"}
@@ -0,0 +1,228 @@
1
+ import { TranslationsOption } from "./translations/index.js";
2
+ import { RichTextBodyOption } from "./actions/body/serializeBody.js";
3
+ import { ConsentSourcesResolver } from "./consent/types.js";
4
+ import { FieldTypesConfig } from "./fields/registry.js";
5
+ import { FormEventSink } from "./events/types.js";
6
+ import { PollOptionSourcesConfig } from "./poll/registry.js";
7
+ import { FromAddressesResolver } from "./actions/fromAddresses.js";
8
+ import { ActionsConfig } from "./actions/registry.js";
9
+ import { FormResultsAccess } from "./aggregation/resolveResultsRequest.js";
10
+ import { ButtonsOption } from "./collections/buttonFields.js";
11
+ import { ConsentSnapshotMode } from "./consent/captureConsent.js";
12
+ import { DepartmentEmailsResolver } from "./email/departments.js";
13
+ import { RecipientsConfig } from "./actions/emailRecipients.js";
14
+ import { ResponseOption } from "./collections/redirectFields.js";
15
+ import { CollectionOverrides } from "./plugin/collectionOverrides.js";
16
+ import { SpamOption } from "./spam/types.js";
17
+ import { UploadsOption } from "./plugin/uploadsCollection.js";
18
+ import { OutcomeFieldsOverride } from "./poll/outcomeFields.js";
19
+ import { PollTypesConfig } from "./poll/pollTypeRegistry.js";
20
+ import { ValidationRulesConfig } from "./validation/registry.js";
21
+ import { CollectionSlug } from "payload";
22
+
23
+ //#region src/options.d.ts
24
+ type FormBuilderPluginOptions = {
25
+ disabled?: boolean;
26
+ /**
27
+ * Per-locale overrides for this plugin's UI strings, keyed by the typed
28
+ * translation keys exported from `@10x-media/form-builder/i18n`. Values win
29
+ * over the built-in locales key-by-key; locales the plugin does not ship are
30
+ * added whole. App-level `i18n.translations` still wins over both.
31
+ */
32
+ translations?: TranslationsOption; /** Pluggable sink for form lifecycle events. Defaults to a no-op; analytics adapters or a future analytics plugin subscribe here. */
33
+ events?: FormEventSink;
34
+ /**
35
+ * Content-bearing author fields (labels, placeholders, option labels, action subjects and
36
+ * bodies) are localized by default. Payload strips the `localized` flag on hosts without
37
+ * `localization` configured, so the default is safe everywhere. Set `false` to keep form
38
+ * content single-locale even on localized hosts. Spread-overrides of the prebuilt default
39
+ * exports (`defaultFieldDefinitionsByType`, `defaultActionDefinitions`) carry `localized`
40
+ * flags from the default-true set; when opting out, derive overrides from
41
+ * `buildDefaultFieldDefinitions(false)` / `buildDefaultActionDefinitions({ localize: false })` instead.
42
+ * Consent statements are unaffected either way: they live on the host's own
43
+ * `consentSourcesField()`, which carries its own `localized` option.
44
+ */
45
+ localizeContent?: boolean; /** Add, override, or remove field types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
46
+ fields?: FieldTypesConfig; /** Add, override, or remove validation rule types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
47
+ rules?: ValidationRulesConfig; /** Add, override, or remove post-submit action types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
48
+ actions?: ActionsConfig;
49
+ /**
50
+ * Customize how the plugin's rich text is authored and rendered. `editor` is the default
51
+ * Lexical/richText editor for every plugin richText field (message content, consent
52
+ * statement, response message, action bodies); `bodyEditor` overrides the action body
53
+ * fields specifically, falling back to `editor`. `converters` spread over the default
54
+ * Lexical node converters; `serialize` replaces the whole action-body pipeline (e.g. to
55
+ * target chat or plain-text channels instead of email HTML). A custom `serialize` receives
56
+ * the submitted `form` (id/title) and `req`, enabling per-tenant lookups or handing the raw
57
+ * body off to a renderer like react-email.
58
+ */
59
+ richText?: RichTextBodyOption;
60
+ /**
61
+ * Email routing for the `emailTeam` and `confirmation` actions. Both sub-options are opt-in by the
62
+ * presence of a resolver (not a `false`/`true`/object flag) and share a shape: a `req`-scoped
63
+ * resolver, evaluated per request via an endpoint, whose choice is validated at save time only and
64
+ * never re-checked when the action sends (the config is admin-authored, not visitor-controlled).
65
+ *
66
+ * `fromAddresses` gives both actions a `from` select whose options come from the resolver; absent,
67
+ * neither action has a `from` field and every send uses the email adapter's default sender. The
68
+ * intended use is multi-tenant hosts where each tenant may only send from particular addresses
69
+ * (derive the tenant from `req`, return its allowed senders). Values are the literal string
70
+ * `payload.sendEmail` accepts as `from` (e.g. `'Name <addr@x.com>'` or a plain address).
71
+ *
72
+ * `departments` turns the `emailTeam` `to` into a select whose options come from the resolver
73
+ * (a `/:id/departments` endpoint); the intended use is to place `departmentsField()` on a document
74
+ * you own and read it back with `resolveDepartmentOptions`, which resolves each department's address
75
+ * for the requesting locale. Because `to` is localized, each admin locale stores its own resolved
76
+ * address and a submission's locale selects the address it routes to (Payload-native, no per-send
77
+ * lookup). Storing the resolved address, rather than a department id resolved live at send (consent's
78
+ * model), is deliberate: the routing target a form was saved with stays audit-stable even if the
79
+ * resolver's data later changes, so do not "fix" it into a live lookup. Multi-tenant hosts scope
80
+ * which document they read by the tenant derived from `req`; absent, the recipient fields simply
81
+ * offer no preset department options (still free-typed emails and field tokens).
82
+ */
83
+ email?: {
84
+ fromAddresses?: FromAddressesResolver;
85
+ departments?: DepartmentEmailsResolver; /** Narrows the recipient fields' behavior (free-typed emails, field tokens). See {@link RecipientsConfig}. */
86
+ recipients?: RecipientsConfig;
87
+ };
88
+ /**
89
+ * Where the consent statements a form can reference come from. Absent (the default): no sources,
90
+ * so the built-in `consent` field type is not registered at all and authors cannot add a consent
91
+ * field with nothing to reference (a developer-registered custom `consent` type via `fields`
92
+ * still wins).
93
+ *
94
+ * `sources` is an async `req`-scoped resolver returning the sources available to this request.
95
+ * The intended shape: place `consentSourcesField()` on a collection or global you own and read it
96
+ * back here. Multi-tenant hosts scope that read by the tenant derived from `req`, so a tenant's
97
+ * authors only ever see, and their visitors only ever agree to, their own statements. A form
98
+ * stores only a source's row `id`; the statement is resolved live per request (see
99
+ * `resolveConsentStatements`) and the proof is rebuilt from the source at submit, so neither is
100
+ * ever a copy the client could stale or forge.
101
+ */
102
+ consent?: {
103
+ sources: ConsentSourcesResolver;
104
+ /**
105
+ * What each submission snapshots of the agreed consent wording, for a versioned audit trail.
106
+ * `'both'` (default) stores a `statementHash`, the plain `statementText`, and the source name;
107
+ * `'hash'` keeps only the tamper-evident hash; `'text'` only the readable text; `false` keeps the
108
+ * lean id-only proof. The proof stays id-based regardless; this only adds the wording snapshot.
109
+ */
110
+ snapshot?: ConsentSnapshotMode;
111
+ /**
112
+ * Whether to resolve each form's consent statements on every read via an afterRead hook (the
113
+ * default, `true`). It attaches `consentStatements` to the form doc so a host can render the
114
+ * wording without a second call, at the cost of one source lookup per form read (N on a list).
115
+ * Set `false` to skip that hook and resolve statements yourself with the exported
116
+ * `resolveConsentStatements` when you render a form; submit-time proof capture is unaffected either
117
+ * way (it always re-resolves from the source).
118
+ */
119
+ resolveOnRead?: boolean;
120
+ };
121
+ /**
122
+ * Form-level button labels: `submitLabel` at the bottom of the Fields tab, `prevLabel` and
123
+ * `nextLabel` in a row at the bottom of the Flow tab (shown once the flow has a step). The
124
+ * rendered chrome resolves each label as `<Form>` prop, then the stored value, then the
125
+ * translated default. `fields` composes them: it receives the three default fields as a
126
+ * `{ submit, prev, next }` map with the content localization flag already applied and returns
127
+ * the map, so wrapping a default in a row with a host field (e.g. an icon select) or replacing
128
+ * one is explicit. `FormDocument.buttons` reassembles only those three known labels, so a
129
+ * host-added sibling field is still stored but is read off the raw document, not `doc.buttons`.
130
+ */
131
+ buttons?: ButtonsOption;
132
+ /**
133
+ * The success-response group. `redirect.fields` composes the fields inside the `response.redirect`
134
+ * group, mirroring `buttons.fields`: it receives the default fields (the `url` text field, plus the
135
+ * polymorphic `reference` relationship when `redirectRelationships` is set) and returns the group's
136
+ * final field array, so a host can prepend a custom link field (the pattern most projects already
137
+ * have), swap `url` for their own picker, reorder, or filter. Omit it and the group stays the
138
+ * built-in `url` (+ optional `reference`), unchanged.
139
+ *
140
+ * The plugin's built-in redirect handling reads `redirect.url` (and `redirect.reference` when
141
+ * configured); `toFormDocument` passes the whole `response` group through, so a host that replaces
142
+ * `url` with their own link field owns resolving it to a destination in their frontend, the same as
143
+ * the internal-reference case.
144
+ */
145
+ response?: ResponseOption;
146
+ /**
147
+ * Collections whose documents `response.redirect` can reference (`response.redirect.reference`),
148
+ * letting an author redirect a visitor to an internal document instead of a URL after a
149
+ * successful submit. Absent (the default): no `reference` field exists at all, matching today's
150
+ * URL-only redirect. Always polymorphic, even for a single slug (a `CollectionSlug[]` array, not
151
+ * a bare `CollectionSlug`), so a host adding a second collection later never changes the stored
152
+ * shape, the same precedent as `consentSourcesField()`'s `page` picker. The plugin never resolves
153
+ * the reference to a URL itself (it has no notion of the host's routing); `toFormDocument` passes
154
+ * the raw `{ relationTo, value }` pair through on `FormDocument.response.redirect.reference` for
155
+ * the host to resolve. Mirrors the `redirectRelationships` option of Payload's native
156
+ * `plugin-form-builder`.
157
+ */
158
+ redirectRelationships?: CollectionSlug[];
159
+ /**
160
+ * File uploads are bring-your-own. Default `false`: no upload collection is involved and the
161
+ * built-in `file` field type is removed from the registry, so form authors cannot add a field
162
+ * with nowhere to land (a developer-registered custom `file` type via `fields` still wins).
163
+ * `{ collection: 'slug' }` points at a host-owned upload collection (created by the app with
164
+ * its storage adapter); the plugin validates it at boot, appends its hidden `owner` field when
165
+ * absent, and prepends the spam upload hooks.
166
+ */
167
+ uploads?: UploadsOption; /** Honeypot + rate-limiting (on by default) + a captcha adapter seam + upload-ownership scoping. `false` disables the whole subsystem. */
168
+ spam?: SpamOption;
169
+ /**
170
+ * Aggregate-results endpoint options. `access` gates anonymous reads after the form is loaded
171
+ * and before anything is served; absent keeps the plugin-default gating (poll opt-in +
172
+ * visibility + enumerable-field guard). Multi-tenant hosts should compare `form.tenant` against
173
+ * the tenant derived from `req` so one tenant's poll counts are never readable under another
174
+ * tenant's id. Authenticated callers bypass this seam.
175
+ */
176
+ results?: {
177
+ access?: FormResultsAccess;
178
+ };
179
+ /**
180
+ * Poll behavior. `votedCookie: true` sets an httpOnly `fb-voted-{formId}=1` cookie on each
181
+ * successful submission to a poll-enabled form, letting SSR hosts read the voted state via
182
+ * `hasVotedCookie` and pass it to `<Poll hasVoted>`. Default `false`.
183
+ * `sources` registers poll option sources (`definePollOptionSource`), letting authors populate
184
+ * a poll's choices from host domain data with stable values; there are no built-ins. With at
185
+ * least one source registered, the forms poll group gains an `optionSource` select plus its
186
+ * per-source `sourceConfig`, and submissions to a sourced poll only accept resolved values.
187
+ * `outcomeFields` composes the poll `outcome` group: it receives the two default fields
188
+ * (`winningValues`, `resolvedAt`) and returns the group's final field array verbatim, so a host
189
+ * can swap `winningValues` for its own component (e.g. a relationship picker over the voteable
190
+ * records). Membership validation runs server-side regardless, so a swap cannot bypass it.
191
+ * `types` registers outcome strategies (`definePollType`) shown in the poll `type` select. The
192
+ * built-ins `manual` (default, hand-picked), `mostVoted` (auto-resolves to the top choice(s) on
193
+ * close), and `source` (delegates to the option source) are always registered; a host entry keyed
194
+ * by (or carrying) a built-in slug replaces it. A closed poll whose strategy is not `manual`
195
+ * auto-resolves via a scheduled job when a runner is present, or on the next results read otherwise.
196
+ */
197
+ poll?: {
198
+ votedCookie?: boolean;
199
+ sources?: PollOptionSourcesConfig;
200
+ types?: PollTypesConfig;
201
+ outcomeFields?: OutcomeFieldsOverride;
202
+ };
203
+ /**
204
+ * When `true`, the raw `values`, `descriptors`, and `consent` JSON fields are visible in the
205
+ * submission admin view. Default `false`, because the `SubmissionAnswers` UI component already
206
+ * represents those fields fully and they are noisy shown alongside it.
207
+ */
208
+ showSubmissionRawFields?: boolean;
209
+ /**
210
+ * Override individual plugin-managed collections using explicit spreads. Each key accepts a
211
+ * `CollectionOverrides` object: top-level keys are spread with the plugin's defaults (spread
212
+ * order per key determines who wins), hooks are appended after the plugin's own hooks, and
213
+ * `fields` is a function that receives the default fields and returns the final array so
214
+ * additions/removals are always intentional.
215
+ */
216
+ overrides?: {
217
+ forms?: CollectionOverrides;
218
+ formSubmissions?: CollectionOverrides;
219
+ };
220
+ };
221
+ declare module 'payload' {
222
+ interface RegisteredPlugins {
223
+ '@10x-media/form-builder': FormBuilderPluginOptions;
224
+ }
225
+ }
226
+ //#endregion
227
+ export { FormBuilderPluginOptions };
228
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1,11 @@
1
+ //#region src/plugin/applyRegistryConfig.d.ts
2
+ /** Any registry entry: an object carrying a string `type` discriminator, keyed by that type. */
3
+ type TypedDefinition = {
4
+ type: string;
5
+ };
6
+ /** Per-type opt-in: `false` removes a type, `true` keeps the seeded default, a definition adds or replaces one. */
7
+ type RegistryOption<T extends TypedDefinition> = boolean | T;
8
+ type RegistryConfig<T extends TypedDefinition> = Record<string, RegistryOption<T>>;
9
+ //#endregion
10
+ export { RegistryConfig };
11
+ //# sourceMappingURL=applyRegistryConfig.d.ts.map
@@ -0,0 +1,20 @@
1
+ //#region src/plugin/applyRegistryConfig.ts
2
+ /**
3
+ * Apply a per-type opt-in override map onto a seeded registry, in place. `false` removes the type,
4
+ * `true` keeps whatever default is already seeded (a no-op when none exists), and a definition adds
5
+ * or replaces one with its `type` forced to the config key, so the authored slug (derived from
6
+ * `type`) and the lookup key can never drift apart and silently orphan the entry. Shared by the
7
+ * field-type, validation-rule, action, poll-source, and poll-type registries.
8
+ */
9
+ const applyRegistryConfig = (registry, config) => {
10
+ for (const [type, option] of Object.entries(config)) if (option === false) registry.delete(type);
11
+ else if (option !== true) registry.set(type, {
12
+ ...option,
13
+ type
14
+ });
15
+ return registry;
16
+ };
17
+ //#endregion
18
+ export { applyRegistryConfig };
19
+
20
+ //# sourceMappingURL=applyRegistryConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyRegistryConfig.js","names":[],"sources":["../../src/plugin/applyRegistryConfig.ts"],"sourcesContent":["/** Any registry entry: an object carrying a string `type` discriminator, keyed by that type. */\nexport type TypedDefinition = { type: string }\n\n/** Per-type opt-in: `false` removes a type, `true` keeps the seeded default, a definition adds or replaces one. */\nexport type RegistryOption<T extends TypedDefinition> = boolean | T\n\nexport type RegistryConfig<T extends TypedDefinition> = Record<string, RegistryOption<T>>\n\n/**\n * Apply a per-type opt-in override map onto a seeded registry, in place. `false` removes the type,\n * `true` keeps whatever default is already seeded (a no-op when none exists), and a definition adds\n * or replaces one with its `type` forced to the config key, so the authored slug (derived from\n * `type`) and the lookup key can never drift apart and silently orphan the entry. Shared by the\n * field-type, validation-rule, action, poll-source, and poll-type registries.\n */\nexport const applyRegistryConfig = <T extends TypedDefinition>(\n\tregistry: Map<string, T>,\n\tconfig: RegistryConfig<T>\n): Map<string, T> => {\n\tfor (const [type, option] of Object.entries(config)) {\n\t\tif (option === false) {\n\t\t\tregistry.delete(type)\n\t\t} else if (option !== true) {\n\t\t\tregistry.set(type, { ...option, type })\n\t\t}\n\t}\n\treturn registry\n}\n"],"mappings":";;;;;;;;AAeA,MAAa,uBACZ,UACA,WACoB;CACpB,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,MAAM,GACjD,IAAI,WAAW,OACd,SAAS,OAAO,IAAI;MACd,IAAI,WAAW,MACrB,SAAS,IAAI,MAAM;EAAE,GAAG;EAAQ;CAAK,CAAC;CAGxC,OAAO;AACR"}
@@ -4,7 +4,7 @@ import { buildSubmissionsCollection } from "../collections/formSubmissions.js";
4
4
  import { registerActionsTask } from "../actions/task.js";
5
5
  import { attachUploadsCollection } from "./uploadsCollection.js";
6
6
  //#region src/plugin/registerCollections.ts
7
- const registerCollections = ({ config, registry, ruleRegistry, consentSources, actionRegistry, richText, hasJobsPlugin, events, uploads, spam, showSubmissionRawFields, localizeContent, resultsAccess, votedCookie, pollSourceRegistry, pollTypeRegistry, outcomeFields, buttons, response, fromAddresses, departments, redirectRelationships, overrides }) => {
7
+ const registerCollections = ({ config, registry, ruleRegistry, consentSources, consentSnapshot, consentResolveOnRead, actionRegistry, richText, hasJobsPlugin, events, uploads, spam, showSubmissionRawFields, localizeContent, resultsAccess, votedCookie, pollSourceRegistry, pollTypeRegistry, outcomeFields, buttons, response, fromAddresses, departments, redirectRelationships, overrides }) => {
8
8
  registerActionsTask(config, actionRegistry, richText);
9
9
  registerPollCloseTask(config);
10
10
  const hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin;
@@ -20,6 +20,7 @@ const registerCollections = ({ config, registry, ruleRegistry, consentSources, a
20
20
  registry,
21
21
  ruleRegistry,
22
22
  consentSources,
23
+ consentSnapshot,
23
24
  actionRegistry,
24
25
  richText,
25
26
  events,
@@ -35,6 +36,7 @@ const registerCollections = ({ config, registry, ruleRegistry, consentSources, a
35
36
  registry,
36
37
  ruleRegistry,
37
38
  consentSources,
39
+ consentResolveOnRead,
38
40
  actionRegistry,
39
41
  localizeContent,
40
42
  richText,
@@ -1 +1 @@
1
- {"version":3,"file":"registerCollections.js","names":[],"sources":["../../src/plugin/registerCollections.ts"],"sourcesContent":["import type { CollectionSlug, Config } from 'payload'\nimport type { RichTextBodyOption } from '../actions/body/serializeBody'\nimport type { FromAddressesResolver } from '../actions/fromAddresses'\nimport type { ActionRegistry } from '../actions/registry'\nimport { registerActionsTask } from '../actions/task'\nimport type { FormResultsAccess } from '../aggregation/resolveResultsRequest'\nimport type { ButtonsOption } from '../collections/buttonFields'\nimport { buildSubmissionsCollection } from '../collections/formSubmissions'\nimport { buildFormsCollection } from '../collections/forms'\nimport type { ResponseOption } from '../collections/redirectFields'\nimport type { ConsentSourcesResolver } from '../consent/types'\nimport type { DepartmentEmailsResolver } from '../email/departments'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { registerPollCloseTask } from '../poll/closeJob'\nimport type { OutcomeFieldsOverride } from '../poll/outcomeFields'\nimport type { PollTypeRegistry } from '../poll/pollTypeRegistry'\nimport type { PollOptionSourceRegistry } from '../poll/registry'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport type { CollectionOverrides } from './collectionOverrides'\nimport { attachUploadsCollection, type UploadsOption } from './uploadsCollection'\n\ntype RegisterCollectionsArgs = {\n\tconfig: Config\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentSources?: ConsentSourcesResolver\n\tactionRegistry: ActionRegistry\n\trichText?: RichTextBodyOption\n\thasJobsPlugin: boolean\n\tevents?: FormEventSink\n\tuploads: UploadsOption\n\tspam: ResolvedSpamConfig | false\n\tshowSubmissionRawFields: boolean\n\tlocalizeContent: boolean\n\tresultsAccess?: FormResultsAccess\n\tvotedCookie: boolean\n\tpollSourceRegistry: PollOptionSourceRegistry\n\tpollTypeRegistry: PollTypeRegistry\n\toutcomeFields?: OutcomeFieldsOverride\n\tbuttons?: ButtonsOption\n\tresponse?: ResponseOption\n\tfromAddresses?: FromAddressesResolver\n\tdepartments?: DepartmentEmailsResolver\n\tredirectRelationships?: CollectionSlug[]\n\toverrides?: {\n\t\tforms?: CollectionOverrides\n\t\tformSubmissions?: CollectionOverrides\n\t}\n}\n\nexport const registerCollections = ({\n\tconfig,\n\tregistry,\n\truleRegistry,\n\tconsentSources,\n\tactionRegistry,\n\trichText,\n\thasJobsPlugin,\n\tevents,\n\tuploads,\n\tspam,\n\tshowSubmissionRawFields,\n\tlocalizeContent,\n\tresultsAccess,\n\tvotedCookie,\n\tpollSourceRegistry,\n\tpollTypeRegistry,\n\toutcomeFields,\n\tbuttons,\n\tresponse,\n\tfromAddresses,\n\tdepartments,\n\tredirectRelationships,\n\toverrides,\n}: RegisterCollectionsArgs): void => {\n\tregisterActionsTask(config, actionRegistry, richText)\n\tregisterPollCloseTask(config)\n\tconst hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin\n\n\tconst uploadSlug = uploads === false ? undefined : uploads.collection\n\tif (uploads !== false) {\n\t\tattachUploadsCollection({ config, slug: uploads.collection, spam })\n\t}\n\n\t// Payload has no nav-sort option, so it lists collections in registration order. Register\n\t// `form-submissions` before `forms` so the \"Forms\" nav group reads alphabetically\n\t// (`form-submissions` < `forms`); ordering plugin nav groups against a host's own collections\n\t// stays a host concern.\n\tconfig.collections = [\n\t\t...(config.collections ?? []),\n\t\tbuildSubmissionsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentSources,\n\t\t\tactionRegistry,\n\t\t\trichText,\n\t\t\tevents,\n\t\t\thasRunner,\n\t\t\tuploadSlug,\n\t\t\tspam,\n\t\t\tvotedCookie,\n\t\t\tpollSourceRegistry,\n\t\t\tshowRawFields: showSubmissionRawFields,\n\t\t\toverrides: overrides?.formSubmissions,\n\t\t}),\n\t\tbuildFormsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentSources,\n\t\t\tactionRegistry,\n\t\t\tlocalizeContent,\n\t\t\trichText,\n\t\t\tuploadsCollectionSlug: uploadSlug,\n\t\t\tresultsAccess,\n\t\t\tpollSourceRegistry,\n\t\t\tpollTypeRegistry,\n\t\t\toutcomeFields,\n\t\t\tbuttons,\n\t\t\tresponse,\n\t\t\tfromAddresses,\n\t\t\tdepartments,\n\t\t\tredirectRelationships,\n\t\t\toverrides: overrides?.forms,\n\t\t}),\n\t]\n}\n"],"mappings":";;;;;;AAoDA,MAAa,uBAAuB,EACnC,QACA,UACA,cACA,gBACA,gBACA,UACA,eACA,QACA,SACA,MACA,yBACA,iBACA,eACA,aACA,oBACA,kBACA,eACA,SACA,UACA,eACA,aACA,uBACA,gBACoC;CACpC,oBAAoB,QAAQ,gBAAgB,QAAQ;CACpD,sBAAsB,MAAM;CAC5B,MAAM,YAAY,QAAQ,OAAO,MAAM,OAAO,KAAK;CAEnD,MAAM,aAAa,YAAY,QAAQ,KAAA,IAAY,QAAQ;CAC3D,IAAI,YAAY,OACf,wBAAwB;EAAE;EAAQ,MAAM,QAAQ;EAAY;CAAK,CAAC;CAOnE,OAAO,cAAc;EACpB,GAAI,OAAO,eAAe,CAAC;EAC3B,2BAA2B;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,eAAe;GACf,WAAW,WAAW;EACvB,CAAC;EACD,qBAAqB;GACpB;GACA;GACA;GACA;GACA;GACA;GACA,uBAAuB;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,WAAW,WAAW;EACvB,CAAC;CACF;AACD"}
1
+ {"version":3,"file":"registerCollections.js","names":[],"sources":["../../src/plugin/registerCollections.ts"],"sourcesContent":["import type { CollectionSlug, Config } from 'payload'\nimport type { RichTextBodyOption } from '../actions/body/serializeBody'\nimport type { FromAddressesResolver } from '../actions/fromAddresses'\nimport type { ActionRegistry } from '../actions/registry'\nimport { registerActionsTask } from '../actions/task'\nimport type { FormResultsAccess } from '../aggregation/resolveResultsRequest'\nimport type { ButtonsOption } from '../collections/buttonFields'\nimport { buildSubmissionsCollection } from '../collections/formSubmissions'\nimport { buildFormsCollection } from '../collections/forms'\nimport type { ResponseOption } from '../collections/redirectFields'\nimport type { ConsentSnapshotMode } from '../consent/captureConsent'\nimport type { ConsentSourcesResolver } from '../consent/types'\nimport type { DepartmentEmailsResolver } from '../email/departments'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { registerPollCloseTask } from '../poll/closeJob'\nimport type { OutcomeFieldsOverride } from '../poll/outcomeFields'\nimport type { PollTypeRegistry } from '../poll/pollTypeRegistry'\nimport type { PollOptionSourceRegistry } from '../poll/registry'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport type { CollectionOverrides } from './collectionOverrides'\nimport { attachUploadsCollection, type UploadsOption } from './uploadsCollection'\n\ntype RegisterCollectionsArgs = {\n\tconfig: Config\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentSources?: ConsentSourcesResolver\n\tconsentSnapshot?: ConsentSnapshotMode\n\t/** Plugin `consent.resolveOnRead` (default true); false skips the per-read consent afterRead hook. */\n\tconsentResolveOnRead?: boolean\n\tactionRegistry: ActionRegistry\n\trichText?: RichTextBodyOption\n\thasJobsPlugin: boolean\n\tevents?: FormEventSink\n\tuploads: UploadsOption\n\tspam: ResolvedSpamConfig | false\n\tshowSubmissionRawFields: boolean\n\tlocalizeContent: boolean\n\tresultsAccess?: FormResultsAccess\n\tvotedCookie: boolean\n\tpollSourceRegistry: PollOptionSourceRegistry\n\tpollTypeRegistry: PollTypeRegistry\n\toutcomeFields?: OutcomeFieldsOverride\n\tbuttons?: ButtonsOption\n\tresponse?: ResponseOption\n\tfromAddresses?: FromAddressesResolver\n\tdepartments?: DepartmentEmailsResolver\n\tredirectRelationships?: CollectionSlug[]\n\toverrides?: {\n\t\tforms?: CollectionOverrides\n\t\tformSubmissions?: CollectionOverrides\n\t}\n}\n\nexport const registerCollections = ({\n\tconfig,\n\tregistry,\n\truleRegistry,\n\tconsentSources,\n\tconsentSnapshot,\n\tconsentResolveOnRead,\n\tactionRegistry,\n\trichText,\n\thasJobsPlugin,\n\tevents,\n\tuploads,\n\tspam,\n\tshowSubmissionRawFields,\n\tlocalizeContent,\n\tresultsAccess,\n\tvotedCookie,\n\tpollSourceRegistry,\n\tpollTypeRegistry,\n\toutcomeFields,\n\tbuttons,\n\tresponse,\n\tfromAddresses,\n\tdepartments,\n\tredirectRelationships,\n\toverrides,\n}: RegisterCollectionsArgs): void => {\n\tregisterActionsTask(config, actionRegistry, richText)\n\tregisterPollCloseTask(config)\n\tconst hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin\n\n\tconst uploadSlug = uploads === false ? undefined : uploads.collection\n\tif (uploads !== false) {\n\t\tattachUploadsCollection({ config, slug: uploads.collection, spam })\n\t}\n\n\t// Payload has no nav-sort option, so it lists collections in registration order. Register\n\t// `form-submissions` before `forms` so the \"Forms\" nav group reads alphabetically\n\t// (`form-submissions` < `forms`); ordering plugin nav groups against a host's own collections\n\t// stays a host concern.\n\tconfig.collections = [\n\t\t...(config.collections ?? []),\n\t\tbuildSubmissionsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentSources,\n\t\t\tconsentSnapshot,\n\t\t\tactionRegistry,\n\t\t\trichText,\n\t\t\tevents,\n\t\t\thasRunner,\n\t\t\tuploadSlug,\n\t\t\tspam,\n\t\t\tvotedCookie,\n\t\t\tpollSourceRegistry,\n\t\t\tshowRawFields: showSubmissionRawFields,\n\t\t\toverrides: overrides?.formSubmissions,\n\t\t}),\n\t\tbuildFormsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentSources,\n\t\t\tconsentResolveOnRead,\n\t\t\tactionRegistry,\n\t\t\tlocalizeContent,\n\t\t\trichText,\n\t\t\tuploadsCollectionSlug: uploadSlug,\n\t\t\tresultsAccess,\n\t\t\tpollSourceRegistry,\n\t\t\tpollTypeRegistry,\n\t\t\toutcomeFields,\n\t\t\tbuttons,\n\t\t\tresponse,\n\t\t\tfromAddresses,\n\t\t\tdepartments,\n\t\t\tredirectRelationships,\n\t\t\toverrides: overrides?.forms,\n\t\t}),\n\t]\n}\n"],"mappings":";;;;;;AAwDA,MAAa,uBAAuB,EACnC,QACA,UACA,cACA,gBACA,iBACA,sBACA,gBACA,UACA,eACA,QACA,SACA,MACA,yBACA,iBACA,eACA,aACA,oBACA,kBACA,eACA,SACA,UACA,eACA,aACA,uBACA,gBACoC;CACpC,oBAAoB,QAAQ,gBAAgB,QAAQ;CACpD,sBAAsB,MAAM;CAC5B,MAAM,YAAY,QAAQ,OAAO,MAAM,OAAO,KAAK;CAEnD,MAAM,aAAa,YAAY,QAAQ,KAAA,IAAY,QAAQ;CAC3D,IAAI,YAAY,OACf,wBAAwB;EAAE;EAAQ,MAAM,QAAQ;EAAY;CAAK,CAAC;CAOnE,OAAO,cAAc;EACpB,GAAI,OAAO,eAAe,CAAC;EAC3B,2BAA2B;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,eAAe;GACf,WAAW,WAAW;EACvB,CAAC;EACD,qBAAqB;GACpB;GACA;GACA;GACA;GACA;GACA;GACA;GACA,uBAAuB;GACvB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,WAAW,WAAW;EACvB,CAAC;CACF;AACD"}
@@ -4,9 +4,12 @@ import { Config } from "payload";
4
4
  /**
5
5
  * Uploads are bring-your-own: `false` (the default) disables file fields entirely, an object points
6
6
  * at a host-owned upload collection (created by the app with its storage adapter of choice).
7
+ * `mimeTypes` optionally constrains the file field's MIME picker for hosts that enforce types by other
8
+ * means than the collection's own `upload.mimeTypes` (which is read automatically when this is absent).
7
9
  */
8
10
  type UploadsOption = false | {
9
11
  collection: string;
12
+ mimeTypes?: string[];
10
13
  };
11
14
  //#endregion
12
15
  export { UploadsOption };
@@ -1,5 +1,14 @@
1
1
  import { buildUploadOwnerStamp, buildUploadRateLimit } from "../spam/uploadHooks.js";
2
2
  //#region src/plugin/uploadsCollection.ts
3
+ /**
4
+ * The MIME types the file field's picker should offer: the host upload collection's own
5
+ * `upload.mimeTypes`, or undefined (no constraint, so the picker keeps its full list). Read at
6
+ * plugin-factory time, before the field registry freezes; `attachUploadsCollection` runs too late.
7
+ */
8
+ const readUploadCollectionMimeTypes = (config, slug) => {
9
+ const upload = (config.collections?.find((collection) => collection.slug === slug))?.upload;
10
+ return typeof upload === "object" && Array.isArray(upload.mimeTypes) ? upload.mimeTypes : void 0;
11
+ };
3
12
  /** Descends presentational rows only: an `owner` field nested in a group/tabs/collapsible is not detected, so a top-level one gets appended alongside it. */
4
13
  const hasFieldNamed = (fields, name) => fields.some((field) => {
5
14
  if ("name" in field && field.name === name) return true;
@@ -39,6 +48,6 @@ const attachUploadsCollection = ({ config, slug, spam }) => {
39
48
  config.collections = collections.map((collection) => collection.slug === slug ? attached : collection);
40
49
  };
41
50
  //#endregion
42
- export { attachUploadsCollection };
51
+ export { attachUploadsCollection, readUploadCollectionMimeTypes };
43
52
 
44
53
  //# sourceMappingURL=uploadsCollection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"uploadsCollection.js","names":[],"sources":["../../src/plugin/uploadsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Config, Field } from 'payload'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { buildUploadOwnerStamp, buildUploadRateLimit } from '../spam/uploadHooks'\n\n/**\n * Uploads are bring-your-own: `false` (the default) disables file fields entirely, an object points\n * at a host-owned upload collection (created by the app with its storage adapter of choice).\n */\nexport type UploadsOption = false | { collection: string }\n\n/** Descends presentational rows only: an `owner` field nested in a group/tabs/collapsible is not detected, so a top-level one gets appended alongside it. */\nconst hasFieldNamed = (fields: Field[], name: string): boolean =>\n\tfields.some((field) => {\n\t\tif ('name' in field && field.name === name) {\n\t\t\treturn true\n\t\t}\n\t\treturn field.type === 'row' && hasFieldNamed(field.fields, name)\n\t})\n\ntype AttachUploadsCollectionArgs = {\n\tconfig: Config\n\tslug: string\n\tspam: ResolvedSpamConfig | false\n}\n\n/**\n * Wire the plugin's upload concerns into the host-owned upload collection named by\n * `uploads.collection`. The collection must already exist in `config.collections` and carry an\n * `upload` config; anything else is a plugin misconfiguration and throws at boot. The plugin\n * appends its hidden `owner` text field unless the host already defines a field named `owner`\n * (then that field is reused and must stay a text-compatible identity slot), and prepends the\n * spam upload hooks (rate limit, owner stamp) ahead of the host's own hooks so abuse checks run\n * first. The host entry is replaced with a new object; the host's config is never mutated.\n */\nexport const attachUploadsCollection = ({\n\tconfig,\n\tslug,\n\tspam,\n}: AttachUploadsCollectionArgs): void => {\n\tconst collections = config.collections ?? []\n\tconst target = collections.find((collection) => collection.slug === slug)\n\tif (!target) {\n\t\tthrow new Error(\n\t\t\t`@10x-media/form-builder: uploads.collection \"${slug}\" was not found in config.collections. ` +\n\t\t\t\t`Create an upload-enabled collection (with your storage adapter) and pass its slug, ` +\n\t\t\t\t`e.g. formBuilder({ uploads: { collection: 'media' } }).`\n\t\t)\n\t}\n\tif (!target.upload) {\n\t\tthrow new Error(\n\t\t\t`@10x-media/form-builder: uploads.collection \"${slug}\" has no \\`upload\\` config. ` +\n\t\t\t\t`File fields store Payload uploads, so the collection must be an upload collection ` +\n\t\t\t\t`(set \\`upload: true\\` or an upload options object on it).`\n\t\t)\n\t}\n\n\tconst fields = hasFieldNamed(target.fields, 'owner')\n\t\t? target.fields\n\t\t: [\n\t\t\t\t...target.fields,\n\t\t\t\t{ name: 'owner', type: 'text', admin: { readOnly: true, hidden: true } } as Field,\n\t\t\t]\n\n\tconst attached: CollectionConfig = {\n\t\t...target,\n\t\tfields,\n\t\t...(spam\n\t\t\t? {\n\t\t\t\t\thooks: {\n\t\t\t\t\t\t...(target.hooks ?? {}),\n\t\t\t\t\t\tbeforeOperation: [buildUploadRateLimit(spam), ...(target.hooks?.beforeOperation ?? [])],\n\t\t\t\t\t\tbeforeValidate: [buildUploadOwnerStamp(spam), ...(target.hooks?.beforeValidate ?? [])],\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t}\n\n\tconfig.collections = collections.map((collection) =>\n\t\tcollection.slug === slug ? attached : collection\n\t)\n}\n"],"mappings":";;;AAWA,MAAM,iBAAiB,QAAiB,SACvC,OAAO,MAAM,UAAU;CACtB,IAAI,UAAU,SAAS,MAAM,SAAS,MACrC,OAAO;CAER,OAAO,MAAM,SAAS,SAAS,cAAc,MAAM,QAAQ,IAAI;AAChE,CAAC;;;;;;;;;;AAiBF,MAAa,2BAA2B,EACvC,QACA,MACA,WACwC;CACxC,MAAM,cAAc,OAAO,eAAe,CAAC;CAC3C,MAAM,SAAS,YAAY,MAAM,eAAe,WAAW,SAAS,IAAI;CACxE,IAAI,CAAC,QACJ,MAAM,IAAI,MACT,gDAAgD,KAAK,kLAGtD;CAED,IAAI,CAAC,OAAO,QACX,MAAM,IAAI,MACT,gDAAgD,KAAK,wKAGtD;CAGD,MAAM,SAAS,cAAc,OAAO,QAAQ,OAAO,IAChD,OAAO,SACP,CACA,GAAG,OAAO,QACV;EAAE,MAAM;EAAS,MAAM;EAAQ,OAAO;GAAE,UAAU;GAAM,QAAQ;EAAK;CAAE,CACxE;CAEF,MAAM,WAA6B;EAClC,GAAG;EACH;EACA,GAAI,OACD,EACA,OAAO;GACN,GAAI,OAAO,SAAS,CAAC;GACrB,iBAAiB,CAAC,qBAAqB,IAAI,GAAG,GAAI,OAAO,OAAO,mBAAmB,CAAC,CAAE;GACtF,gBAAgB,CAAC,sBAAsB,IAAI,GAAG,GAAI,OAAO,OAAO,kBAAkB,CAAC,CAAE;EACtF,EACD,IACC,CAAC;CACL;CAEA,OAAO,cAAc,YAAY,KAAK,eACrC,WAAW,SAAS,OAAO,WAAW,UACvC;AACD"}
1
+ {"version":3,"file":"uploadsCollection.js","names":[],"sources":["../../src/plugin/uploadsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Config, Field } from 'payload'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { buildUploadOwnerStamp, buildUploadRateLimit } from '../spam/uploadHooks'\n\n/**\n * Uploads are bring-your-own: `false` (the default) disables file fields entirely, an object points\n * at a host-owned upload collection (created by the app with its storage adapter of choice).\n * `mimeTypes` optionally constrains the file field's MIME picker for hosts that enforce types by other\n * means than the collection's own `upload.mimeTypes` (which is read automatically when this is absent).\n */\nexport type UploadsOption = false | { collection: string; mimeTypes?: string[] }\n\n/**\n * The MIME types the file field's picker should offer: the host upload collection's own\n * `upload.mimeTypes`, or undefined (no constraint, so the picker keeps its full list). Read at\n * plugin-factory time, before the field registry freezes; `attachUploadsCollection` runs too late.\n */\nexport const readUploadCollectionMimeTypes = (\n\tconfig: Config,\n\tslug: string\n): string[] | undefined => {\n\tconst target = config.collections?.find((collection) => collection.slug === slug)\n\tconst upload = target?.upload\n\treturn typeof upload === 'object' && Array.isArray(upload.mimeTypes)\n\t\t? upload.mimeTypes\n\t\t: undefined\n}\n\n/** Descends presentational rows only: an `owner` field nested in a group/tabs/collapsible is not detected, so a top-level one gets appended alongside it. */\nconst hasFieldNamed = (fields: Field[], name: string): boolean =>\n\tfields.some((field) => {\n\t\tif ('name' in field && field.name === name) {\n\t\t\treturn true\n\t\t}\n\t\treturn field.type === 'row' && hasFieldNamed(field.fields, name)\n\t})\n\ntype AttachUploadsCollectionArgs = {\n\tconfig: Config\n\tslug: string\n\tspam: ResolvedSpamConfig | false\n}\n\n/**\n * Wire the plugin's upload concerns into the host-owned upload collection named by\n * `uploads.collection`. The collection must already exist in `config.collections` and carry an\n * `upload` config; anything else is a plugin misconfiguration and throws at boot. The plugin\n * appends its hidden `owner` text field unless the host already defines a field named `owner`\n * (then that field is reused and must stay a text-compatible identity slot), and prepends the\n * spam upload hooks (rate limit, owner stamp) ahead of the host's own hooks so abuse checks run\n * first. The host entry is replaced with a new object; the host's config is never mutated.\n */\nexport const attachUploadsCollection = ({\n\tconfig,\n\tslug,\n\tspam,\n}: AttachUploadsCollectionArgs): void => {\n\tconst collections = config.collections ?? []\n\tconst target = collections.find((collection) => collection.slug === slug)\n\tif (!target) {\n\t\tthrow new Error(\n\t\t\t`@10x-media/form-builder: uploads.collection \"${slug}\" was not found in config.collections. ` +\n\t\t\t\t`Create an upload-enabled collection (with your storage adapter) and pass its slug, ` +\n\t\t\t\t`e.g. formBuilder({ uploads: { collection: 'media' } }).`\n\t\t)\n\t}\n\tif (!target.upload) {\n\t\tthrow new Error(\n\t\t\t`@10x-media/form-builder: uploads.collection \"${slug}\" has no \\`upload\\` config. ` +\n\t\t\t\t`File fields store Payload uploads, so the collection must be an upload collection ` +\n\t\t\t\t`(set \\`upload: true\\` or an upload options object on it).`\n\t\t)\n\t}\n\n\tconst fields = hasFieldNamed(target.fields, 'owner')\n\t\t? target.fields\n\t\t: [\n\t\t\t\t...target.fields,\n\t\t\t\t{ name: 'owner', type: 'text', admin: { readOnly: true, hidden: true } } as Field,\n\t\t\t]\n\n\tconst attached: CollectionConfig = {\n\t\t...target,\n\t\tfields,\n\t\t...(spam\n\t\t\t? {\n\t\t\t\t\thooks: {\n\t\t\t\t\t\t...(target.hooks ?? {}),\n\t\t\t\t\t\tbeforeOperation: [buildUploadRateLimit(spam), ...(target.hooks?.beforeOperation ?? [])],\n\t\t\t\t\t\tbeforeValidate: [buildUploadOwnerStamp(spam), ...(target.hooks?.beforeValidate ?? [])],\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t}\n\n\tconfig.collections = collections.map((collection) =>\n\t\tcollection.slug === slug ? attached : collection\n\t)\n}\n"],"mappings":";;;;;;;AAiBA,MAAa,iCACZ,QACA,SAC0B;CAE1B,MAAM,UADS,OAAO,aAAa,MAAM,eAAe,WAAW,SAAS,IAAI,IACzD;CACvB,OAAO,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,SAAS,IAChE,OAAO,YACP,KAAA;AACJ;;AAGA,MAAM,iBAAiB,QAAiB,SACvC,OAAO,MAAM,UAAU;CACtB,IAAI,UAAU,SAAS,MAAM,SAAS,MACrC,OAAO;CAER,OAAO,MAAM,SAAS,SAAS,cAAc,MAAM,QAAQ,IAAI;AAChE,CAAC;;;;;;;;;;AAiBF,MAAa,2BAA2B,EACvC,QACA,MACA,WACwC;CACxC,MAAM,cAAc,OAAO,eAAe,CAAC;CAC3C,MAAM,SAAS,YAAY,MAAM,eAAe,WAAW,SAAS,IAAI;CACxE,IAAI,CAAC,QACJ,MAAM,IAAI,MACT,gDAAgD,KAAK,kLAGtD;CAED,IAAI,CAAC,OAAO,QACX,MAAM,IAAI,MACT,gDAAgD,KAAK,wKAGtD;CAGD,MAAM,SAAS,cAAc,OAAO,QAAQ,OAAO,IAChD,OAAO,SACP,CACA,GAAG,OAAO,QACV;EAAE,MAAM;EAAS,MAAM;EAAQ,OAAO;GAAE,UAAU;GAAM,QAAQ;EAAK;CAAE,CACxE;CAEF,MAAM,WAA6B;EAClC,GAAG;EACH;EACA,GAAI,OACD,EACA,OAAO;GACN,GAAI,OAAO,SAAS,CAAC;GACrB,iBAAiB,CAAC,qBAAqB,IAAI,GAAG,GAAI,OAAO,OAAO,mBAAmB,CAAC,CAAE;GACtF,gBAAgB,CAAC,sBAAsB,IAAI,GAAG,GAAI,OAAO,OAAO,kBAAkB,CAAC,CAAE;EACtF,EACD,IACC,CAAC;CACL;CAEA,OAAO,cAAc,YAAY,KAAK,eACrC,WAAW,SAAS,OAAO,WAAW,UACvC;AACD"}
@@ -1,5 +1,5 @@
1
1
  import { keys } from "../translations/keys.js";
2
- import { labelFor, labelForKey } from "../translations/server.js";
2
+ import { labelForKey, resolveDefinitionLabel } from "../translations/server.js";
3
3
  //#region src/poll/buildPollOptionSourceFields.ts
4
4
  /**
5
5
  * Builds the `optionSource` select and `sourceConfig` group for the forms poll group; returns
@@ -29,7 +29,7 @@ const buildPollOptionSourceFields = (registry) => {
29
29
  label: labelForKey(keys.pollOptionSource),
30
30
  admin: { description: labelForKey(keys.pollOptionSourceDescription) },
31
31
  options: sources.map((source) => ({
32
- label: labelFor(source.label),
32
+ label: resolveDefinitionLabel(source.label),
33
33
  value: source.type
34
34
  }))
35
35
  }, {