@10x-media/form-builder 0.1.0-beta.1 → 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +4 -2
  3. package/dist/actions/body/converters.d.ts +20 -0
  4. package/dist/actions/body/converters.js +57 -0
  5. package/dist/actions/body/converters.js.map +1 -0
  6. package/dist/actions/body/escapeHtml.d.ts +6 -0
  7. package/dist/actions/body/escapeHtml.js +14 -0
  8. package/dist/actions/body/escapeHtml.js.map +1 -0
  9. package/dist/actions/body/serializeBody.d.ts +35 -0
  10. package/dist/actions/body/serializeBody.js +76 -0
  11. package/dist/actions/body/serializeBody.js.map +1 -0
  12. package/dist/actions/body/serializeSlate.js +31 -0
  13. package/dist/actions/body/serializeSlate.js.map +1 -0
  14. package/dist/actions/body/wildcards.d.ts +10 -0
  15. package/dist/actions/body/wildcards.js +33 -0
  16. package/dist/actions/body/wildcards.js.map +1 -0
  17. package/dist/actions/builtin/confirmation.js +28 -12
  18. package/dist/actions/builtin/confirmation.js.map +1 -1
  19. package/dist/actions/builtin/emailTeam.js +7 -9
  20. package/dist/actions/builtin/emailTeam.js.map +1 -1
  21. package/dist/actions/builtin/signedWebhook.js +21 -3
  22. package/dist/actions/builtin/signedWebhook.js.map +1 -1
  23. package/dist/actions/defineAction.d.ts +2 -1
  24. package/dist/actions/defineAction.js.map +1 -1
  25. package/dist/actions/dispatch.js +2 -1
  26. package/dist/actions/dispatch.js.map +1 -1
  27. package/dist/actions/runActions.js +8 -1
  28. package/dist/actions/runActions.js.map +1 -1
  29. package/dist/actions/task.js +7 -5
  30. package/dist/actions/task.js.map +1 -1
  31. package/dist/calc/normalizeCalc.js +5 -17
  32. package/dist/calc/normalizeCalc.js.map +1 -1
  33. package/dist/calc/types.d.ts +8 -2
  34. package/dist/calc/types.js +22 -0
  35. package/dist/calc/types.js.map +1 -0
  36. package/dist/client/ByteSizeField.d.ts +21 -0
  37. package/dist/client/ByteSizeField.js +56 -0
  38. package/dist/client/ByteSizeField.js.map +1 -0
  39. package/dist/client/ConditionBuilder.js +2 -1
  40. package/dist/client/ConditionBuilder.js.map +1 -1
  41. package/dist/client/FieldNameSelect.d.ts +20 -0
  42. package/dist/client/FieldNameSelect.js +58 -0
  43. package/dist/client/FieldNameSelect.js.map +1 -0
  44. package/dist/client/FlowBuilder.d.ts +0 -1
  45. package/dist/client/FlowBuilder.js +4 -6
  46. package/dist/client/FlowBuilder.js.map +1 -1
  47. package/dist/client/FormConditionField.js +1 -4
  48. package/dist/client/FormConditionField.js.map +1 -1
  49. package/dist/client/admin.css +192 -0
  50. package/dist/client/toStaticLabel.js +13 -0
  51. package/dist/client/toStaticLabel.js.map +1 -0
  52. package/dist/collections/formSubmissions.js +60 -39
  53. package/dist/collections/formSubmissions.js.map +1 -1
  54. package/dist/collections/forms.js +143 -115
  55. package/dist/collections/forms.js.map +1 -1
  56. package/dist/collections/uploads.d.ts +2 -1
  57. package/dist/collections/uploads.js +22 -13
  58. package/dist/collections/uploads.js.map +1 -1
  59. package/dist/exports/client.d.ts +3 -1
  60. package/dist/exports/client.js +3 -1
  61. package/dist/exports/react.d.ts +7 -2
  62. package/dist/exports/react.js +6 -2
  63. package/dist/exports/types.d.ts +2 -1
  64. package/dist/fields/buildFieldBlocks.js +22 -12
  65. package/dist/fields/buildFieldBlocks.js.map +1 -1
  66. package/dist/fields/builtin/calculation.js +115 -2
  67. package/dist/fields/builtin/calculation.js.map +1 -1
  68. package/dist/fields/builtin/date.js +29 -0
  69. package/dist/fields/builtin/date.js.map +1 -0
  70. package/dist/fields/builtin/file.d.ts +15 -0
  71. package/dist/fields/builtin/file.js +79 -3
  72. package/dist/fields/builtin/file.js.map +1 -1
  73. package/dist/fields/builtin/index.d.ts +14 -0
  74. package/dist/fields/builtin/index.js +13 -2
  75. package/dist/fields/builtin/index.js.map +1 -1
  76. package/dist/fields/builtin/repeater.js +44 -0
  77. package/dist/fields/builtin/repeater.js.map +1 -0
  78. package/dist/fields/fieldNamesOfType.js +25 -0
  79. package/dist/fields/fieldNamesOfType.js.map +1 -0
  80. package/dist/fields/sharedConfig.js +30 -18
  81. package/dist/fields/sharedConfig.js.map +1 -1
  82. package/dist/fields/types.d.ts +2 -1
  83. package/dist/index.d.ts +38 -2
  84. package/dist/index.js +17 -5
  85. package/dist/index.js.map +1 -1
  86. package/dist/plugin/collectionOverrides.d.ts +22 -0
  87. package/dist/plugin/registerCollections.js +39 -10
  88. package/dist/plugin/registerCollections.js.map +1 -1
  89. package/dist/react/Form.d.ts +32 -3
  90. package/dist/react/Form.js +50 -8
  91. package/dist/react/Form.js.map +1 -1
  92. package/dist/react/FormContext.js.map +1 -1
  93. package/dist/react/Poll.js +1 -1
  94. package/dist/react/captcha/HcaptchaCaptcha.d.ts +28 -0
  95. package/dist/react/captcha/HcaptchaCaptcha.js +53 -0
  96. package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -0
  97. package/dist/react/captcha/RecaptchaCaptcha.d.ts +41 -0
  98. package/dist/react/captcha/RecaptchaCaptcha.js +93 -0
  99. package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -0
  100. package/dist/react/captcha/TurnstileCaptcha.d.ts +28 -0
  101. package/dist/react/captcha/TurnstileCaptcha.js +53 -0
  102. package/dist/react/captcha/TurnstileCaptcha.js.map +1 -0
  103. package/dist/react/captcha/types.d.ts +8 -0
  104. package/dist/react/captcha/useCaptchaScript.js +45 -0
  105. package/dist/react/captcha/useCaptchaScript.js.map +1 -0
  106. package/dist/react/captcha/vendors.js +8 -0
  107. package/dist/react/captcha/vendors.js.map +1 -0
  108. package/dist/react/primitives/Input.d.ts +1 -1
  109. package/dist/react/primitives/Input.js.map +1 -1
  110. package/dist/react/renderers/calculation.js +1 -1
  111. package/dist/react/renderers/checkbox.js +1 -1
  112. package/dist/react/renderers/consent.js +1 -1
  113. package/dist/react/renderers/date.js +37 -0
  114. package/dist/react/renderers/date.js.map +1 -0
  115. package/dist/react/renderers/email.js +1 -1
  116. package/dist/react/renderers/file.js +20 -7
  117. package/dist/react/renderers/file.js.map +1 -1
  118. package/dist/react/renderers/index.js +4 -0
  119. package/dist/react/renderers/index.js.map +1 -1
  120. package/dist/react/renderers/number.js +1 -1
  121. package/dist/react/renderers/repeater.js +113 -0
  122. package/dist/react/renderers/repeater.js.map +1 -0
  123. package/dist/react/renderers/select.js +1 -1
  124. package/dist/react/renderers/text.js +1 -1
  125. package/dist/react/renderers/textarea.js +1 -1
  126. package/dist/spam/captcha.d.ts +3 -2
  127. package/dist/spam/captcha.js +3 -2
  128. package/dist/spam/captcha.js.map +1 -1
  129. package/dist/spam/clientIp.js +9 -0
  130. package/dist/spam/clientIp.js.map +1 -0
  131. package/dist/spam/identify.js +3 -6
  132. package/dist/spam/identify.js.map +1 -1
  133. package/dist/spam/providers/hcaptcha.d.ts +17 -0
  134. package/dist/spam/providers/hcaptcha.js +18 -0
  135. package/dist/spam/providers/hcaptcha.js.map +1 -0
  136. package/dist/spam/providers/recaptcha.d.ts +23 -0
  137. package/dist/spam/providers/recaptcha.js +23 -0
  138. package/dist/spam/providers/recaptcha.js.map +1 -0
  139. package/dist/spam/providers/siteverify.js +34 -0
  140. package/dist/spam/providers/siteverify.js.map +1 -0
  141. package/dist/spam/providers/turnstile.d.ts +17 -0
  142. package/dist/spam/providers/turnstile.js +18 -0
  143. package/dist/spam/providers/turnstile.js.map +1 -0
  144. package/dist/spam/spamGuard.js +2 -5
  145. package/dist/spam/spamGuard.js.map +1 -1
  146. package/dist/spam/types.d.ts +2 -2
  147. package/dist/submissions/SubmissionAnswers.d.ts +2 -4
  148. package/dist/submissions/SubmissionAnswers.js +110 -34
  149. package/dist/submissions/SubmissionAnswers.js.map +1 -1
  150. package/dist/submissions/SubmissionAnswersClient.js +142 -0
  151. package/dist/submissions/SubmissionAnswersClient.js.map +1 -0
  152. package/dist/submissions/runSubmission.js +54 -2
  153. package/dist/submissions/runSubmission.js.map +1 -1
  154. package/dist/submissions/types.d.ts +2 -1
  155. package/dist/translations/en.js +52 -5
  156. package/dist/translations/en.js.map +1 -1
  157. package/dist/translations/keys.d.ts +51 -4
  158. package/dist/translations/keys.js +52 -5
  159. package/dist/translations/keys.js.map +1 -1
  160. package/dist/uploads/formatBytes.d.ts +9 -0
  161. package/dist/uploads/formatBytes.js +25 -0
  162. package/dist/uploads/formatBytes.js.map +1 -0
  163. package/dist/uploads/resolveFileRef.js +1 -1
  164. package/dist/uploads/resolveFileRef.js.map +1 -1
  165. package/dist/validation/builtin/index.d.ts +14 -0
  166. package/dist/validation/builtin/index.js +12 -1
  167. package/dist/validation/builtin/index.js.map +1 -1
  168. package/dist/validation/builtin/maxDate.js +23 -0
  169. package/dist/validation/builtin/maxDate.js.map +1 -0
  170. package/dist/validation/builtin/minDate.js +23 -0
  171. package/dist/validation/builtin/minDate.js.map +1 -0
  172. package/dist/validation/builtin/validateDateBound.js +18 -0
  173. package/dist/validation/builtin/validateDateBound.js.map +1 -0
  174. package/dist/validation/runValidation.js +1 -1
  175. package/dist/validation/runValidation.js.map +1 -1
  176. package/package.json +4 -3
  177. package/styles/form-builder.css +0 -190
package/dist/index.d.ts CHANGED
@@ -14,11 +14,15 @@ import { PresentationDensity, PresentationDescriptor, PresentationSurface } from
14
14
  import { interpolate } from "./recall/interpolate.js";
15
15
  import { RecallResolver, buildRecallResolver, optionLabelsFor } from "./recall/resolver.js";
16
16
  import { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from "./spam/constants.js";
17
+ import { formatBytes } from "./uploads/formatBytes.js";
17
18
  import { ActionDefinition, ActionRunArgs, AnyActionDefinition, defineAction } from "./actions/defineAction.js";
18
19
  import { ActionOption, ActionRegistry, ActionsConfig, resolveActions } from "./actions/registry.js";
20
+ import { BodyConverter, BodyConverterArgs, BodyRender, defaultBodyConverters, sanitizeUrl } from "./actions/body/converters.js";
21
+ import { BodyContext, RichTextBodyOption, SerializeBodyArgs, serializeBody } from "./actions/body/serializeBody.js";
19
22
  import { ActionResult } from "./actions/runActions.js";
20
23
  import { AggregateFieldResponsesArgs, AggregateFormResponsesArgs, aggregateFieldResponses, aggregateFormResponses, fieldHasOptions } from "./aggregation/aggregateResponses.js";
21
24
  import { ResolveResultsRequestArgs, ResolveResultsRequestResult, resolveFormResultsRequest } from "./aggregation/resolveResultsRequest.js";
25
+ import { CollectionOverrides } from "./plugin/collectionOverrides.js";
22
26
  import { FORM_UPLOADS_SLUG, UploadsCollectionConfig, UploadsOption, buildUploadsCollection, resolveUploads } from "./collections/uploads.js";
23
27
  import { FieldCondition } from "./conditions/types.js";
24
28
  import { AnyConsentSource, ConsentLink, ConsentResolveArgs, ConsentResolved, ConsentSource, defineConsentSource } from "./consent/defineConsentSource.js";
@@ -27,6 +31,8 @@ import { ConsentProof, captureConsent } from "./consent/captureConsent.js";
27
31
  import { PresentationDescriptorOption, PresentationDescriptorRegistry, PresentationsDescriptorConfig, resolvePresentationDescriptors } from "./presentations/registry.js";
28
32
  import { CaptchaProvider, CaptchaVerifyArgs, IdentifyFn, RateLimitCheckArgs, RateLimitConfig, RateLimitResult, RateLimiter, SpamConfig, SpamMetadataConfig, SpamOption } from "./spam/types.js";
29
33
  import { ValidationRuleOption, ValidationRuleRegistry, ValidationRulesConfig } from "./validation/registry.js";
34
+ import { escapeHtml } from "./actions/body/escapeHtml.js";
35
+ import { renderAllValues, renderAllValuesTable } from "./actions/body/wildcards.js";
30
36
  import { defaultActionDefinitions } from "./actions/builtin/index.js";
31
37
  import { SIGNATURE_HEADER, signPayload } from "./actions/sign.js";
32
38
  import { aggregateRowForField, aggregateRowsForFields } from "./aggregation/aggregateRows.js";
@@ -34,14 +40,20 @@ import { normalizeCalc } from "./calc/normalizeCalc.js";
34
40
  import { defaultConsentSources } from "./consent/builtin/index.js";
35
41
  import { resolveConsentLinks } from "./consent/resolveConsentLinks.js";
36
42
  import { resolvePublishedVersionRef } from "./consent/resolvePublishedVersionRef.js";
43
+ import { defaultFieldDefinitions, defaultFieldDefinitionsByType } from "./fields/builtin/index.js";
44
+ import { fileMimeTypeOptions } from "./fields/builtin/file.js";
37
45
  import { defineFormField } from "./fields/defineFormField.js";
38
46
  import { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from "./presentations/defaults.js";
39
47
  import { defineCaptchaProvider } from "./spam/captcha.js";
40
48
  import { defaultIdentify } from "./spam/identify.js";
49
+ import { HcaptchaProviderOptions, hcaptchaProvider } from "./spam/providers/hcaptcha.js";
50
+ import { RecaptchaProviderOptions, recaptchaProvider } from "./spam/providers/recaptcha.js";
51
+ import { TurnstileProviderOptions, turnstileProvider } from "./spam/providers/turnstile.js";
41
52
  import { createKvRateLimiter } from "./spam/rateLimiter.js";
42
53
  import { resolveSpamConfig } from "./spam/resolveSpam.js";
43
54
  import { resolveFileRef } from "./uploads/resolveFileRef.js";
44
55
  import { captureFileRef } from "./uploads/captureFileRef.js";
56
+ import { defaultValidationRules, defaultValidationRulesByType } from "./validation/builtin/index.js";
45
57
  import { defineValidationRule } from "./validation/defineValidationRule.js";
46
58
 
47
59
  //#region src/index.d.ts
@@ -58,10 +70,34 @@ type FormBuilderPluginOptions = {
58
70
  fields?: FieldTypesConfig; /** Add, override, or remove presentations. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
59
71
  presentations?: PresentationsDescriptorConfig; /** Add, override, or remove validation rule types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
60
72
  rules?: ValidationRulesConfig; /** Add, override, or remove post-submit action types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
61
- actions?: ActionsConfig; /** Add, override, or remove consent source types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
73
+ actions?: ActionsConfig;
74
+ /**
75
+ * Customize how rich text action bodies are rendered. `converters` spread over the default
76
+ * Lexical node converters; `serialize` replaces the whole pipeline (e.g. to target chat or
77
+ * plain-text channels instead of email HTML).
78
+ */
79
+ richText?: RichTextBodyOption; /** Add, override, or remove consent source types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */
62
80
  consentSources?: ConsentSourcesConfig; /** The built-in `form-uploads` collection backing file fields. `false` disables it (bring your own); an object overrides slug/upload/access/fields. */
63
81
  uploads?: UploadsOption; /** Honeypot + rate-limiting (on by default) + a captcha adapter seam + upload-ownership scoping. `false` disables the whole subsystem. */
64
82
  spam?: SpamOption;
83
+ /**
84
+ * When `true`, the raw `values`, `descriptors`, and `consent` JSON fields are visible in the
85
+ * submission admin view. Default `false` — those fields are fully represented by the
86
+ * `SubmissionAnswers` UI component and are noisy when shown alongside it.
87
+ */
88
+ showSubmissionRawFields?: boolean;
89
+ /**
90
+ * Override individual plugin-managed collections using explicit spreads. Each key accepts a
91
+ * `CollectionOverrides` object: top-level keys are spread with the plugin's defaults (spread
92
+ * order per key determines who wins), hooks are appended after the plugin's own hooks, and
93
+ * `fields` is a function that receives the default fields and returns the final array so
94
+ * additions/removals are always intentional.
95
+ */
96
+ overrides?: {
97
+ forms?: CollectionOverrides;
98
+ formSubmissions?: CollectionOverrides;
99
+ uploads?: CollectionOverrides;
100
+ };
65
101
  };
66
102
  declare module 'payload' {
67
103
  interface RegisteredPlugins {
@@ -70,5 +106,5 @@ declare module 'payload' {
70
106
  }
71
107
  declare const formBuilder: (options: FormBuilderPluginOptions) => import("payload").Plugin;
72
108
  //#endregion
73
- export { type ActionDefinition, type ActionOption, type ActionRegistry, type ActionResult, type ActionRunArgs, type ActionsConfig, type AggregateFieldResponsesArgs, type AggregateFormResponsesArgs, type AggregationBucket, type AggregationRow, type AnyActionDefinition, type AnyConsentSource, type AnyFormFieldDefinition, type AnyValidationRuleDefinition, CAPTCHA_TOKEN_KEY, type CalcExpression, type CaptchaProvider, type CaptchaVerifyArgs, type ConsentLink, type ConsentProof, type ConsentResolveArgs, type ConsentResolved, type ConsentSource, type ConsentSourceOption, type ConsentSourceRegistry, type ConsentSourcesConfig, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, FORM_UPLOADS_SLUG, type FieldAggregation, type FieldCondition, type FieldMeta, type FieldTypeOption, type FieldTypeRegistry, type FieldTypesConfig, type FileFieldConfig, type FileRef, type FileRefError, FormBuilderPluginOptions, type FormBuilderPluginOptions as PluginOptions, type FormFieldDefinition, type FormFieldFormat, type FormFieldValidate, type FormFieldValueKind, type IdentifyFn, type PrefillOptions, type PresentationDensity, type PresentationDescriptor, type PresentationDescriptorOption, type PresentationDescriptorRegistry, type PresentationSurface, type PresentationsDescriptorConfig, type RateLimitCheckArgs, type RateLimitConfig, type RateLimitResult, type RateLimiter, type RecallResolver, type ResolveResultsRequestArgs, type ResolveResultsRequestResult, SIGNATURE_HEADER, type SpamConfig, type SpamMetadataConfig, type SpamOption, type SubmissionStatusFilter, type UploadsCollectionConfig, type UploadsOption, type ValidationRuleDefinition, type ValidationRuleOption, type ValidationRuleRegistry, type ValidationRuleResult, type ValidationRulesConfig, type ValidationSeverity, aggregateFieldResponses, aggregateFormResponses, aggregateRowForField, aggregateRowsForFields, buildRecallResolver, buildUploadsCollection, calcExpressionOf, captureConsent, captureFileRef, computeCalcFields, createKvRateLimiter, defaultActionDefinitions, defaultConsentSources, defaultIdentify, defaultPresentationDescriptors, defineAction, defineCaptchaProvider, defineConsentSource, defineFormField, defineValidationRule, evaluateCalc, evaluateCondition, fieldHasOptions, formBuilder, interpolate, normalizeCalc, optionLabelsFor, resolveActions, resolveConsentLinks, resolveConsentSources, resolveFileRef, resolveFormResultsRequest, resolvePresentationDescriptors, resolvePublishedVersionRef, resolveSpamConfig, resolveUploads, signPayload, valuesFromSearchParams };
109
+ export { type ActionDefinition, type ActionOption, type ActionRegistry, type ActionResult, type ActionRunArgs, type ActionsConfig, type AggregateFieldResponsesArgs, type AggregateFormResponsesArgs, type AggregationBucket, type AggregationRow, type AnyActionDefinition, type AnyConsentSource, type AnyFormFieldDefinition, type AnyValidationRuleDefinition, type BodyContext, type BodyConverter, type BodyConverterArgs, type BodyRender, CAPTCHA_TOKEN_KEY, type CalcExpression, type CaptchaProvider, type CaptchaVerifyArgs, type ConsentLink, type ConsentProof, type ConsentResolveArgs, type ConsentResolved, type ConsentSource, type ConsentSourceOption, type ConsentSourceRegistry, type ConsentSourcesConfig, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, FORM_UPLOADS_SLUG, type FieldAggregation, type FieldCondition, type FieldMeta, type FieldTypeOption, type FieldTypeRegistry, type FieldTypesConfig, type FileFieldConfig, type FileRef, type FileRefError, FormBuilderPluginOptions, type FormBuilderPluginOptions as PluginOptions, type FormFieldDefinition, type FormFieldFormat, type FormFieldValidate, type FormFieldValueKind, type HcaptchaProviderOptions, type IdentifyFn, type PrefillOptions, type PresentationDensity, type PresentationDescriptor, type PresentationDescriptorOption, type PresentationDescriptorRegistry, type PresentationSurface, type PresentationsDescriptorConfig, type RateLimitCheckArgs, type RateLimitConfig, type RateLimitResult, type RateLimiter, type RecallResolver, type RecaptchaProviderOptions, type ResolveResultsRequestArgs, type ResolveResultsRequestResult, type RichTextBodyOption, SIGNATURE_HEADER, type SerializeBodyArgs, type SpamConfig, type SpamMetadataConfig, type SpamOption, type SubmissionStatusFilter, type TurnstileProviderOptions, type UploadsCollectionConfig, type UploadsOption, type ValidationRuleDefinition, type ValidationRuleOption, type ValidationRuleRegistry, type ValidationRuleResult, type ValidationRulesConfig, type ValidationSeverity, aggregateFieldResponses, aggregateFormResponses, aggregateRowForField, aggregateRowsForFields, buildRecallResolver, buildUploadsCollection, calcExpressionOf, captureConsent, captureFileRef, computeCalcFields, createKvRateLimiter, defaultActionDefinitions, defaultBodyConverters, defaultConsentSources, defaultFieldDefinitions, defaultFieldDefinitionsByType, defaultIdentify, defaultPresentationDescriptors, defaultValidationRules, defaultValidationRulesByType, defineAction, defineCaptchaProvider, defineConsentSource, defineFormField, defineValidationRule, escapeHtml, evaluateCalc, evaluateCondition, fieldHasOptions, fileMimeTypeOptions, formBuilder, formatBytes, hcaptchaProvider, interpolate, normalizeCalc, optionLabelsFor, recaptchaProvider, renderAllValues, renderAllValuesTable, resolveActions, resolveConsentLinks, resolveConsentSources, resolveFileRef, resolveFormResultsRequest, resolvePresentationDescriptors, resolvePublishedVersionRef, resolveSpamConfig, resolveUploads, sanitizeUrl, serializeBody, signPayload, turnstileProvider, valuesFromSearchParams };
74
110
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import { interpolate } from "./recall/interpolate.js";
2
+ import { escapeHtml } from "./actions/body/escapeHtml.js";
3
+ import { defaultBodyConverters, sanitizeUrl } from "./actions/body/converters.js";
4
+ import { renderAllValues, renderAllValuesTable } from "./actions/body/wildcards.js";
5
+ import { serializeBody } from "./actions/body/serializeBody.js";
2
6
  import { defineAction } from "./actions/defineAction.js";
3
7
  import { SIGNATURE_HEADER, signPayload } from "./actions/sign.js";
4
8
  import { defaultActionDefinitions } from "./actions/builtin/index.js";
@@ -8,14 +12,15 @@ import { defineConsentSource } from "./consent/defineConsentSource.js";
8
12
  import { resolvePublishedVersionRef } from "./consent/resolvePublishedVersionRef.js";
9
13
  import { defaultConsentSources } from "./consent/builtin/index.js";
10
14
  import { resolveConsentSources } from "./consent/registry.js";
15
+ import { normalizeCalc } from "./calc/normalizeCalc.js";
11
16
  import { defineFormField } from "./fields/defineFormField.js";
12
- import { defaultFieldDefinitions } from "./fields/builtin/index.js";
17
+ import { fileMimeTypeOptions } from "./fields/builtin/file.js";
18
+ import { defaultFieldDefinitions, defaultFieldDefinitionsByType } from "./fields/builtin/index.js";
13
19
  import { resolveFieldTypes } from "./fields/registry.js";
14
20
  import { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from "./spam/constants.js";
15
21
  import { aggregateRowForField, aggregateRowsForFields } from "./aggregation/aggregateRows.js";
16
22
  import { aggregateFieldResponses, aggregateFormResponses, fieldHasOptions } from "./aggregation/aggregateResponses.js";
17
23
  import { resolveFormResultsRequest } from "./aggregation/resolveResultsRequest.js";
18
- import { normalizeCalc } from "./calc/normalizeCalc.js";
19
24
  import { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from "./presentations/defaults.js";
20
25
  import { evaluateCalc } from "./calc/evaluate.js";
21
26
  import { calcExpressionOf, computeCalcFields } from "./calc/computeCalcFields.js";
@@ -31,11 +36,15 @@ import { defaultIdentify } from "./spam/identify.js";
31
36
  import { createKvRateLimiter } from "./spam/rateLimiter.js";
32
37
  import { resolveSpamConfig } from "./spam/resolveSpam.js";
33
38
  import { defineValidationRule } from "./validation/defineValidationRule.js";
34
- import { defaultValidationRules } from "./validation/builtin/index.js";
39
+ import { defaultValidationRules, defaultValidationRulesByType } from "./validation/builtin/index.js";
35
40
  import { resolveValidationRules } from "./validation/registry.js";
36
41
  import { valuesFromSearchParams } from "./prefill/valuesFromSearchParams.js";
37
42
  import { buildRecallResolver, optionLabelsFor } from "./recall/resolver.js";
38
43
  import { defineCaptchaProvider } from "./spam/captcha.js";
44
+ import { hcaptchaProvider } from "./spam/providers/hcaptcha.js";
45
+ import { recaptchaProvider } from "./spam/providers/recaptcha.js";
46
+ import { turnstileProvider } from "./spam/providers/turnstile.js";
47
+ import { formatBytes } from "./uploads/formatBytes.js";
39
48
  import { definePlugin } from "payload";
40
49
  //#region src/index.ts
41
50
  const formBuilder = definePlugin({
@@ -58,15 +67,18 @@ const formBuilder = definePlugin({
58
67
  consentRegistry,
59
68
  presentationRegistry,
60
69
  actionRegistry,
70
+ richText: options.richText,
61
71
  hasJobsPlugin: Boolean(plugins["@10x-media/jobs"]),
62
72
  events: options.events,
63
73
  uploads,
64
- spam
74
+ spam,
75
+ showSubmissionRawFields: options.showSubmissionRawFields ?? false,
76
+ overrides: options.overrides
65
77
  });
66
78
  return config;
67
79
  }
68
80
  });
69
81
  //#endregion
70
- export { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, FORM_UPLOADS_SLUG, SIGNATURE_HEADER, aggregateFieldResponses, aggregateFormResponses, aggregateRowForField, aggregateRowsForFields, buildRecallResolver, buildUploadsCollection, calcExpressionOf, captureConsent, captureFileRef, computeCalcFields, createKvRateLimiter, defaultActionDefinitions, defaultConsentSources, defaultIdentify, defaultPresentationDescriptors, defineAction, defineCaptchaProvider, defineConsentSource, defineFormField, defineValidationRule, evaluateCalc, evaluateCondition, fieldHasOptions, formBuilder, interpolate, normalizeCalc, optionLabelsFor, resolveActions, resolveConsentLinks, resolveConsentSources, resolveFileRef, resolveFormResultsRequest, resolvePresentationDescriptors, resolvePublishedVersionRef, resolveSpamConfig, resolveUploads, signPayload, valuesFromSearchParams };
82
+ export { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, FORM_UPLOADS_SLUG, SIGNATURE_HEADER, aggregateFieldResponses, aggregateFormResponses, aggregateRowForField, aggregateRowsForFields, buildRecallResolver, buildUploadsCollection, calcExpressionOf, captureConsent, captureFileRef, computeCalcFields, createKvRateLimiter, defaultActionDefinitions, defaultBodyConverters, defaultConsentSources, defaultFieldDefinitions, defaultFieldDefinitionsByType, defaultIdentify, defaultPresentationDescriptors, defaultValidationRules, defaultValidationRulesByType, defineAction, defineCaptchaProvider, defineConsentSource, defineFormField, defineValidationRule, escapeHtml, evaluateCalc, evaluateCondition, fieldHasOptions, fileMimeTypeOptions, formBuilder, formatBytes, hcaptchaProvider, interpolate, normalizeCalc, optionLabelsFor, recaptchaProvider, renderAllValues, renderAllValuesTable, resolveActions, resolveConsentLinks, resolveConsentSources, resolveFileRef, resolveFormResultsRequest, resolvePresentationDescriptors, resolvePublishedVersionRef, resolveSpamConfig, resolveUploads, sanitizeUrl, serializeBody, signPayload, turnstileProvider, valuesFromSearchParams };
71
83
 
72
84
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type Config, definePlugin } from 'payload'\nimport { defaultActionDefinitions } from './actions/builtin'\nimport type { ActionsConfig } from './actions/registry'\nimport { resolveActions } from './actions/registry'\nimport { resolveUploads, type UploadsOption } from './collections/uploads'\nimport { defaultConsentSources } from './consent/builtin'\nimport type { ConsentSourcesConfig } from './consent/registry'\nimport { resolveConsentSources } from './consent/registry'\nimport type { FormEventSink } from './events/types'\nimport { defaultFieldDefinitions } from './fields/builtin'\nimport { type FieldTypesConfig, resolveFieldTypes } from './fields/registry'\nimport { registerCollections } from './plugin/registerCollections'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport { defaultPresentationDescriptors } from './presentations/defaults'\nimport type { PresentationsDescriptorConfig } from './presentations/registry'\nimport { resolvePresentationDescriptors } from './presentations/registry'\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/** 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 presentations. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */\n\tpresentations?: PresentationsDescriptorConfig\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/** Add, override, or remove consent source types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */\n\tconsentSources?: ConsentSourcesConfig\n\t/** The built-in `form-uploads` collection backing file fields. `false` disables it (bring your own); an object overrides slug/upload/access/fields. */\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}\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 registry = resolveFieldTypes(defaultFieldDefinitions, options.fields)\n\t\tconst ruleRegistry = resolveValidationRules(defaultValidationRules, options.rules)\n\t\tconst consentRegistry = resolveConsentSources(defaultConsentSources, options.consentSources)\n\t\tconst presentationRegistry = resolvePresentationDescriptors(\n\t\t\tdefaultPresentationDescriptors,\n\t\t\toptions.presentations\n\t\t)\n\t\tconst actionRegistry = resolveActions(defaultActionDefinitions, options.actions)\n\t\tconst uploads = resolveUploads(options.uploads)\n\t\tconst spam = resolveSpamConfig(options.spam)\n\t\tregisterTranslations(config, options.translations)\n\t\tregisterCollections({\n\t\t\tconfig,\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tpresentationRegistry,\n\t\t\tactionRegistry,\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})\n\t\treturn config\n\t},\n})\n\nexport { defaultActionDefinitions } from './actions/builtin'\nexport type { ActionDefinition, ActionRunArgs, AnyActionDefinition } from './actions/defineAction'\nexport { defineAction } from './actions/defineAction'\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\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 { UploadsCollectionConfig, UploadsOption } from './collections/uploads'\nexport { buildUploadsCollection, FORM_UPLOADS_SLUG, resolveUploads } from './collections/uploads'\nexport { evaluateCondition } from './conditions/evaluate'\nexport type { FieldCondition } from './conditions/types'\nexport { defaultConsentSources } from './consent/builtin'\nexport type { ConsentProof } from './consent/captureConsent'\nexport { captureConsent } from './consent/captureConsent'\nexport type {\n\tAnyConsentSource,\n\tConsentLink,\n\tConsentResolveArgs,\n\tConsentResolved,\n\tConsentSource,\n} from './consent/defineConsentSource'\nexport { defineConsentSource } from './consent/defineConsentSource'\nexport type {\n\tConsentSourceOption,\n\tConsentSourceRegistry,\n\tConsentSourcesConfig,\n} from './consent/registry'\nexport { resolveConsentSources } from './consent/registry'\nexport { resolveConsentLinks } from './consent/resolveConsentLinks'\nexport { resolvePublishedVersionRef } from './consent/resolvePublishedVersionRef'\nexport { defineFormField } from './fields/defineFormField'\nexport type { FieldTypeOption, FieldTypeRegistry, FieldTypesConfig } from './fields/registry'\nexport type {\n\tAnyFormFieldDefinition,\n\tFormFieldDefinition,\n\tFormFieldFormat,\n\tFormFieldValidate,\n\tFormFieldValueKind,\n} from './fields/types'\nexport type { PrefillOptions } from './prefill/valuesFromSearchParams'\nexport { valuesFromSearchParams } from './prefill/valuesFromSearchParams'\nexport { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from './presentations/defaults'\nexport type {\n\tPresentationDescriptorOption,\n\tPresentationDescriptorRegistry,\n\tPresentationsDescriptorConfig,\n} from './presentations/registry'\nexport { resolvePresentationDescriptors } from './presentations/registry'\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 { 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 { captureFileRef } from './uploads/captureFileRef'\nexport { resolveFileRef } from './uploads/resolveFileRef'\nexport type { FileFieldConfig, FileRef, FileRefError } from './uploads/types'\nexport { defineValidationRule } from './validation/defineValidationRule'\nexport type {\n\tValidationRuleOption,\n\tValidationRuleRegistry,\n\tValidationRulesConfig,\n} from './validation/registry'\nexport type {\n\tAnyValidationRuleDefinition,\n\tValidationRuleDefinition,\n\tValidationRuleResult,\n\tValidationSeverity,\n} from './validation/types'\nexport type { FormBuilderPluginOptions as PluginOptions }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,MAAa,cAAc,aAAuC;CACjE,MAAM;CACN,OAAO;CACP,SAAS,EAAE,QAAQ,SAAS,GAAG,cAAsB;EACpD,IAAI,QAAQ,aAAa,MACxB,OAAO;EAER,MAAM,WAAW,kBAAkB,yBAAyB,QAAQ,MAAM;EAC1E,MAAM,eAAe,uBAAuB,wBAAwB,QAAQ,KAAK;EACjF,MAAM,kBAAkB,sBAAsB,uBAAuB,QAAQ,cAAc;EAC3F,MAAM,uBAAuB,+BAC5B,gCACA,QAAQ,aACT;EACA,MAAM,iBAAiB,eAAe,0BAA0B,QAAQ,OAAO;EAC/E,MAAM,UAAU,eAAe,QAAQ,OAAO;EAC9C,MAAM,OAAO,kBAAkB,QAAQ,IAAI;EAC3C,qBAAqB,QAAQ,QAAQ,YAAY;EACjD,oBAAoB;GACnB;GACA;GACA;GACA;GACA;GACA;GACA,eAAe,QAAQ,QAAQ,kBAAkB;GACjD,QAAQ,QAAQ;GAChB;GACA;EACD,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 type { RichTextBodyOption } from './actions/body/serializeBody'\nimport { defaultActionDefinitions } from './actions/builtin'\nimport type { ActionsConfig } from './actions/registry'\nimport { resolveActions } from './actions/registry'\nimport { resolveUploads, type UploadsOption } from './collections/uploads'\nimport { defaultConsentSources } from './consent/builtin'\nimport type { ConsentSourcesConfig } from './consent/registry'\nimport { resolveConsentSources } from './consent/registry'\nimport type { FormEventSink } from './events/types'\nimport { defaultFieldDefinitions } from './fields/builtin'\nimport { type FieldTypesConfig, resolveFieldTypes } from './fields/registry'\nimport type { CollectionOverrides } from './plugin/collectionOverrides'\nimport { registerCollections } from './plugin/registerCollections'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport { defaultPresentationDescriptors } from './presentations/defaults'\nimport type { PresentationsDescriptorConfig } from './presentations/registry'\nimport { resolvePresentationDescriptors } from './presentations/registry'\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/** 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 presentations. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */\n\tpresentations?: PresentationsDescriptorConfig\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 rich text action bodies are rendered. `converters` spread over the default\n\t * Lexical node converters; `serialize` replaces the whole pipeline (e.g. to target chat or\n\t * plain-text channels instead of email HTML).\n\t */\n\trichText?: RichTextBodyOption\n\t/** Add, override, or remove consent source types. `false` removes a built-in, `true` keeps it, an object adds or replaces one. */\n\tconsentSources?: ConsentSourcesConfig\n\t/** The built-in `form-uploads` collection backing file fields. `false` disables it (bring your own); an object overrides slug/upload/access/fields. */\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 * When `true`, the raw `values`, `descriptors`, and `consent` JSON fields are visible in the\n\t * submission admin view. Default `false` — those fields are fully represented by the\n\t * `SubmissionAnswers` UI component and are noisy when 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\tuploads?: 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 registry = resolveFieldTypes(defaultFieldDefinitions, options.fields)\n\t\tconst ruleRegistry = resolveValidationRules(defaultValidationRules, options.rules)\n\t\tconst consentRegistry = resolveConsentSources(defaultConsentSources, options.consentSources)\n\t\tconst presentationRegistry = resolvePresentationDescriptors(\n\t\t\tdefaultPresentationDescriptors,\n\t\t\toptions.presentations\n\t\t)\n\t\tconst actionRegistry = resolveActions(defaultActionDefinitions, options.actions)\n\t\tconst uploads = resolveUploads(options.uploads)\n\t\tconst spam = resolveSpamConfig(options.spam)\n\t\tregisterTranslations(config, options.translations)\n\t\tregisterCollections({\n\t\t\tconfig,\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tpresentationRegistry,\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\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 { renderAllValues, renderAllValuesTable } from './actions/body/wildcards'\nexport { defaultActionDefinitions } from './actions/builtin'\nexport type { ActionDefinition, ActionRunArgs, AnyActionDefinition } from './actions/defineAction'\nexport { defineAction } from './actions/defineAction'\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\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 { UploadsCollectionConfig, UploadsOption } from './collections/uploads'\nexport { buildUploadsCollection, FORM_UPLOADS_SLUG, resolveUploads } from './collections/uploads'\nexport { evaluateCondition } from './conditions/evaluate'\nexport type { FieldCondition } from './conditions/types'\nexport { defaultConsentSources } from './consent/builtin'\nexport type { ConsentProof } from './consent/captureConsent'\nexport { captureConsent } from './consent/captureConsent'\nexport type {\n\tAnyConsentSource,\n\tConsentLink,\n\tConsentResolveArgs,\n\tConsentResolved,\n\tConsentSource,\n} from './consent/defineConsentSource'\nexport { defineConsentSource } from './consent/defineConsentSource'\nexport type {\n\tConsentSourceOption,\n\tConsentSourceRegistry,\n\tConsentSourcesConfig,\n} from './consent/registry'\nexport { resolveConsentSources } from './consent/registry'\nexport { resolveConsentLinks } from './consent/resolveConsentLinks'\nexport { resolvePublishedVersionRef } from './consent/resolvePublishedVersionRef'\nexport { defaultFieldDefinitions, defaultFieldDefinitionsByType } from './fields/builtin'\nexport { fileMimeTypeOptions } from './fields/builtin/file'\nexport { defineFormField } from './fields/defineFormField'\nexport type { FieldTypeOption, FieldTypeRegistry, FieldTypesConfig } from './fields/registry'\nexport type {\n\tAnyFormFieldDefinition,\n\tFormFieldDefinition,\n\tFormFieldFormat,\n\tFormFieldValidate,\n\tFormFieldValueKind,\n} from './fields/types'\nexport type { PrefillOptions } from './prefill/valuesFromSearchParams'\nexport { valuesFromSearchParams } from './prefill/valuesFromSearchParams'\nexport { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from './presentations/defaults'\nexport type {\n\tPresentationDescriptorOption,\n\tPresentationDescriptorRegistry,\n\tPresentationsDescriptorConfig,\n} from './presentations/registry'\nexport { resolvePresentationDescriptors } from './presentations/registry'\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 { 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 {\n\tValidationRuleOption,\n\tValidationRuleRegistry,\n\tValidationRulesConfig,\n} from './validation/registry'\nexport type {\n\tAnyValidationRuleDefinition,\n\tValidationRuleDefinition,\n\tValidationRuleResult,\n\tValidationSeverity,\n} from './validation/types'\nexport type { FormBuilderPluginOptions as PluginOptions }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFA,MAAa,cAAc,aAAuC;CACjE,MAAM;CACN,OAAO;CACP,SAAS,EAAE,QAAQ,SAAS,GAAG,cAAsB;EACpD,IAAI,QAAQ,aAAa,MACxB,OAAO;EAER,MAAM,WAAW,kBAAkB,yBAAyB,QAAQ,MAAM;EAC1E,MAAM,eAAe,uBAAuB,wBAAwB,QAAQ,KAAK;EACjF,MAAM,kBAAkB,sBAAsB,uBAAuB,QAAQ,cAAc;EAC3F,MAAM,uBAAuB,+BAC5B,gCACA,QAAQ,aACT;EACA,MAAM,iBAAiB,eAAe,0BAA0B,QAAQ,OAAO;EAC/E,MAAM,UAAU,eAAe,QAAQ,OAAO;EAC9C,MAAM,OAAO,kBAAkB,QAAQ,IAAI;EAC3C,qBAAqB,QAAQ,QAAQ,YAAY;EACjD,oBAAoB;GACnB;GACA;GACA;GACA;GACA;GACA;GACA,UAAU,QAAQ;GAClB,eAAe,QAAQ,QAAQ,kBAAkB;GACjD,QAAQ,QAAQ;GAChB;GACA;GACA,yBAAyB,QAAQ,2BAA2B;GAC5D,WAAW,QAAQ;EACpB,CAAC;EACD,OAAO;CACR;AACD,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { CollectionConfig, Field } from "payload";
2
+
3
+ //#region src/plugin/collectionOverrides.d.ts
4
+ /**
5
+ * Instead of accepting a static field array (which deepMerge would silently replace), the consumer
6
+ * receives the plugin's default fields and returns the final array. This makes the intent explicit:
7
+ * append, prepend, filter out, or wrap any field without risk of accidentally wiping the defaults.
8
+ */
9
+ type FieldsOverride = (args: {
10
+ defaultFields: Field[];
11
+ }) => Field[];
12
+ /**
13
+ * Typed override surface for plugin-managed collections. Top-level keys use explicit spread order
14
+ * (see each collection builder) so the consumer always knows which values they can override and
15
+ * which are locked by the plugin for correctness or security.
16
+ */
17
+ type CollectionOverrides = {
18
+ fields?: FieldsOverride;
19
+ } & Partial<Omit<CollectionConfig, 'fields'>>;
20
+ //#endregion
21
+ export { CollectionOverrides, FieldsOverride };
22
+ //# sourceMappingURL=collectionOverrides.d.ts.map
@@ -3,34 +3,63 @@ import { buildSubmissionsCollection } from "../collections/formSubmissions.js";
3
3
  import { registerActionsTask } from "../actions/task.js";
4
4
  import { buildUploadOwnerStamp, buildUploadRateLimit } from "../spam/uploadHooks.js";
5
5
  //#region src/plugin/registerCollections.ts
6
- const registerCollections = ({ config, registry, ruleRegistry, consentRegistry, presentationRegistry, actionRegistry, hasJobsPlugin, events, uploads, spam }) => {
7
- registerActionsTask(config, actionRegistry);
6
+ const registerCollections = ({ config, registry, ruleRegistry, consentRegistry, presentationRegistry, actionRegistry, richText, hasJobsPlugin, events, uploads, spam, showSubmissionRawFields, overrides }) => {
7
+ registerActionsTask(config, actionRegistry, richText);
8
8
  const hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin;
9
- if (spam && uploads.enabled && uploads.collection) {
10
- const collection = uploads.collection;
11
- collection.hooks = collection.hooks ?? {};
12
- collection.hooks.beforeOperation = [...collection.hooks.beforeOperation ?? [], buildUploadRateLimit(spam)];
13
- collection.hooks.beforeValidate = [...collection.hooks.beforeValidate ?? [], buildUploadOwnerStamp(spam)];
9
+ let uploadsCollection = null;
10
+ if (uploads.enabled && uploads.collection) {
11
+ const col = uploads.collection;
12
+ if (spam) {
13
+ col.hooks = col.hooks ?? {};
14
+ col.hooks.beforeOperation = [...col.hooks.beforeOperation ?? [], buildUploadRateLimit(spam)];
15
+ col.hooks.beforeValidate = [...col.hooks.beforeValidate ?? [], buildUploadOwnerStamp(spam)];
16
+ }
17
+ const ov = overrides?.uploads;
18
+ if (ov) {
19
+ const defaultFields = col.fields ?? [];
20
+ uploadsCollection = {
21
+ ...col,
22
+ ...ov ?? {},
23
+ access: {
24
+ ...col.access ?? {},
25
+ ...ov.access ?? {}
26
+ },
27
+ admin: {
28
+ ...col.admin ?? {},
29
+ ...ov.admin ?? {}
30
+ },
31
+ hooks: {
32
+ ...ov.hooks ?? {},
33
+ beforeOperation: [...col.hooks?.beforeOperation ?? [], ...ov.hooks?.beforeOperation ?? []],
34
+ beforeValidate: [...col.hooks?.beforeValidate ?? [], ...ov.hooks?.beforeValidate ?? []]
35
+ },
36
+ fields: ov.fields ? ov.fields({ defaultFields }) : defaultFields
37
+ };
38
+ } else uploadsCollection = col;
14
39
  }
15
40
  config.collections = [
16
41
  ...config.collections ?? [],
17
- ...uploads.enabled && uploads.collection ? [uploads.collection] : [],
42
+ ...uploadsCollection ? [uploadsCollection] : [],
18
43
  buildFormsCollection({
19
44
  registry,
20
45
  ruleRegistry,
21
46
  consentRegistry,
22
47
  presentationRegistry,
23
- actionRegistry
48
+ actionRegistry,
49
+ overrides: overrides?.forms
24
50
  }),
25
51
  buildSubmissionsCollection({
26
52
  registry,
27
53
  ruleRegistry,
28
54
  consentRegistry,
29
55
  actionRegistry,
56
+ richText,
30
57
  events,
31
58
  hasRunner,
32
59
  uploadSlug: uploads.slug,
33
- spam
60
+ spam,
61
+ showRawFields: showSubmissionRawFields,
62
+ overrides: overrides?.formSubmissions
34
63
  })
35
64
  ];
36
65
  };
@@ -1 +1 @@
1
- {"version":3,"file":"registerCollections.js","names":[],"sources":["../../src/plugin/registerCollections.ts"],"sourcesContent":["import type { CollectionConfig, Config } from 'payload'\nimport type { ActionRegistry } from '../actions/registry'\nimport { registerActionsTask } from '../actions/task'\nimport { buildSubmissionsCollection } from '../collections/formSubmissions'\nimport { buildFormsCollection } from '../collections/forms'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { PresentationDescriptorRegistry } from '../presentations/registry'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { buildUploadOwnerStamp, buildUploadRateLimit } from '../spam/uploadHooks'\nimport type { ValidationRuleRegistry } from '../validation/registry'\n\ntype RegisterCollectionsArgs = {\n\tconfig: Config\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tpresentationRegistry: PresentationDescriptorRegistry\n\tactionRegistry: ActionRegistry\n\thasJobsPlugin: boolean\n\tevents?: FormEventSink\n\tuploads: { enabled: boolean; slug: string; collection?: CollectionConfig }\n\tspam: ResolvedSpamConfig | false\n}\n\nexport const registerCollections = ({\n\tconfig,\n\tregistry,\n\truleRegistry,\n\tconsentRegistry,\n\tpresentationRegistry,\n\tactionRegistry,\n\thasJobsPlugin,\n\tevents,\n\tuploads,\n\tspam,\n}: RegisterCollectionsArgs): void => {\n\tregisterActionsTask(config, actionRegistry)\n\tconst hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin\n\tif (spam && uploads.enabled && uploads.collection) {\n\t\tconst collection = uploads.collection\n\t\tcollection.hooks = collection.hooks ?? {}\n\t\tcollection.hooks.beforeOperation = [\n\t\t\t...(collection.hooks.beforeOperation ?? []),\n\t\t\tbuildUploadRateLimit(spam),\n\t\t]\n\t\tcollection.hooks.beforeValidate = [\n\t\t\t...(collection.hooks.beforeValidate ?? []),\n\t\t\tbuildUploadOwnerStamp(spam),\n\t\t]\n\t}\n\tconfig.collections = [\n\t\t...(config.collections ?? []),\n\t\t...(uploads.enabled && uploads.collection ? [uploads.collection] : []),\n\t\tbuildFormsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tpresentationRegistry,\n\t\t\tactionRegistry,\n\t\t}),\n\t\tbuildSubmissionsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tactionRegistry,\n\t\t\tevents,\n\t\t\thasRunner,\n\t\t\tuploadSlug: uploads.slug,\n\t\t\tspam,\n\t\t}),\n\t]\n}\n"],"mappings":";;;;;AA0BA,MAAa,uBAAuB,EACnC,QACA,UACA,cACA,iBACA,sBACA,gBACA,eACA,QACA,SACA,WACoC;CACpC,oBAAoB,QAAQ,cAAc;CAC1C,MAAM,YAAY,QAAQ,OAAO,MAAM,OAAO,KAAK;CACnD,IAAI,QAAQ,QAAQ,WAAW,QAAQ,YAAY;EAClD,MAAM,aAAa,QAAQ;EAC3B,WAAW,QAAQ,WAAW,SAAS,CAAC;EACxC,WAAW,MAAM,kBAAkB,CAClC,GAAI,WAAW,MAAM,mBAAmB,CAAC,GACzC,qBAAqB,IAAI,CAC1B;EACA,WAAW,MAAM,iBAAiB,CACjC,GAAI,WAAW,MAAM,kBAAkB,CAAC,GACxC,sBAAsB,IAAI,CAC3B;CACD;CACA,OAAO,cAAc;EACpB,GAAI,OAAO,eAAe,CAAC;EAC3B,GAAI,QAAQ,WAAW,QAAQ,aAAa,CAAC,QAAQ,UAAU,IAAI,CAAC;EACpE,qBAAqB;GACpB;GACA;GACA;GACA;GACA;EACD,CAAC;EACD,2BAA2B;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA,YAAY,QAAQ;GACpB;EACD,CAAC;CACF;AACD"}
1
+ {"version":3,"file":"registerCollections.js","names":[],"sources":["../../src/plugin/registerCollections.ts"],"sourcesContent":["import type { CollectionConfig, Config, Field } from 'payload'\nimport type { RichTextBodyOption } from '../actions/body/serializeBody'\nimport type { ActionRegistry } from '../actions/registry'\nimport { registerActionsTask } from '../actions/task'\nimport { buildSubmissionsCollection } from '../collections/formSubmissions'\nimport { buildFormsCollection } from '../collections/forms'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { PresentationDescriptorRegistry } from '../presentations/registry'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { buildUploadOwnerStamp, buildUploadRateLimit } from '../spam/uploadHooks'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport type { CollectionOverrides } from './collectionOverrides'\n\ntype RegisterCollectionsArgs = {\n\tconfig: Config\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tpresentationRegistry: PresentationDescriptorRegistry\n\tactionRegistry: ActionRegistry\n\trichText?: RichTextBodyOption\n\thasJobsPlugin: boolean\n\tevents?: FormEventSink\n\tuploads: { enabled: boolean; slug: string; collection?: CollectionConfig }\n\tspam: ResolvedSpamConfig | false\n\tshowSubmissionRawFields: boolean\n\toverrides?: {\n\t\tforms?: CollectionOverrides\n\t\tformSubmissions?: CollectionOverrides\n\t\tuploads?: CollectionOverrides\n\t}\n}\n\nexport const registerCollections = ({\n\tconfig,\n\tregistry,\n\truleRegistry,\n\tconsentRegistry,\n\tpresentationRegistry,\n\tactionRegistry,\n\trichText,\n\thasJobsPlugin,\n\tevents,\n\tuploads,\n\tspam,\n\tshowSubmissionRawFields,\n\toverrides,\n}: RegisterCollectionsArgs): void => {\n\tregisterActionsTask(config, actionRegistry, richText)\n\tconst hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin\n\n\tlet uploadsCollection: CollectionConfig | null = null\n\tif (uploads.enabled && uploads.collection) {\n\t\tconst col = uploads.collection\n\n\t\t// Spam hooks are added first (plugin-owned invariant)\n\t\tif (spam) {\n\t\t\tcol.hooks = col.hooks ?? {}\n\t\t\tcol.hooks.beforeOperation = [...(col.hooks.beforeOperation ?? []), buildUploadRateLimit(spam)]\n\t\t\tcol.hooks.beforeValidate = [...(col.hooks.beforeValidate ?? []), buildUploadOwnerStamp(spam)]\n\t\t}\n\n\t\t// Apply CollectionOverrides using explicit spreads after spam hooks are in place.\n\t\t// ...col first so the plugin defaults are the base, ...(ov ?? {}) second so the consumer\n\t\t// wins for any top-level key not explicitly re-set below (consistent with forms/formSubmissions).\n\t\t// The locked keys (hooks array ordering, fields function) are spread last.\n\t\tconst ov = overrides?.uploads\n\t\tif (ov) {\n\t\t\tconst defaultFields = (col.fields ?? []) as Field[]\n\t\t\tuploadsCollection = {\n\t\t\t\t...col,\n\t\t\t\t...(ov ?? {}),\n\t\t\t\taccess: { ...(col.access ?? {}), ...(ov.access ?? {}) },\n\t\t\t\tadmin: { ...(col.admin ?? {}), ...(ov.admin ?? {}) },\n\t\t\t\thooks: {\n\t\t\t\t\t...(ov.hooks ?? {}),\n\t\t\t\t\t// Spam hooks stay first in each array; consumer hooks appended\n\t\t\t\t\tbeforeOperation: [\n\t\t\t\t\t\t...(col.hooks?.beforeOperation ?? []),\n\t\t\t\t\t\t...(ov.hooks?.beforeOperation ?? []),\n\t\t\t\t\t],\n\t\t\t\t\tbeforeValidate: [\n\t\t\t\t\t\t...(col.hooks?.beforeValidate ?? []),\n\t\t\t\t\t\t...(ov.hooks?.beforeValidate ?? []),\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tfields: ov.fields ? ov.fields({ defaultFields }) : defaultFields,\n\t\t\t}\n\t\t} else {\n\t\t\tuploadsCollection = col\n\t\t}\n\t}\n\n\tconfig.collections = [\n\t\t...(config.collections ?? []),\n\t\t...(uploadsCollection ? [uploadsCollection] : []),\n\t\tbuildFormsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tpresentationRegistry,\n\t\t\tactionRegistry,\n\t\t\toverrides: overrides?.forms,\n\t\t}),\n\t\tbuildSubmissionsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tactionRegistry,\n\t\t\trichText,\n\t\t\tevents,\n\t\t\thasRunner,\n\t\t\tuploadSlug: uploads.slug,\n\t\t\tspam,\n\t\t\tshowRawFields: showSubmissionRawFields,\n\t\t\toverrides: overrides?.formSubmissions,\n\t\t}),\n\t]\n}\n"],"mappings":";;;;;AAmCA,MAAa,uBAAuB,EACnC,QACA,UACA,cACA,iBACA,sBACA,gBACA,UACA,eACA,QACA,SACA,MACA,yBACA,gBACoC;CACpC,oBAAoB,QAAQ,gBAAgB,QAAQ;CACpD,MAAM,YAAY,QAAQ,OAAO,MAAM,OAAO,KAAK;CAEnD,IAAI,oBAA6C;CACjD,IAAI,QAAQ,WAAW,QAAQ,YAAY;EAC1C,MAAM,MAAM,QAAQ;EAGpB,IAAI,MAAM;GACT,IAAI,QAAQ,IAAI,SAAS,CAAC;GAC1B,IAAI,MAAM,kBAAkB,CAAC,GAAI,IAAI,MAAM,mBAAmB,CAAC,GAAI,qBAAqB,IAAI,CAAC;GAC7F,IAAI,MAAM,iBAAiB,CAAC,GAAI,IAAI,MAAM,kBAAkB,CAAC,GAAI,sBAAsB,IAAI,CAAC;EAC7F;EAMA,MAAM,KAAK,WAAW;EACtB,IAAI,IAAI;GACP,MAAM,gBAAiB,IAAI,UAAU,CAAC;GACtC,oBAAoB;IACnB,GAAG;IACH,GAAI,MAAM,CAAC;IACX,QAAQ;KAAE,GAAI,IAAI,UAAU,CAAC;KAAI,GAAI,GAAG,UAAU,CAAC;IAAG;IACtD,OAAO;KAAE,GAAI,IAAI,SAAS,CAAC;KAAI,GAAI,GAAG,SAAS,CAAC;IAAG;IACnD,OAAO;KACN,GAAI,GAAG,SAAS,CAAC;KAEjB,iBAAiB,CAChB,GAAI,IAAI,OAAO,mBAAmB,CAAC,GACnC,GAAI,GAAG,OAAO,mBAAmB,CAAC,CACnC;KACA,gBAAgB,CACf,GAAI,IAAI,OAAO,kBAAkB,CAAC,GAClC,GAAI,GAAG,OAAO,kBAAkB,CAAC,CAClC;IACD;IACA,QAAQ,GAAG,SAAS,GAAG,OAAO,EAAE,cAAc,CAAC,IAAI;GACpD;EACD,OACC,oBAAoB;CAEtB;CAEA,OAAO,cAAc;EACpB,GAAI,OAAO,eAAe,CAAC;EAC3B,GAAI,oBAAoB,CAAC,iBAAiB,IAAI,CAAC;EAC/C,qBAAqB;GACpB;GACA;GACA;GACA;GACA;GACA,WAAW,WAAW;EACvB,CAAC;EACD,2BAA2B;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA,YAAY,QAAQ;GACpB;GACA,eAAe;GACf,WAAW,WAAW;EACvB,CAAC;CACF;AACD"}
@@ -17,6 +17,23 @@ type FormDocument = {
17
17
  flow?: FormFlow; /** Stored presentation name; overridden by the `presentation` prop. */
18
18
  defaultPresentation?: string;
19
19
  };
20
+ /** Props passed to `renderSubmit`. */
21
+ type SubmitButtonRenderProps = {
22
+ label: string;
23
+ submitting: boolean;
24
+ };
25
+ /** Props passed to `renderNext`. */
26
+ type NextButtonRenderProps = {
27
+ label: string;
28
+ submitting: boolean;
29
+ onClick: () => void;
30
+ };
31
+ /** Props passed to `renderBack`. */
32
+ type BackButtonRenderProps = {
33
+ label: string;
34
+ submitting: boolean;
35
+ onClick: () => void;
36
+ };
20
37
  type FormProps = {
21
38
  form: FormDocument;
22
39
  fieldTypes?: AnyFormFieldDefinition[];
@@ -45,7 +62,13 @@ type FormProps = {
45
62
  }; /** A token from your captcha widget; verified server-side when a captcha provider is configured. */
46
63
  captchaToken?: string; /** Custom layout: render fields with `useField`/`useFormState` instead of the auto-rendered field loop. */
47
64
  children?: ReactNode; /** Additional CSS class names applied to the root `<form>` element (and the success node). */
48
- className?: string;
65
+ className?: string; /** Replace the default submit button entirely. Receives the resolved label and submitting state. */
66
+ renderSubmit?: (props: SubmitButtonRenderProps) => ReactNode; /** Replace the default "Next" button in multi-step forms. */
67
+ renderNext?: (props: NextButtonRenderProps) => ReactNode; /** Replace the default "Back" button in multi-step forms. */
68
+ renderBack?: (props: BackButtonRenderProps) => ReactNode; /** CSS class forwarded to the default submit `<button>`. Ignored when `renderSubmit` is provided. */
69
+ submitButtonClassName?: string; /** CSS class forwarded to the default "Next" `<button>`. Ignored when `renderNext` is provided. */
70
+ nextButtonClassName?: string; /** CSS class forwarded to the default "Back" `<button>`. Ignored when `renderBack` is provided. */
71
+ backButtonClassName?: string;
49
72
  };
50
73
  /** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */
51
74
  declare const Form: ({
@@ -74,8 +97,14 @@ declare const Form: ({
74
97
  honeypot,
75
98
  captchaToken,
76
99
  children,
77
- className
100
+ className,
101
+ renderSubmit,
102
+ renderNext,
103
+ renderBack,
104
+ submitButtonClassName,
105
+ nextButtonClassName,
106
+ backButtonClassName
78
107
  }: FormProps) => import("react/jsx-runtime").JSX.Element;
79
108
  //#endregion
80
- export { Form, FormDocument, FormProps };
109
+ export { BackButtonRenderProps, Form, FormDocument, FormProps, NextButtonRenderProps, SubmitButtonRenderProps };
81
110
  //# sourceMappingURL=Form.d.ts.map
@@ -18,14 +18,14 @@ import { defaultPresentations } from "./presentation/presentations.js";
18
18
  import { resolvePresentations } from "./presentation/registry.js";
19
19
  import { applyRecall } from "./recall.js";
20
20
  import { resolveRenderers } from "./registry.js";
21
+ import { useField } from "./useField.js";
21
22
  import { defaultRenderers } from "./renderers/index.js";
22
23
  import { buildFieldTypeRegistry, buildValidationRuleRegistry, visibleFields } from "./resolveForm.js";
23
24
  import { formReducer, initialFormState } from "./state.js";
24
25
  import { submitForm } from "./submitForm.js";
25
- import { useField } from "./useField.js";
26
26
  import { validateFieldValue } from "./validateField.js";
27
- import { createElement, useCallback, useEffect, useId, useMemo, useReducer, useRef, useState } from "react";
28
27
  import { jsx, jsxs } from "react/jsx-runtime";
28
+ import { createElement, useCallback, useEffect, useId, useMemo, useReducer, useRef, useState } from "react";
29
29
  //#region src/react/Form.tsx
30
30
  const isEmpty = (value) => value == null || value === "" || Array.isArray(value) && value.length === 0;
31
31
  const FIELD_WIDTHS = new Set([
@@ -69,7 +69,7 @@ const CalcFieldHost = ({ field, renderer, value, locale, t }) => {
69
69
  });
70
70
  };
71
71
  /** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */
72
- const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, events, t, locale = "en", layout, submitLabel = "Submit", nextLabel = "Next", backLabel = "Back", closeLabel = "Close", successMessage = "Thank you.", presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, children, className }) => {
72
+ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, events, t, locale = "en", layout, submitLabel = "Submit", nextLabel = "Next", backLabel = "Back", closeLabel = "Close", successMessage = "Thank you.", presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, children, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName }) => {
73
73
  const honeypotName = honeypot === false ? null : honeypot?.name ?? "website";
74
74
  const honeypotRef = useRef(null);
75
75
  const registry = useMemo(() => buildFieldTypeRegistry(fieldTypes), [fieldTypes]);
@@ -193,7 +193,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
193
193
  if (result.errors.length > 0) hasError = true;
194
194
  }
195
195
  if (hasError) return;
196
- const next = resolveNextStepId(flow, currentStepId, state.values);
196
+ const next = resolveNextStepId(flow, currentStepId, effectiveValues);
197
197
  if (!next) return;
198
198
  emitFormEvent(sinkRef.current, formIdRef.current, {
199
199
  type: "step.completed",
@@ -264,6 +264,29 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
264
264
  }
265
265
  if (result.warnings.length > 0) warnings[result.field.name] = result.warnings;
266
266
  }
267
+ for (const field of visible.filter((f) => f.blockType === "repeater")) {
268
+ const rows = Array.isArray(effectiveValues[field.name]) ? effectiveValues[field.name] : [];
269
+ const subFields = Array.isArray(field.subFields) ? field.subFields : [];
270
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
271
+ const row = rows[rowIndex] ?? {};
272
+ for (const subField of subFields) {
273
+ if (!evaluateCondition(subField.visibleWhen, row)) continue;
274
+ if (!evaluateCondition(subField.validateWhen, row)) continue;
275
+ const subResult = await validateFieldValue({
276
+ field: subField,
277
+ value: row[subField.name],
278
+ registry,
279
+ ruleRegistry,
280
+ answers: row,
281
+ locale,
282
+ t: translate
283
+ });
284
+ const compositeKey = `${field.name}[${rowIndex}].${subField.name}`;
285
+ if (subResult.errors.length > 0) errors[compositeKey] = subResult.errors;
286
+ if (subResult.warnings.length > 0) warnings[compositeKey] = subResult.warnings;
287
+ }
288
+ }
289
+ }
267
290
  rawDispatch({
268
291
  type: "SET_ALL_ISSUES",
269
292
  errors,
@@ -327,7 +350,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
327
350
  stepIndex: flow.steps.findIndex((s) => s.id === currentStepId),
328
351
  stepCount: flow.steps.length,
329
352
  isFirst: history.length === 0,
330
- isTerminal: currentStepId ? isTerminalStepId(flow, currentStepId, state.values) : true,
353
+ isTerminal: currentStepId ? isTerminalStepId(flow, currentStepId, effectiveValues) : true,
331
354
  goNext: () => {
332
355
  goNext();
333
356
  },
@@ -345,7 +368,8 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
345
368
  dispatch,
346
369
  validateField,
347
370
  locale,
348
- step
371
+ step,
372
+ rendererRegistry
349
373
  };
350
374
  const PresentationWrapper = activePresentation.Wrapper;
351
375
  const wrap = (content) => PresentationWrapper ? /* @__PURE__ */ jsx(PresentationWrapper, {
@@ -425,25 +449,43 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
425
449
  }) : null,
426
450
  flow ? /* @__PURE__ */ jsxs("div", {
427
451
  className: "fb-form__controls",
428
- children: [!step.isFirst ? /* @__PURE__ */ jsx("button", {
452
+ children: [!step.isFirst ? renderBack ? renderBack({
453
+ label: backLabel,
454
+ submitting: state.submitting,
455
+ onClick: goBack
456
+ }) : /* @__PURE__ */ jsx("button", {
429
457
  type: "button",
458
+ className: backButtonClassName,
430
459
  onClick: goBack,
431
460
  disabled: state.submitting,
432
461
  children: backLabel
433
- }) : null, step.isTerminal ? /* @__PURE__ */ jsx("button", {
462
+ }) : null, step.isTerminal ? renderSubmit ? renderSubmit({
463
+ label: submitLabel,
464
+ submitting: state.submitting
465
+ }) : /* @__PURE__ */ jsx("button", {
434
466
  type: "submit",
467
+ className: submitButtonClassName,
435
468
  disabled: state.submitting,
436
469
  children: submitLabel
470
+ }) : renderNext ? renderNext({
471
+ label: nextLabel,
472
+ submitting: state.submitting,
473
+ onClick: () => void goNext()
437
474
  }) : /* @__PURE__ */ jsx("button", {
438
475
  type: "button",
476
+ className: nextButtonClassName,
439
477
  disabled: state.submitting,
440
478
  onClick: () => {
441
479
  goNext();
442
480
  },
443
481
  children: nextLabel
444
482
  })]
483
+ }) : renderSubmit ? renderSubmit({
484
+ label: submitLabel,
485
+ submitting: state.submitting
445
486
  }) : /* @__PURE__ */ jsx("button", {
446
487
  type: "submit",
488
+ className: submitButtonClassName,
447
489
  disabled: state.submitting,
448
490
  children: submitLabel
449
491
  })