@10x-media/form-builder 0.1.0-beta.13 → 0.1.0-beta.15
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 +12 -0
- package/dist/collections/formSubmissions.js +40 -20
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +20 -0
- package/dist/collections/forms.js.map +1 -1
- package/dist/exports/react.d.ts +3 -1
- package/dist/exports/rsc.d.ts +2 -1
- package/dist/exports/rsc.js +4 -3
- package/dist/form/pollState.d.ts +2 -1
- package/dist/form/pollState.js.map +1 -1
- package/dist/form/toFormDocument.d.ts +1 -0
- package/dist/form/toFormDocument.js +1 -0
- package/dist/form/toFormDocument.js.map +1 -1
- package/dist/form/types.d.ts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/poll/votes/aggregateFromVotes.js +2 -1
- package/dist/poll/votes/aggregateFromVotes.js.map +1 -1
- package/dist/poll/votes/answerValues.js +16 -0
- package/dist/poll/votes/answerValues.js.map +1 -0
- package/dist/poll/votes/voteTallyHook.js +31 -13
- package/dist/poll/votes/voteTallyHook.js.map +1 -1
- package/dist/react/Form.d.ts +12 -2
- package/dist/react/Form.js +8 -3
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormResults.d.ts +4 -2
- package/dist/react/FormResults.js +21 -8
- package/dist/react/FormResults.js.map +1 -1
- package/dist/react/Poll.d.ts +14 -1
- package/dist/react/Poll.js +66 -21
- package/dist/react/Poll.js.map +1 -1
- package/dist/react/adapters.d.ts +35 -0
- package/dist/react/adapters.js +11 -0
- package/dist/react/adapters.js.map +1 -0
- package/dist/react/captcha/HcaptchaCaptcha.d.ts +4 -1
- package/dist/react/captcha/HcaptchaCaptcha.js +2 -2
- package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -1
- package/dist/react/captcha/RecaptchaCaptcha.d.ts +4 -1
- package/dist/react/captcha/RecaptchaCaptcha.js +2 -2
- package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -1
- package/dist/react/captcha/TurnstileCaptcha.d.ts +4 -1
- package/dist/react/captcha/TurnstileCaptcha.js +2 -2
- package/dist/react/captcha/TurnstileCaptcha.js.map +1 -1
- package/dist/react/captcha/useCaptchaScript.js +23 -5
- package/dist/react/captcha/useCaptchaScript.js.map +1 -1
- package/dist/spam/spamGuard.js +2 -1
- package/dist/spam/spamGuard.js.map +1 -1
- package/dist/submissions/resolveVotedSubmission.d.ts +27 -0
- package/dist/submissions/resolveVotedSubmission.js +51 -0
- package/dist/submissions/resolveVotedSubmission.js.map +1 -0
- package/dist/submissions/validateSubmission.js +17 -6
- package/dist/submissions/validateSubmission.js.map +1 -1
- package/dist/submissions/verifyContext.js +1 -1
- package/dist/submissions/voteChangeEndpoint.js +94 -0
- package/dist/submissions/voteChangeEndpoint.js.map +1 -0
- package/dist/submissions/votedCookie.d.ts +2 -0
- package/dist/submissions/votedCookie.js +53 -6
- package/dist/submissions/votedCookie.js.map +1 -1
- package/dist/translations/clientRuntimeKeys.js +3 -1
- package/dist/translations/clientRuntimeKeys.js.map +1 -1
- package/dist/translations/de.js +5 -0
- package/dist/translations/de.js.map +1 -1
- package/dist/translations/en.js +5 -0
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/keys.d.ts +5 -0
- package/dist/translations/keys.js +5 -0
- package/dist/translations/keys.js.map +1 -1
- package/dist/validation/builtin/notAlreadySubmitted.js +3 -1
- package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
- package/package.json +3 -3
|
@@ -52,6 +52,7 @@ function toFormDocument(form, options) {
|
|
|
52
52
|
const poll = form.poll ? {
|
|
53
53
|
resultsVisibility: form.poll.resultsVisibility ?? void 0,
|
|
54
54
|
closesAt: form.poll.closesAt ?? void 0,
|
|
55
|
+
...form.poll.allowChange === true ? { allowChange: true } : {},
|
|
55
56
|
...winningValues.length > 0 ? { outcome: { winningValues } } : {}
|
|
56
57
|
} : void 0;
|
|
57
58
|
let fields = form.fields ?? [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toFormDocument.js","names":[],"sources":["../../src/form/toFormDocument.ts"],"sourcesContent":["import type { CalcResolved } from '../calc/evaluate'\nimport { applyConsentStatements } from '../consent/applyConsentStatements'\nimport type { ConsentStatements } from '../consent/resolveConsentStatements'\nimport type { FormFlow } from '../flow/types'\nimport { applyPollOptions } from '../poll/applyPollOptions'\nimport type { PollOption } from '../poll/definePollOptionSource'\nimport type { FormFieldInstance } from '../submissions/types'\nimport type { FormButtonSettings, FormDocument, FormResponseSettings } from './types'\n\nexport type ToFormDocumentOptions = {\n\t/**\n\t * Source-resolved poll options (from `resolvePollOptions`), injected as the options of the\n\t * field instance named by the form's `poll.resultsField` so the client renders the host's\n\t * current choices instead of hand-authored ones.\n\t */\n\tpollOptions?: PollOption[]\n\t/**\n\t * Source-resolved consent statements (from `resolveConsentStatements`), injected into the\n\t * consent field instances they name. A form stores only a source id, so without these its\n\t * consent fields render with no statement at all.\n\t */\n\tconsentStatements?: ConsentStatements\n\t/**\n\t * Resolve an internal redirect `reference` to a same-site URL. Called only when\n\t * `response.type === 'redirect'`, a `reference` is present, and no `redirect.url` is set. Keep it\n\t * sync: operate on the already-loaded (populated) reference, since resolving a Doc to a URL is host\n\t * routing knowledge the plugin cannot supply. The result is written to `redirect.url`, so `<Form>`\n\t * navigates with no client change.\n\t */\n\tresolveRedirect?: (reference: {\n\t\trelationTo: string\n\t\tvalue: number | string | object\n\t}) => string | null | undefined\n}\n\n/**\n * Reassemble the client button labels from the document's top-level `submitLabel`/`prevLabel`/\n * `nextLabel` fields, keeping only non-empty strings (mirroring the client's own `storedLabel`\n * fallback). Returns `undefined` when the author set no label, so the client uses its defaults.\n */\nconst buttonSettingsOf = (form: {\n\tsubmitLabel?: string | null\n\tprevLabel?: string | null\n\tnextLabel?: string | null\n}): FormButtonSettings | undefined => {\n\tconst label = (value: unknown): string | undefined =>\n\t\ttypeof value === 'string' && value.length > 0 ? value : undefined\n\tconst settings: FormButtonSettings = {}\n\tconst submitLabel = label(form.submitLabel)\n\tconst prevLabel = label(form.prevLabel)\n\tconst nextLabel = label(form.nextLabel)\n\tif (submitLabel !== undefined) settings.submitLabel = submitLabel\n\tif (prevLabel !== undefined) settings.prevLabel = prevLabel\n\tif (nextLabel !== undefined) settings.nextLabel = nextLabel\n\treturn Object.keys(settings).length > 0 ? settings : undefined\n}\n\n/**\n * Narrows a Payload-generated form document (from `getPayload().findByID()` or `fetch`) to\n * `FormDocument` without an unsafe `as` cast. Handles the small structural mismatches between\n * the auto-generated collection types and what `<Form>` expects:\n * - `fields` may be a typed blocks-union array or null; normalized to `FormFieldInstance[]`\n * (nameless bare rows, e.g. message blocks, pass through with their row `id` intact)\n * - `flow` is stored as opaque JSON; typed as `FormFlow | undefined`\n * - `response` may be null; coerced to `undefined`, otherwise passed through wholesale, which\n * includes `response.redirect.reference` (present only when the plugin's `redirectRelationships`\n * option is set): `value` may be a bare id (`depth: 0`) or a populated document, and the host\n * resolves it to a URL either by writing `redirect.url` or by passing `options.resolveRedirect`\n * - the button labels (`submitLabel`/`prevLabel`/`nextLabel`) live at the document root now (there\n * is no `buttons` group); the non-empty ones are reassembled into `FormDocument.buttons`\n * - `title` may be null; coerced to `undefined`\n * - `multistep`/`pollEnabled` are the two top-level form-type flags, coerced to strict booleans\n * - `poll` may be null; coerced to `undefined` (`resultsField`, `optionSource`, and\n * `sourceConfig` are dropped: server-side only; `outcome` passes through `winningValues` only)\n *\n * Unknown keys survive on `response` but not on `poll`, which is deliberate rather than incidental.\n * `response` is a visitor-facing group a host is meant to extend (`overrides.forms.fields`) and read\n * back off `FormDocument` in custom chrome, so it is cast wholesale. `poll` is plugin-owned config\n * that is split between\n * client-safe lifecycle state and server-only members, so it is rebuilt from an allowlist: a\n * passthrough would ship whatever a host put in `sourceConfig` (domain config, ids, credentials)\n * to every anonymous visitor. Adding a client-visible poll member means naming it here.\n *\n * Pure and framework-agnostic (no 'use client'): safe to call from a Server Component or any\n * other server-side code before handing the result to the client `<Form>`.\n */\nexport function toFormDocument(\n\tform: {\n\t\tid: number | string\n\t\tfields?: { blockType: string; name?: string; [key: string]: unknown }[] | null\n\t\tflow?: unknown\n\t\tresponse?: {\n\t\t\ttype?: string | null\n\t\t\tmessage?: unknown\n\t\t\tredirect?: {\n\t\t\t\turl?: string | null\n\t\t\t\treference?: { relationTo?: string | null; value?: number | string | object | null } | null\n\t\t\t} | null\n\t\t} | null\n\t\tsubmitLabel?: string | null\n\t\tprevLabel?: string | null\n\t\tnextLabel?: string | null\n\t\ttitle?: string | null\n\t\tmultistep?: boolean | null\n\t\tpollEnabled?: boolean | null\n\t\tpoll?: {\n\t\t\tresultsField?: string | null\n\t\t\tresultsVisibility?: string | null\n\t\t\tclosesAt?: string | null\n\t\t\toutcome?: { winningValues?: (string | null)[] | null } | null\n\t\t} | null\n\t\t/**\n\t\t * Consent statements resolved onto the doc by the plugin's forms `afterRead` hook (when\n\t\t * `consent.sources` is set), so consent renders on any fetch path (modal/client, not just RSC).\n\t\t * An explicit `options.consentStatements` takes precedence when both are present.\n\t\t */\n\t\tconsentStatements?: ConsentStatements\n\t\t/**\n\t\t * Calc source values resolved onto the doc by the plugin's forms `afterRead` hook (when calc\n\t\t * sources are registered and used); passed through so the client's live preview computes with\n\t\t * real values. `functions` can never ride a document, hence the Pick.\n\t\t */\n\t\tcalcResolved?: Pick<CalcResolved, 'sources' | 'weights'>\n\t},\n\toptions?: ToFormDocumentOptions\n): FormDocument {\n\tconst rawWinningValues = form.poll?.outcome?.winningValues\n\tconst winningValues = Array.isArray(rawWinningValues)\n\t\t? rawWinningValues.filter(\n\t\t\t\t(value): value is string => typeof value === 'string' && value.length > 0\n\t\t\t)\n\t\t: []\n\tconst poll = form.poll\n\t\t? {\n\t\t\t\tresultsVisibility: (form.poll.resultsVisibility ?? undefined) as\n\t\t\t\t\t| 'afterVote'\n\t\t\t\t\t| 'afterClose'\n\t\t\t\t\t| undefined,\n\t\t\t\tclosesAt: form.poll.closesAt ?? undefined,\n\t\t\t\t...(winningValues.length > 0 ? { outcome: { winningValues } } : {}),\n\t\t\t}\n\t\t: undefined\n\tlet fields = (form.fields ?? []) as FormFieldInstance[]\n\tif (options?.pollOptions) {\n\t\tfields = applyPollOptions(fields, form.poll?.resultsField, options.pollOptions)\n\t}\n\tconst consentStatements = options?.consentStatements ?? form.consentStatements\n\tif (consentStatements) {\n\t\tfields = applyConsentStatements(fields, consentStatements)\n\t}\n\tlet response = (form.response as FormResponseSettings | null | undefined) ?? undefined\n\t// A host that passes `resolveRedirect` gets internal references turned into `redirect.url` (all\n\t// `<Form>` navigates by), but only when the author set no explicit url. Non-mutating: clone the group.\n\tif (\n\t\toptions?.resolveRedirect &&\n\t\tresponse?.type === 'redirect' &&\n\t\tresponse.redirect?.reference &&\n\t\t!response.redirect.url\n\t) {\n\t\tconst url = options.resolveRedirect(response.redirect.reference)\n\t\tif (url) {\n\t\t\tresponse = { ...response, redirect: { ...response.redirect, url } }\n\t\t}\n\t}\n\treturn {\n\t\tid: form.id,\n\t\tfields,\n\t\tflow: form.flow as FormFlow | undefined,\n\t\tresponse,\n\t\tbuttons: buttonSettingsOf(form),\n\t\ttitle: form.title ?? undefined,\n\t\tmultistep: form.multistep === true,\n\t\tpollEnabled: form.pollEnabled === true,\n\t\tpoll,\n\t\tcalcResolved: form.calcResolved,\n\t}\n}\n"],"mappings":";;;;;;;;AAwCA,MAAM,oBAAoB,SAIY;CACrC,MAAM,SAAS,UACd,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ,KAAA;CACzD,MAAM,WAA+B,CAAC;CACtC,MAAM,cAAc,MAAM,KAAK,WAAW;CAC1C,MAAM,YAAY,MAAM,KAAK,SAAS;CACtC,MAAM,YAAY,MAAM,KAAK,SAAS;CACtC,IAAI,gBAAgB,KAAA,GAAW,SAAS,cAAc;CACtD,IAAI,cAAc,KAAA,GAAW,SAAS,YAAY;CAClD,IAAI,cAAc,KAAA,GAAW,SAAS,YAAY;CAClD,OAAO,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,WAAW,KAAA;AACtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,eACf,MAqCA,SACe;CACf,MAAM,mBAAmB,KAAK,MAAM,SAAS;CAC7C,MAAM,gBAAgB,MAAM,QAAQ,gBAAgB,IACjD,iBAAiB,QAChB,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CACzE,IACC,CAAC;CACJ,MAAM,OAAO,KAAK,OACf;EACA,mBAAoB,KAAK,KAAK,qBAAqB,KAAA;EAInD,UAAU,KAAK,KAAK,YAAY,KAAA;EAChC,GAAI,cAAc,SAAS,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,IAAI,CAAC;CAClE,IACC,KAAA;CACH,IAAI,SAAU,KAAK,UAAU,CAAC;CAC9B,IAAI,SAAS,aACZ,SAAS,iBAAiB,QAAQ,KAAK,MAAM,cAAc,QAAQ,WAAW;CAE/E,MAAM,oBAAoB,SAAS,qBAAqB,KAAK;CAC7D,IAAI,mBACH,SAAS,uBAAuB,QAAQ,iBAAiB;CAE1D,IAAI,WAAY,KAAK,YAAwD,KAAA;CAG7E,IACC,SAAS,mBACT,UAAU,SAAS,cACnB,SAAS,UAAU,aACnB,CAAC,SAAS,SAAS,KAClB;EACD,MAAM,MAAM,QAAQ,gBAAgB,SAAS,SAAS,SAAS;EAC/D,IAAI,KACH,WAAW;GAAE,GAAG;GAAU,UAAU;IAAE,GAAG,SAAS;IAAU;GAAI;EAAE;CAEpE;CACA,OAAO;EACN,IAAI,KAAK;EACT;EACA,MAAM,KAAK;EACX;EACA,SAAS,iBAAiB,IAAI;EAC9B,OAAO,KAAK,SAAS,KAAA;EACrB,WAAW,KAAK,cAAc;EAC9B,aAAa,KAAK,gBAAgB;EAClC;EACA,cAAc,KAAK;CACpB;AACD"}
|
|
1
|
+
{"version":3,"file":"toFormDocument.js","names":[],"sources":["../../src/form/toFormDocument.ts"],"sourcesContent":["import type { CalcResolved } from '../calc/evaluate'\nimport { applyConsentStatements } from '../consent/applyConsentStatements'\nimport type { ConsentStatements } from '../consent/resolveConsentStatements'\nimport type { FormFlow } from '../flow/types'\nimport { applyPollOptions } from '../poll/applyPollOptions'\nimport type { PollOption } from '../poll/definePollOptionSource'\nimport type { FormFieldInstance } from '../submissions/types'\nimport type { FormButtonSettings, FormDocument, FormResponseSettings } from './types'\n\nexport type ToFormDocumentOptions = {\n\t/**\n\t * Source-resolved poll options (from `resolvePollOptions`), injected as the options of the\n\t * field instance named by the form's `poll.resultsField` so the client renders the host's\n\t * current choices instead of hand-authored ones.\n\t */\n\tpollOptions?: PollOption[]\n\t/**\n\t * Source-resolved consent statements (from `resolveConsentStatements`), injected into the\n\t * consent field instances they name. A form stores only a source id, so without these its\n\t * consent fields render with no statement at all.\n\t */\n\tconsentStatements?: ConsentStatements\n\t/**\n\t * Resolve an internal redirect `reference` to a same-site URL. Called only when\n\t * `response.type === 'redirect'`, a `reference` is present, and no `redirect.url` is set. Keep it\n\t * sync: operate on the already-loaded (populated) reference, since resolving a Doc to a URL is host\n\t * routing knowledge the plugin cannot supply. The result is written to `redirect.url`, so `<Form>`\n\t * navigates with no client change.\n\t */\n\tresolveRedirect?: (reference: {\n\t\trelationTo: string\n\t\tvalue: number | string | object\n\t}) => string | null | undefined\n}\n\n/**\n * Reassemble the client button labels from the document's top-level `submitLabel`/`prevLabel`/\n * `nextLabel` fields, keeping only non-empty strings (mirroring the client's own `storedLabel`\n * fallback). Returns `undefined` when the author set no label, so the client uses its defaults.\n */\nconst buttonSettingsOf = (form: {\n\tsubmitLabel?: string | null\n\tprevLabel?: string | null\n\tnextLabel?: string | null\n}): FormButtonSettings | undefined => {\n\tconst label = (value: unknown): string | undefined =>\n\t\ttypeof value === 'string' && value.length > 0 ? value : undefined\n\tconst settings: FormButtonSettings = {}\n\tconst submitLabel = label(form.submitLabel)\n\tconst prevLabel = label(form.prevLabel)\n\tconst nextLabel = label(form.nextLabel)\n\tif (submitLabel !== undefined) settings.submitLabel = submitLabel\n\tif (prevLabel !== undefined) settings.prevLabel = prevLabel\n\tif (nextLabel !== undefined) settings.nextLabel = nextLabel\n\treturn Object.keys(settings).length > 0 ? settings : undefined\n}\n\n/**\n * Narrows a Payload-generated form document (from `getPayload().findByID()` or `fetch`) to\n * `FormDocument` without an unsafe `as` cast. Handles the small structural mismatches between\n * the auto-generated collection types and what `<Form>` expects:\n * - `fields` may be a typed blocks-union array or null; normalized to `FormFieldInstance[]`\n * (nameless bare rows, e.g. message blocks, pass through with their row `id` intact)\n * - `flow` is stored as opaque JSON; typed as `FormFlow | undefined`\n * - `response` may be null; coerced to `undefined`, otherwise passed through wholesale, which\n * includes `response.redirect.reference` (present only when the plugin's `redirectRelationships`\n * option is set): `value` may be a bare id (`depth: 0`) or a populated document, and the host\n * resolves it to a URL either by writing `redirect.url` or by passing `options.resolveRedirect`\n * - the button labels (`submitLabel`/`prevLabel`/`nextLabel`) live at the document root now (there\n * is no `buttons` group); the non-empty ones are reassembled into `FormDocument.buttons`\n * - `title` may be null; coerced to `undefined`\n * - `multistep`/`pollEnabled` are the two top-level form-type flags, coerced to strict booleans\n * - `poll` may be null; coerced to `undefined` (`resultsField`, `optionSource`, and\n * `sourceConfig` are dropped: server-side only; `outcome` passes through `winningValues` only)\n *\n * Unknown keys survive on `response` but not on `poll`, which is deliberate rather than incidental.\n * `response` is a visitor-facing group a host is meant to extend (`overrides.forms.fields`) and read\n * back off `FormDocument` in custom chrome, so it is cast wholesale. `poll` is plugin-owned config\n * that is split between\n * client-safe lifecycle state and server-only members, so it is rebuilt from an allowlist: a\n * passthrough would ship whatever a host put in `sourceConfig` (domain config, ids, credentials)\n * to every anonymous visitor. Adding a client-visible poll member means naming it here.\n *\n * Pure and framework-agnostic (no 'use client'): safe to call from a Server Component or any\n * other server-side code before handing the result to the client `<Form>`.\n */\nexport function toFormDocument(\n\tform: {\n\t\tid: number | string\n\t\tfields?: { blockType: string; name?: string; [key: string]: unknown }[] | null\n\t\tflow?: unknown\n\t\tresponse?: {\n\t\t\ttype?: string | null\n\t\t\tmessage?: unknown\n\t\t\tredirect?: {\n\t\t\t\turl?: string | null\n\t\t\t\treference?: { relationTo?: string | null; value?: number | string | object | null } | null\n\t\t\t} | null\n\t\t} | null\n\t\tsubmitLabel?: string | null\n\t\tprevLabel?: string | null\n\t\tnextLabel?: string | null\n\t\ttitle?: string | null\n\t\tmultistep?: boolean | null\n\t\tpollEnabled?: boolean | null\n\t\tpoll?: {\n\t\t\tresultsField?: string | null\n\t\t\tresultsVisibility?: string | null\n\t\t\tclosesAt?: string | null\n\t\t\tallowChange?: boolean | null\n\t\t\toutcome?: { winningValues?: (string | null)[] | null } | null\n\t\t} | null\n\t\t/**\n\t\t * Consent statements resolved onto the doc by the plugin's forms `afterRead` hook (when\n\t\t * `consent.sources` is set), so consent renders on any fetch path (modal/client, not just RSC).\n\t\t * An explicit `options.consentStatements` takes precedence when both are present.\n\t\t */\n\t\tconsentStatements?: ConsentStatements\n\t\t/**\n\t\t * Calc source values resolved onto the doc by the plugin's forms `afterRead` hook (when calc\n\t\t * sources are registered and used); passed through so the client's live preview computes with\n\t\t * real values. `functions` can never ride a document, hence the Pick.\n\t\t */\n\t\tcalcResolved?: Pick<CalcResolved, 'sources' | 'weights'>\n\t},\n\toptions?: ToFormDocumentOptions\n): FormDocument {\n\tconst rawWinningValues = form.poll?.outcome?.winningValues\n\tconst winningValues = Array.isArray(rawWinningValues)\n\t\t? rawWinningValues.filter(\n\t\t\t\t(value): value is string => typeof value === 'string' && value.length > 0\n\t\t\t)\n\t\t: []\n\tconst poll = form.poll\n\t\t? {\n\t\t\t\tresultsVisibility: (form.poll.resultsVisibility ?? undefined) as\n\t\t\t\t\t| 'afterVote'\n\t\t\t\t\t| 'afterClose'\n\t\t\t\t\t| undefined,\n\t\t\t\tclosesAt: form.poll.closesAt ?? undefined,\n\t\t\t\t...(form.poll.allowChange === true ? { allowChange: true } : {}),\n\t\t\t\t...(winningValues.length > 0 ? { outcome: { winningValues } } : {}),\n\t\t\t}\n\t\t: undefined\n\tlet fields = (form.fields ?? []) as FormFieldInstance[]\n\tif (options?.pollOptions) {\n\t\tfields = applyPollOptions(fields, form.poll?.resultsField, options.pollOptions)\n\t}\n\tconst consentStatements = options?.consentStatements ?? form.consentStatements\n\tif (consentStatements) {\n\t\tfields = applyConsentStatements(fields, consentStatements)\n\t}\n\tlet response = (form.response as FormResponseSettings | null | undefined) ?? undefined\n\t// A host that passes `resolveRedirect` gets internal references turned into `redirect.url` (all\n\t// `<Form>` navigates by), but only when the author set no explicit url. Non-mutating: clone the group.\n\tif (\n\t\toptions?.resolveRedirect &&\n\t\tresponse?.type === 'redirect' &&\n\t\tresponse.redirect?.reference &&\n\t\t!response.redirect.url\n\t) {\n\t\tconst url = options.resolveRedirect(response.redirect.reference)\n\t\tif (url) {\n\t\t\tresponse = { ...response, redirect: { ...response.redirect, url } }\n\t\t}\n\t}\n\treturn {\n\t\tid: form.id,\n\t\tfields,\n\t\tflow: form.flow as FormFlow | undefined,\n\t\tresponse,\n\t\tbuttons: buttonSettingsOf(form),\n\t\ttitle: form.title ?? undefined,\n\t\tmultistep: form.multistep === true,\n\t\tpollEnabled: form.pollEnabled === true,\n\t\tpoll,\n\t\tcalcResolved: form.calcResolved,\n\t}\n}\n"],"mappings":";;;;;;;;AAwCA,MAAM,oBAAoB,SAIY;CACrC,MAAM,SAAS,UACd,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ,KAAA;CACzD,MAAM,WAA+B,CAAC;CACtC,MAAM,cAAc,MAAM,KAAK,WAAW;CAC1C,MAAM,YAAY,MAAM,KAAK,SAAS;CACtC,MAAM,YAAY,MAAM,KAAK,SAAS;CACtC,IAAI,gBAAgB,KAAA,GAAW,SAAS,cAAc;CACtD,IAAI,cAAc,KAAA,GAAW,SAAS,YAAY;CAClD,IAAI,cAAc,KAAA,GAAW,SAAS,YAAY;CAClD,OAAO,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,WAAW,KAAA;AACtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,eACf,MAsCA,SACe;CACf,MAAM,mBAAmB,KAAK,MAAM,SAAS;CAC7C,MAAM,gBAAgB,MAAM,QAAQ,gBAAgB,IACjD,iBAAiB,QAChB,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CACzE,IACC,CAAC;CACJ,MAAM,OAAO,KAAK,OACf;EACA,mBAAoB,KAAK,KAAK,qBAAqB,KAAA;EAInD,UAAU,KAAK,KAAK,YAAY,KAAA;EAChC,GAAI,KAAK,KAAK,gBAAgB,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;EAC9D,GAAI,cAAc,SAAS,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,IAAI,CAAC;CAClE,IACC,KAAA;CACH,IAAI,SAAU,KAAK,UAAU,CAAC;CAC9B,IAAI,SAAS,aACZ,SAAS,iBAAiB,QAAQ,KAAK,MAAM,cAAc,QAAQ,WAAW;CAE/E,MAAM,oBAAoB,SAAS,qBAAqB,KAAK;CAC7D,IAAI,mBACH,SAAS,uBAAuB,QAAQ,iBAAiB;CAE1D,IAAI,WAAY,KAAK,YAAwD,KAAA;CAG7E,IACC,SAAS,mBACT,UAAU,SAAS,cACnB,SAAS,UAAU,aACnB,CAAC,SAAS,SAAS,KAClB;EACD,MAAM,MAAM,QAAQ,gBAAgB,SAAS,SAAS,SAAS;EAC/D,IAAI,KACH,WAAW;GAAE,GAAG;GAAU,UAAU;IAAE,GAAG,SAAS;IAAU;GAAI;EAAE;CAEpE;CACA,OAAO;EACN,IAAI,KAAK;EACT;EACA,MAAM,KAAK;EACX;EACA,SAAS,iBAAiB,IAAI;EAC9B,OAAO,KAAK,SAAS,KAAA;EACrB,WAAW,KAAK,cAAc;EAC9B,aAAa,KAAK,gBAAgB;EAClC;EACA,cAAc,KAAK;CACpB;AACD"}
|
package/dist/form/types.d.ts
CHANGED
|
@@ -40,7 +40,8 @@ type FormButtonSettings = {
|
|
|
40
40
|
*/
|
|
41
41
|
type FormPollSettings = {
|
|
42
42
|
/** When anonymous callers may read aggregate results. Default 'afterVote'. */resultsVisibility?: 'afterVote' | 'afterClose' | null; /** ISO date after which submissions are rejected and the poll renders as closed. */
|
|
43
|
-
closesAt?: string | null; /**
|
|
43
|
+
closesAt?: string | null; /** Whether a voter may change their vote while the poll is open (`poll.allowChange`). Present only when enabled. */
|
|
44
|
+
allowChange?: boolean; /** Present once the host recorded a final outcome via `resolvePollOutcome`; `<Poll>` renders the final state. A tie carries more than one value. */
|
|
44
45
|
outcome?: {
|
|
45
46
|
winningValues?: string[];
|
|
46
47
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { PrefillOptions, valuesFromSearchParams } from "./prefill/valuesFromSear
|
|
|
22
22
|
import { AnyValidationRuleDefinition, ValidationRuleDefinition, ValidationRuleResult } from "./validation/types.js";
|
|
23
23
|
import { PresentationDensity, PresentationDescriptor, PresentationSurface } from "./presentations/types.js";
|
|
24
24
|
import { RecallResolver, buildRecallResolver, optionLabelsFor } from "./recall/resolver.js";
|
|
25
|
+
import { VotedSubmission, resolveVotedSubmission } from "./submissions/resolveVotedSubmission.js";
|
|
25
26
|
import { interpolate } from "./recall/interpolate.js";
|
|
26
27
|
import { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from "./spam/constants.js";
|
|
27
28
|
import { formatBytes } from "./uploads/formatBytes.js";
|
|
@@ -94,5 +95,5 @@ import { FieldTargetParamOptions, fieldTargetParam } from "./validation/fieldTar
|
|
|
94
95
|
//#region src/index.d.ts
|
|
95
96
|
declare const formBuilder: (options: FormBuilderPluginOptions) => import("payload").Plugin;
|
|
96
97
|
//#endregion
|
|
97
|
-
export { type ActionDefinition, type ActionOption, type ActionRegistry, type ActionResult, type ActionRunArgs, type ActionsConfig, type AggregateFieldResponsesArgs, type AggregateFormResponsesArgs, type AggregationBucket, type AggregationRow, type AnyActionDefinition, type AnyFormFieldDefinition, type AnyPollOptionSource, type AnyValidationRuleDefinition, type BodyContext, type BodyConverter, type BodyConverterArgs, type BodyRender, type ButtonFieldsOverride, type ButtonsOption, CAPTCHA_TOKEN_KEY, type CalcAllowed, type CalcDisplayConfig, type CalcExpression, type CalcFunction, type CalcResolved, type CalcSource, type CalcSourceResolveArgs, type CalcWeightResolveArgs, type CaptchaProvider, type CaptchaVerifyArgs, type ConsentProof, type ConsentSnapshotMode, type ConsentSourceEntry, type ConsentSourceOption, type ConsentSourcePage, type ConsentSourcesFieldOptions, type ConsentSourcesResolver, type ConsentStatement, type ConsentStatements, type CreateSubmissionArgs, type CreatedSubmission, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, type DefaultButtonFields, type DefaultOutcomeFields, type DefaultSettingsFields, type DepartmentEmailsResolver, type DepartmentOption, type DepartmentsFieldOptions, type FieldAggregation, type FieldCondition, type FieldMeta, type FieldTargetParamOptions, type FieldTypeOption, type FieldTypeRegistry, type FieldTypesConfig, type FileFieldConfig, type FileRef, type FileRefError, type FormBuilderPluginOptions, type FormButtonSettings, type FormContextReference, type FormDocument, type FormFieldDefinition, type FormFieldFormat, type FormFieldValidate, type FormFieldValueKind, type FormPollSettings, type FormResponseSettings, type FormResultsAccess, type FormResultsAccessArgs, type FromAddressOption, type FromAddressesResolver, type HcaptchaProviderOptions, type IdentifyFn, type OmittableSharedField, type OutcomeFieldsOverride, POLL_CLOSE_TASK_SLUG, POLL_VOTES_SLUG, type FormBuilderPluginOptions as PluginOptions, type PollCloseTaskInput, type PollOption, type PollOptionResolveArgs, type PollOptionSource, type PollOptionSourceOption, type PollOptionSourceRegistry, type PollOptionSourcesConfig, type PollOutcomeStrategy, type PollOutcomeStrategyArgs, type PollTypeRegistry, type PollTypesConfig, type PrefillOptions, type PresentationDensity, type PresentationDescriptor, type PresentationSurface, RESPONDENTS_VALUE, type RateLimitCheckArgs, type RateLimitConfig, type RateLimitResult, type RateLimiter, type RecallResolver, type RecaptchaProviderOptions, type RecipientResolveArgs, type RecipientSource, type RecipientSourceRegistry, type RedirectFieldsOverride, type RedirectOption, type ResolveCalcContextArgs, type ResolveConsentEntriesArgs, type ResolveConsentSourcesRequestArgs, type ResolveConsentSourcesRequestResult, type ResolveConsentStatementsArgs, type ResolveDepartmentOptionsArgs, type ResolveDepartmentsRequestArgs, type ResolveDepartmentsRequestResult, type ResolveEffectivePollOptionsArgs, type ResolveFieldOptionsArgs, type ResolvePollOptionsArgs, type ResolvePollOutcomeArgs, type ResolvePublishedVersionRefArgs, type ResolveResultsRequestArgs, type ResolveResultsRequestResult, type ResponseOption, type RichTextBodyOption, SIGNATURE_HEADER, type SerializeBodyArgs, type SettingsFieldsOverride, type SettingsOption, type SpamConfig, type SpamMetadataConfig, type SpamOption, type SubmissionStatusFilter, type ToFormDocumentOptions, type TurnstileProviderOptions, type UploadsOption, VOTE_SHARDS, type ValidationRuleDefinition, type ValidationRuleOption, type ValidationRuleRegistry, type ValidationRuleResult, type ValidationRulesConfig, aggregateFieldResponses, aggregateFormResponses, aggregateFromVotes, aggregateRowForField, aggregateRowsForFields, applyConsentStatements, buildDefaultActionDefinitions, buildDefaultButtonFields, buildDefaultFieldDefinitions, buildDefaultOutcomeFields, buildDefaultSettingsFields, buildNextLabelField, buildPollCloseTask, buildPrevLabelField, buildRecallResolver, buildResolvedAtField, buildSubmitLabelField, buildWinningValuesField, calcExpressionOf, calcUsesSources, calcWeightKey, captureConsent, captureFileRef, computeCalcFields, consentSourcesField, countryField, createKvRateLimiter, createSubmission, defaultActionDefinitions, defaultBodyConverters, defaultFieldDefinitions, defaultFieldDefinitionsByType, defaultIdentify, defaultPresentationDescriptors, defaultValidationRules, defaultValidationRulesByType, defineAction, defineCalcFunction, defineCalcSource, defineCaptchaProvider, defineFormField, definePollOptionSource, definePollType, defineValidationRule, departmentsField, enqueuePollClose, escapeHtml, evaluateCalc, evaluateCondition, fieldHasOptions, fieldKey, fieldTargetParam, fileMimeTypeOptions, formBuilder, formatBytes, formatCalc, formatCalcValue, hasVotedCookie, hcaptchaProvider, interpolate, isPollClosed, localizedIf, manualStrategy, mostVotedStrategy, normalizeCalc, optionLabelsFor, pollTypesOf, recaptchaProvider, recountPollVotes, registerPollCloseTask, renderAllValues, renderAllValuesTable, resolveActions, resolveCalcContext, resolveConsentEntries, resolveConsentSourcesRequest, resolveConsentStatements, resolveDepartmentOptions, resolveEffectivePollOptions, resolveFileRef, resolveFormResultsRequest, resolvePollOptionSources, resolvePollOptions, resolvePollOutcome, resolvePollTypes, resolvePublishedVersionRef, resolveSpamConfig, runPollClose, sanitizeUrl, serializeBody, shouldAutoResolvePoll, signFormContext, signPayload, sourceStrategy, stashPollTypes, stateField, textOfBody, toFormDocument, topBucketValues, turnstileProvider, valuesFromSearchParams, verifyFormContext, votedCookieName };
|
|
98
|
+
export { type ActionDefinition, type ActionOption, type ActionRegistry, type ActionResult, type ActionRunArgs, type ActionsConfig, type AggregateFieldResponsesArgs, type AggregateFormResponsesArgs, type AggregationBucket, type AggregationRow, type AnyActionDefinition, type AnyFormFieldDefinition, type AnyPollOptionSource, type AnyValidationRuleDefinition, type BodyContext, type BodyConverter, type BodyConverterArgs, type BodyRender, type ButtonFieldsOverride, type ButtonsOption, CAPTCHA_TOKEN_KEY, type CalcAllowed, type CalcDisplayConfig, type CalcExpression, type CalcFunction, type CalcResolved, type CalcSource, type CalcSourceResolveArgs, type CalcWeightResolveArgs, type CaptchaProvider, type CaptchaVerifyArgs, type ConsentProof, type ConsentSnapshotMode, type ConsentSourceEntry, type ConsentSourceOption, type ConsentSourcePage, type ConsentSourcesFieldOptions, type ConsentSourcesResolver, type ConsentStatement, type ConsentStatements, type CreateSubmissionArgs, type CreatedSubmission, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, type DefaultButtonFields, type DefaultOutcomeFields, type DefaultSettingsFields, type DepartmentEmailsResolver, type DepartmentOption, type DepartmentsFieldOptions, type FieldAggregation, type FieldCondition, type FieldMeta, type FieldTargetParamOptions, type FieldTypeOption, type FieldTypeRegistry, type FieldTypesConfig, type FileFieldConfig, type FileRef, type FileRefError, type FormBuilderPluginOptions, type FormButtonSettings, type FormContextReference, type FormDocument, type FormFieldDefinition, type FormFieldFormat, type FormFieldValidate, type FormFieldValueKind, type FormPollSettings, type FormResponseSettings, type FormResultsAccess, type FormResultsAccessArgs, type FromAddressOption, type FromAddressesResolver, type HcaptchaProviderOptions, type IdentifyFn, type OmittableSharedField, type OutcomeFieldsOverride, POLL_CLOSE_TASK_SLUG, POLL_VOTES_SLUG, type FormBuilderPluginOptions as PluginOptions, type PollCloseTaskInput, type PollOption, type PollOptionResolveArgs, type PollOptionSource, type PollOptionSourceOption, type PollOptionSourceRegistry, type PollOptionSourcesConfig, type PollOutcomeStrategy, type PollOutcomeStrategyArgs, type PollTypeRegistry, type PollTypesConfig, type PrefillOptions, type PresentationDensity, type PresentationDescriptor, type PresentationSurface, RESPONDENTS_VALUE, type RateLimitCheckArgs, type RateLimitConfig, type RateLimitResult, type RateLimiter, type RecallResolver, type RecaptchaProviderOptions, type RecipientResolveArgs, type RecipientSource, type RecipientSourceRegistry, type RedirectFieldsOverride, type RedirectOption, type ResolveCalcContextArgs, type ResolveConsentEntriesArgs, type ResolveConsentSourcesRequestArgs, type ResolveConsentSourcesRequestResult, type ResolveConsentStatementsArgs, type ResolveDepartmentOptionsArgs, type ResolveDepartmentsRequestArgs, type ResolveDepartmentsRequestResult, type ResolveEffectivePollOptionsArgs, type ResolveFieldOptionsArgs, type ResolvePollOptionsArgs, type ResolvePollOutcomeArgs, type ResolvePublishedVersionRefArgs, type ResolveResultsRequestArgs, type ResolveResultsRequestResult, type ResponseOption, type RichTextBodyOption, SIGNATURE_HEADER, type SerializeBodyArgs, type SettingsFieldsOverride, type SettingsOption, type SpamConfig, type SpamMetadataConfig, type SpamOption, type SubmissionStatusFilter, type ToFormDocumentOptions, type TurnstileProviderOptions, type UploadsOption, VOTE_SHARDS, type ValidationRuleDefinition, type ValidationRuleOption, type ValidationRuleRegistry, type ValidationRuleResult, type ValidationRulesConfig, type VotedSubmission, aggregateFieldResponses, aggregateFormResponses, aggregateFromVotes, aggregateRowForField, aggregateRowsForFields, applyConsentStatements, buildDefaultActionDefinitions, buildDefaultButtonFields, buildDefaultFieldDefinitions, buildDefaultOutcomeFields, buildDefaultSettingsFields, buildNextLabelField, buildPollCloseTask, buildPrevLabelField, buildRecallResolver, buildResolvedAtField, buildSubmitLabelField, buildWinningValuesField, calcExpressionOf, calcUsesSources, calcWeightKey, captureConsent, captureFileRef, computeCalcFields, consentSourcesField, countryField, createKvRateLimiter, createSubmission, defaultActionDefinitions, defaultBodyConverters, defaultFieldDefinitions, defaultFieldDefinitionsByType, defaultIdentify, defaultPresentationDescriptors, defaultValidationRules, defaultValidationRulesByType, defineAction, defineCalcFunction, defineCalcSource, defineCaptchaProvider, defineFormField, definePollOptionSource, definePollType, defineValidationRule, departmentsField, enqueuePollClose, escapeHtml, evaluateCalc, evaluateCondition, fieldHasOptions, fieldKey, fieldTargetParam, fileMimeTypeOptions, formBuilder, formatBytes, formatCalc, formatCalcValue, hasVotedCookie, hcaptchaProvider, interpolate, isPollClosed, localizedIf, manualStrategy, mostVotedStrategy, normalizeCalc, optionLabelsFor, pollTypesOf, recaptchaProvider, recountPollVotes, registerPollCloseTask, renderAllValues, renderAllValuesTable, resolveActions, resolveCalcContext, resolveConsentEntries, resolveConsentSourcesRequest, resolveConsentStatements, resolveDepartmentOptions, resolveEffectivePollOptions, resolveFileRef, resolveFormResultsRequest, resolvePollOptionSources, resolvePollOptions, resolvePollOutcome, resolvePollTypes, resolvePublishedVersionRef, resolveSpamConfig, resolveVotedSubmission, runPollClose, sanitizeUrl, serializeBody, shouldAutoResolvePoll, signFormContext, signPayload, sourceStrategy, stashPollTypes, stateField, textOfBody, toFormDocument, topBucketValues, turnstileProvider, valuesFromSearchParams, verifyFormContext, votedCookieName };
|
|
98
99
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -18,12 +18,12 @@ import { resolveFileRef } from "./uploads/resolveFileRef.js";
|
|
|
18
18
|
import { fileMimeTypeOptions } from "./fields/builtin/file.js";
|
|
19
19
|
import { buildDefaultFieldDefinitions, defaultFieldDefinitions, defaultFieldDefinitionsByType } from "./fields/builtin/index.js";
|
|
20
20
|
import { resolveFieldTypes, stashFieldTypes } from "./fields/registry.js";
|
|
21
|
+
import { isPollClosed } from "./form/pollState.js";
|
|
21
22
|
import { fieldKey } from "./fields/fieldKey.js";
|
|
22
23
|
import { calcWeightKey, evaluateCalc } from "./calc/evaluate.js";
|
|
23
24
|
import { calcExpressionOf, computeCalcFields } from "./calc/computeCalcFields.js";
|
|
24
25
|
import { calcUsesSources, resolveCalcContext } from "./calc/resolveCalcContext.js";
|
|
25
26
|
import { resolveConsentStatements } from "./consent/resolveConsentStatements.js";
|
|
26
|
-
import { isPollClosed } from "./form/pollState.js";
|
|
27
27
|
import { aggregateRowForField, aggregateRowsForFields } from "./aggregation/aggregateRows.js";
|
|
28
28
|
import { aggregateFieldResponses, aggregateFormResponses, fieldHasOptions } from "./aggregation/aggregateResponses.js";
|
|
29
29
|
import { resolvePollOptions, stashPollOptionSources } from "./poll/resolvePollOptions.js";
|
|
@@ -41,14 +41,14 @@ import { resolveFormResultsRequest } from "./aggregation/resolveResultsRequest.j
|
|
|
41
41
|
import { resolveConsentSourcesRequest } from "./consent/resolveConsentSourcesRequest.js";
|
|
42
42
|
import { resolveDepartmentOptions } from "./email/departments.js";
|
|
43
43
|
import { buildDefaultSettingsFields } from "./collections/settingsFields.js";
|
|
44
|
+
import { signFormContext, verifyFormContext } from "./context/formContext.js";
|
|
45
|
+
import { hasVotedCookie, votedCookieName } from "./submissions/votedCookie.js";
|
|
44
46
|
import { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from "./spam/constants.js";
|
|
45
47
|
import { evaluateCondition } from "./conditions/evaluate.js";
|
|
46
48
|
import { textOfBody } from "./actions/body/textOfBody.js";
|
|
47
49
|
import { resolvePublishedVersionRef } from "./consent/resolvePublishedVersionRef.js";
|
|
48
50
|
import { captureConsent } from "./consent/captureConsent.js";
|
|
49
51
|
import { captureFileRef } from "./uploads/captureFileRef.js";
|
|
50
|
-
import { hasVotedCookie, votedCookieName } from "./submissions/votedCookie.js";
|
|
51
|
-
import { signFormContext, verifyFormContext } from "./context/formContext.js";
|
|
52
52
|
import { readUploadCollectionMimeTypes } from "./plugin/uploadsCollection.js";
|
|
53
53
|
import { registerCollections } from "./plugin/registerCollections.js";
|
|
54
54
|
import { registerTranslations } from "./plugin/registerTranslations.js";
|
|
@@ -77,6 +77,7 @@ import { hcaptchaProvider } from "./spam/providers/hcaptcha.js";
|
|
|
77
77
|
import { recaptchaProvider } from "./spam/providers/recaptcha.js";
|
|
78
78
|
import { turnstileProvider } from "./spam/providers/turnstile.js";
|
|
79
79
|
import { createSubmission } from "./submissions/createSubmission.js";
|
|
80
|
+
import { resolveVotedSubmission } from "./submissions/resolveVotedSubmission.js";
|
|
80
81
|
import { formatBytes } from "./uploads/formatBytes.js";
|
|
81
82
|
import { definePlugin } from "payload";
|
|
82
83
|
//#region src/index.ts
|
|
@@ -160,6 +161,6 @@ const formBuilder = definePlugin({
|
|
|
160
161
|
}
|
|
161
162
|
});
|
|
162
163
|
//#endregion
|
|
163
|
-
export { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, POLL_CLOSE_TASK_SLUG, POLL_VOTES_SLUG, RESPONDENTS_VALUE, SIGNATURE_HEADER, VOTE_SHARDS, aggregateFieldResponses, aggregateFormResponses, aggregateFromVotes, aggregateRowForField, aggregateRowsForFields, applyConsentStatements, buildDefaultActionDefinitions, buildDefaultButtonFields, buildDefaultFieldDefinitions, buildDefaultOutcomeFields, buildDefaultSettingsFields, buildNextLabelField, buildPollCloseTask, buildPrevLabelField, buildRecallResolver, buildResolvedAtField, buildSubmitLabelField, buildWinningValuesField, calcExpressionOf, calcUsesSources, calcWeightKey, captureConsent, captureFileRef, computeCalcFields, consentSourcesField, countryField, createKvRateLimiter, createSubmission, defaultActionDefinitions, defaultBodyConverters, defaultFieldDefinitions, defaultFieldDefinitionsByType, defaultIdentify, defaultPresentationDescriptors, defaultValidationRules, defaultValidationRulesByType, defineAction, defineCalcFunction, defineCalcSource, defineCaptchaProvider, defineFormField, definePollOptionSource, definePollType, defineValidationRule, departmentsField, enqueuePollClose, escapeHtml, evaluateCalc, evaluateCondition, fieldHasOptions, fieldKey, fieldTargetParam, fileMimeTypeOptions, formBuilder, formatBytes, formatCalc, formatCalcValue, hasVotedCookie, hcaptchaProvider, interpolate, isPollClosed, localizedIf, manualStrategy, mostVotedStrategy, normalizeCalc, optionLabelsFor, pollTypesOf, recaptchaProvider, recountPollVotes, registerPollCloseTask, renderAllValues, renderAllValuesTable, resolveActions, resolveCalcContext, resolveConsentEntries, resolveConsentSourcesRequest, resolveConsentStatements, resolveDepartmentOptions, resolveEffectivePollOptions, resolveFileRef, resolveFormResultsRequest, resolvePollOptionSources, resolvePollOptions, resolvePollOutcome, resolvePollTypes, resolvePublishedVersionRef, resolveSpamConfig, runPollClose, sanitizeUrl, serializeBody, shouldAutoResolvePoll, signFormContext, signPayload, sourceStrategy, stashPollTypes, stateField, textOfBody, toFormDocument, topBucketValues, turnstileProvider, valuesFromSearchParams, verifyFormContext, votedCookieName };
|
|
164
|
+
export { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD, DEFAULT_PRESENTATION_NAME, POLL_CLOSE_TASK_SLUG, POLL_VOTES_SLUG, RESPONDENTS_VALUE, SIGNATURE_HEADER, VOTE_SHARDS, aggregateFieldResponses, aggregateFormResponses, aggregateFromVotes, aggregateRowForField, aggregateRowsForFields, applyConsentStatements, buildDefaultActionDefinitions, buildDefaultButtonFields, buildDefaultFieldDefinitions, buildDefaultOutcomeFields, buildDefaultSettingsFields, buildNextLabelField, buildPollCloseTask, buildPrevLabelField, buildRecallResolver, buildResolvedAtField, buildSubmitLabelField, buildWinningValuesField, calcExpressionOf, calcUsesSources, calcWeightKey, captureConsent, captureFileRef, computeCalcFields, consentSourcesField, countryField, createKvRateLimiter, createSubmission, defaultActionDefinitions, defaultBodyConverters, defaultFieldDefinitions, defaultFieldDefinitionsByType, defaultIdentify, defaultPresentationDescriptors, defaultValidationRules, defaultValidationRulesByType, defineAction, defineCalcFunction, defineCalcSource, defineCaptchaProvider, defineFormField, definePollOptionSource, definePollType, defineValidationRule, departmentsField, enqueuePollClose, escapeHtml, evaluateCalc, evaluateCondition, fieldHasOptions, fieldKey, fieldTargetParam, fileMimeTypeOptions, formBuilder, formatBytes, formatCalc, formatCalcValue, hasVotedCookie, hcaptchaProvider, interpolate, isPollClosed, localizedIf, manualStrategy, mostVotedStrategy, normalizeCalc, optionLabelsFor, pollTypesOf, recaptchaProvider, recountPollVotes, registerPollCloseTask, renderAllValues, renderAllValuesTable, resolveActions, resolveCalcContext, resolveConsentEntries, resolveConsentSourcesRequest, resolveConsentStatements, resolveDepartmentOptions, resolveEffectivePollOptions, resolveFileRef, resolveFormResultsRequest, resolvePollOptionSources, resolvePollOptions, resolvePollOutcome, resolvePollTypes, resolvePublishedVersionRef, resolveSpamConfig, resolveVotedSubmission, runPollClose, sanitizeUrl, serializeBody, shouldAutoResolvePoll, signFormContext, signPayload, sourceStrategy, stashPollTypes, stateField, textOfBody, toFormDocument, topBucketValues, turnstileProvider, valuesFromSearchParams, verifyFormContext, votedCookieName };
|
|
164
165
|
|
|
165
166
|
//# 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 { assertNoActionBlockCollision } from './actions/assertNoBlockCollision'\nimport { buildDefaultActionDefinitions } from './actions/builtin'\nimport { resolveActions } from './actions/registry'\nimport { assertNoCalcFunctionCollision, assertValidCalcSourceKeys } from './calc/registry'\nimport { stashConsentSources } from './consent/resolveConsentEntries'\nimport { buildDefaultFieldDefinitions } from './fields/builtin'\nimport { resolveFieldTypes, stashFieldTypes } from './fields/registry'\nimport type { FormBuilderPluginOptions } from './options'\nimport { registerCollections } from './plugin/registerCollections'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport { readUploadCollectionMimeTypes } from './plugin/uploadsCollection'\nimport { resolvePollTypes, stashPollTypes } from './poll/pollTypeRegistry'\nimport { resolvePollOptionSources } from './poll/registry'\nimport { stashPollOptionSources } from './poll/resolvePollOptions'\nimport { resolveSpamConfig } from './spam/resolveSpam'\nimport { defaultValidationRules } from './validation/builtin'\nimport { resolveValidationRules } from './validation/registry'\n\nexport const formBuilder = definePlugin<FormBuilderPluginOptions>({\n\tslug: '@10x-media/form-builder',\n\torder: 50,\n\tplugin: ({ config, plugins, ...options }): Config => {\n\t\tif (options.disabled === true) {\n\t\t\treturn config\n\t\t}\n\t\tconst localizeContent = options.localizeContent !== false\n\t\tconst uploads = options.uploads ?? false\n\t\tconst calcSources = options.calc?.sources ?? {}\n\t\tconst calcFunctions = options.calc?.functions ?? {}\n\t\t// Fail fast: the evaluator resolves built-ins first, so a colliding custom function could never\n\t\t// run; a source key with a space would make the weight-map key (`calcWeightKey`) ambiguous.\n\t\tassertNoCalcFunctionCollision(calcFunctions)\n\t\tassertValidCalcSourceKeys(calcSources)\n\t\t// The allowed extension names, threaded into every normalizeCalc gate (the calculation field's\n\t\t// validate and the forms beforeValidate) so a stored expression can only ever reference a\n\t\t// registered source or function.\n\t\tconst calcAllowed = {\n\t\t\tsources: new Set(Object.keys(calcSources)),\n\t\t\tfunctions: new Set(Object.keys(calcFunctions)),\n\t\t}\n\t\t// Serializable source metadata for the builder UI: key, label, and implemented modes. The\n\t\t// resolver functions themselves never leave the server.\n\t\tconst calcSourceMeta = Object.entries(calcSources).map(([key, source]) => ({\n\t\t\tkey,\n\t\t\tlabel: source.label,\n\t\t\tscalar: typeof source.resolve === 'function',\n\t\t\tweights: typeof source.resolveWeights === 'function',\n\t\t}))\n\t\t// The file field's MIME picker is constrained to what the host upload collection accepts: the\n\t\t// explicit `uploads.mimeTypes` override, else the collection's own `upload.mimeTypes`. Read here,\n\t\t// before the field registry freezes below (attachUploadsCollection runs too late).\n\t\tconst uploadMimeTypes =\n\t\t\tuploads === false\n\t\t\t\t? undefined\n\t\t\t\t: (uploads.mimeTypes ?? readUploadCollectionMimeTypes(config, uploads.collection))\n\t\tconst consentSources = options.consent?.sources\n\t\t// A built-in with nowhere to point never enters the registry, so an author is never offered a\n\t\t// field that cannot work: file without an uploads collection has nowhere to store anything,\n\t\t// consent without sources has no statement to reference. An explicit `fields.file` /\n\t\t// `fields.consent` definition remains a developer choice.\n\t\tconst defaultFieldDefinitions = buildDefaultFieldDefinitions(\n\t\t\tlocalizeContent,\n\t\t\toptions.richText?.editor,\n\t\t\tuploadMimeTypes,\n\t\t\tcalcAllowed,\n\t\t\tcalcSourceMeta\n\t\t).filter(\n\t\t\t(definition) =>\n\t\t\t\t(uploads !== false || definition.type !== 'file') &&\n\t\t\t\t(consentSources !== undefined || definition.type !== 'consent')\n\t\t)\n\t\tconst registry = resolveFieldTypes(defaultFieldDefinitions, options.fields)\n\t\tconst ruleRegistry = resolveValidationRules(defaultValidationRules, options.rules)\n\t\tconst fromAddresses = options.email?.fromAddresses\n\t\tconst departments = options.email?.departments\n\t\tconst actionRegistry = resolveActions(\n\t\t\tbuildDefaultActionDefinitions({\n\t\t\t\tlocalize: localizeContent,\n\t\t\t\teditor: options.richText?.bodyEditor ?? options.richText?.editor,\n\t\t\t\tfromAddresses,\n\t\t\t\tdepartments,\n\t\t\t\trecipients: options.email?.recipients,\n\t\t\t\trecipientSources: options.email?.recipientSources,\n\t\t\t}),\n\t\t\toptions.actions\n\t\t)\n\t\t// Fail fast if a custom action type collides with a host block slug (Payload resolves blocks\n\t\t// globally by slug, which would silently merge the content block's fields into saved actions).\n\t\tassertNoActionBlockCollision(config, actionRegistry)\n\t\tconst spam = resolveSpamConfig(options.spam)\n\t\tconst pollSourceRegistry = resolvePollOptionSources(options.poll?.sources)\n\t\tconst pollTypeRegistry = resolvePollTypes(options.poll?.types)\n\t\t// Stashed on config.custom so the root-level resolvePollOptions, resolveEffectivePollOptions,\n\t\t// resolvePollOutcome, and resolveConsentStatements helpers can reach these through\n\t\t// `payload.config` at request time, without threading plugin state through the host's own\n\t\t// server code. The field-type registry rides along so resolveEffectivePollOptions can look up a\n\t\t// results field's definition (and its `resolveOptions`) from a bare `payload` instance.\n\t\tconfig.custom = stashPollOptionSources(config.custom, pollSourceRegistry)\n\t\tconfig.custom = stashPollTypes(config.custom, pollTypeRegistry)\n\t\tconfig.custom = stashFieldTypes(config.custom, registry)\n\t\tif (consentSources) {\n\t\t\tconfig.custom = stashConsentSources(config.custom, consentSources)\n\t\t}\n\t\tregisterTranslations(config, options.translations)\n\t\tregisterCollections({\n\t\t\tconfig,\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tcalcAllowed,\n\t\t\tcalcSources,\n\t\t\tcalcFunctions,\n\t\t\tconsentSources,\n\t\t\tconsentSnapshot: options.consent?.snapshot ?? 'both',\n\t\t\tconsentResolveOnRead: options.consent?.resolveOnRead,\n\t\t\tactionRegistry,\n\t\t\trichText: options.richText,\n\t\t\thasJobsPlugin: Boolean(plugins['@10x-media/jobs']),\n\t\t\tevents: options.events,\n\t\t\tuploads,\n\t\t\tspam,\n\t\t\tshowSubmissionRawFields: options.showSubmissionRawFields ?? false,\n\t\t\tlocalizeContent,\n\t\t\tresultsAccess: options.results?.access,\n\t\t\tvotedCookie: options.poll?.votedCookie === true,\n\t\t\tpollSourceRegistry,\n\t\t\tpollTypeRegistry,\n\t\t\toutcomeFields: options.poll?.outcomeFields,\n\t\t\tpollVotes: options.poll?.votes === false ? false : (options.poll?.votes ?? {}),\n\t\t\tbuttons: options.buttons,\n\t\t\tsettings: options.settings,\n\t\t\tresponse: options.response,\n\t\t\tfromAddresses,\n\t\t\tdepartments,\n\t\t\tredirectRelationships: options.redirectRelationships,\n\t\t\toverrides: options.overrides,\n\t\t})\n\t\treturn config\n\t},\n})\n\nexport type {\n\tBodyConverter,\n\tBodyConverterArgs,\n\tBodyRender,\n} from './actions/body/converters'\nexport { defaultBodyConverters, sanitizeUrl } from './actions/body/converters'\nexport { escapeHtml } from './actions/body/escapeHtml'\nexport type {\n\tBodyContext,\n\tRichTextBodyOption,\n\tSerializeBodyArgs,\n} from './actions/body/serializeBody'\nexport { serializeBody } from './actions/body/serializeBody'\nexport { textOfBody } from './actions/body/textOfBody'\nexport { renderAllValues, renderAllValuesTable } from './actions/body/wildcards'\nexport { buildDefaultActionDefinitions, defaultActionDefinitions } from './actions/builtin'\nexport type { ActionDefinition, ActionRunArgs, AnyActionDefinition } from './actions/defineAction'\nexport { defineAction } from './actions/defineAction'\nexport type { FromAddressesResolver, FromAddressOption } from './actions/fromAddresses'\nexport type {\n\tRecipientResolveArgs,\n\tRecipientSource,\n\tRecipientSourceRegistry,\n} from './actions/recipientSources'\nexport type { ActionOption, ActionRegistry, ActionsConfig } from './actions/registry'\nexport { resolveActions } from './actions/registry'\nexport type { ActionResult } from './actions/runActions'\nexport { SIGNATURE_HEADER, signPayload } from './actions/sign'\nexport type {\n\tAggregateFieldResponsesArgs,\n\tAggregateFormResponsesArgs,\n} from './aggregation/aggregateResponses'\nexport {\n\taggregateFieldResponses,\n\taggregateFormResponses,\n\tfieldHasOptions,\n} from './aggregation/aggregateResponses'\nexport { aggregateRowForField, aggregateRowsForFields } from './aggregation/aggregateRows'\nexport type {\n\tFormResultsAccess,\n\tFormResultsAccessArgs,\n\tResolveResultsRequestArgs,\n\tResolveResultsRequestResult,\n} from './aggregation/resolveResultsRequest'\nexport { resolveFormResultsRequest } from './aggregation/resolveResultsRequest'\nexport type {\n\tAggregationBucket,\n\tAggregationRow,\n\tFieldAggregation,\n\tFieldMeta,\n\tSubmissionStatusFilter,\n} from './aggregation/types'\nexport { calcExpressionOf, computeCalcFields } from './calc/computeCalcFields'\nexport type { CalcResolved } from './calc/evaluate'\nexport { calcWeightKey, evaluateCalc } from './calc/evaluate'\nexport { formatCalc } from './calc/formatCalc'\nexport type { CalcDisplayConfig } from './calc/formatCalcValue'\nexport { formatCalcValue } from './calc/formatCalcValue'\nexport type { CalcAllowed } from './calc/normalizeCalc'\nexport { normalizeCalc } from './calc/normalizeCalc'\nexport type {\n\tCalcFunction,\n\tCalcSource,\n\tCalcSourceResolveArgs,\n\tCalcWeightResolveArgs,\n} from './calc/registry'\nexport { defineCalcFunction, defineCalcSource } from './calc/registry'\nexport type { ResolveCalcContextArgs } from './calc/resolveCalcContext'\nexport { calcUsesSources, resolveCalcContext } from './calc/resolveCalcContext'\nexport type { CalcExpression } from './calc/types'\nexport type {\n\tButtonFieldsOverride,\n\tButtonsOption,\n\tDefaultButtonFields,\n} from './collections/buttonFields'\nexport {\n\tbuildDefaultButtonFields,\n\tbuildNextLabelField,\n\tbuildPrevLabelField,\n\tbuildSubmitLabelField,\n} from './collections/buttonFields'\nexport type {\n\tRedirectFieldsOverride,\n\tRedirectOption,\n\tResponseOption,\n} from './collections/redirectFields'\nexport type {\n\tDefaultSettingsFields,\n\tSettingsFieldsOverride,\n\tSettingsOption,\n} from './collections/settingsFields'\nexport { buildDefaultSettingsFields } from './collections/settingsFields'\nexport { evaluateCondition } from './conditions/evaluate'\nexport type { FieldCondition } from './conditions/types'\nexport { applyConsentStatements } from './consent/applyConsentStatements'\nexport type { ConsentProof, ConsentSnapshotMode } from './consent/captureConsent'\nexport { captureConsent } from './consent/captureConsent'\nexport type { ConsentSourcesFieldOptions } from './consent/consentSourcesField'\nexport { consentSourcesField } from './consent/consentSourcesField'\nexport type { ResolveConsentEntriesArgs } from './consent/resolveConsentEntries'\nexport { resolveConsentEntries } from './consent/resolveConsentEntries'\nexport type {\n\tConsentSourceOption,\n\tResolveConsentSourcesRequestArgs,\n\tResolveConsentSourcesRequestResult,\n} from './consent/resolveConsentSourcesRequest'\nexport { resolveConsentSourcesRequest } from './consent/resolveConsentSourcesRequest'\nexport type {\n\tConsentStatement,\n\tConsentStatements,\n\tResolveConsentStatementsArgs,\n} from './consent/resolveConsentStatements'\nexport { resolveConsentStatements } from './consent/resolveConsentStatements'\nexport type { ResolvePublishedVersionRefArgs } from './consent/resolvePublishedVersionRef'\nexport { resolvePublishedVersionRef } from './consent/resolvePublishedVersionRef'\nexport type {\n\tConsentSourceEntry,\n\tConsentSourcePage,\n\tConsentSourcesResolver,\n} from './consent/types'\nexport type { FormContextReference } from './context/formContext'\nexport { signFormContext, verifyFormContext } from './context/formContext'\nexport type {\n\tDepartmentEmailsResolver,\n\tDepartmentOption,\n\tResolveDepartmentOptionsArgs,\n\tResolveDepartmentsRequestArgs,\n\tResolveDepartmentsRequestResult,\n} from './email/departments'\nexport { resolveDepartmentOptions } from './email/departments'\nexport type { DepartmentsFieldOptions } from './email/departmentsField'\nexport { departmentsField } from './email/departmentsField'\nexport {\n\tbuildDefaultFieldDefinitions,\n\tdefaultFieldDefinitions,\n\tdefaultFieldDefinitionsByType,\n} from './fields/builtin'\nexport { countryField } from './fields/builtin/country'\nexport { fileMimeTypeOptions } from './fields/builtin/file'\nexport { stateField } from './fields/builtin/state'\nexport { defineFormField } from './fields/defineFormField'\nexport { fieldKey } from './fields/fieldKey'\nexport { localizedIf } from './fields/localizedIf'\nexport type { FieldTypeOption, FieldTypeRegistry, FieldTypesConfig } from './fields/registry'\nexport type {\n\tAnyFormFieldDefinition,\n\tFormFieldDefinition,\n\tFormFieldFormat,\n\tFormFieldValidate,\n\tFormFieldValueKind,\n\tOmittableSharedField,\n\tResolveFieldOptionsArgs,\n} from './fields/types'\nexport { isPollClosed } from './form/pollState'\nexport type { ToFormDocumentOptions } from './form/toFormDocument'\nexport { toFormDocument } from './form/toFormDocument'\nexport type {\n\tFormButtonSettings,\n\tFormDocument,\n\tFormPollSettings,\n\tFormResponseSettings,\n} from './form/types'\nexport type {\n\tFormBuilderPluginOptions,\n\tFormBuilderPluginOptions as PluginOptions,\n} from './options'\nexport type { UploadsOption } from './plugin/uploadsCollection'\nexport type { PollCloseTaskInput } from './poll/closeJob'\nexport {\n\tbuildPollCloseTask,\n\tenqueuePollClose,\n\tPOLL_CLOSE_TASK_SLUG,\n\tregisterPollCloseTask,\n\trunPollClose,\n\tshouldAutoResolvePoll,\n} from './poll/closeJob'\nexport type {\n\tAnyPollOptionSource,\n\tPollOption,\n\tPollOptionResolveArgs,\n\tPollOptionSource,\n} from './poll/definePollOptionSource'\nexport { definePollOptionSource } from './poll/definePollOptionSource'\nexport type { PollOutcomeStrategy, PollOutcomeStrategyArgs } from './poll/definePollType'\nexport { definePollType } from './poll/definePollType'\nexport type { ResolveEffectivePollOptionsArgs } from './poll/effectivePollOptions'\nexport { resolveEffectivePollOptions } from './poll/effectivePollOptions'\nexport { mostVotedStrategy, topBucketValues } from './poll/mostVoted'\nexport type { DefaultOutcomeFields, OutcomeFieldsOverride } from './poll/outcomeFields'\nexport {\n\tbuildDefaultOutcomeFields,\n\tbuildResolvedAtField,\n\tbuildWinningValuesField,\n} from './poll/outcomeFields'\nexport type { PollTypeRegistry, PollTypesConfig } from './poll/pollTypeRegistry'\nexport {\n\tmanualStrategy,\n\tpollTypesOf,\n\tresolvePollTypes,\n\tsourceStrategy,\n\tstashPollTypes,\n} from './poll/pollTypeRegistry'\nexport type {\n\tPollOptionSourceOption,\n\tPollOptionSourceRegistry,\n\tPollOptionSourcesConfig,\n} from './poll/registry'\nexport { resolvePollOptionSources } from './poll/registry'\nexport type { ResolvePollOptionsArgs } from './poll/resolvePollOptions'\nexport { resolvePollOptions } from './poll/resolvePollOptions'\nexport type { ResolvePollOutcomeArgs } from './poll/resolvePollOutcome'\nexport { resolvePollOutcome } from './poll/resolvePollOutcome'\nexport { aggregateFromVotes } from './poll/votes/aggregateFromVotes'\nexport { recountPollVotes } from './poll/votes/recountPollVotes'\nexport { POLL_VOTES_SLUG, RESPONDENTS_VALUE, VOTE_SHARDS } from './poll/votes/votesCollection'\nexport type { PrefillOptions } from './prefill/valuesFromSearchParams'\nexport { valuesFromSearchParams } from './prefill/valuesFromSearchParams'\nexport { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from './presentations/defaults'\nexport type {\n\tPresentationDensity,\n\tPresentationDescriptor,\n\tPresentationSurface,\n} from './presentations/types'\nexport { interpolate } from './recall/interpolate'\nexport type { RecallResolver } from './recall/resolver'\nexport { buildRecallResolver, optionLabelsFor } from './recall/resolver'\nexport { defineCaptchaProvider } from './spam/captcha'\nexport { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from './spam/constants'\nexport { defaultIdentify } from './spam/identify'\nexport type { HcaptchaProviderOptions } from './spam/providers/hcaptcha'\nexport { hcaptchaProvider } from './spam/providers/hcaptcha'\nexport type { RecaptchaProviderOptions } from './spam/providers/recaptcha'\nexport { recaptchaProvider } from './spam/providers/recaptcha'\nexport type { TurnstileProviderOptions } from './spam/providers/turnstile'\nexport { turnstileProvider } from './spam/providers/turnstile'\nexport { createKvRateLimiter } from './spam/rateLimiter'\nexport { resolveSpamConfig } from './spam/resolveSpam'\nexport type {\n\tCaptchaProvider,\n\tCaptchaVerifyArgs,\n\tIdentifyFn,\n\tRateLimitCheckArgs,\n\tRateLimitConfig,\n\tRateLimiter,\n\tRateLimitResult,\n\tSpamConfig,\n\tSpamMetadataConfig,\n\tSpamOption,\n} from './spam/types'\nexport type { CreatedSubmission, CreateSubmissionArgs } from './submissions/createSubmission'\nexport { createSubmission } from './submissions/createSubmission'\nexport { hasVotedCookie, votedCookieName } from './submissions/votedCookie'\nexport { captureFileRef } from './uploads/captureFileRef'\nexport { formatBytes } from './uploads/formatBytes'\nexport { resolveFileRef } from './uploads/resolveFileRef'\nexport type { FileFieldConfig, FileRef, FileRefError } from './uploads/types'\nexport { defaultValidationRules, defaultValidationRulesByType } from './validation/builtin'\nexport { defineValidationRule } from './validation/defineValidationRule'\nexport type { FieldTargetParamOptions } from './validation/fieldTargetParam'\nexport { fieldTargetParam } from './validation/fieldTargetParam'\nexport type {\n\tValidationRuleOption,\n\tValidationRuleRegistry,\n\tValidationRulesConfig,\n} from './validation/registry'\nexport type {\n\tAnyValidationRuleDefinition,\n\tValidationRuleDefinition,\n\tValidationRuleResult,\n} from './validation/types'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,MAAa,cAAc,aAAuC;CACjE,MAAM;CACN,OAAO;CACP,SAAS,EAAE,QAAQ,SAAS,GAAG,cAAsB;EACpD,IAAI,QAAQ,aAAa,MACxB,OAAO;EAER,MAAM,kBAAkB,QAAQ,oBAAoB;EACpD,MAAM,UAAU,QAAQ,WAAW;EACnC,MAAM,cAAc,QAAQ,MAAM,WAAW,CAAC;EAC9C,MAAM,gBAAgB,QAAQ,MAAM,aAAa,CAAC;EAGlD,8BAA8B,aAAa;EAC3C,0BAA0B,WAAW;EAIrC,MAAM,cAAc;GACnB,SAAS,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;GACzC,WAAW,IAAI,IAAI,OAAO,KAAK,aAAa,CAAC;EAC9C;EAGA,MAAM,iBAAiB,OAAO,QAAQ,WAAW,EAAE,KAAK,CAAC,KAAK,aAAa;GAC1E;GACA,OAAO,OAAO;GACd,QAAQ,OAAO,OAAO,YAAY;GAClC,SAAS,OAAO,OAAO,mBAAmB;EAC3C,EAAE;EAIF,MAAM,kBACL,YAAY,QACT,KAAA,IACC,QAAQ,aAAa,8BAA8B,QAAQ,QAAQ,UAAU;EAClF,MAAM,iBAAiB,QAAQ,SAAS;EAgBxC,MAAM,WAAW,kBAXe,6BAC/B,iBACA,QAAQ,UAAU,QAClB,iBACA,aACA,cACD,EAAE,QACA,gBACC,YAAY,SAAS,WAAW,SAAS,YACzC,mBAAmB,KAAA,KAAa,WAAW,SAAS,UAEE,GAAG,QAAQ,MAAM;EAC1E,MAAM,eAAe,uBAAuB,wBAAwB,QAAQ,KAAK;EACjF,MAAM,gBAAgB,QAAQ,OAAO;EACrC,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,iBAAiB,eACtB,8BAA8B;GAC7B,UAAU;GACV,QAAQ,QAAQ,UAAU,cAAc,QAAQ,UAAU;GAC1D;GACA;GACA,YAAY,QAAQ,OAAO;GAC3B,kBAAkB,QAAQ,OAAO;EAClC,CAAC,GACD,QAAQ,OACT;EAGA,6BAA6B,QAAQ,cAAc;EACnD,MAAM,OAAO,kBAAkB,QAAQ,IAAI;EAC3C,MAAM,qBAAqB,yBAAyB,QAAQ,MAAM,OAAO;EACzE,MAAM,mBAAmB,iBAAiB,QAAQ,MAAM,KAAK;EAM7D,OAAO,SAAS,uBAAuB,OAAO,QAAQ,kBAAkB;EACxE,OAAO,SAAS,eAAe,OAAO,QAAQ,gBAAgB;EAC9D,OAAO,SAAS,gBAAgB,OAAO,QAAQ,QAAQ;EACvD,IAAI,gBACH,OAAO,SAAS,oBAAoB,OAAO,QAAQ,cAAc;EAElE,qBAAqB,QAAQ,QAAQ,YAAY;EACjD,oBAAoB;GACnB;GACA;GACA;GACA;GACA;GACA;GACA;GACA,iBAAiB,QAAQ,SAAS,YAAY;GAC9C,sBAAsB,QAAQ,SAAS;GACvC;GACA,UAAU,QAAQ;GAClB,eAAe,QAAQ,QAAQ,kBAAkB;GACjD,QAAQ,QAAQ;GAChB;GACA;GACA,yBAAyB,QAAQ,2BAA2B;GAC5D;GACA,eAAe,QAAQ,SAAS;GAChC,aAAa,QAAQ,MAAM,gBAAgB;GAC3C;GACA;GACA,eAAe,QAAQ,MAAM;GAC7B,WAAW,QAAQ,MAAM,UAAU,QAAQ,QAAS,QAAQ,MAAM,SAAS,CAAC;GAC5E,SAAS,QAAQ;GACjB,UAAU,QAAQ;GAClB,UAAU,QAAQ;GAClB;GACA;GACA,uBAAuB,QAAQ;GAC/B,WAAW,QAAQ;EACpB,CAAC;EACD,OAAO;CACR;AACD,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type Config, definePlugin } from 'payload'\nimport { assertNoActionBlockCollision } from './actions/assertNoBlockCollision'\nimport { buildDefaultActionDefinitions } from './actions/builtin'\nimport { resolveActions } from './actions/registry'\nimport { assertNoCalcFunctionCollision, assertValidCalcSourceKeys } from './calc/registry'\nimport { stashConsentSources } from './consent/resolveConsentEntries'\nimport { buildDefaultFieldDefinitions } from './fields/builtin'\nimport { resolveFieldTypes, stashFieldTypes } from './fields/registry'\nimport type { FormBuilderPluginOptions } from './options'\nimport { registerCollections } from './plugin/registerCollections'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport { readUploadCollectionMimeTypes } from './plugin/uploadsCollection'\nimport { resolvePollTypes, stashPollTypes } from './poll/pollTypeRegistry'\nimport { resolvePollOptionSources } from './poll/registry'\nimport { stashPollOptionSources } from './poll/resolvePollOptions'\nimport { resolveSpamConfig } from './spam/resolveSpam'\nimport { defaultValidationRules } from './validation/builtin'\nimport { resolveValidationRules } from './validation/registry'\n\nexport const formBuilder = definePlugin<FormBuilderPluginOptions>({\n\tslug: '@10x-media/form-builder',\n\torder: 50,\n\tplugin: ({ config, plugins, ...options }): Config => {\n\t\tif (options.disabled === true) {\n\t\t\treturn config\n\t\t}\n\t\tconst localizeContent = options.localizeContent !== false\n\t\tconst uploads = options.uploads ?? false\n\t\tconst calcSources = options.calc?.sources ?? {}\n\t\tconst calcFunctions = options.calc?.functions ?? {}\n\t\t// Fail fast: the evaluator resolves built-ins first, so a colliding custom function could never\n\t\t// run; a source key with a space would make the weight-map key (`calcWeightKey`) ambiguous.\n\t\tassertNoCalcFunctionCollision(calcFunctions)\n\t\tassertValidCalcSourceKeys(calcSources)\n\t\t// The allowed extension names, threaded into every normalizeCalc gate (the calculation field's\n\t\t// validate and the forms beforeValidate) so a stored expression can only ever reference a\n\t\t// registered source or function.\n\t\tconst calcAllowed = {\n\t\t\tsources: new Set(Object.keys(calcSources)),\n\t\t\tfunctions: new Set(Object.keys(calcFunctions)),\n\t\t}\n\t\t// Serializable source metadata for the builder UI: key, label, and implemented modes. The\n\t\t// resolver functions themselves never leave the server.\n\t\tconst calcSourceMeta = Object.entries(calcSources).map(([key, source]) => ({\n\t\t\tkey,\n\t\t\tlabel: source.label,\n\t\t\tscalar: typeof source.resolve === 'function',\n\t\t\tweights: typeof source.resolveWeights === 'function',\n\t\t}))\n\t\t// The file field's MIME picker is constrained to what the host upload collection accepts: the\n\t\t// explicit `uploads.mimeTypes` override, else the collection's own `upload.mimeTypes`. Read here,\n\t\t// before the field registry freezes below (attachUploadsCollection runs too late).\n\t\tconst uploadMimeTypes =\n\t\t\tuploads === false\n\t\t\t\t? undefined\n\t\t\t\t: (uploads.mimeTypes ?? readUploadCollectionMimeTypes(config, uploads.collection))\n\t\tconst consentSources = options.consent?.sources\n\t\t// A built-in with nowhere to point never enters the registry, so an author is never offered a\n\t\t// field that cannot work: file without an uploads collection has nowhere to store anything,\n\t\t// consent without sources has no statement to reference. An explicit `fields.file` /\n\t\t// `fields.consent` definition remains a developer choice.\n\t\tconst defaultFieldDefinitions = buildDefaultFieldDefinitions(\n\t\t\tlocalizeContent,\n\t\t\toptions.richText?.editor,\n\t\t\tuploadMimeTypes,\n\t\t\tcalcAllowed,\n\t\t\tcalcSourceMeta\n\t\t).filter(\n\t\t\t(definition) =>\n\t\t\t\t(uploads !== false || definition.type !== 'file') &&\n\t\t\t\t(consentSources !== undefined || definition.type !== 'consent')\n\t\t)\n\t\tconst registry = resolveFieldTypes(defaultFieldDefinitions, options.fields)\n\t\tconst ruleRegistry = resolveValidationRules(defaultValidationRules, options.rules)\n\t\tconst fromAddresses = options.email?.fromAddresses\n\t\tconst departments = options.email?.departments\n\t\tconst actionRegistry = resolveActions(\n\t\t\tbuildDefaultActionDefinitions({\n\t\t\t\tlocalize: localizeContent,\n\t\t\t\teditor: options.richText?.bodyEditor ?? options.richText?.editor,\n\t\t\t\tfromAddresses,\n\t\t\t\tdepartments,\n\t\t\t\trecipients: options.email?.recipients,\n\t\t\t\trecipientSources: options.email?.recipientSources,\n\t\t\t}),\n\t\t\toptions.actions\n\t\t)\n\t\t// Fail fast if a custom action type collides with a host block slug (Payload resolves blocks\n\t\t// globally by slug, which would silently merge the content block's fields into saved actions).\n\t\tassertNoActionBlockCollision(config, actionRegistry)\n\t\tconst spam = resolveSpamConfig(options.spam)\n\t\tconst pollSourceRegistry = resolvePollOptionSources(options.poll?.sources)\n\t\tconst pollTypeRegistry = resolvePollTypes(options.poll?.types)\n\t\t// Stashed on config.custom so the root-level resolvePollOptions, resolveEffectivePollOptions,\n\t\t// resolvePollOutcome, and resolveConsentStatements helpers can reach these through\n\t\t// `payload.config` at request time, without threading plugin state through the host's own\n\t\t// server code. The field-type registry rides along so resolveEffectivePollOptions can look up a\n\t\t// results field's definition (and its `resolveOptions`) from a bare `payload` instance.\n\t\tconfig.custom = stashPollOptionSources(config.custom, pollSourceRegistry)\n\t\tconfig.custom = stashPollTypes(config.custom, pollTypeRegistry)\n\t\tconfig.custom = stashFieldTypes(config.custom, registry)\n\t\tif (consentSources) {\n\t\t\tconfig.custom = stashConsentSources(config.custom, consentSources)\n\t\t}\n\t\tregisterTranslations(config, options.translations)\n\t\tregisterCollections({\n\t\t\tconfig,\n\t\t\tregistry,\n\t\t\truleRegistry,\n\t\t\tcalcAllowed,\n\t\t\tcalcSources,\n\t\t\tcalcFunctions,\n\t\t\tconsentSources,\n\t\t\tconsentSnapshot: options.consent?.snapshot ?? 'both',\n\t\t\tconsentResolveOnRead: options.consent?.resolveOnRead,\n\t\t\tactionRegistry,\n\t\t\trichText: options.richText,\n\t\t\thasJobsPlugin: Boolean(plugins['@10x-media/jobs']),\n\t\t\tevents: options.events,\n\t\t\tuploads,\n\t\t\tspam,\n\t\t\tshowSubmissionRawFields: options.showSubmissionRawFields ?? false,\n\t\t\tlocalizeContent,\n\t\t\tresultsAccess: options.results?.access,\n\t\t\tvotedCookie: options.poll?.votedCookie === true,\n\t\t\tpollSourceRegistry,\n\t\t\tpollTypeRegistry,\n\t\t\toutcomeFields: options.poll?.outcomeFields,\n\t\t\tpollVotes: options.poll?.votes === false ? false : (options.poll?.votes ?? {}),\n\t\t\tbuttons: options.buttons,\n\t\t\tsettings: options.settings,\n\t\t\tresponse: options.response,\n\t\t\tfromAddresses,\n\t\t\tdepartments,\n\t\t\tredirectRelationships: options.redirectRelationships,\n\t\t\toverrides: options.overrides,\n\t\t})\n\t\treturn config\n\t},\n})\n\nexport type {\n\tBodyConverter,\n\tBodyConverterArgs,\n\tBodyRender,\n} from './actions/body/converters'\nexport { defaultBodyConverters, sanitizeUrl } from './actions/body/converters'\nexport { escapeHtml } from './actions/body/escapeHtml'\nexport type {\n\tBodyContext,\n\tRichTextBodyOption,\n\tSerializeBodyArgs,\n} from './actions/body/serializeBody'\nexport { serializeBody } from './actions/body/serializeBody'\nexport { textOfBody } from './actions/body/textOfBody'\nexport { renderAllValues, renderAllValuesTable } from './actions/body/wildcards'\nexport { buildDefaultActionDefinitions, defaultActionDefinitions } from './actions/builtin'\nexport type { ActionDefinition, ActionRunArgs, AnyActionDefinition } from './actions/defineAction'\nexport { defineAction } from './actions/defineAction'\nexport type { FromAddressesResolver, FromAddressOption } from './actions/fromAddresses'\nexport type {\n\tRecipientResolveArgs,\n\tRecipientSource,\n\tRecipientSourceRegistry,\n} from './actions/recipientSources'\nexport type { ActionOption, ActionRegistry, ActionsConfig } from './actions/registry'\nexport { resolveActions } from './actions/registry'\nexport type { ActionResult } from './actions/runActions'\nexport { SIGNATURE_HEADER, signPayload } from './actions/sign'\nexport type {\n\tAggregateFieldResponsesArgs,\n\tAggregateFormResponsesArgs,\n} from './aggregation/aggregateResponses'\nexport {\n\taggregateFieldResponses,\n\taggregateFormResponses,\n\tfieldHasOptions,\n} from './aggregation/aggregateResponses'\nexport { aggregateRowForField, aggregateRowsForFields } from './aggregation/aggregateRows'\nexport type {\n\tFormResultsAccess,\n\tFormResultsAccessArgs,\n\tResolveResultsRequestArgs,\n\tResolveResultsRequestResult,\n} from './aggregation/resolveResultsRequest'\nexport { resolveFormResultsRequest } from './aggregation/resolveResultsRequest'\nexport type {\n\tAggregationBucket,\n\tAggregationRow,\n\tFieldAggregation,\n\tFieldMeta,\n\tSubmissionStatusFilter,\n} from './aggregation/types'\nexport { calcExpressionOf, computeCalcFields } from './calc/computeCalcFields'\nexport type { CalcResolved } from './calc/evaluate'\nexport { calcWeightKey, evaluateCalc } from './calc/evaluate'\nexport { formatCalc } from './calc/formatCalc'\nexport type { CalcDisplayConfig } from './calc/formatCalcValue'\nexport { formatCalcValue } from './calc/formatCalcValue'\nexport type { CalcAllowed } from './calc/normalizeCalc'\nexport { normalizeCalc } from './calc/normalizeCalc'\nexport type {\n\tCalcFunction,\n\tCalcSource,\n\tCalcSourceResolveArgs,\n\tCalcWeightResolveArgs,\n} from './calc/registry'\nexport { defineCalcFunction, defineCalcSource } from './calc/registry'\nexport type { ResolveCalcContextArgs } from './calc/resolveCalcContext'\nexport { calcUsesSources, resolveCalcContext } from './calc/resolveCalcContext'\nexport type { CalcExpression } from './calc/types'\nexport type {\n\tButtonFieldsOverride,\n\tButtonsOption,\n\tDefaultButtonFields,\n} from './collections/buttonFields'\nexport {\n\tbuildDefaultButtonFields,\n\tbuildNextLabelField,\n\tbuildPrevLabelField,\n\tbuildSubmitLabelField,\n} from './collections/buttonFields'\nexport type {\n\tRedirectFieldsOverride,\n\tRedirectOption,\n\tResponseOption,\n} from './collections/redirectFields'\nexport type {\n\tDefaultSettingsFields,\n\tSettingsFieldsOverride,\n\tSettingsOption,\n} from './collections/settingsFields'\nexport { buildDefaultSettingsFields } from './collections/settingsFields'\nexport { evaluateCondition } from './conditions/evaluate'\nexport type { FieldCondition } from './conditions/types'\nexport { applyConsentStatements } from './consent/applyConsentStatements'\nexport type { ConsentProof, ConsentSnapshotMode } from './consent/captureConsent'\nexport { captureConsent } from './consent/captureConsent'\nexport type { ConsentSourcesFieldOptions } from './consent/consentSourcesField'\nexport { consentSourcesField } from './consent/consentSourcesField'\nexport type { ResolveConsentEntriesArgs } from './consent/resolveConsentEntries'\nexport { resolveConsentEntries } from './consent/resolveConsentEntries'\nexport type {\n\tConsentSourceOption,\n\tResolveConsentSourcesRequestArgs,\n\tResolveConsentSourcesRequestResult,\n} from './consent/resolveConsentSourcesRequest'\nexport { resolveConsentSourcesRequest } from './consent/resolveConsentSourcesRequest'\nexport type {\n\tConsentStatement,\n\tConsentStatements,\n\tResolveConsentStatementsArgs,\n} from './consent/resolveConsentStatements'\nexport { resolveConsentStatements } from './consent/resolveConsentStatements'\nexport type { ResolvePublishedVersionRefArgs } from './consent/resolvePublishedVersionRef'\nexport { resolvePublishedVersionRef } from './consent/resolvePublishedVersionRef'\nexport type {\n\tConsentSourceEntry,\n\tConsentSourcePage,\n\tConsentSourcesResolver,\n} from './consent/types'\nexport type { FormContextReference } from './context/formContext'\nexport { signFormContext, verifyFormContext } from './context/formContext'\nexport type {\n\tDepartmentEmailsResolver,\n\tDepartmentOption,\n\tResolveDepartmentOptionsArgs,\n\tResolveDepartmentsRequestArgs,\n\tResolveDepartmentsRequestResult,\n} from './email/departments'\nexport { resolveDepartmentOptions } from './email/departments'\nexport type { DepartmentsFieldOptions } from './email/departmentsField'\nexport { departmentsField } from './email/departmentsField'\nexport {\n\tbuildDefaultFieldDefinitions,\n\tdefaultFieldDefinitions,\n\tdefaultFieldDefinitionsByType,\n} from './fields/builtin'\nexport { countryField } from './fields/builtin/country'\nexport { fileMimeTypeOptions } from './fields/builtin/file'\nexport { stateField } from './fields/builtin/state'\nexport { defineFormField } from './fields/defineFormField'\nexport { fieldKey } from './fields/fieldKey'\nexport { localizedIf } from './fields/localizedIf'\nexport type { FieldTypeOption, FieldTypeRegistry, FieldTypesConfig } from './fields/registry'\nexport type {\n\tAnyFormFieldDefinition,\n\tFormFieldDefinition,\n\tFormFieldFormat,\n\tFormFieldValidate,\n\tFormFieldValueKind,\n\tOmittableSharedField,\n\tResolveFieldOptionsArgs,\n} from './fields/types'\nexport { isPollClosed } from './form/pollState'\nexport type { ToFormDocumentOptions } from './form/toFormDocument'\nexport { toFormDocument } from './form/toFormDocument'\nexport type {\n\tFormButtonSettings,\n\tFormDocument,\n\tFormPollSettings,\n\tFormResponseSettings,\n} from './form/types'\nexport type {\n\tFormBuilderPluginOptions,\n\tFormBuilderPluginOptions as PluginOptions,\n} from './options'\nexport type { UploadsOption } from './plugin/uploadsCollection'\nexport type { PollCloseTaskInput } from './poll/closeJob'\nexport {\n\tbuildPollCloseTask,\n\tenqueuePollClose,\n\tPOLL_CLOSE_TASK_SLUG,\n\tregisterPollCloseTask,\n\trunPollClose,\n\tshouldAutoResolvePoll,\n} from './poll/closeJob'\nexport type {\n\tAnyPollOptionSource,\n\tPollOption,\n\tPollOptionResolveArgs,\n\tPollOptionSource,\n} from './poll/definePollOptionSource'\nexport { definePollOptionSource } from './poll/definePollOptionSource'\nexport type { PollOutcomeStrategy, PollOutcomeStrategyArgs } from './poll/definePollType'\nexport { definePollType } from './poll/definePollType'\nexport type { ResolveEffectivePollOptionsArgs } from './poll/effectivePollOptions'\nexport { resolveEffectivePollOptions } from './poll/effectivePollOptions'\nexport { mostVotedStrategy, topBucketValues } from './poll/mostVoted'\nexport type { DefaultOutcomeFields, OutcomeFieldsOverride } from './poll/outcomeFields'\nexport {\n\tbuildDefaultOutcomeFields,\n\tbuildResolvedAtField,\n\tbuildWinningValuesField,\n} from './poll/outcomeFields'\nexport type { PollTypeRegistry, PollTypesConfig } from './poll/pollTypeRegistry'\nexport {\n\tmanualStrategy,\n\tpollTypesOf,\n\tresolvePollTypes,\n\tsourceStrategy,\n\tstashPollTypes,\n} from './poll/pollTypeRegistry'\nexport type {\n\tPollOptionSourceOption,\n\tPollOptionSourceRegistry,\n\tPollOptionSourcesConfig,\n} from './poll/registry'\nexport { resolvePollOptionSources } from './poll/registry'\nexport type { ResolvePollOptionsArgs } from './poll/resolvePollOptions'\nexport { resolvePollOptions } from './poll/resolvePollOptions'\nexport type { ResolvePollOutcomeArgs } from './poll/resolvePollOutcome'\nexport { resolvePollOutcome } from './poll/resolvePollOutcome'\nexport { aggregateFromVotes } from './poll/votes/aggregateFromVotes'\nexport { recountPollVotes } from './poll/votes/recountPollVotes'\nexport { POLL_VOTES_SLUG, RESPONDENTS_VALUE, VOTE_SHARDS } from './poll/votes/votesCollection'\nexport type { PrefillOptions } from './prefill/valuesFromSearchParams'\nexport { valuesFromSearchParams } from './prefill/valuesFromSearchParams'\nexport { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from './presentations/defaults'\nexport type {\n\tPresentationDensity,\n\tPresentationDescriptor,\n\tPresentationSurface,\n} from './presentations/types'\nexport { interpolate } from './recall/interpolate'\nexport type { RecallResolver } from './recall/resolver'\nexport { buildRecallResolver, optionLabelsFor } from './recall/resolver'\nexport { defineCaptchaProvider } from './spam/captcha'\nexport { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from './spam/constants'\nexport { defaultIdentify } from './spam/identify'\nexport type { HcaptchaProviderOptions } from './spam/providers/hcaptcha'\nexport { hcaptchaProvider } from './spam/providers/hcaptcha'\nexport type { RecaptchaProviderOptions } from './spam/providers/recaptcha'\nexport { recaptchaProvider } from './spam/providers/recaptcha'\nexport type { TurnstileProviderOptions } from './spam/providers/turnstile'\nexport { turnstileProvider } from './spam/providers/turnstile'\nexport { createKvRateLimiter } from './spam/rateLimiter'\nexport { resolveSpamConfig } from './spam/resolveSpam'\nexport type {\n\tCaptchaProvider,\n\tCaptchaVerifyArgs,\n\tIdentifyFn,\n\tRateLimitCheckArgs,\n\tRateLimitConfig,\n\tRateLimiter,\n\tRateLimitResult,\n\tSpamConfig,\n\tSpamMetadataConfig,\n\tSpamOption,\n} from './spam/types'\nexport type { CreatedSubmission, CreateSubmissionArgs } from './submissions/createSubmission'\nexport { createSubmission } from './submissions/createSubmission'\nexport type { VotedSubmission } from './submissions/resolveVotedSubmission'\nexport { resolveVotedSubmission } from './submissions/resolveVotedSubmission'\nexport { hasVotedCookie, votedCookieName } from './submissions/votedCookie'\nexport { captureFileRef } from './uploads/captureFileRef'\nexport { formatBytes } from './uploads/formatBytes'\nexport { resolveFileRef } from './uploads/resolveFileRef'\nexport type { FileFieldConfig, FileRef, FileRefError } from './uploads/types'\nexport { defaultValidationRules, defaultValidationRulesByType } from './validation/builtin'\nexport { defineValidationRule } from './validation/defineValidationRule'\nexport type { FieldTargetParamOptions } from './validation/fieldTargetParam'\nexport { fieldTargetParam } from './validation/fieldTargetParam'\nexport type {\n\tValidationRuleOption,\n\tValidationRuleRegistry,\n\tValidationRulesConfig,\n} from './validation/registry'\nexport type {\n\tAnyValidationRuleDefinition,\n\tValidationRuleDefinition,\n\tValidationRuleResult,\n} from './validation/types'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,MAAa,cAAc,aAAuC;CACjE,MAAM;CACN,OAAO;CACP,SAAS,EAAE,QAAQ,SAAS,GAAG,cAAsB;EACpD,IAAI,QAAQ,aAAa,MACxB,OAAO;EAER,MAAM,kBAAkB,QAAQ,oBAAoB;EACpD,MAAM,UAAU,QAAQ,WAAW;EACnC,MAAM,cAAc,QAAQ,MAAM,WAAW,CAAC;EAC9C,MAAM,gBAAgB,QAAQ,MAAM,aAAa,CAAC;EAGlD,8BAA8B,aAAa;EAC3C,0BAA0B,WAAW;EAIrC,MAAM,cAAc;GACnB,SAAS,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;GACzC,WAAW,IAAI,IAAI,OAAO,KAAK,aAAa,CAAC;EAC9C;EAGA,MAAM,iBAAiB,OAAO,QAAQ,WAAW,EAAE,KAAK,CAAC,KAAK,aAAa;GAC1E;GACA,OAAO,OAAO;GACd,QAAQ,OAAO,OAAO,YAAY;GAClC,SAAS,OAAO,OAAO,mBAAmB;EAC3C,EAAE;EAIF,MAAM,kBACL,YAAY,QACT,KAAA,IACC,QAAQ,aAAa,8BAA8B,QAAQ,QAAQ,UAAU;EAClF,MAAM,iBAAiB,QAAQ,SAAS;EAgBxC,MAAM,WAAW,kBAXe,6BAC/B,iBACA,QAAQ,UAAU,QAClB,iBACA,aACA,cACD,EAAE,QACA,gBACC,YAAY,SAAS,WAAW,SAAS,YACzC,mBAAmB,KAAA,KAAa,WAAW,SAAS,UAEE,GAAG,QAAQ,MAAM;EAC1E,MAAM,eAAe,uBAAuB,wBAAwB,QAAQ,KAAK;EACjF,MAAM,gBAAgB,QAAQ,OAAO;EACrC,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,iBAAiB,eACtB,8BAA8B;GAC7B,UAAU;GACV,QAAQ,QAAQ,UAAU,cAAc,QAAQ,UAAU;GAC1D;GACA;GACA,YAAY,QAAQ,OAAO;GAC3B,kBAAkB,QAAQ,OAAO;EAClC,CAAC,GACD,QAAQ,OACT;EAGA,6BAA6B,QAAQ,cAAc;EACnD,MAAM,OAAO,kBAAkB,QAAQ,IAAI;EAC3C,MAAM,qBAAqB,yBAAyB,QAAQ,MAAM,OAAO;EACzE,MAAM,mBAAmB,iBAAiB,QAAQ,MAAM,KAAK;EAM7D,OAAO,SAAS,uBAAuB,OAAO,QAAQ,kBAAkB;EACxE,OAAO,SAAS,eAAe,OAAO,QAAQ,gBAAgB;EAC9D,OAAO,SAAS,gBAAgB,OAAO,QAAQ,QAAQ;EACvD,IAAI,gBACH,OAAO,SAAS,oBAAoB,OAAO,QAAQ,cAAc;EAElE,qBAAqB,QAAQ,QAAQ,YAAY;EACjD,oBAAoB;GACnB;GACA;GACA;GACA;GACA;GACA;GACA;GACA,iBAAiB,QAAQ,SAAS,YAAY;GAC9C,sBAAsB,QAAQ,SAAS;GACvC;GACA,UAAU,QAAQ;GAClB,eAAe,QAAQ,QAAQ,kBAAkB;GACjD,QAAQ,QAAQ;GAChB;GACA;GACA,yBAAyB,QAAQ,2BAA2B;GAC5D;GACA,eAAe,QAAQ,SAAS;GAChC,aAAa,QAAQ,MAAM,gBAAgB;GAC3C;GACA;GACA,eAAe,QAAQ,MAAM;GAC7B,WAAW,QAAQ,MAAM,UAAU,QAAQ,QAAS,QAAQ,MAAM,SAAS,CAAC;GAC5E,SAAS,QAAQ;GACjB,UAAU,QAAQ;GAClB,UAAU,QAAQ;GAClB;GACA;GACA,uBAAuB,QAAQ;GAC/B,WAAW,QAAQ;EACpB,CAAC;EACD,OAAO;CACR;AACD,CAAC"}
|
|
@@ -26,11 +26,12 @@ const aggregateFromVotes = async (args) => {
|
|
|
26
26
|
if (value === "") total += count;
|
|
27
27
|
else counts.set(value, (counts.get(value) ?? 0) + count);
|
|
28
28
|
}
|
|
29
|
+
total = Math.max(0, total);
|
|
29
30
|
const optionValues = new Set(options.map((option) => option.value));
|
|
30
31
|
const leftovers = [...counts.keys()].filter((value) => !optionValues.has(value)).sort((a, b) => (counts.get(b) ?? 0) - (counts.get(a) ?? 0));
|
|
31
32
|
const labelByValue = new Map(options.map((option) => [option.value, option.label]));
|
|
32
33
|
const buckets = [...options.map((option) => option.value), ...leftovers].map((value) => {
|
|
33
|
-
const count = counts.get(value) ?? 0;
|
|
34
|
+
const count = Math.max(0, counts.get(value) ?? 0);
|
|
34
35
|
return {
|
|
35
36
|
value,
|
|
36
37
|
label: labelByValue.get(value) ?? value,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregateFromVotes.js","names":[],"sources":["../../../src/poll/votes/aggregateFromVotes.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport type { FieldAggregation } from '../../aggregation/types'\nimport { POLL_VOTES_SLUG, RESPONDENTS_VALUE } from './votesCollection'\n\ntype VoteRow = { field?: unknown; value?: unknown; count?: unknown }\n\nconst round1 = (n: number): number => Math.round(n * 10) / 10\n\n/**\n * Poll results from the tally store: one find over the form's rows for the field (O(options),\n * no scan, no truncation), buckets zero-seeded from the effective options so order and labels\n * match the scan-based shape. Counts sum across shard rows (see VOTE_SHARDS). `total` comes\n * from the respondents rows, keeping the percentage denominator exact for multi-value answers.\n */\nexport const aggregateFromVotes = async (args: {\n\tpayload: Payload\n\tformId: number | string\n\tfield: string\n\tmeta?: { label?: string; fieldType?: string }\n\toptions: { value: string; label: string }[]\n\treq?: PayloadRequest\n}): Promise<FieldAggregation> => {\n\tconst { payload, formId, field, meta, options, req } = args\n\tconst { docs } = await payload.find({\n\t\tcollection: POLL_VOTES_SLUG,\n\t\twhere: { and: [{ form: { equals: String(formId) } }, { field: { equals: field } }] },\n\t\tlimit: 100_000,\n\t\tpagination: false,\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\n\tconst counts = new Map<string, number>()\n\tlet total = 0\n\tfor (const doc of docs as VoteRow[]) {\n\t\tconst value = doc.value == null ? RESPONDENTS_VALUE : String(doc.value)\n\t\tconst count = typeof doc.count === 'number' ? doc.count : 0\n\t\tif (value === RESPONDENTS_VALUE) {\n\t\t\ttotal += count\n\t\t} else {\n\t\t\tcounts.set(value, (counts.get(value) ?? 0) + count)\n\t\t}\n\t}\n\n\tconst optionValues = new Set(options.map((option) => option.value))\n\tconst leftovers = [...counts.keys()]\n\t\t.filter((value) => !optionValues.has(value))\n\t\t.sort((a, b) => (counts.get(b) ?? 0) - (counts.get(a) ?? 0))\n\tconst labelByValue = new Map(options.map((option) => [option.value, option.label]))\n\n\tconst buckets = [...options.map((option) => option.value), ...leftovers].map((value) => {\n\t\tconst count = counts.get(value) ?? 0\n\t\treturn {\n\t\t\tvalue,\n\t\t\tlabel: labelByValue.get(value) ?? value,\n\t\t\tcount,\n\t\t\tpercentage: total > 0 ? round1((count / total) * 100) : 0,\n\t\t}\n\t})\n\n\treturn {\n\t\tfield,\n\t\tlabel: meta?.label,\n\t\tfieldType: meta?.fieldType,\n\t\ttotal,\n\t\tbuckets,\n\t\ttruncated: false,\n\t}\n}\n"],"mappings":";;AAMA,MAAM,UAAU,MAAsB,KAAK,MAAM,IAAI,EAAE,IAAI;;;;;;;AAQ3D,MAAa,qBAAqB,OAAO,SAOR;CAChC,MAAM,EAAE,SAAS,QAAQ,OAAO,MAAM,SAAS,QAAQ;CACvD,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK;EACnC,YAAY;EACZ,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,OAAO,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,MAAM,EAAE,CAAC,EAAE;EACnF,OAAO;EACP,YAAY;EACZ,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;CAED,MAAM,yBAAS,IAAI,IAAoB;CACvC,IAAI,QAAQ;CACZ,KAAK,MAAM,OAAO,MAAmB;EACpC,MAAM,QAAQ,IAAI,SAAS,OAAA,KAA2B,OAAO,IAAI,KAAK;EACtE,MAAM,QAAQ,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ;EAC1D,IAAI,UAAA,IACH,SAAS;OAET,OAAO,IAAI,QAAQ,OAAO,IAAI,KAAK,KAAK,KAAK,KAAK;CAEpD;
|
|
1
|
+
{"version":3,"file":"aggregateFromVotes.js","names":[],"sources":["../../../src/poll/votes/aggregateFromVotes.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport type { FieldAggregation } from '../../aggregation/types'\nimport { POLL_VOTES_SLUG, RESPONDENTS_VALUE } from './votesCollection'\n\ntype VoteRow = { field?: unknown; value?: unknown; count?: unknown }\n\nconst round1 = (n: number): number => Math.round(n * 10) / 10\n\n/**\n * Poll results from the tally store: one find over the form's rows for the field (O(options),\n * no scan, no truncation), buckets zero-seeded from the effective options so order and labels\n * match the scan-based shape. Counts sum across shard rows (see VOTE_SHARDS). `total` comes\n * from the respondents rows, keeping the percentage denominator exact for multi-value answers.\n */\nexport const aggregateFromVotes = async (args: {\n\tpayload: Payload\n\tformId: number | string\n\tfield: string\n\tmeta?: { label?: string; fieldType?: string }\n\toptions: { value: string; label: string }[]\n\treq?: PayloadRequest\n}): Promise<FieldAggregation> => {\n\tconst { payload, formId, field, meta, options, req } = args\n\tconst { docs } = await payload.find({\n\t\tcollection: POLL_VOTES_SLUG,\n\t\twhere: { and: [{ form: { equals: String(formId) } }, { field: { equals: field } }] },\n\t\tlimit: 100_000,\n\t\tpagination: false,\n\t\tdepth: 0,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\n\tconst counts = new Map<string, number>()\n\tlet total = 0\n\tfor (const doc of docs as VoteRow[]) {\n\t\tconst value = doc.value == null ? RESPONDENTS_VALUE : String(doc.value)\n\t\tconst count = typeof doc.count === 'number' ? doc.count : 0\n\t\tif (value === RESPONDENTS_VALUE) {\n\t\t\ttotal += count\n\t\t} else {\n\t\t\tcounts.set(value, (counts.get(value) ?? 0) + count)\n\t\t}\n\t}\n\n\t// Clamp at read: a concurrent double-change can transiently double-decrement one value (see\n\t// the tally hook's drift note), and a negative bucket count must never surface to a reader.\n\ttotal = Math.max(0, total)\n\n\tconst optionValues = new Set(options.map((option) => option.value))\n\tconst leftovers = [...counts.keys()]\n\t\t.filter((value) => !optionValues.has(value))\n\t\t.sort((a, b) => (counts.get(b) ?? 0) - (counts.get(a) ?? 0))\n\tconst labelByValue = new Map(options.map((option) => [option.value, option.label]))\n\n\tconst buckets = [...options.map((option) => option.value), ...leftovers].map((value) => {\n\t\tconst count = Math.max(0, counts.get(value) ?? 0)\n\t\treturn {\n\t\t\tvalue,\n\t\t\tlabel: labelByValue.get(value) ?? value,\n\t\t\tcount,\n\t\t\tpercentage: total > 0 ? round1((count / total) * 100) : 0,\n\t\t}\n\t})\n\n\treturn {\n\t\tfield,\n\t\tlabel: meta?.label,\n\t\tfieldType: meta?.fieldType,\n\t\ttotal,\n\t\tbuckets,\n\t\ttruncated: false,\n\t}\n}\n"],"mappings":";;AAMA,MAAM,UAAU,MAAsB,KAAK,MAAM,IAAI,EAAE,IAAI;;;;;;;AAQ3D,MAAa,qBAAqB,OAAO,SAOR;CAChC,MAAM,EAAE,SAAS,QAAQ,OAAO,MAAM,SAAS,QAAQ;CACvD,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK;EACnC,YAAY;EACZ,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,OAAO,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,MAAM,EAAE,CAAC,EAAE;EACnF,OAAO;EACP,YAAY;EACZ,OAAO;EACP,gBAAgB;EAChB;CACD,CAAC;CAED,MAAM,yBAAS,IAAI,IAAoB;CACvC,IAAI,QAAQ;CACZ,KAAK,MAAM,OAAO,MAAmB;EACpC,MAAM,QAAQ,IAAI,SAAS,OAAA,KAA2B,OAAO,IAAI,KAAK;EACtE,MAAM,QAAQ,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ;EAC1D,IAAI,UAAA,IACH,SAAS;OAET,OAAO,IAAI,QAAQ,OAAO,IAAI,KAAK,KAAK,KAAK,KAAK;CAEpD;CAIA,QAAQ,KAAK,IAAI,GAAG,KAAK;CAEzB,MAAM,eAAe,IAAI,IAAI,QAAQ,KAAK,WAAW,OAAO,KAAK,CAAC;CAClE,MAAM,YAAY,CAAC,GAAG,OAAO,KAAK,CAAC,EACjC,QAAQ,UAAU,CAAC,aAAa,IAAI,KAAK,CAAC,EAC1C,MAAM,GAAG,OAAO,OAAO,IAAI,CAAC,KAAK,MAAM,OAAO,IAAI,CAAC,KAAK,EAAE;CAC5D,MAAM,eAAe,IAAI,IAAI,QAAQ,KAAK,WAAW,CAAC,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC;CAElF,MAAM,UAAU,CAAC,GAAG,QAAQ,KAAK,WAAW,OAAO,KAAK,GAAG,GAAG,SAAS,EAAE,KAAK,UAAU;EACvF,MAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,IAAI,KAAK,KAAK,CAAC;EAChD,OAAO;GACN;GACA,OAAO,aAAa,IAAI,KAAK,KAAK;GAClC;GACA,YAAY,QAAQ,IAAI,OAAQ,QAAQ,QAAS,GAAG,IAAI;EACzD;CACD,CAAC;CAED,OAAO;EACN;EACA,OAAO,MAAM;EACb,WAAW,MAAM;EACjB;EACA;EACA,WAAW;CACZ;AACD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/poll/votes/answerValues.ts
|
|
2
|
+
/**
|
|
3
|
+
* A submission's answer(s) for one field as normalized non-empty strings: the shape both the vote
|
|
4
|
+
* tally and the voted-pick resolver count by (a multi-select answer contributes one entry per
|
|
5
|
+
* selected value).
|
|
6
|
+
*/
|
|
7
|
+
const answerValues = (values, field) => {
|
|
8
|
+
if (!Array.isArray(values)) return [];
|
|
9
|
+
const entry = values.find((row) => row.field === field);
|
|
10
|
+
if (entry == null) return [];
|
|
11
|
+
return (Array.isArray(entry.value) ? entry.value : [entry.value]).filter((value) => value != null && value !== "").map((value) => String(value));
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { answerValues };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=answerValues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answerValues.js","names":[],"sources":["../../../src/poll/votes/answerValues.ts"],"sourcesContent":["import type { SubmissionValue } from '../../submissions/types'\n\n/**\n * A submission's answer(s) for one field as normalized non-empty strings: the shape both the vote\n * tally and the voted-pick resolver count by (a multi-select answer contributes one entry per\n * selected value).\n */\nexport const answerValues = (values: unknown, field: string): string[] => {\n\tif (!Array.isArray(values)) return []\n\tconst entry = (values as SubmissionValue[]).find((row) => row.field === field)\n\tif (entry == null) return []\n\tconst raw = Array.isArray(entry.value) ? entry.value : [entry.value]\n\treturn raw.filter((value) => value != null && value !== '').map((value) => String(value))\n}\n"],"mappings":";;;;;;AAOA,MAAa,gBAAgB,QAAiB,UAA4B;CACzE,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC;CACpC,MAAM,QAAS,OAA6B,MAAM,QAAQ,IAAI,UAAU,KAAK;CAC7E,IAAI,SAAS,MAAM,OAAO,CAAC;CAE3B,QADY,MAAM,QAAQ,MAAM,KAAK,IAAI,MAAM,QAAQ,CAAC,MAAM,KAAK,GACxD,QAAQ,UAAU,SAAS,QAAQ,UAAU,EAAE,EAAE,KAAK,UAAU,OAAO,KAAK,CAAC;AACzF"}
|
|
@@ -2,27 +2,29 @@ import { pollConfigOf } from "../../form/pollState.js";
|
|
|
2
2
|
import "./votesCollection.js";
|
|
3
3
|
import { FORMS_SLUG } from "../../collections/forms.js";
|
|
4
4
|
import { formIdOf } from "../../submissions/formIdOf.js";
|
|
5
|
+
import { answerValues } from "./answerValues.js";
|
|
5
6
|
import { bumpPollVote } from "./bumpPollVote.js";
|
|
6
7
|
//#region src/poll/votes/voteTallyHook.ts
|
|
7
8
|
const isComplete = (doc) => doc.status == null || doc.status === "complete";
|
|
8
|
-
const answerValues = (values, field) => {
|
|
9
|
-
if (!Array.isArray(values)) return [];
|
|
10
|
-
const entry = values.find((row) => row.field === field);
|
|
11
|
-
if (entry == null) return [];
|
|
12
|
-
return (Array.isArray(entry.value) ? entry.value : [entry.value]).filter((value) => value != null && value !== "").map((value) => String(value));
|
|
13
|
-
};
|
|
14
9
|
/** Narrow a request's loosely typed `transactionID` to the value `bumpPollVote` accepts. */
|
|
15
10
|
const transactionIDOf = (transactionID) => typeof transactionID === "number" || typeof transactionID === "string" ? transactionID : void 0;
|
|
16
11
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
12
|
+
* Vote counting: a submission entering `complete` bumps one tally per answer value for the poll
|
|
13
|
+
* results field, plus the shared respondents row. Votes are permanent by default: no decrement on
|
|
14
|
+
* delete, edit, or prune, which is what lets a pruned submission's vote survive. The one
|
|
15
|
+
* exception is an `allowChange` poll, where an update of an already-complete submission moves the
|
|
16
|
+
* tally with the changed answer (decrement removed values, increment added ones, respondents
|
|
17
|
+
* untouched); delete and prune stay permanent even then. Two concurrent changes of the same
|
|
18
|
+
* submission can each read the same previous answer and double-adjust; the client's double-submit
|
|
19
|
+
* guard makes that rare and `recountPollVotes` is the healer, matching `bumpPollVote`'s residual
|
|
20
|
+
* drift note. Bump failures propagate so the enclosing transaction rolls the submission back
|
|
21
21
|
* rather than silently undercounting; registered before the swallow-all dispatch hook.
|
|
22
22
|
*/
|
|
23
23
|
const makeVoteTallyHook = () => async ({ doc, previousDoc, operation, req }) => {
|
|
24
24
|
if (operation !== "create" && operation !== "update") return doc;
|
|
25
|
-
|
|
25
|
+
const entering = operation === "create" && isComplete(doc) || operation === "update" && isComplete(doc) && !isComplete(previousDoc ?? {});
|
|
26
|
+
const changing = operation === "update" && isComplete(doc) && isComplete(previousDoc ?? {}) && !entering;
|
|
27
|
+
if (!entering && !changing) return doc;
|
|
26
28
|
const formId = formIdOf(doc.form);
|
|
27
29
|
if (formId == null) return doc;
|
|
28
30
|
const form = await req.payload.findByID({
|
|
@@ -35,13 +37,29 @@ const makeVoteTallyHook = () => async ({ doc, previousDoc, operation, req }) =>
|
|
|
35
37
|
const poll = form?.pollEnabled === true ? pollConfigOf(form.poll) : void 0;
|
|
36
38
|
const resultsField = typeof poll?.resultsField === "string" && poll.resultsField.length > 0 ? poll.resultsField : void 0;
|
|
37
39
|
if (!resultsField) return doc;
|
|
38
|
-
const votes = answerValues(doc.values, resultsField);
|
|
39
|
-
if (votes.length === 0) return doc;
|
|
40
40
|
const transactionID = transactionIDOf(req.transactionID);
|
|
41
41
|
const base = {
|
|
42
42
|
form: String(formId),
|
|
43
43
|
field: resultsField
|
|
44
44
|
};
|
|
45
|
+
if (changing) {
|
|
46
|
+
if (poll?.allowChange !== true) return doc;
|
|
47
|
+
const previous = answerValues(previousDoc?.values, resultsField);
|
|
48
|
+
const next = answerValues(doc.values, resultsField);
|
|
49
|
+
const removed = previous.filter((value) => !next.includes(value));
|
|
50
|
+
const added = next.filter((value) => !previous.includes(value));
|
|
51
|
+
for (const value of removed) await bumpPollVote(req.payload, {
|
|
52
|
+
...base,
|
|
53
|
+
value
|
|
54
|
+
}, -1, transactionID);
|
|
55
|
+
for (const value of added) await bumpPollVote(req.payload, {
|
|
56
|
+
...base,
|
|
57
|
+
value
|
|
58
|
+
}, 1, transactionID);
|
|
59
|
+
return doc;
|
|
60
|
+
}
|
|
61
|
+
const votes = answerValues(doc.values, resultsField);
|
|
62
|
+
if (votes.length === 0) return doc;
|
|
45
63
|
for (const value of votes) await bumpPollVote(req.payload, {
|
|
46
64
|
...base,
|
|
47
65
|
value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voteTallyHook.js","names":[],"sources":["../../../src/poll/votes/voteTallyHook.ts"],"sourcesContent":["import type { CollectionAfterChangeHook } from 'payload'\nimport { FORMS_SLUG } from '../../collections/forms'\nimport { pollConfigOf } from '../../form/pollState'\nimport { formIdOf } from '../../submissions/formIdOf'\nimport
|
|
1
|
+
{"version":3,"file":"voteTallyHook.js","names":[],"sources":["../../../src/poll/votes/voteTallyHook.ts"],"sourcesContent":["import type { CollectionAfterChangeHook } from 'payload'\nimport { FORMS_SLUG } from '../../collections/forms'\nimport { pollConfigOf } from '../../form/pollState'\nimport { formIdOf } from '../../submissions/formIdOf'\nimport { answerValues } from './answerValues'\nimport { bumpPollVote } from './bumpPollVote'\nimport { RESPONDENTS_VALUE } from './votesCollection'\n\nconst isComplete = (doc: { status?: unknown }): boolean =>\n\tdoc.status == null || doc.status === 'complete'\n\n/** Narrow a request's loosely typed `transactionID` to the value `bumpPollVote` accepts. */\nexport const transactionIDOf = (transactionID: unknown): number | string | undefined =>\n\ttypeof transactionID === 'number' || typeof transactionID === 'string' ? transactionID : undefined\n\n/**\n * Vote counting: a submission entering `complete` bumps one tally per answer value for the poll\n * results field, plus the shared respondents row. Votes are permanent by default: no decrement on\n * delete, edit, or prune, which is what lets a pruned submission's vote survive. The one\n * exception is an `allowChange` poll, where an update of an already-complete submission moves the\n * tally with the changed answer (decrement removed values, increment added ones, respondents\n * untouched); delete and prune stay permanent even then. Two concurrent changes of the same\n * submission can each read the same previous answer and double-adjust; the client's double-submit\n * guard makes that rare and `recountPollVotes` is the healer, matching `bumpPollVote`'s residual\n * drift note. Bump failures propagate so the enclosing transaction rolls the submission back\n * rather than silently undercounting; registered before the swallow-all dispatch hook.\n */\nexport const makeVoteTallyHook =\n\t(): CollectionAfterChangeHook =>\n\tasync ({ doc, previousDoc, operation, req }) => {\n\t\tif (operation !== 'create' && operation !== 'update') return doc\n\t\tconst entering =\n\t\t\t(operation === 'create' && isComplete(doc)) ||\n\t\t\t(operation === 'update' && isComplete(doc) && !isComplete(previousDoc ?? {}))\n\t\tconst changing =\n\t\t\toperation === 'update' && isComplete(doc) && isComplete(previousDoc ?? {}) && !entering\n\t\tif (!entering && !changing) return doc\n\t\tconst formId = formIdOf(doc.form)\n\t\tif (formId == null) return doc\n\t\tconst form = await req.payload\n\t\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0, overrideAccess: true, req })\n\t\t\t.catch(() => null)\n\t\tconst poll = form?.pollEnabled === true ? pollConfigOf(form.poll) : undefined\n\t\tconst resultsField =\n\t\t\ttypeof poll?.resultsField === 'string' && poll.resultsField.length > 0\n\t\t\t\t? poll.resultsField\n\t\t\t\t: undefined\n\t\tif (!resultsField) return doc\n\t\tconst transactionID = transactionIDOf(req.transactionID)\n\t\tconst base = { form: String(formId), field: resultsField }\n\t\tif (changing) {\n\t\t\tif (poll?.allowChange !== true) return doc\n\t\t\tconst previous = answerValues(previousDoc?.values, resultsField)\n\t\t\tconst next = answerValues(doc.values, resultsField)\n\t\t\tconst removed = previous.filter((value) => !next.includes(value))\n\t\t\tconst added = next.filter((value) => !previous.includes(value))\n\t\t\tfor (const value of removed) {\n\t\t\t\tawait bumpPollVote(req.payload, { ...base, value }, -1, transactionID)\n\t\t\t}\n\t\t\tfor (const value of added) {\n\t\t\t\tawait bumpPollVote(req.payload, { ...base, value }, 1, transactionID)\n\t\t\t}\n\t\t\treturn doc\n\t\t}\n\t\tconst votes = answerValues(doc.values, resultsField)\n\t\tif (votes.length === 0) return doc\n\t\tfor (const value of votes) {\n\t\t\tawait bumpPollVote(req.payload, { ...base, value }, 1, transactionID)\n\t\t}\n\t\tawait bumpPollVote(req.payload, { ...base, value: RESPONDENTS_VALUE }, 1, transactionID)\n\t\treturn doc\n\t}\n"],"mappings":";;;;;;;AAQA,MAAM,cAAc,QACnB,IAAI,UAAU,QAAQ,IAAI,WAAW;;AAGtC,MAAa,mBAAmB,kBAC/B,OAAO,kBAAkB,YAAY,OAAO,kBAAkB,WAAW,gBAAgB,KAAA;;;;;;;;;;;;;AAc1F,MAAa,0BAEZ,OAAO,EAAE,KAAK,aAAa,WAAW,UAAU;CAC/C,IAAI,cAAc,YAAY,cAAc,UAAU,OAAO;CAC7D,MAAM,WACJ,cAAc,YAAY,WAAW,GAAG,KACxC,cAAc,YAAY,WAAW,GAAG,KAAK,CAAC,WAAW,eAAe,CAAC,CAAC;CAC5E,MAAM,WACL,cAAc,YAAY,WAAW,GAAG,KAAK,WAAW,eAAe,CAAC,CAAC,KAAK,CAAC;CAChF,IAAI,CAAC,YAAY,CAAC,UAAU,OAAO;CACnC,MAAM,SAAS,SAAS,IAAI,IAAI;CAChC,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,OAAO,MAAM,IAAI,QACrB,SAAS;EAAE,YAAY;EAAY,IAAI;EAAQ,OAAO;EAAG,gBAAgB;EAAM;CAAI,CAAC,EACpF,YAAY,IAAI;CAClB,MAAM,OAAO,MAAM,gBAAgB,OAAO,aAAa,KAAK,IAAI,IAAI,KAAA;CACpE,MAAM,eACL,OAAO,MAAM,iBAAiB,YAAY,KAAK,aAAa,SAAS,IAClE,KAAK,eACL,KAAA;CACJ,IAAI,CAAC,cAAc,OAAO;CAC1B,MAAM,gBAAgB,gBAAgB,IAAI,aAAa;CACvD,MAAM,OAAO;EAAE,MAAM,OAAO,MAAM;EAAG,OAAO;CAAa;CACzD,IAAI,UAAU;EACb,IAAI,MAAM,gBAAgB,MAAM,OAAO;EACvC,MAAM,WAAW,aAAa,aAAa,QAAQ,YAAY;EAC/D,MAAM,OAAO,aAAa,IAAI,QAAQ,YAAY;EAClD,MAAM,UAAU,SAAS,QAAQ,UAAU,CAAC,KAAK,SAAS,KAAK,CAAC;EAChE,MAAM,QAAQ,KAAK,QAAQ,UAAU,CAAC,SAAS,SAAS,KAAK,CAAC;EAC9D,KAAK,MAAM,SAAS,SACnB,MAAM,aAAa,IAAI,SAAS;GAAE,GAAG;GAAM;EAAM,GAAG,IAAI,aAAa;EAEtE,KAAK,MAAM,SAAS,OACnB,MAAM,aAAa,IAAI,SAAS;GAAE,GAAG;GAAM;EAAM,GAAG,GAAG,aAAa;EAErE,OAAO;CACR;CACA,MAAM,QAAQ,aAAa,IAAI,QAAQ,YAAY;CACnD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,KAAK,MAAM,SAAS,OACnB,MAAM,aAAa,IAAI,SAAS;EAAE,GAAG;EAAM;CAAM,GAAG,GAAG,aAAa;CAErE,MAAM,aAAa,IAAI,SAAS;EAAE,GAAG;EAAM,OAAA;CAAyB,GAAG,GAAG,aAAa;CACvF,OAAO;AACR"}
|
package/dist/react/Form.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { SubmissionValue } from "../submissions/types.js";
|
|
1
2
|
import { AnyFormFieldDefinition } from "../fields/types.js";
|
|
2
3
|
import { BodyConverter } from "../actions/body/converters.js";
|
|
3
4
|
import { FormButtonSettings, FormDocument, FormPollSettings, FormResponseSettings } from "../form/types.js";
|
|
5
|
+
import { FormAdapters } from "./adapters.js";
|
|
4
6
|
import { RendererTranslate } from "./contract.js";
|
|
5
7
|
import { FormEventSink } from "../events/types.js";
|
|
6
8
|
import { AnyValidationRuleDefinition } from "../validation/types.js";
|
|
@@ -25,7 +27,8 @@ type FormSuccessResponse = {
|
|
|
25
27
|
};
|
|
26
28
|
/** The second argument to `onSuccess`: the resolved success response (an object, so it can grow). */
|
|
27
29
|
type FormSuccessResult = {
|
|
28
|
-
response?: FormSuccessResponse;
|
|
30
|
+
response?: FormSuccessResponse; /** The submitted answers (reserved keys like the honeypot and captcha token excluded), e.g. for `<Poll>` to track the voter's pick. */
|
|
31
|
+
values?: SubmissionValue[];
|
|
29
32
|
};
|
|
30
33
|
type FormProps = {
|
|
31
34
|
form: FormDocument;
|
|
@@ -96,6 +99,12 @@ type FormProps = {
|
|
|
96
99
|
submitButtonClassName?: string; /** CSS class forwarded to the default "Next" `<button>`. Ignored when `renderNext` is provided. */
|
|
97
100
|
nextButtonClassName?: string; /** CSS class forwarded to the default "Back" `<button>`. Ignored when `renderBack` is provided. */
|
|
98
101
|
backButtonClassName?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Host-owned effects (navigation, vote persistence, script loading). Each member defaults to
|
|
104
|
+
* the built-in DOM behavior; see {@link FormAdapters}. Pass a stable reference (module scope or
|
|
105
|
+
* `useMemo`), matching the other injectable props.
|
|
106
|
+
*/
|
|
107
|
+
adapters?: FormAdapters;
|
|
99
108
|
};
|
|
100
109
|
/** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */
|
|
101
110
|
declare const Form: ({
|
|
@@ -135,7 +144,8 @@ declare const Form: ({
|
|
|
135
144
|
renderBack,
|
|
136
145
|
submitButtonClassName,
|
|
137
146
|
nextButtonClassName,
|
|
138
|
-
backButtonClassName
|
|
147
|
+
backButtonClassName,
|
|
148
|
+
adapters
|
|
139
149
|
}: FormProps) => import("react/jsx-runtime").JSX.Element;
|
|
140
150
|
//#endregion
|
|
141
151
|
export { Form, FormProps, FormSuccessResponse, FormSuccessResult };
|
package/dist/react/Form.js
CHANGED
|
@@ -14,6 +14,7 @@ import { buildRecallResolver, descriptorsFor } from "../recall/resolver.js";
|
|
|
14
14
|
import { firstStepId, isTerminalStepId, resolveNextStepId, stepFieldNames } from "../flow/engine.js";
|
|
15
15
|
import { cn } from "./cn.js";
|
|
16
16
|
import { makeTranslate } from "../translations/makeTranslate.js";
|
|
17
|
+
import { defaultNavigate } from "./adapters.js";
|
|
17
18
|
import { emitFormEvent } from "./events.js";
|
|
18
19
|
import { FormContext } from "./FormContext.js";
|
|
19
20
|
import { FormControls } from "./FormControls.js";
|
|
@@ -60,7 +61,7 @@ const focusFirstIn = (root, selector) => {
|
|
|
60
61
|
/** A stored button label counts only when it is a non-empty string; anything else falls through. */
|
|
61
62
|
const storedLabel = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
|
|
62
63
|
/** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */
|
|
63
|
-
const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, converters, successBehavior = "replace", calcFunctions, events, t, locale = "en", layout, submitLabel, nextLabel, prevLabel, closeLabel, successMessage, presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, context, children, header, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName }) => {
|
|
64
|
+
const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, converters, successBehavior = "replace", calcFunctions, events, t, locale = "en", layout, submitLabel, nextLabel, prevLabel, closeLabel, successMessage, presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, context, children, header, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName, adapters }) => {
|
|
64
65
|
const honeypotName = honeypot === false ? null : honeypot?.name ?? "website";
|
|
65
66
|
const honeypotRef = useRef(null);
|
|
66
67
|
const registry = useMemo(() => buildFieldTypeRegistry(fieldTypes), [fieldTypes]);
|
|
@@ -421,6 +422,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
421
422
|
}
|
|
422
423
|
rawDispatch({ type: "SUBMIT_START" });
|
|
423
424
|
const values = answeredValues();
|
|
425
|
+
const answers = [...values];
|
|
424
426
|
if (honeypotName) {
|
|
425
427
|
const decoy = honeypotRef.current?.value ?? "";
|
|
426
428
|
if (decoy !== "") values.push({
|
|
@@ -451,7 +453,10 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
451
453
|
type: "submission.created",
|
|
452
454
|
submissionId: result.submissionId
|
|
453
455
|
});
|
|
454
|
-
onSuccess?.(result.submissionId, {
|
|
456
|
+
onSuccess?.(result.submissionId, {
|
|
457
|
+
response: resolveSuccessResponse(),
|
|
458
|
+
values: answers
|
|
459
|
+
});
|
|
455
460
|
if (successBehavior === "reset") {
|
|
456
461
|
rawDispatch({
|
|
457
462
|
type: "RESET",
|
|
@@ -470,7 +475,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
470
475
|
if (activePresentation.dismissOnSuccess) handleClose();
|
|
471
476
|
}
|
|
472
477
|
const redirectUrl = form.response?.type === "redirect" ? form.response.redirect?.url : void 0;
|
|
473
|
-
if (typeof redirectUrl === "string" && redirectUrl.length > 0
|
|
478
|
+
if (typeof redirectUrl === "string" && redirectUrl.length > 0) (adapters?.navigate ?? defaultNavigate)(redirectUrl, { replace: false });
|
|
474
479
|
} else {
|
|
475
480
|
if (result.fieldErrors) rawDispatch({
|
|
476
481
|
type: "SET_ALL_ISSUES",
|