@10x-media/form-builder 0.1.0-beta.2 → 0.1.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/README.md +4 -4
- package/dist/actions/body/converters.d.ts +20 -0
- package/dist/actions/body/converters.js +57 -0
- package/dist/actions/body/converters.js.map +1 -0
- package/dist/actions/body/escapeHtml.d.ts +6 -0
- package/dist/actions/body/escapeHtml.js +14 -0
- package/dist/actions/body/escapeHtml.js.map +1 -0
- package/dist/actions/body/serializeBody.d.ts +50 -0
- package/dist/actions/body/serializeBody.js +78 -0
- package/dist/actions/body/serializeBody.js.map +1 -0
- package/dist/actions/body/serializeSlate.js +31 -0
- package/dist/actions/body/serializeSlate.js.map +1 -0
- package/dist/actions/body/wildcards.d.ts +10 -0
- package/dist/actions/body/wildcards.js +33 -0
- package/dist/actions/body/wildcards.js.map +1 -0
- package/dist/actions/builtin/confirmation.js +40 -15
- package/dist/actions/builtin/confirmation.js.map +1 -1
- package/dist/actions/builtin/emailTeam.js +19 -12
- package/dist/actions/builtin/emailTeam.js.map +1 -1
- package/dist/actions/builtin/index.d.ts +13 -1
- package/dist/actions/builtin/index.js +18 -7
- package/dist/actions/builtin/index.js.map +1 -1
- package/dist/actions/builtin/signedWebhook.js +6 -2
- package/dist/actions/builtin/signedWebhook.js.map +1 -1
- package/dist/actions/defineAction.d.ts +2 -1
- package/dist/actions/defineAction.js.map +1 -1
- package/dist/actions/dispatch.js +2 -1
- package/dist/actions/dispatch.js.map +1 -1
- package/dist/actions/runActions.js +10 -1
- package/dist/actions/runActions.js.map +1 -1
- package/dist/actions/task.js +7 -5
- package/dist/actions/task.js.map +1 -1
- package/dist/aggregation/resolveResultsRequest.d.ts +22 -6
- package/dist/aggregation/resolveResultsRequest.js +18 -7
- package/dist/aggregation/resolveResultsRequest.js.map +1 -1
- package/dist/calc/normalizeCalc.js +5 -17
- package/dist/calc/normalizeCalc.js.map +1 -1
- package/dist/calc/types.d.ts +8 -2
- package/dist/calc/types.js +22 -0
- package/dist/calc/types.js.map +1 -0
- package/dist/client/ByteSizeField.d.ts +21 -0
- package/dist/client/ByteSizeField.js +56 -0
- package/dist/client/ByteSizeField.js.map +1 -0
- package/dist/client/ConditionBuilder.js +2 -1
- package/dist/client/ConditionBuilder.js.map +1 -1
- package/dist/client/FieldNameSelect.d.ts +20 -0
- package/dist/client/FieldNameSelect.js +58 -0
- package/dist/client/FieldNameSelect.js.map +1 -0
- package/dist/client/FlowBuilder.d.ts +0 -1
- package/dist/client/FlowBuilder.js +102 -59
- package/dist/client/FlowBuilder.js.map +1 -1
- package/dist/client/FormConditionField.js +1 -4
- package/dist/client/FormConditionField.js.map +1 -1
- package/dist/client/admin.css +234 -0
- package/dist/client/flowAuthoring.js +35 -0
- package/dist/client/flowAuthoring.js.map +1 -0
- package/dist/client/synthesizeClientField.js +6 -2
- package/dist/client/synthesizeClientField.js.map +1 -1
- package/dist/client/toStaticLabel.js +13 -0
- package/dist/client/toStaticLabel.js.map +1 -0
- package/dist/collections/formSubmissions.js +49 -9
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +251 -88
- package/dist/collections/forms.js.map +1 -1
- package/dist/conditions/conditionType.js +9 -2
- package/dist/conditions/conditionType.js.map +1 -1
- package/dist/conditions/normalizeConditions.js +2 -1
- package/dist/conditions/normalizeConditions.js.map +1 -1
- package/dist/consent/builtin/index.d.ts +11 -1
- package/dist/consent/builtin/index.js +15 -5
- package/dist/consent/builtin/index.js.map +1 -1
- package/dist/consent/builtin/static.js +7 -3
- package/dist/consent/builtin/static.js.map +1 -1
- package/dist/exports/client.d.ts +3 -1
- package/dist/exports/client.js +3 -1
- package/dist/exports/react.d.ts +15 -6
- package/dist/exports/react.js +10 -2
- package/dist/exports/rsc.d.ts +6 -1
- package/dist/exports/rsc.js +6 -1
- package/dist/exports/types.d.ts +4 -4
- package/dist/fields/buildFieldBlocks.js +20 -15
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/calculation.js +115 -2
- package/dist/fields/builtin/calculation.js.map +1 -1
- package/dist/fields/builtin/consent.js +8 -4
- package/dist/fields/builtin/consent.js.map +1 -1
- package/dist/fields/builtin/date.js +29 -0
- package/dist/fields/builtin/date.js.map +1 -0
- package/dist/fields/builtin/file.d.ts +15 -0
- package/dist/fields/builtin/file.js +87 -7
- package/dist/fields/builtin/file.js.map +1 -1
- package/dist/fields/builtin/index.d.ts +24 -0
- package/dist/fields/builtin/index.js +29 -8
- package/dist/fields/builtin/index.js.map +1 -1
- package/dist/fields/builtin/message.js +26 -0
- package/dist/fields/builtin/message.js.map +1 -0
- package/dist/fields/builtin/repeater.js +7 -3
- package/dist/fields/builtin/repeater.js.map +1 -1
- package/dist/fields/builtin/select.js +7 -3
- package/dist/fields/builtin/select.js.map +1 -1
- package/dist/fields/fieldNamesOfType.js +25 -0
- package/dist/fields/fieldNamesOfType.js.map +1 -0
- package/dist/fields/localizedIf.d.ts +13 -0
- package/dist/fields/localizedIf.js +12 -0
- package/dist/fields/localizedIf.js.map +1 -0
- package/dist/fields/sharedConfig.js +82 -57
- package/dist/fields/sharedConfig.js.map +1 -1
- package/dist/fields/types.d.ts +7 -2
- package/dist/flow/normalizeFlow.js +7 -1
- package/dist/flow/normalizeFlow.js.map +1 -1
- package/dist/flow/stepLabel.js +15 -0
- package/dist/flow/stepLabel.js.map +1 -0
- package/dist/form/pollState.d.ts +22 -0
- package/dist/form/pollState.js +17 -0
- package/dist/form/pollState.js.map +1 -0
- package/dist/form/toFormDocument.d.ts +60 -0
- package/dist/form/toFormDocument.js +39 -0
- package/dist/form/toFormDocument.js.map +1 -0
- package/dist/form/types.d.ts +48 -0
- package/dist/index.d.ts +81 -13
- package/dist/index.js +36 -15
- package/dist/index.js.map +1 -1
- package/dist/plugin/registerCollections.js +17 -37
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/plugin/uploadsCollection.d.ts +11 -0
- package/dist/plugin/uploadsCollection.js +44 -0
- package/dist/plugin/uploadsCollection.js.map +1 -0
- package/dist/poll/applyPollOptions.js +17 -0
- package/dist/poll/applyPollOptions.js.map +1 -0
- package/dist/poll/buildPollOptionSourceFields.js +53 -0
- package/dist/poll/buildPollOptionSourceFields.js.map +1 -0
- package/dist/poll/definePollOptionSource.d.ts +35 -0
- package/dist/poll/definePollOptionSource.js +6 -0
- package/dist/poll/definePollOptionSource.js.map +1 -0
- package/dist/poll/registry.d.ts +16 -0
- package/dist/poll/registry.js +16 -0
- package/dist/poll/registry.js.map +1 -0
- package/dist/poll/resolvePollOptions.d.ts +28 -0
- package/dist/poll/resolvePollOptions.js +64 -0
- package/dist/poll/resolvePollOptions.js.map +1 -0
- package/dist/poll/resolvePollOutcome.d.ts +21 -0
- package/dist/poll/resolvePollOutcome.js +36 -0
- package/dist/poll/resolvePollOutcome.js.map +1 -0
- package/dist/prefill/valuesFromSearchParams.js +1 -1
- package/dist/prefill/valuesFromSearchParams.js.map +1 -1
- package/dist/presentations/types.d.ts +3 -2
- package/dist/react/Form.d.ts +13 -31
- package/dist/react/Form.js +98 -85
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormContext.d.ts +35 -1
- package/dist/react/FormContext.js.map +1 -1
- package/dist/react/FormControls.d.ts +55 -0
- package/dist/react/FormControls.js +56 -0
- package/dist/react/FormControls.js.map +1 -0
- package/dist/react/FormResults.d.ts +6 -3
- package/dist/react/FormResults.js +37 -29
- package/dist/react/FormResults.js.map +1 -1
- package/dist/react/FormSteps.d.ts +17 -0
- package/dist/react/FormSteps.js +35 -0
- package/dist/react/FormSteps.js.map +1 -0
- package/dist/react/Poll.d.ts +17 -6
- package/dist/react/Poll.js +66 -18
- package/dist/react/Poll.js.map +1 -1
- package/dist/react/captcha/HcaptchaCaptcha.d.ts +28 -0
- package/dist/react/captcha/HcaptchaCaptcha.js +53 -0
- package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/RecaptchaCaptcha.d.ts +41 -0
- package/dist/react/captcha/RecaptchaCaptcha.js +93 -0
- package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/TurnstileCaptcha.d.ts +28 -0
- package/dist/react/captcha/TurnstileCaptcha.js +53 -0
- package/dist/react/captcha/TurnstileCaptcha.js.map +1 -0
- package/dist/react/captcha/types.d.ts +8 -0
- package/dist/react/captcha/useCaptchaScript.js +45 -0
- package/dist/react/captcha/useCaptchaScript.js.map +1 -0
- package/dist/react/captcha/vendors.js +8 -0
- package/dist/react/captcha/vendors.js.map +1 -0
- package/dist/react/fetchResults.d.ts +1 -1
- package/dist/react/fetchResults.js +1 -1
- package/dist/react/fetchResults.js.map +1 -1
- package/dist/react/primitives/Input.d.ts +1 -1
- package/dist/react/primitives/Input.js.map +1 -1
- package/dist/react/renderers/calculation.js +1 -1
- package/dist/react/renderers/checkbox.js +1 -1
- package/dist/react/renderers/consent.js +1 -1
- package/dist/react/renderers/date.js +37 -0
- package/dist/react/renderers/date.js.map +1 -0
- package/dist/react/renderers/email.js +1 -1
- package/dist/react/renderers/file.js +25 -8
- package/dist/react/renderers/file.js.map +1 -1
- package/dist/react/renderers/index.js +4 -0
- package/dist/react/renderers/index.js.map +1 -1
- package/dist/react/renderers/message.js +35 -0
- package/dist/react/renderers/message.js.map +1 -0
- package/dist/react/renderers/number.js +1 -1
- package/dist/react/renderers/repeater.js +1 -1
- package/dist/react/renderers/select.js +1 -1
- package/dist/react/renderers/text.js +1 -1
- package/dist/react/renderers/textarea.js +1 -1
- package/dist/react/state.d.ts +25 -1
- package/dist/react/state.js +14 -1
- package/dist/react/state.js.map +1 -1
- package/dist/react/uploadFile.d.ts +2 -2
- package/dist/react/uploadFile.js +1 -1
- package/dist/react/uploadFile.js.map +1 -1
- package/dist/spam/captcha.d.ts +3 -2
- package/dist/spam/captcha.js +3 -2
- package/dist/spam/captcha.js.map +1 -1
- package/dist/spam/clientIp.js +9 -0
- package/dist/spam/clientIp.js.map +1 -0
- package/dist/spam/identify.js +3 -6
- package/dist/spam/identify.js.map +1 -1
- package/dist/spam/providers/hcaptcha.d.ts +17 -0
- package/dist/spam/providers/hcaptcha.js +18 -0
- package/dist/spam/providers/hcaptcha.js.map +1 -0
- package/dist/spam/providers/recaptcha.d.ts +23 -0
- package/dist/spam/providers/recaptcha.js +23 -0
- package/dist/spam/providers/recaptcha.js.map +1 -0
- package/dist/spam/providers/siteverify.js +34 -0
- package/dist/spam/providers/siteverify.js.map +1 -0
- package/dist/spam/providers/turnstile.d.ts +17 -0
- package/dist/spam/providers/turnstile.js +18 -0
- package/dist/spam/providers/turnstile.js.map +1 -0
- package/dist/spam/spamGuard.js +2 -5
- package/dist/spam/spamGuard.js.map +1 -1
- package/dist/spam/types.d.ts +2 -2
- package/dist/submissions/runSubmission.js +70 -33
- package/dist/submissions/runSubmission.js.map +1 -1
- package/dist/submissions/validateSubmission.js +41 -7
- package/dist/submissions/validateSubmission.js.map +1 -1
- package/dist/submissions/votedCookie.d.ts +13 -0
- package/dist/submissions/votedCookie.js +27 -0
- package/dist/submissions/votedCookie.js.map +1 -0
- package/dist/translations/de.js +219 -0
- package/dist/translations/de.js.map +1 -0
- package/dist/translations/en.js +71 -10
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/index.d.ts +2 -1
- package/dist/translations/index.js +6 -2
- package/dist/translations/index.js.map +1 -1
- package/dist/translations/keys.d.ts +69 -8
- package/dist/translations/keys.js +70 -9
- package/dist/translations/keys.js.map +1 -1
- package/dist/uploads/formatBytes.d.ts +9 -0
- package/dist/uploads/formatBytes.js +25 -0
- package/dist/uploads/formatBytes.js.map +1 -0
- package/dist/uploads/resolveFileRef.js +1 -1
- package/dist/uploads/resolveFileRef.js.map +1 -1
- package/dist/uploads/types.d.ts +1 -2
- package/dist/validation/builtin/index.d.ts +14 -0
- package/dist/validation/builtin/index.js +12 -1
- package/dist/validation/builtin/index.js.map +1 -1
- package/dist/validation/builtin/maxDate.js +23 -0
- package/dist/validation/builtin/maxDate.js.map +1 -0
- package/dist/validation/builtin/minDate.js +23 -0
- package/dist/validation/builtin/minDate.js.map +1 -0
- package/dist/validation/builtin/validateDateBound.js +18 -0
- package/dist/validation/builtin/validateDateBound.js.map +1 -0
- package/dist/validation/validateUrl.js +22 -0
- package/dist/validation/validateUrl.js.map +1 -0
- package/package.json +4 -2
- package/styles/form-builder.css +10 -190
- package/dist/collections/uploads.d.ts +0 -30
- package/dist/collections/uploads.js +0 -55
- package/dist/collections/uploads.js.map +0 -1
- package/dist/presentations/registry.d.ts +0 -15
- package/dist/presentations/registry.js +0 -15
- package/dist/presentations/registry.js.map +0 -1
- package/dist/react/toFormDocument.d.ts +0 -24
- package/dist/react/toFormDocument.js +0 -22
- package/dist/react/toFormDocument.js.map +0 -1
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 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/** 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\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 { 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,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;GACA,yBAAyB,QAAQ,2BAA2B;GAC5D,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 type { RichTextBodyOption } from './actions/body/serializeBody'\nimport { buildDefaultActionDefinitions } from './actions/builtin'\nimport type { ActionsConfig } from './actions/registry'\nimport { resolveActions } from './actions/registry'\nimport type { FormResultsAccess } from './aggregation/resolveResultsRequest'\nimport { buildDefaultConsentSources } from './consent/builtin'\nimport type { ConsentSourcesConfig } from './consent/registry'\nimport { resolveConsentSources } from './consent/registry'\nimport type { FormEventSink } from './events/types'\nimport { buildDefaultFieldDefinitions } 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 type { UploadsOption } from './plugin/uploadsCollection'\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, consent statements,\n\t * action subjects and bodies) are localized by default. Payload strips the `localized` flag\n\t * on hosts without `localization` configured, so the default is safe everywhere. Set `false`\n\t * to keep form content single-locale even on localized hosts. Spread-overrides of the prebuilt\n\t * default exports (`defaultFieldDefinitionsByType`, `defaultActionDefinitions`,\n\t * `defaultConsentSources`) carry `localized` flags from the default-true set; when opting out,\n\t * derive overrides from `buildDefaultFieldDefinitions(false)` /\n\t * `buildDefaultActionDefinitions(false)` / `buildDefaultConsentSources(false)` instead.\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 rich text action bodies are authored and rendered. `editor` overrides the\n\t * Lexical/richText editor on both the emailTeam and confirmation action body fields.\n\t * `converters` spread over the default Lexical node converters; `serialize` replaces the\n\t * whole pipeline (e.g. to target chat or plain-text channels instead of email HTML). A\n\t * custom `serialize` receives the submitted `form` (id/title) and `req`, enabling per-tenant\n\t * lookups or handing the raw body off to a renderer like react-email.\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/**\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 */\n\tpoll?: { votedCookie?: boolean; sources?: PollOptionSourcesConfig }\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}\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\t// Without an uploads collection the built-in file type has nowhere to store anything, so it\n\t\t// never enters the registry; an explicit `fields.file` definition remains a developer choice.\n\t\tconst defaultFieldDefinitions = buildDefaultFieldDefinitions(localizeContent).filter(\n\t\t\t(definition) => uploads !== false || definition.type !== 'file'\n\t\t)\n\t\tconst registry = resolveFieldTypes(defaultFieldDefinitions, options.fields)\n\t\tconst ruleRegistry = resolveValidationRules(defaultValidationRules, options.rules)\n\t\tconst consentRegistry = resolveConsentSources(\n\t\t\tbuildDefaultConsentSources(localizeContent),\n\t\t\toptions.consentSources\n\t\t)\n\t\tconst actionRegistry = resolveActions(\n\t\t\tbuildDefaultActionDefinitions(localizeContent, options.richText?.editor),\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\t// Stashed on config.custom so the root-level resolvePollOptions helper can reach the\n\t\t// registry through `payload.config` at request time, without threading plugin state.\n\t\tconfig.custom = stashPollOptionSources(config.custom, pollSourceRegistry)\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\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\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 { buildDefaultActionDefinitions, 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\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 { evaluateCondition } from './conditions/evaluate'\nexport type { FieldCondition } from './conditions/types'\nexport { buildDefaultConsentSources, 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 {\n\tbuildDefaultFieldDefinitions,\n\tdefaultFieldDefinitions,\n\tdefaultFieldDefinitionsByType,\n} from './fields/builtin'\nexport { fileMimeTypeOptions } from './fields/builtin/file'\nexport { defineFormField } from './fields/defineFormField'\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} from './fields/types'\nexport { isPollClosed } from './form/pollState'\nexport type { ToFormDocumentOptions } from './form/toFormDocument'\nexport { toFormDocument } from './form/toFormDocument'\nexport type {\n\tFormDisplaySettings,\n\tFormDocument,\n\tFormPollSettings,\n\tFormResponseSettings,\n} from './form/types'\nexport type { UploadsOption } from './plugin/uploadsCollection'\nexport type {\n\tAnyPollOptionSource,\n\tPollOption,\n\tPollOptionResolveArgs,\n\tPollOptionSource,\n} from './poll/definePollOptionSource'\nexport { definePollOptionSource } from './poll/definePollOptionSource'\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 { 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 {\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHA,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;EAMnC,MAAM,WAAW,kBAHe,6BAA6B,eAAe,EAAE,QAC5E,eAAe,YAAY,SAAS,WAAW,SAAS,MAED,GAAG,QAAQ,MAAM;EAC1E,MAAM,eAAe,uBAAuB,wBAAwB,QAAQ,KAAK;EACjF,MAAM,kBAAkB,sBACvB,2BAA2B,eAAe,GAC1C,QAAQ,cACT;EACA,MAAM,iBAAiB,eACtB,8BAA8B,iBAAiB,QAAQ,UAAU,MAAM,GACvE,QAAQ,OACT;EACA,MAAM,OAAO,kBAAkB,QAAQ,IAAI;EAC3C,MAAM,qBAAqB,yBAAyB,QAAQ,MAAM,OAAO;EAGzE,OAAO,SAAS,uBAAuB,OAAO,QAAQ,kBAAkB;EACxE,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,WAAW,QAAQ;EACpB,CAAC;EACD,OAAO;CACR;AACD,CAAC"}
|
|
@@ -1,51 +1,28 @@
|
|
|
1
1
|
import { buildFormsCollection } from "../collections/forms.js";
|
|
2
2
|
import { buildSubmissionsCollection } from "../collections/formSubmissions.js";
|
|
3
3
|
import { registerActionsTask } from "../actions/task.js";
|
|
4
|
-
import {
|
|
4
|
+
import { attachUploadsCollection } from "./uploadsCollection.js";
|
|
5
5
|
//#region src/plugin/registerCollections.ts
|
|
6
|
-
const registerCollections = ({ config, registry, ruleRegistry, consentRegistry,
|
|
7
|
-
registerActionsTask(config, actionRegistry);
|
|
6
|
+
const registerCollections = ({ config, registry, ruleRegistry, consentRegistry, actionRegistry, richText, hasJobsPlugin, events, uploads, spam, showSubmissionRawFields, localizeContent, resultsAccess, votedCookie, pollSourceRegistry, overrides }) => {
|
|
7
|
+
registerActionsTask(config, actionRegistry, richText);
|
|
8
8
|
const hasRunner = Boolean(config.jobs?.autoRun) || hasJobsPlugin;
|
|
9
|
-
|
|
10
|
-
if (uploads
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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;
|
|
39
|
-
}
|
|
9
|
+
const uploadSlug = uploads === false ? void 0 : uploads.collection;
|
|
10
|
+
if (uploads !== false) attachUploadsCollection({
|
|
11
|
+
config,
|
|
12
|
+
slug: uploads.collection,
|
|
13
|
+
spam
|
|
14
|
+
});
|
|
40
15
|
config.collections = [
|
|
41
16
|
...config.collections ?? [],
|
|
42
|
-
...uploadsCollection ? [uploadsCollection] : [],
|
|
43
17
|
buildFormsCollection({
|
|
44
18
|
registry,
|
|
45
19
|
ruleRegistry,
|
|
46
20
|
consentRegistry,
|
|
47
|
-
presentationRegistry,
|
|
48
21
|
actionRegistry,
|
|
22
|
+
localizeContent,
|
|
23
|
+
uploadsCollectionSlug: uploadSlug,
|
|
24
|
+
resultsAccess,
|
|
25
|
+
pollSourceRegistry,
|
|
49
26
|
overrides: overrides?.forms
|
|
50
27
|
}),
|
|
51
28
|
buildSubmissionsCollection({
|
|
@@ -53,10 +30,13 @@ const registerCollections = ({ config, registry, ruleRegistry, consentRegistry,
|
|
|
53
30
|
ruleRegistry,
|
|
54
31
|
consentRegistry,
|
|
55
32
|
actionRegistry,
|
|
33
|
+
richText,
|
|
56
34
|
events,
|
|
57
35
|
hasRunner,
|
|
58
|
-
uploadSlug
|
|
36
|
+
uploadSlug,
|
|
59
37
|
spam,
|
|
38
|
+
votedCookie,
|
|
39
|
+
pollSourceRegistry,
|
|
60
40
|
showRawFields: showSubmissionRawFields,
|
|
61
41
|
overrides: overrides?.formSubmissions
|
|
62
42
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerCollections.js","names":[],"sources":["../../src/plugin/registerCollections.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"registerCollections.js","names":[],"sources":["../../src/plugin/registerCollections.ts"],"sourcesContent":["import type { Config } from 'payload'\nimport type { RichTextBodyOption } from '../actions/body/serializeBody'\nimport type { ActionRegistry } from '../actions/registry'\nimport { registerActionsTask } from '../actions/task'\nimport type { FormResultsAccess } from '../aggregation/resolveResultsRequest'\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 { 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\tconsentRegistry: ConsentSourceRegistry\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\toverrides?: {\n\t\tforms?: CollectionOverrides\n\t\tformSubmissions?: CollectionOverrides\n\t}\n}\n\nexport const registerCollections = ({\n\tconfig,\n\tregistry,\n\truleRegistry,\n\tconsentRegistry,\n\tactionRegistry,\n\trichText,\n\thasJobsPlugin,\n\tevents,\n\tuploads,\n\tspam,\n\tshowSubmissionRawFields,\n\tlocalizeContent,\n\tresultsAccess,\n\tvotedCookie,\n\tpollSourceRegistry,\n\toverrides,\n}: RegisterCollectionsArgs): void => {\n\tregisterActionsTask(config, actionRegistry, richText)\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\tconfig.collections = [\n\t\t...(config.collections ?? []),\n\t\tbuildFormsCollection({\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tconsentRegistry,\n\t\t\tactionRegistry,\n\t\t\tlocalizeContent,\n\t\t\tuploadsCollectionSlug: uploadSlug,\n\t\t\tresultsAccess,\n\t\t\tpollSourceRegistry,\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,\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]\n}\n"],"mappings":";;;;;AAsCA,MAAa,uBAAuB,EACnC,QACA,UACA,cACA,iBACA,gBACA,UACA,eACA,QACA,SACA,MACA,yBACA,iBACA,eACA,aACA,oBACA,gBACoC;CACpC,oBAAoB,QAAQ,gBAAgB,QAAQ;CACpD,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;CAGnE,OAAO,cAAc;EACpB,GAAI,OAAO,eAAe,CAAC;EAC3B,qBAAqB;GACpB;GACA;GACA;GACA;GACA;GACA,uBAAuB;GACvB;GACA;GACA,WAAW,WAAW;EACvB,CAAC;EACD,2BAA2B;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,eAAe;GACf,WAAW,WAAW;EACvB,CAAC;CACF;AACD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/plugin/uploadsCollection.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Uploads are bring-your-own: `false` (the default) disables file fields entirely, an object points
|
|
4
|
+
* at a host-owned upload collection (created by the app with its storage adapter of choice).
|
|
5
|
+
*/
|
|
6
|
+
type UploadsOption = false | {
|
|
7
|
+
collection: string;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { UploadsOption };
|
|
11
|
+
//# sourceMappingURL=uploadsCollection.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { buildUploadOwnerStamp, buildUploadRateLimit } from "../spam/uploadHooks.js";
|
|
2
|
+
//#region src/plugin/uploadsCollection.ts
|
|
3
|
+
/** 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
|
+
const hasFieldNamed = (fields, name) => fields.some((field) => {
|
|
5
|
+
if ("name" in field && field.name === name) return true;
|
|
6
|
+
return field.type === "row" && hasFieldNamed(field.fields, name);
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* Wire the plugin's upload concerns into the host-owned upload collection named by
|
|
10
|
+
* `uploads.collection`. The collection must already exist in `config.collections` and carry an
|
|
11
|
+
* `upload` config; anything else is a plugin misconfiguration and throws at boot. The plugin
|
|
12
|
+
* appends its hidden `owner` text field unless the host already defines a field named `owner`
|
|
13
|
+
* (then that field is reused and must stay a text-compatible identity slot), and prepends the
|
|
14
|
+
* spam upload hooks (rate limit, owner stamp) ahead of the host's own hooks so abuse checks run
|
|
15
|
+
* first. The host entry is replaced with a new object; the host's config is never mutated.
|
|
16
|
+
*/
|
|
17
|
+
const attachUploadsCollection = ({ config, slug, spam }) => {
|
|
18
|
+
const collections = config.collections ?? [];
|
|
19
|
+
const target = collections.find((collection) => collection.slug === slug);
|
|
20
|
+
if (!target) throw new Error(`@10x-media/form-builder: uploads.collection "${slug}" was not found in config.collections. Create an upload-enabled collection (with your storage adapter) and pass its slug, e.g. formBuilder({ uploads: { collection: 'media' } }).`);
|
|
21
|
+
if (!target.upload) throw new Error(`@10x-media/form-builder: uploads.collection "${slug}" has no \`upload\` config. File fields store Payload uploads, so the collection must be an upload collection (set \`upload: true\` or an upload options object on it).`);
|
|
22
|
+
const fields = hasFieldNamed(target.fields, "owner") ? target.fields : [...target.fields, {
|
|
23
|
+
name: "owner",
|
|
24
|
+
type: "text",
|
|
25
|
+
admin: {
|
|
26
|
+
readOnly: true,
|
|
27
|
+
hidden: true
|
|
28
|
+
}
|
|
29
|
+
}];
|
|
30
|
+
const attached = {
|
|
31
|
+
...target,
|
|
32
|
+
fields,
|
|
33
|
+
...spam ? { hooks: {
|
|
34
|
+
...target.hooks ?? {},
|
|
35
|
+
beforeOperation: [buildUploadRateLimit(spam), ...target.hooks?.beforeOperation ?? []],
|
|
36
|
+
beforeValidate: [buildUploadOwnerStamp(spam), ...target.hooks?.beforeValidate ?? []]
|
|
37
|
+
} } : {}
|
|
38
|
+
};
|
|
39
|
+
config.collections = collections.map((collection) => collection.slug === slug ? attached : collection);
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
export { attachUploadsCollection };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=uploadsCollection.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/poll/applyPollOptions.ts
|
|
2
|
+
/**
|
|
3
|
+
* Replace the options of the field instance named `resultsField` with source-resolved options,
|
|
4
|
+
* without mutating the input. No-op when `resultsField` is unset or matches no instance, so
|
|
5
|
+
* hand-authored options stay untouched on forms without an option source.
|
|
6
|
+
*/
|
|
7
|
+
const applyPollOptions = (fields, resultsField, options) => {
|
|
8
|
+
if (typeof resultsField !== "string" || resultsField.length === 0) return fields;
|
|
9
|
+
return fields.map((instance) => instance.name === resultsField ? {
|
|
10
|
+
...instance,
|
|
11
|
+
options
|
|
12
|
+
} : instance);
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { applyPollOptions };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=applyPollOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applyPollOptions.js","names":[],"sources":["../../src/poll/applyPollOptions.ts"],"sourcesContent":["import type { PollOption } from './definePollOptionSource'\n\n/**\n * Replace the options of the field instance named `resultsField` with source-resolved options,\n * without mutating the input. No-op when `resultsField` is unset or matches no instance, so\n * hand-authored options stay untouched on forms without an option source.\n */\nexport const applyPollOptions = <T extends { name: string }>(\n\tfields: T[],\n\tresultsField: string | null | undefined,\n\toptions: PollOption[]\n): T[] => {\n\tif (typeof resultsField !== 'string' || resultsField.length === 0) {\n\t\treturn fields\n\t}\n\treturn fields.map((instance) =>\n\t\tinstance.name === resultsField ? { ...instance, options } : instance\n\t)\n}\n"],"mappings":";;;;;;AAOA,MAAa,oBACZ,QACA,cACA,YACS;CACT,IAAI,OAAO,iBAAiB,YAAY,aAAa,WAAW,GAC/D,OAAO;CAER,OAAO,OAAO,KAAK,aAClB,SAAS,SAAS,eAAe;EAAE,GAAG;EAAU;CAAQ,IAAI,QAC7D;AACD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { keys } from "../translations/keys.js";
|
|
2
|
+
import { labelFor, labelForKey } from "../translations/server.js";
|
|
3
|
+
//#region src/poll/buildPollOptionSourceFields.ts
|
|
4
|
+
/**
|
|
5
|
+
* Builds the `optionSource` select and `sourceConfig` group for the forms poll group; returns
|
|
6
|
+
* nothing when no sources are registered, so the fields only exist for hosts that opted in.
|
|
7
|
+
* Fields inside `sourceConfig` are annotated with `admin.condition` so only the fields of the
|
|
8
|
+
* currently selected source type are visible; like consent's `sourceConfig`, field names within
|
|
9
|
+
* the group must be unique across all registered sources. Unlike consent's `source` select there
|
|
10
|
+
* is no default: hand-authored options remain the baseline and a source is an explicit opt-in per
|
|
11
|
+
* form (the select stays clearable).
|
|
12
|
+
*/
|
|
13
|
+
const buildPollOptionSourceFields = (registry) => {
|
|
14
|
+
const sources = [...registry.values()];
|
|
15
|
+
if (sources.length === 0) return [];
|
|
16
|
+
const sourceConfigFields = sources.flatMap((source) => (source.config ?? []).map((field) => {
|
|
17
|
+
const existingAdmin = typeof field.admin === "object" && field.admin !== null ? field.admin : {};
|
|
18
|
+
return {
|
|
19
|
+
...field,
|
|
20
|
+
admin: {
|
|
21
|
+
...existingAdmin,
|
|
22
|
+
condition: (data) => data?.poll?.optionSource === source.type
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}));
|
|
26
|
+
return [{
|
|
27
|
+
name: "optionSource",
|
|
28
|
+
type: "select",
|
|
29
|
+
label: labelForKey(keys.pollOptionSource),
|
|
30
|
+
admin: {
|
|
31
|
+
description: labelForKey(keys.pollOptionSourceDescription),
|
|
32
|
+
condition: (_data, siblingData) => Boolean(siblingData?.enabled)
|
|
33
|
+
},
|
|
34
|
+
options: sources.map((source) => ({
|
|
35
|
+
label: labelFor(source.label),
|
|
36
|
+
value: source.type
|
|
37
|
+
}))
|
|
38
|
+
}, {
|
|
39
|
+
name: "sourceConfig",
|
|
40
|
+
type: "group",
|
|
41
|
+
label: labelForKey(keys.pollSourceConfig),
|
|
42
|
+
admin: { condition: (_data, siblingData) => Boolean(siblingData?.enabled && siblingData?.optionSource) },
|
|
43
|
+
fields: sourceConfigFields.length > 0 ? sourceConfigFields : [{
|
|
44
|
+
name: "_placeholder",
|
|
45
|
+
type: "text",
|
|
46
|
+
admin: { hidden: true }
|
|
47
|
+
}]
|
|
48
|
+
}];
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
export { buildPollOptionSourceFields };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=buildPollOptionSourceFields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildPollOptionSourceFields.js","names":[],"sources":["../../src/poll/buildPollOptionSourceFields.ts"],"sourcesContent":["import type { Field } from 'payload'\nimport { keys } from '../translations/keys'\nimport { labelFor, labelForKey } from '../translations/server'\nimport type { PollOptionSourceRegistry } from './registry'\n\n/**\n * Builds the `optionSource` select and `sourceConfig` group for the forms poll group; returns\n * nothing when no sources are registered, so the fields only exist for hosts that opted in.\n * Fields inside `sourceConfig` are annotated with `admin.condition` so only the fields of the\n * currently selected source type are visible; like consent's `sourceConfig`, field names within\n * the group must be unique across all registered sources. Unlike consent's `source` select there\n * is no default: hand-authored options remain the baseline and a source is an explicit opt-in per\n * form (the select stays clearable).\n */\nexport const buildPollOptionSourceFields = (registry: PollOptionSourceRegistry): Field[] => {\n\tconst sources = [...registry.values()]\n\tif (sources.length === 0) {\n\t\treturn []\n\t}\n\n\tconst sourceConfigFields: Field[] = sources.flatMap((source) =>\n\t\t(source.config ?? []).map((field) => {\n\t\t\tconst existingAdmin =\n\t\t\t\ttypeof field.admin === 'object' && field.admin !== null ? field.admin : {}\n\t\t\treturn {\n\t\t\t\t...field,\n\t\t\t\tadmin: {\n\t\t\t\t\t...existingAdmin,\n\t\t\t\t\tcondition: (data: unknown) =>\n\t\t\t\t\t\t(data as { poll?: { optionSource?: string } })?.poll?.optionSource === source.type,\n\t\t\t\t},\n\t\t\t} as Field\n\t\t})\n\t)\n\n\treturn [\n\t\t{\n\t\t\tname: 'optionSource',\n\t\t\ttype: 'select',\n\t\t\tlabel: labelForKey(keys.pollOptionSource),\n\t\t\tadmin: {\n\t\t\t\tdescription: labelForKey(keys.pollOptionSourceDescription),\n\t\t\t\tcondition: (_data, siblingData) => Boolean(siblingData?.enabled),\n\t\t\t},\n\t\t\toptions: sources.map((source) => ({ label: labelFor(source.label), value: source.type })),\n\t\t},\n\t\t{\n\t\t\tname: 'sourceConfig',\n\t\t\ttype: 'group',\n\t\t\tlabel: labelForKey(keys.pollSourceConfig),\n\t\t\tadmin: {\n\t\t\t\tcondition: (_data, siblingData) =>\n\t\t\t\t\tBoolean(siblingData?.enabled && siblingData?.optionSource),\n\t\t\t},\n\t\t\tfields:\n\t\t\t\tsourceConfigFields.length > 0\n\t\t\t\t\t? sourceConfigFields\n\t\t\t\t\t: [{ name: '_placeholder', type: 'text', admin: { hidden: true } }],\n\t\t},\n\t]\n}\n"],"mappings":";;;;;;;;;;;;AAcA,MAAa,+BAA+B,aAAgD;CAC3F,MAAM,UAAU,CAAC,GAAG,SAAS,OAAO,CAAC;CACrC,IAAI,QAAQ,WAAW,GACtB,OAAO,CAAC;CAGT,MAAM,qBAA8B,QAAQ,SAAS,YACnD,OAAO,UAAU,CAAC,GAAG,KAAK,UAAU;EACpC,MAAM,gBACL,OAAO,MAAM,UAAU,YAAY,MAAM,UAAU,OAAO,MAAM,QAAQ,CAAC;EAC1E,OAAO;GACN,GAAG;GACH,OAAO;IACN,GAAG;IACH,YAAY,SACV,MAA+C,MAAM,iBAAiB,OAAO;GAChF;EACD;CACD,CAAC,CACF;CAEA,OAAO,CACN;EACC,MAAM;EACN,MAAM;EACN,OAAO,YAAY,KAAK,gBAAgB;EACxC,OAAO;GACN,aAAa,YAAY,KAAK,2BAA2B;GACzD,YAAY,OAAO,gBAAgB,QAAQ,aAAa,OAAO;EAChE;EACA,SAAS,QAAQ,KAAK,YAAY;GAAE,OAAO,SAAS,OAAO,KAAK;GAAG,OAAO,OAAO;EAAK,EAAE;CACzF,GACA;EACC,MAAM;EACN,MAAM;EACN,OAAO,YAAY,KAAK,gBAAgB;EACxC,OAAO,EACN,YAAY,OAAO,gBAClB,QAAQ,aAAa,WAAW,aAAa,YAAY,EAC3D;EACA,QACC,mBAAmB,SAAS,IACzB,qBACA,CAAC;GAAE,MAAM;GAAgB,MAAM;GAAQ,OAAO,EAAE,QAAQ,KAAK;EAAE,CAAC;CACrE,CACD;AACD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Field, Payload, PayloadRequest } from "payload";
|
|
2
|
+
|
|
3
|
+
//#region src/poll/definePollOptionSource.d.ts
|
|
4
|
+
/** One selectable poll choice. `value` is the host's stable identifier contract: it is stored in submissions and later matched against `outcome.winningValue`, so it must never change once votes exist. */
|
|
5
|
+
type PollOption = {
|
|
6
|
+
label: string;
|
|
7
|
+
value: string;
|
|
8
|
+
};
|
|
9
|
+
type PollOptionResolveArgs<TConfig extends Record<string, unknown> = Record<string, unknown>> = {
|
|
10
|
+
config: TConfig;
|
|
11
|
+
form: {
|
|
12
|
+
id: number | string;
|
|
13
|
+
title?: string;
|
|
14
|
+
};
|
|
15
|
+
payload: Payload;
|
|
16
|
+
req?: PayloadRequest;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A poll option source type, authored once by the host: `config` is the admin `Field[]` an author
|
|
20
|
+
* fills per form (e.g. an event id); `resolve` maps that config to the current options from host
|
|
21
|
+
* domain data. Called at render time (via `resolvePollOptions`) and again at submission time, where
|
|
22
|
+
* the resolved values are the only accepted answers (fail closed).
|
|
23
|
+
*/
|
|
24
|
+
type PollOptionSource<TConfig extends Record<string, unknown> = Record<string, unknown>> = {
|
|
25
|
+
type: string;
|
|
26
|
+
label: string;
|
|
27
|
+
config?: Field[];
|
|
28
|
+
resolve: (args: PollOptionResolveArgs<TConfig>) => Promise<PollOption[]> | PollOption[];
|
|
29
|
+
};
|
|
30
|
+
/** Erased shape stored in the registry; config re-narrows per matched type at resolution. */
|
|
31
|
+
type AnyPollOptionSource = PollOptionSource<Record<string, unknown>>;
|
|
32
|
+
declare const definePollOptionSource: <TConfig extends Record<string, unknown> = Record<string, unknown>>(source: PollOptionSource<TConfig>) => PollOptionSource<TConfig>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { AnyPollOptionSource, PollOption, PollOptionResolveArgs, PollOptionSource, definePollOptionSource };
|
|
35
|
+
//# sourceMappingURL=definePollOptionSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definePollOptionSource.js","names":[],"sources":["../../src/poll/definePollOptionSource.ts"],"sourcesContent":["import type { Field, Payload, PayloadRequest } from 'payload'\n\n/** One selectable poll choice. `value` is the host's stable identifier contract: it is stored in submissions and later matched against `outcome.winningValue`, so it must never change once votes exist. */\nexport type PollOption = { label: string; value: string }\n\nexport type PollOptionResolveArgs<\n\tTConfig extends Record<string, unknown> = Record<string, unknown>,\n> = {\n\tconfig: TConfig\n\tform: { id: number | string; title?: string }\n\tpayload: Payload\n\treq?: PayloadRequest\n}\n\n/**\n * A poll option source type, authored once by the host: `config` is the admin `Field[]` an author\n * fills per form (e.g. an event id); `resolve` maps that config to the current options from host\n * domain data. Called at render time (via `resolvePollOptions`) and again at submission time, where\n * the resolved values are the only accepted answers (fail closed).\n */\nexport type PollOptionSource<TConfig extends Record<string, unknown> = Record<string, unknown>> = {\n\ttype: string\n\tlabel: string\n\tconfig?: Field[]\n\tresolve: (args: PollOptionResolveArgs<TConfig>) => Promise<PollOption[]> | PollOption[]\n}\n\n/** Erased shape stored in the registry; config re-narrows per matched type at resolution. */\nexport type AnyPollOptionSource = PollOptionSource<Record<string, unknown>>\n\nexport const definePollOptionSource = <\n\tTConfig extends Record<string, unknown> = Record<string, unknown>,\n>(\n\tsource: PollOptionSource<TConfig>\n): PollOptionSource<TConfig> => source\n"],"mappings":";AA8BA,MAAa,0BAGZ,WAC+B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AnyPollOptionSource } from "./definePollOptionSource.js";
|
|
2
|
+
|
|
3
|
+
//#region src/poll/registry.d.ts
|
|
4
|
+
type PollOptionSourceRegistry = Map<string, AnyPollOptionSource>;
|
|
5
|
+
/** `false` removes a source, `true` keeps it (no-op: there are no built-ins), a definition adds or replaces one. */
|
|
6
|
+
type PollOptionSourceOption = boolean | AnyPollOptionSource;
|
|
7
|
+
type PollOptionSourcesConfig = Record<string, PollOptionSourceOption>;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the active poll option-source registry from a consumer map. Unlike the consent and
|
|
10
|
+
* action registries there are no built-in defaults: the registry starts empty and only host
|
|
11
|
+
* definitions populate it, so `true`/`false` entries are accepted but change nothing.
|
|
12
|
+
*/
|
|
13
|
+
declare const resolvePollOptionSources: (config?: PollOptionSourcesConfig) => PollOptionSourceRegistry;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { PollOptionSourceOption, PollOptionSourceRegistry, PollOptionSourcesConfig, resolvePollOptionSources };
|
|
16
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/poll/registry.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the active poll option-source registry from a consumer map. Unlike the consent and
|
|
4
|
+
* action registries there are no built-in defaults: the registry starts empty and only host
|
|
5
|
+
* definitions populate it, so `true`/`false` entries are accepted but change nothing.
|
|
6
|
+
*/
|
|
7
|
+
const resolvePollOptionSources = (config = {}) => {
|
|
8
|
+
const registry = /* @__PURE__ */ new Map();
|
|
9
|
+
for (const [type, option] of Object.entries(config)) if (option === false) registry.delete(type);
|
|
10
|
+
else if (option === true) {} else registry.set(type, option);
|
|
11
|
+
return registry;
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { resolvePollOptionSources };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","names":[],"sources":["../../src/poll/registry.ts"],"sourcesContent":["import type { AnyPollOptionSource } from './definePollOptionSource'\n\nexport type PollOptionSourceRegistry = Map<string, AnyPollOptionSource>\n\n/** `false` removes a source, `true` keeps it (no-op: there are no built-ins), a definition adds or replaces one. */\nexport type PollOptionSourceOption = boolean | AnyPollOptionSource\n\nexport type PollOptionSourcesConfig = Record<string, PollOptionSourceOption>\n\n/**\n * Resolve the active poll option-source registry from a consumer map. Unlike the consent and\n * action registries there are no built-in defaults: the registry starts empty and only host\n * definitions populate it, so `true`/`false` entries are accepted but change nothing.\n */\nexport const resolvePollOptionSources = (\n\tconfig: PollOptionSourcesConfig = {}\n): PollOptionSourceRegistry => {\n\tconst registry: PollOptionSourceRegistry = new Map()\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\t// keep the default; no-op because no built-in poll option sources exist\n\t\t} else {\n\t\t\tregistry.set(type, option)\n\t\t}\n\t}\n\treturn registry\n}\n"],"mappings":";;;;;;AAcA,MAAa,4BACZ,SAAkC,CAAC,MACL;CAC9B,MAAM,2BAAqC,IAAI,IAAI;CACnD,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,MAAM,GACjD,IAAI,WAAW,OACd,SAAS,OAAO,IAAI;MACd,IAAI,WAAW,MAAM,CAE5B,OACC,SAAS,IAAI,MAAM,MAAM;CAG3B,OAAO;AACR"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PollOption } from "./definePollOptionSource.js";
|
|
2
|
+
import { PollOptionSourceRegistry } from "./registry.js";
|
|
3
|
+
import { Payload, PayloadRequest } from "payload";
|
|
4
|
+
|
|
5
|
+
//#region src/poll/resolvePollOptions.d.ts
|
|
6
|
+
type ResolvePollOptionsArgs = {
|
|
7
|
+
payload: Payload;
|
|
8
|
+
req?: PayloadRequest; /** A loaded forms document (its `poll` group drives resolution). */
|
|
9
|
+
form: {
|
|
10
|
+
id: number | string;
|
|
11
|
+
title?: string | null;
|
|
12
|
+
poll?: unknown;
|
|
13
|
+
}; /** Registry override for plugin-internal callers; defaults to the registry the plugin stashed on the config. */
|
|
14
|
+
sources?: PollOptionSourceRegistry;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a poll-enabled form's choice options from its configured option source (plugin option
|
|
18
|
+
* `poll.sources`). Returns `undefined` when the form is not poll-enabled or has no `optionSource`
|
|
19
|
+
* set, so hand-authored options render unchanged. Hosts call this server-side and hand the result
|
|
20
|
+
* to `toFormDocument(doc, { pollOptions })`; submission validation calls it too, making the
|
|
21
|
+
* resolved values the only accepted answers. Throws when the configured source type is not
|
|
22
|
+
* registered or its `resolve` fails; results are cached per request (keyed by form id) when a
|
|
23
|
+
* `req` is provided.
|
|
24
|
+
*/
|
|
25
|
+
declare const resolvePollOptions: (args: ResolvePollOptionsArgs) => Promise<PollOption[] | undefined>;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { ResolvePollOptionsArgs, resolvePollOptions };
|
|
28
|
+
//# sourceMappingURL=resolvePollOptions.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { pollConfigOf } from "../form/pollState.js";
|
|
2
|
+
//#region src/poll/resolvePollOptions.ts
|
|
3
|
+
/** Root key under `config.custom` where the plugin stashes runtime state for root-level helpers. */
|
|
4
|
+
const CUSTOM_KEY = "@10x-media/form-builder";
|
|
5
|
+
/** Store the resolved registry on the config so `resolvePollOptions` can find it via `payload.config`. */
|
|
6
|
+
const stashPollOptionSources = (custom, registry) => {
|
|
7
|
+
const existing = custom?.[CUSTOM_KEY] != null && typeof custom[CUSTOM_KEY] === "object" ? custom[CUSTOM_KEY] : {};
|
|
8
|
+
return {
|
|
9
|
+
...custom ?? {},
|
|
10
|
+
[CUSTOM_KEY]: {
|
|
11
|
+
...existing,
|
|
12
|
+
pollOptionSources: registry
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
const pollOptionSourcesOf = (payload) => {
|
|
17
|
+
return (payload.config.custom?.[CUSTOM_KEY])?.pollOptionSources ?? /* @__PURE__ */ new Map();
|
|
18
|
+
};
|
|
19
|
+
const CACHE_KEY = "formBuilderPollOptionsCache";
|
|
20
|
+
const cacheOf = (req) => {
|
|
21
|
+
if (!req?.context) return;
|
|
22
|
+
const existing = req.context[CACHE_KEY];
|
|
23
|
+
if (existing instanceof Map) return existing;
|
|
24
|
+
const created = /* @__PURE__ */ new Map();
|
|
25
|
+
req.context[CACHE_KEY] = created;
|
|
26
|
+
return created;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Resolve a poll-enabled form's choice options from its configured option source (plugin option
|
|
30
|
+
* `poll.sources`). Returns `undefined` when the form is not poll-enabled or has no `optionSource`
|
|
31
|
+
* set, so hand-authored options render unchanged. Hosts call this server-side and hand the result
|
|
32
|
+
* to `toFormDocument(doc, { pollOptions })`; submission validation calls it too, making the
|
|
33
|
+
* resolved values the only accepted answers. Throws when the configured source type is not
|
|
34
|
+
* registered or its `resolve` fails; results are cached per request (keyed by form id) when a
|
|
35
|
+
* `req` is provided.
|
|
36
|
+
*/
|
|
37
|
+
const resolvePollOptions = async (args) => {
|
|
38
|
+
const { payload, req, form, sources } = args;
|
|
39
|
+
const poll = pollConfigOf(form.poll);
|
|
40
|
+
const optionSource = typeof poll?.optionSource === "string" ? poll.optionSource : void 0;
|
|
41
|
+
if (poll?.enabled !== true || !optionSource) return;
|
|
42
|
+
const source = (sources ?? pollOptionSourcesOf(payload)).get(optionSource);
|
|
43
|
+
if (!source) throw new Error(`Poll option source "${optionSource}" is not registered. Register it via the plugin's poll.sources option.`);
|
|
44
|
+
const cache = cacheOf(req);
|
|
45
|
+
const cacheKey = String(form.id);
|
|
46
|
+
const cached = cache?.get(cacheKey);
|
|
47
|
+
if (cached) return cached;
|
|
48
|
+
const config = poll.sourceConfig != null && typeof poll.sourceConfig === "object" ? poll.sourceConfig : {};
|
|
49
|
+
const options = await source.resolve({
|
|
50
|
+
config,
|
|
51
|
+
form: {
|
|
52
|
+
id: form.id,
|
|
53
|
+
title: typeof form.title === "string" ? form.title : void 0
|
|
54
|
+
},
|
|
55
|
+
payload,
|
|
56
|
+
req
|
|
57
|
+
});
|
|
58
|
+
cache?.set(cacheKey, options);
|
|
59
|
+
return options;
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { resolvePollOptions, stashPollOptionSources };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=resolvePollOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolvePollOptions.js","names":[],"sources":["../../src/poll/resolvePollOptions.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { pollConfigOf } from '../form/pollState'\nimport type { PollOption } from './definePollOptionSource'\nimport type { PollOptionSourceRegistry } from './registry'\n\n/** Root key under `config.custom` where the plugin stashes runtime state for root-level helpers. */\nconst CUSTOM_KEY = '@10x-media/form-builder'\n\n/** Store the resolved registry on the config so `resolvePollOptions` can find it via `payload.config`. */\nexport const stashPollOptionSources = (\n\tcustom: Record<string, unknown> | undefined,\n\tregistry: PollOptionSourceRegistry\n): Record<string, unknown> => {\n\tconst existing =\n\t\tcustom?.[CUSTOM_KEY] != null && typeof custom[CUSTOM_KEY] === 'object'\n\t\t\t? (custom[CUSTOM_KEY] as Record<string, unknown>)\n\t\t\t: {}\n\treturn { ...(custom ?? {}), [CUSTOM_KEY]: { ...existing, pollOptionSources: registry } }\n}\n\nexport const pollOptionSourcesOf = (payload: Payload): PollOptionSourceRegistry => {\n\tconst custom = payload.config.custom?.[CUSTOM_KEY] as\n\t\t| { pollOptionSources?: PollOptionSourceRegistry }\n\t\t| undefined\n\treturn custom?.pollOptionSources ?? new Map()\n}\n\nconst CACHE_KEY = 'formBuilderPollOptionsCache'\n\ntype OptionsCache = Map<string, PollOption[]>\n\nconst cacheOf = (req: PayloadRequest | undefined): OptionsCache | undefined => {\n\tif (!req?.context) {\n\t\treturn undefined\n\t}\n\tconst existing = req.context[CACHE_KEY]\n\tif (existing instanceof Map) {\n\t\treturn existing as OptionsCache\n\t}\n\tconst created: OptionsCache = new Map()\n\treq.context[CACHE_KEY] = created\n\treturn created\n}\n\nexport type ResolvePollOptionsArgs = {\n\tpayload: Payload\n\treq?: PayloadRequest\n\t/** A loaded forms document (its `poll` group drives resolution). */\n\tform: { id: number | string; title?: string | null; poll?: unknown }\n\t/** Registry override for plugin-internal callers; defaults to the registry the plugin stashed on the config. */\n\tsources?: PollOptionSourceRegistry\n}\n\n/**\n * Resolve a poll-enabled form's choice options from its configured option source (plugin option\n * `poll.sources`). Returns `undefined` when the form is not poll-enabled or has no `optionSource`\n * set, so hand-authored options render unchanged. Hosts call this server-side and hand the result\n * to `toFormDocument(doc, { pollOptions })`; submission validation calls it too, making the\n * resolved values the only accepted answers. Throws when the configured source type is not\n * registered or its `resolve` fails; results are cached per request (keyed by form id) when a\n * `req` is provided.\n */\nexport const resolvePollOptions = async (\n\targs: ResolvePollOptionsArgs\n): Promise<PollOption[] | undefined> => {\n\tconst { payload, req, form, sources } = args\n\tconst poll = pollConfigOf(form.poll)\n\tconst optionSource = typeof poll?.optionSource === 'string' ? poll.optionSource : undefined\n\tif (poll?.enabled !== true || !optionSource) {\n\t\treturn undefined\n\t}\n\tconst source = (sources ?? pollOptionSourcesOf(payload)).get(optionSource)\n\tif (!source) {\n\t\tthrow new Error(\n\t\t\t`Poll option source \"${optionSource}\" is not registered. Register it via the plugin's poll.sources option.`\n\t\t)\n\t}\n\tconst cache = cacheOf(req)\n\tconst cacheKey = String(form.id)\n\tconst cached = cache?.get(cacheKey)\n\tif (cached) {\n\t\treturn cached\n\t}\n\tconst config =\n\t\tpoll.sourceConfig != null && typeof poll.sourceConfig === 'object'\n\t\t\t? (poll.sourceConfig as Record<string, unknown>)\n\t\t\t: {}\n\tconst options = await source.resolve({\n\t\tconfig,\n\t\tform: { id: form.id, title: typeof form.title === 'string' ? form.title : undefined },\n\t\tpayload,\n\t\treq,\n\t})\n\tcache?.set(cacheKey, options)\n\treturn options\n}\n"],"mappings":";;;AAMA,MAAM,aAAa;;AAGnB,MAAa,0BACZ,QACA,aAC6B;CAC7B,MAAM,WACL,SAAS,eAAe,QAAQ,OAAO,OAAO,gBAAgB,WAC1D,OAAO,cACR,CAAC;CACL,OAAO;EAAE,GAAI,UAAU,CAAC;GAAK,aAAa;GAAE,GAAG;GAAU,mBAAmB;EAAS;CAAE;AACxF;AAEA,MAAa,uBAAuB,YAA+C;CAIlF,QAHe,QAAQ,OAAO,SAAS,cAGxB,qCAAqB,IAAI,IAAI;AAC7C;AAEA,MAAM,YAAY;AAIlB,MAAM,WAAW,QAA8D;CAC9E,IAAI,CAAC,KAAK,SACT;CAED,MAAM,WAAW,IAAI,QAAQ;CAC7B,IAAI,oBAAoB,KACvB,OAAO;CAER,MAAM,0BAAwB,IAAI,IAAI;CACtC,IAAI,QAAQ,aAAa;CACzB,OAAO;AACR;;;;;;;;;;AAoBA,MAAa,qBAAqB,OACjC,SACuC;CACvC,MAAM,EAAE,SAAS,KAAK,MAAM,YAAY;CACxC,MAAM,OAAO,aAAa,KAAK,IAAI;CACnC,MAAM,eAAe,OAAO,MAAM,iBAAiB,WAAW,KAAK,eAAe,KAAA;CAClF,IAAI,MAAM,YAAY,QAAQ,CAAC,cAC9B;CAED,MAAM,UAAU,WAAW,oBAAoB,OAAO,GAAG,IAAI,YAAY;CACzE,IAAI,CAAC,QACJ,MAAM,IAAI,MACT,uBAAuB,aAAa,uEACrC;CAED,MAAM,QAAQ,QAAQ,GAAG;CACzB,MAAM,WAAW,OAAO,KAAK,EAAE;CAC/B,MAAM,SAAS,OAAO,IAAI,QAAQ;CAClC,IAAI,QACH,OAAO;CAER,MAAM,SACL,KAAK,gBAAgB,QAAQ,OAAO,KAAK,iBAAiB,WACtD,KAAK,eACN,CAAC;CACL,MAAM,UAAU,MAAM,OAAO,QAAQ;EACpC;EACA,MAAM;GAAE,IAAI,KAAK;GAAI,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;EAAU;EACpF;EACA;CACD,CAAC;CACD,OAAO,IAAI,UAAU,OAAO;CAC5B,OAAO;AACR"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Payload, PayloadRequest } from "payload";
|
|
2
|
+
|
|
3
|
+
//#region src/poll/resolvePollOutcome.d.ts
|
|
4
|
+
type ResolvePollOutcomeArgs = {
|
|
5
|
+
payload: Payload;
|
|
6
|
+
formId: number | string; /** A stable option value, matching one of the poll's choice values (the host's contract with `PollOption.value`). */
|
|
7
|
+
winningValue: string;
|
|
8
|
+
req?: PayloadRequest;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Record a poll's final outcome from host domain logic (e.g. after the race is decided): writes
|
|
12
|
+
* `poll.outcome.winningValue` and stamps `resolvedAt` with the current time. The outcome fields are
|
|
13
|
+
* admin read-only with field-level `update: () => false`, so this server-side call (with
|
|
14
|
+
* `overrideAccess`) is the only write path. Throws when the form does not exist or is not
|
|
15
|
+
* poll-enabled. `<Poll>` treats a present `winningValue` as the final state and highlights the
|
|
16
|
+
* matching result bucket.
|
|
17
|
+
*/
|
|
18
|
+
declare const resolvePollOutcome: (args: ResolvePollOutcomeArgs) => Promise<void>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { ResolvePollOutcomeArgs, resolvePollOutcome };
|
|
21
|
+
//# sourceMappingURL=resolvePollOutcome.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { pollConfigOf } from "../form/pollState.js";
|
|
2
|
+
import { FORMS_SLUG } from "../collections/forms.js";
|
|
3
|
+
//#region src/poll/resolvePollOutcome.ts
|
|
4
|
+
/**
|
|
5
|
+
* Record a poll's final outcome from host domain logic (e.g. after the race is decided): writes
|
|
6
|
+
* `poll.outcome.winningValue` and stamps `resolvedAt` with the current time. The outcome fields are
|
|
7
|
+
* admin read-only with field-level `update: () => false`, so this server-side call (with
|
|
8
|
+
* `overrideAccess`) is the only write path. Throws when the form does not exist or is not
|
|
9
|
+
* poll-enabled. `<Poll>` treats a present `winningValue` as the final state and highlights the
|
|
10
|
+
* matching result bucket.
|
|
11
|
+
*/
|
|
12
|
+
const resolvePollOutcome = async (args) => {
|
|
13
|
+
const { payload, formId, winningValue, req } = args;
|
|
14
|
+
if (pollConfigOf((await payload.findByID({
|
|
15
|
+
collection: "forms",
|
|
16
|
+
id: formId,
|
|
17
|
+
depth: 0,
|
|
18
|
+
overrideAccess: true,
|
|
19
|
+
req
|
|
20
|
+
})).poll)?.enabled !== true) throw new Error(`Form ${String(formId)} is not poll-enabled; cannot record an outcome.`);
|
|
21
|
+
await payload.update({
|
|
22
|
+
collection: FORMS_SLUG,
|
|
23
|
+
id: formId,
|
|
24
|
+
data: { poll: { outcome: {
|
|
25
|
+
winningValue,
|
|
26
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27
|
+
} } },
|
|
28
|
+
depth: 0,
|
|
29
|
+
overrideAccess: true,
|
|
30
|
+
req
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { resolvePollOutcome };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=resolvePollOutcome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolvePollOutcome.js","names":[],"sources":["../../src/poll/resolvePollOutcome.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { FORMS_SLUG } from '../collections/forms'\nimport { pollConfigOf } from '../form/pollState'\n\nexport type ResolvePollOutcomeArgs = {\n\tpayload: Payload\n\tformId: number | string\n\t/** A stable option value, matching one of the poll's choice values (the host's contract with `PollOption.value`). */\n\twinningValue: string\n\treq?: PayloadRequest\n}\n\n/**\n * Record a poll's final outcome from host domain logic (e.g. after the race is decided): writes\n * `poll.outcome.winningValue` and stamps `resolvedAt` with the current time. The outcome fields are\n * admin read-only with field-level `update: () => false`, so this server-side call (with\n * `overrideAccess`) is the only write path. Throws when the form does not exist or is not\n * poll-enabled. `<Poll>` treats a present `winningValue` as the final state and highlights the\n * matching result bucket.\n */\nexport const resolvePollOutcome = async (args: ResolvePollOutcomeArgs): Promise<void> => {\n\tconst { payload, formId, winningValue, req } = args\n\tconst form = await payload.findByID({\n\t\tcollection: FORMS_SLUG,\n\t\tid: formId,\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\tconst poll = pollConfigOf(form.poll)\n\tif (poll?.enabled !== true) {\n\t\tthrow new Error(`Form ${String(formId)} is not poll-enabled; cannot record an outcome.`)\n\t}\n\tawait payload.update({\n\t\tcollection: FORMS_SLUG,\n\t\tid: formId,\n\t\tdata: { poll: { outcome: { winningValue, resolvedAt: new Date().toISOString() } } },\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n}\n"],"mappings":";;;;;;;;;;;AAoBA,MAAa,qBAAqB,OAAO,SAAgD;CACxF,MAAM,EAAE,SAAS,QAAQ,cAAc,QAAQ;CAS/C,IADa,cAAa,MAPP,QAAQ,SAAS;EACnC,YAAA;EACA,IAAI;EACJ,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC,GAC8B,IACxB,GAAG,YAAY,MACrB,MAAM,IAAI,MAAM,QAAQ,OAAO,MAAM,EAAE,gDAAgD;CAExF,MAAM,QAAQ,OAAO;EACpB,YAAY;EACZ,IAAI;EACJ,MAAM,EAAE,MAAM,EAAE,SAAS;GAAE;GAAc,6BAAY,IAAI,KAAK,GAAE,YAAY;EAAE,EAAE,EAAE;EAClF,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;AACF"}
|
|
@@ -32,7 +32,7 @@ const valuesFromSearchParams = (params, fields, registry, options = {}) => {
|
|
|
32
32
|
if (options.deny?.includes(fieldName)) continue;
|
|
33
33
|
const field = byName.get(fieldName);
|
|
34
34
|
const definition = field && registry.get(field.blockType);
|
|
35
|
-
if (!field || !definition) continue;
|
|
35
|
+
if (!field || !definition || definition.value === "none") continue;
|
|
36
36
|
const all = isSearch ? params.getAll(param) : [params[param]];
|
|
37
37
|
const value = coerce(definition.value, all[0] ?? "", all);
|
|
38
38
|
if (value !== void 0) result[fieldName] = value;
|