@10x-media/form-builder 0.1.0-beta.0 → 0.1.0-beta.1
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 +52 -0
- package/README.md +43 -1402
- package/dist/actions/buildActionBlocks.js +19 -0
- package/dist/actions/buildActionBlocks.js.map +1 -0
- package/dist/actions/builtin/confirmation.js +49 -0
- package/dist/actions/builtin/confirmation.js.map +1 -0
- package/dist/actions/builtin/emailTeam.js +46 -0
- package/dist/actions/builtin/emailTeam.js.map +1 -0
- package/dist/actions/builtin/index.d.ts +7 -0
- package/dist/actions/builtin/index.js +13 -0
- package/dist/actions/builtin/index.js.map +1 -0
- package/dist/actions/builtin/signedWebhook.js +49 -0
- package/dist/actions/builtin/signedWebhook.js.map +1 -0
- package/dist/actions/defineAction.d.ts +36 -0
- package/dist/actions/defineAction.js +6 -0
- package/dist/actions/defineAction.js.map +1 -0
- package/dist/actions/dispatch.js +48 -0
- package/dist/actions/dispatch.js.map +1 -0
- package/dist/actions/registry.d.ts +16 -0
- package/dist/actions/registry.js +16 -0
- package/dist/actions/registry.js.map +1 -0
- package/dist/actions/runActions.d.ts +9 -0
- package/dist/actions/runActions.js +34 -0
- package/dist/actions/runActions.js.map +1 -0
- package/dist/actions/sign.d.ts +7 -0
- package/dist/actions/sign.js +9 -0
- package/dist/actions/sign.js.map +1 -0
- package/dist/actions/task.js +82 -0
- package/dist/actions/task.js.map +1 -0
- package/dist/aggregation/aggregateResponses.d.ts +31 -0
- package/dist/aggregation/aggregateResponses.js +86 -0
- package/dist/aggregation/aggregateResponses.js.map +1 -0
- package/dist/aggregation/aggregateRows.d.ts +10 -0
- package/dist/aggregation/aggregateRows.js +64 -0
- package/dist/aggregation/aggregateRows.js.map +1 -0
- package/dist/aggregation/resolveResultsRequest.d.ts +32 -0
- package/dist/aggregation/resolveResultsRequest.js +56 -0
- package/dist/aggregation/resolveResultsRequest.js.map +1 -0
- package/dist/aggregation/types.d.ts +39 -0
- package/dist/calc/computeCalcFields.d.ts +15 -0
- package/dist/calc/computeCalcFields.js +27 -0
- package/dist/calc/computeCalcFields.js.map +1 -0
- package/dist/calc/evaluate.d.ts +8 -0
- package/dist/calc/evaluate.js +59 -0
- package/dist/calc/evaluate.js.map +1 -0
- package/dist/calc/normalizeCalc.d.ts +8 -0
- package/dist/calc/normalizeCalc.js +100 -0
- package/dist/calc/normalizeCalc.js.map +1 -0
- package/dist/calc/types.d.ts +28 -0
- package/dist/client/ConditionBuilder.js +121 -0
- package/dist/client/ConditionBuilder.js.map +1 -0
- package/dist/client/ConditionRow.js +150 -0
- package/dist/client/ConditionRow.js.map +1 -0
- package/dist/client/FlowBuilder.d.ts +15 -0
- package/dist/client/FlowBuilder.js +408 -0
- package/dist/client/FlowBuilder.js.map +1 -0
- package/dist/client/FormConditionField.d.ts +21 -0
- package/dist/client/FormConditionField.js +33 -0
- package/dist/client/FormConditionField.js.map +1 -0
- package/dist/client/synthesizeClientField.js +46 -0
- package/dist/client/synthesizeClientField.js.map +1 -0
- package/dist/collections/formSubmissions.js +138 -0
- package/dist/collections/formSubmissions.js.map +1 -0
- package/dist/collections/forms.js +187 -0
- package/dist/collections/forms.js.map +1 -0
- package/dist/collections/uploads.d.ts +29 -0
- package/dist/collections/uploads.js +46 -0
- package/dist/collections/uploads.js.map +1 -0
- package/dist/conditions/conditionType.js +14 -0
- package/dist/conditions/conditionType.js.map +1 -0
- package/dist/conditions/evaluate.d.ts +17 -0
- package/dist/conditions/evaluate.js +94 -0
- package/dist/conditions/evaluate.js.map +1 -0
- package/dist/conditions/fieldTypes.d.ts +9 -0
- package/dist/{fieldTypes-CzhhJXjg.js → conditions/fieldTypes.js} +2 -2
- package/dist/conditions/fieldTypes.js.map +1 -0
- package/dist/conditions/normalizeConditions.js +47 -0
- package/dist/conditions/normalizeConditions.js.map +1 -0
- package/dist/conditions/types.d.ts +8 -0
- package/dist/consent/buildConsentSourceConfig.js +49 -0
- package/dist/consent/buildConsentSourceConfig.js.map +1 -0
- package/dist/consent/builtin/index.d.ts +7 -0
- package/dist/consent/builtin/index.js +11 -0
- package/dist/consent/builtin/index.js.map +1 -0
- package/dist/consent/builtin/pageReference.js +75 -0
- package/dist/consent/builtin/pageReference.js.map +1 -0
- package/dist/consent/builtin/static.js +44 -0
- package/dist/consent/builtin/static.js.map +1 -0
- package/dist/consent/captureConsent.d.ts +28 -0
- package/dist/consent/captureConsent.js +26 -0
- package/dist/consent/captureConsent.js.map +1 -0
- package/dist/consent/defineConsentSource.d.ts +35 -0
- package/dist/consent/defineConsentSource.js +6 -0
- package/dist/consent/defineConsentSource.js.map +1 -0
- package/dist/consent/registry.d.ts +16 -0
- package/dist/consent/registry.js +16 -0
- package/dist/consent/registry.js.map +1 -0
- package/dist/consent/resolveConsentLinks.d.ts +19 -0
- package/dist/consent/resolveConsentLinks.js +26 -0
- package/dist/consent/resolveConsentLinks.js.map +1 -0
- package/dist/consent/resolvePublishedVersionRef.d.ts +17 -0
- package/dist/consent/resolvePublishedVersionRef.js +27 -0
- package/dist/consent/resolvePublishedVersionRef.js.map +1 -0
- package/dist/events/noopSink.js +6 -0
- package/dist/events/noopSink.js.map +1 -0
- package/dist/events/resolveEventSink.js +8 -0
- package/dist/events/resolveEventSink.js.map +1 -0
- package/dist/events/types.d.ts +31 -0
- package/dist/exports/client.d.ts +3 -21
- package/dist/exports/client.js +3 -319
- package/dist/exports/i18n.d.ts +3 -138
- package/dist/exports/i18n.js +2 -2
- package/dist/exports/react.d.ts +45 -550
- package/dist/exports/react.js +39 -1563
- package/dist/exports/rsc.d.ts +2 -16
- package/dist/exports/rsc.js +1 -54
- package/dist/exports/types.d.ts +27 -5
- package/dist/fields/buildFieldBlocks.js +46 -0
- package/dist/fields/buildFieldBlocks.js.map +1 -0
- package/dist/fields/builtin/calculation.js +24 -0
- package/dist/fields/builtin/calculation.js.map +1 -0
- package/dist/fields/builtin/checkbox.js +13 -0
- package/dist/fields/builtin/checkbox.js.map +1 -0
- package/dist/fields/builtin/consent.js +32 -0
- package/dist/fields/builtin/consent.js.map +1 -0
- package/dist/fields/builtin/email.js +18 -0
- package/dist/fields/builtin/email.js.map +1 -0
- package/dist/fields/builtin/file.js +40 -0
- package/dist/fields/builtin/file.js.map +1 -0
- package/dist/fields/builtin/index.js +25 -0
- package/dist/fields/builtin/index.js.map +1 -0
- package/dist/fields/builtin/number.js +17 -0
- package/dist/fields/builtin/number.js.map +1 -0
- package/dist/fields/builtin/select.js +44 -0
- package/dist/fields/builtin/select.js.map +1 -0
- package/dist/fields/builtin/text.js +13 -0
- package/dist/fields/builtin/text.js.map +1 -0
- package/dist/fields/builtin/textarea.js +13 -0
- package/dist/fields/builtin/textarea.js.map +1 -0
- package/dist/fields/defineFormField.d.ts +13 -0
- package/dist/fields/defineFormField.js +12 -0
- package/dist/fields/defineFormField.js.map +1 -0
- package/dist/fields/registry.d.ts +10 -0
- package/dist/fields/registry.js +24 -0
- package/dist/fields/registry.js.map +1 -0
- package/dist/fields/sharedConfig.js +89 -0
- package/dist/fields/sharedConfig.js.map +1 -0
- package/dist/fields/types.d.ts +88 -0
- package/dist/flow/engine.d.ts +17 -0
- package/dist/flow/engine.js +24 -0
- package/dist/flow/engine.js.map +1 -0
- package/dist/flow/normalizeFlow.js +35 -0
- package/dist/flow/normalizeFlow.js.map +1 -0
- package/dist/{types-DsJ_6kGJ.d.ts → flow/types.d.ts} +2 -2
- package/dist/index.d.ts +74 -4
- package/dist/index.js +40 -1889
- package/dist/index.js.map +1 -1
- package/dist/plugin/access.js +12 -0
- package/dist/plugin/access.js.map +1 -0
- package/dist/plugin/registerCollections.js +40 -0
- package/dist/plugin/registerCollections.js.map +1 -0
- package/dist/plugin/registerTranslations.js +19 -0
- package/dist/plugin/registerTranslations.js.map +1 -0
- package/dist/prefill/valuesFromSearchParams.d.ts +18 -0
- package/dist/prefill/valuesFromSearchParams.js +45 -0
- package/dist/prefill/valuesFromSearchParams.js.map +1 -0
- package/dist/presentations/defaults.d.ts +33 -0
- package/dist/presentations/defaults.js +35 -0
- package/dist/presentations/defaults.js.map +1 -0
- package/dist/presentations/registry.d.ts +15 -0
- package/dist/presentations/registry.js +15 -0
- package/dist/presentations/registry.js.map +1 -0
- package/dist/presentations/types.d.ts +18 -0
- package/dist/react/Form.d.ts +81 -0
- package/dist/react/Form.js +457 -0
- package/dist/react/Form.js.map +1 -0
- package/dist/react/FormContext.d.ts +16 -0
- package/dist/react/FormContext.js +14 -0
- package/dist/react/FormContext.js.map +1 -0
- package/dist/react/FormLayout.d.ts +20 -0
- package/dist/react/FormLayout.js +14 -0
- package/dist/react/FormLayout.js.map +1 -0
- package/dist/react/FormResults.d.ts +25 -0
- package/dist/react/FormResults.js +85 -0
- package/dist/react/FormResults.js.map +1 -0
- package/dist/react/Honeypot.d.ts +22 -0
- package/dist/react/Honeypot.js +38 -0
- package/dist/react/Honeypot.js.map +1 -0
- package/dist/react/Poll.d.ts +32 -0
- package/dist/react/Poll.js +77 -0
- package/dist/react/Poll.js.map +1 -0
- package/dist/react/cn.d.ts +6 -0
- package/dist/react/cn.js +8 -0
- package/dist/react/cn.js.map +1 -0
- package/dist/react/contract.d.ts +32 -0
- package/dist/react/contract.js +7 -0
- package/dist/react/contract.js.map +1 -0
- package/dist/react/events.js +14 -0
- package/dist/react/events.js.map +1 -0
- package/dist/react/fetchResults.d.ts +25 -0
- package/dist/react/fetchResults.js +31 -0
- package/dist/react/fetchResults.js.map +1 -0
- package/dist/react/presentation/Backdrop.d.ts +18 -0
- package/dist/react/presentation/Backdrop.js +14 -0
- package/dist/react/presentation/Backdrop.js.map +1 -0
- package/dist/react/presentation/DialogSurface.d.ts +35 -0
- package/dist/react/presentation/DialogSurface.js +56 -0
- package/dist/react/presentation/DialogSurface.js.map +1 -0
- package/dist/react/presentation/presentations.d.ts +7 -0
- package/dist/react/presentation/presentations.js +32 -0
- package/dist/react/presentation/presentations.js.map +1 -0
- package/dist/react/presentation/registry.d.ts +11 -0
- package/dist/react/presentation/registry.js +12 -0
- package/dist/react/presentation/registry.js.map +1 -0
- package/dist/react/presentation/types.d.ts +19 -0
- package/dist/react/presentation/useDismiss.d.ts +19 -0
- package/dist/react/presentation/useDismiss.js +33 -0
- package/dist/react/presentation/useDismiss.js.map +1 -0
- package/dist/react/presentation/useFocusTrap.d.ts +8 -0
- package/dist/react/presentation/useFocusTrap.js +35 -0
- package/dist/react/presentation/useFocusTrap.js.map +1 -0
- package/dist/react/presentation/useScrollLock.d.ts +6 -0
- package/dist/react/presentation/useScrollLock.js +18 -0
- package/dist/react/presentation/useScrollLock.js.map +1 -0
- package/dist/react/primitives/Checkbox.d.ts +26 -0
- package/dist/react/primitives/Checkbox.js +20 -0
- package/dist/react/primitives/Checkbox.js.map +1 -0
- package/dist/react/primitives/FieldShell.d.ts +31 -0
- package/dist/react/primitives/FieldShell.js +51 -0
- package/dist/react/primitives/FieldShell.js.map +1 -0
- package/dist/react/primitives/Input.d.ts +30 -0
- package/dist/react/primitives/Input.js +21 -0
- package/dist/react/primitives/Input.js.map +1 -0
- package/dist/react/primitives/Select.d.ts +34 -0
- package/dist/react/primitives/Select.js +26 -0
- package/dist/react/primitives/Select.js.map +1 -0
- package/dist/react/primitives/Textarea.d.ts +28 -0
- package/dist/react/primitives/Textarea.js +20 -0
- package/dist/react/primitives/Textarea.js.map +1 -0
- package/dist/react/recall.js +20 -0
- package/dist/react/recall.js.map +1 -0
- package/dist/react/registry.d.ts +16 -0
- package/dist/react/registry.js +16 -0
- package/dist/react/registry.js.map +1 -0
- package/dist/react/renderers/calculation.js +30 -0
- package/dist/react/renderers/calculation.js.map +1 -0
- package/dist/react/renderers/checkbox.js +35 -0
- package/dist/react/renderers/checkbox.js.map +1 -0
- package/dist/react/renderers/consent.js +54 -0
- package/dist/react/renderers/consent.js.map +1 -0
- package/dist/react/renderers/email.js +37 -0
- package/dist/react/renderers/email.js.map +1 -0
- package/dist/react/renderers/file.js +92 -0
- package/dist/react/renderers/file.js.map +1 -0
- package/dist/react/renderers/index.d.ts +8 -0
- package/dist/react/renderers/index.js +26 -0
- package/dist/react/renderers/index.js.map +1 -0
- package/dist/react/renderers/number.js +40 -0
- package/dist/react/renderers/number.js.map +1 -0
- package/dist/react/renderers/select.js +38 -0
- package/dist/react/renderers/select.js.map +1 -0
- package/dist/react/renderers/text.js +37 -0
- package/dist/react/renderers/text.js.map +1 -0
- package/dist/react/renderers/textarea.js +36 -0
- package/dist/react/renderers/textarea.js.map +1 -0
- package/dist/react/resolveForm.js +16 -0
- package/dist/react/resolveForm.js.map +1 -0
- package/dist/react/state.d.ts +15 -0
- package/dist/react/state.js +70 -0
- package/dist/react/state.js.map +1 -0
- package/dist/react/submitForm.d.ts +31 -0
- package/dist/react/submitForm.js +58 -0
- package/dist/react/submitForm.js.map +1 -0
- package/dist/react/toFormDocument.d.ts +24 -0
- package/dist/react/toFormDocument.js +22 -0
- package/dist/react/toFormDocument.js.map +1 -0
- package/dist/react/uploadFile.d.ts +23 -0
- package/dist/react/uploadFile.js +39 -0
- package/dist/react/uploadFile.js.map +1 -0
- package/dist/react/useField.d.ts +14 -0
- package/dist/react/useField.js +49 -0
- package/dist/react/useField.js.map +1 -0
- package/dist/react/useFormState.d.ts +8 -0
- package/dist/react/useFormState.js +9 -0
- package/dist/react/useFormState.js.map +1 -0
- package/dist/react/useFormStep.d.ts +8 -0
- package/dist/react/useFormStep.js +9 -0
- package/dist/react/useFormStep.js.map +1 -0
- package/dist/react/validateField.js +30 -0
- package/dist/react/validateField.js.map +1 -0
- package/dist/recall/interpolate.d.ts +10 -0
- package/dist/recall/interpolate.js +16 -0
- package/dist/recall/interpolate.js.map +1 -0
- package/dist/recall/resolver.d.ts +22 -0
- package/dist/recall/resolver.js +36 -0
- package/dist/recall/resolver.js.map +1 -0
- package/dist/spam/captcha.d.ts +12 -0
- package/dist/spam/captcha.js +11 -0
- package/dist/spam/captcha.js.map +1 -0
- package/dist/spam/constants.d.ts +12 -0
- package/dist/spam/constants.js +15 -0
- package/dist/spam/constants.js.map +1 -0
- package/dist/spam/identify.d.ts +12 -0
- package/dist/spam/identify.js +20 -0
- package/dist/spam/identify.js.map +1 -0
- package/dist/spam/rateLimiter.d.ts +18 -0
- package/dist/spam/rateLimiter.js +41 -0
- package/dist/spam/rateLimiter.js.map +1 -0
- package/dist/spam/reserved.js +30 -0
- package/dist/spam/reserved.js.map +1 -0
- package/dist/spam/resolveSpam.d.ts +12 -0
- package/dist/spam/resolveSpam.js +42 -0
- package/dist/spam/resolveSpam.js.map +1 -0
- package/dist/spam/spamGuard.js +68 -0
- package/dist/spam/spamGuard.js.map +1 -0
- package/dist/spam/types.d.ts +73 -0
- package/dist/spam/uploadHooks.js +33 -0
- package/dist/spam/uploadHooks.js.map +1 -0
- package/dist/submissions/SubmissionAnswers.d.ts +16 -0
- package/dist/submissions/SubmissionAnswers.js +57 -0
- package/dist/submissions/SubmissionAnswers.js.map +1 -0
- package/dist/submissions/runSubmission.js +205 -0
- package/dist/submissions/runSubmission.js.map +1 -0
- package/dist/submissions/types.d.ts +33 -0
- package/dist/submissions/validateSubmission.js +59 -0
- package/dist/submissions/validateSubmission.js.map +1 -0
- package/dist/translations/en.d.ts +12 -0
- package/dist/{translations-edMqq_2e.js → translations/en.js} +3 -22
- package/dist/translations/en.js.map +1 -0
- package/dist/translations/index.d.ts +14 -0
- package/dist/translations/index.js +26 -0
- package/dist/translations/index.js.map +1 -0
- package/dist/translations/keys.d.ts +132 -0
- package/dist/{keys-N5xGiUsh.js → translations/keys.js} +2 -2
- package/dist/translations/keys.js.map +1 -0
- package/dist/translations/makeTranslate.d.ts +11 -0
- package/dist/translations/makeTranslate.js +11 -0
- package/dist/translations/makeTranslate.js.map +1 -0
- package/dist/translations/server.js +25 -0
- package/dist/translations/server.js.map +1 -0
- package/dist/translations/useTranslation.js +12 -0
- package/dist/translations/useTranslation.js.map +1 -0
- package/dist/uploads/captureFileRef.d.ts +27 -0
- package/dist/uploads/captureFileRef.js +34 -0
- package/dist/uploads/captureFileRef.js.map +1 -0
- package/dist/uploads/resolveFileRef.d.ts +26 -0
- package/dist/uploads/resolveFileRef.js +44 -0
- package/dist/uploads/resolveFileRef.js.map +1 -0
- package/dist/uploads/types.d.ts +20 -0
- package/dist/validation/buildRuleBlocks.js +46 -0
- package/dist/validation/buildRuleBlocks.js.map +1 -0
- package/dist/validation/builtin/email.js +19 -0
- package/dist/validation/builtin/email.js.map +1 -0
- package/dist/validation/builtin/index.js +27 -0
- package/dist/validation/builtin/index.js.map +1 -0
- package/dist/validation/builtin/matchesField.js +20 -0
- package/dist/validation/builtin/matchesField.js.map +1 -0
- package/dist/validation/builtin/max.js +21 -0
- package/dist/validation/builtin/max.js.map +1 -0
- package/dist/validation/builtin/maxLength.js +26 -0
- package/dist/validation/builtin/maxLength.js.map +1 -0
- package/dist/validation/builtin/min.js +21 -0
- package/dist/validation/builtin/min.js.map +1 -0
- package/dist/validation/builtin/minLength.js +26 -0
- package/dist/validation/builtin/minLength.js.map +1 -0
- package/dist/validation/builtin/notAlreadySubmitted.js +32 -0
- package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -0
- package/dist/validation/builtin/oneOf.js +34 -0
- package/dist/validation/builtin/oneOf.js.map +1 -0
- package/dist/validation/builtin/pattern.js +41 -0
- package/dist/validation/builtin/pattern.js.map +1 -0
- package/dist/validation/builtin/url.js +22 -0
- package/dist/validation/builtin/url.js.map +1 -0
- package/dist/validation/defineValidationRule.d.ts +12 -0
- package/dist/validation/defineValidationRule.js +11 -0
- package/dist/validation/defineValidationRule.js.map +1 -0
- package/dist/validation/message.js +14 -0
- package/dist/validation/message.js.map +1 -0
- package/dist/validation/registry.d.ts +10 -0
- package/dist/validation/registry.js +20 -0
- package/dist/validation/registry.js.map +1 -0
- package/dist/validation/runValidation.js +111 -0
- package/dist/validation/runValidation.js.map +1 -0
- package/dist/validation/types.d.ts +76 -0
- package/package.json +8 -5
- package/styles/form-builder.css +201 -11
- package/dist/constants-B-BUfetP.d.ts +0 -288
- package/dist/exports/client.js.map +0 -1
- package/dist/exports/react.js.map +0 -1
- package/dist/exports/rsc.js.map +0 -1
- package/dist/fieldTypes-B8jkNRob.d.ts +0 -188
- package/dist/fieldTypes-CzhhJXjg.js.map +0 -1
- package/dist/index-DfFYGbVF.d.ts +0 -481
- package/dist/keys-N5xGiUsh.js.map +0 -1
- package/dist/registry-CoCyhtvB.js +0 -282
- package/dist/registry-CoCyhtvB.js.map +0 -1
- package/dist/resolver-OeQyVH2N.js +0 -665
- package/dist/resolver-OeQyVH2N.js.map +0 -1
- package/dist/translations-edMqq_2e.js.map +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { isLoggedIn } from "../plugin/access.js";
|
|
2
|
+
import { dispatchActions } from "../actions/dispatch.js";
|
|
3
|
+
import { resolveEventSink } from "../events/resolveEventSink.js";
|
|
4
|
+
import { buildSpamGuard } from "../spam/spamGuard.js";
|
|
5
|
+
import { FORMS_SLUG } from "./forms.js";
|
|
6
|
+
import { validateSubmission } from "../submissions/validateSubmission.js";
|
|
7
|
+
//#region src/collections/formSubmissions.ts
|
|
8
|
+
const FORM_SUBMISSIONS_SLUG = "form-submissions";
|
|
9
|
+
const formIdOf = (form) => {
|
|
10
|
+
if (typeof form === "number" || typeof form === "string") return form;
|
|
11
|
+
if (form && typeof form === "object" && "id" in form) {
|
|
12
|
+
const id = form.id;
|
|
13
|
+
if (typeof id === "number" || typeof id === "string") return id;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* On a completed submission create, dispatch the form's post-submit actions and emit `submission.created`.
|
|
18
|
+
* Both are side effects on an already-written row, so the whole body is wrapped: nothing it does can throw
|
|
19
|
+
* past the hook (a failed action or sink must never fail the submission write). Dispatch is itself bounded
|
|
20
|
+
* and non-throwing; the inline fallback is awaited so the bound caps the added latency, while the queued
|
|
21
|
+
* path returns immediately. The form's `actions` are null-guarded for legacy rows created before the field
|
|
22
|
+
* existed.
|
|
23
|
+
*/
|
|
24
|
+
const makeAfterChange = (args) => async ({ doc, operation, req }) => {
|
|
25
|
+
if (operation !== "create" || doc.status != null && doc.status !== "complete") return doc;
|
|
26
|
+
const { payload } = req;
|
|
27
|
+
try {
|
|
28
|
+
const formId = formIdOf(doc.form);
|
|
29
|
+
if (formId == null) return doc;
|
|
30
|
+
await dispatchActions({
|
|
31
|
+
actions: (await payload.findByID({
|
|
32
|
+
collection: "forms",
|
|
33
|
+
id: formId,
|
|
34
|
+
depth: 0,
|
|
35
|
+
overrideAccess: true,
|
|
36
|
+
req
|
|
37
|
+
}).catch(() => null))?.actions ?? null,
|
|
38
|
+
formId,
|
|
39
|
+
submissionId: doc.id,
|
|
40
|
+
registry: args.actionRegistry,
|
|
41
|
+
payload,
|
|
42
|
+
req,
|
|
43
|
+
hasRunner: args.hasRunner
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
await resolveEventSink(args.events).emit({
|
|
47
|
+
type: "submission.created",
|
|
48
|
+
formId: String(formId),
|
|
49
|
+
submissionId: String(doc.id),
|
|
50
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
payload.logger?.error(`@10x-media/form-builder: submission.created sink threw: ${error instanceof Error ? error.message : String(error)}`);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
payload.logger?.error(`@10x-media/form-builder: afterChange dispatch failed for submission ${String(doc.id)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
57
|
+
}
|
|
58
|
+
return doc;
|
|
59
|
+
};
|
|
60
|
+
const buildSubmissionsCollection = ({ registry, ruleRegistry, consentRegistry, actionRegistry = /* @__PURE__ */ new Map(), events, hasRunner = false, uploadSlug, spam }) => ({
|
|
61
|
+
slug: FORM_SUBMISSIONS_SLUG,
|
|
62
|
+
labels: {
|
|
63
|
+
singular: "Submission",
|
|
64
|
+
plural: "Submissions"
|
|
65
|
+
},
|
|
66
|
+
admin: { group: "Forms" },
|
|
67
|
+
access: {
|
|
68
|
+
create: () => true,
|
|
69
|
+
read: isLoggedIn,
|
|
70
|
+
update: () => false
|
|
71
|
+
},
|
|
72
|
+
hooks: {
|
|
73
|
+
beforeValidate: [...spam ? [buildSpamGuard(spam)] : [], validateSubmission({
|
|
74
|
+
registry,
|
|
75
|
+
ruleRegistry,
|
|
76
|
+
consentRegistry,
|
|
77
|
+
uploadSlug
|
|
78
|
+
})],
|
|
79
|
+
afterChange: [makeAfterChange({
|
|
80
|
+
actionRegistry,
|
|
81
|
+
events,
|
|
82
|
+
hasRunner
|
|
83
|
+
})]
|
|
84
|
+
},
|
|
85
|
+
fields: [
|
|
86
|
+
{
|
|
87
|
+
name: "form",
|
|
88
|
+
type: "relationship",
|
|
89
|
+
relationTo: FORMS_SLUG,
|
|
90
|
+
required: true
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "status",
|
|
94
|
+
type: "select",
|
|
95
|
+
defaultValue: "complete",
|
|
96
|
+
options: [{
|
|
97
|
+
label: "Complete",
|
|
98
|
+
value: "complete"
|
|
99
|
+
}, {
|
|
100
|
+
label: "Partial",
|
|
101
|
+
value: "partial"
|
|
102
|
+
}],
|
|
103
|
+
access: {
|
|
104
|
+
create: isLoggedIn,
|
|
105
|
+
update: isLoggedIn
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "locale",
|
|
110
|
+
type: "text"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "values",
|
|
114
|
+
type: "json"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "descriptors",
|
|
118
|
+
type: "json"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "consent",
|
|
122
|
+
type: "json"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "meta",
|
|
126
|
+
type: "json"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "answers",
|
|
130
|
+
type: "ui",
|
|
131
|
+
admin: { components: { Field: "@10x-media/form-builder/rsc#SubmissionAnswers" } }
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
});
|
|
135
|
+
//#endregion
|
|
136
|
+
export { FORM_SUBMISSIONS_SLUG, buildSubmissionsCollection };
|
|
137
|
+
|
|
138
|
+
//# sourceMappingURL=formSubmissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formSubmissions.js","names":[],"sources":["../../src/collections/formSubmissions.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, CollectionConfig } from 'payload'\nimport { dispatchActions } from '../actions/dispatch'\nimport type { ActionRegistry } from '../actions/registry'\nimport type { ActionInstance } from '../actions/runActions'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport { resolveEventSink } from '../events/resolveEventSink'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { isLoggedIn } from '../plugin/access'\nimport { buildSpamGuard } from '../spam/spamGuard'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { validateSubmission } from '../submissions/validateSubmission'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport { FORMS_SLUG } from './forms'\n\nexport const FORM_SUBMISSIONS_SLUG = 'form-submissions'\n\ntype BuildSubmissionsCollectionArgs = {\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tactionRegistry?: ActionRegistry\n\tevents?: FormEventSink\n\t/** Whether a job runner is likely present; gates the queued vs bounded-inline dispatch path. */\n\thasRunner?: boolean\n\t/** Upload collection slug for file fields without an explicit `relationTo`. */\n\tuploadSlug?: string\n\t/** Resolved spam config; when active, prepends the spam guard before validation. `false` disables it. */\n\tspam?: ResolvedSpamConfig | false\n}\n\nconst formIdOf = (form: unknown): number | string | undefined => {\n\tif (typeof form === 'number' || typeof form === 'string') {\n\t\treturn form\n\t}\n\tif (form && typeof form === 'object' && 'id' in form) {\n\t\tconst id = (form as { id: unknown }).id\n\t\tif (typeof id === 'number' || typeof id === 'string') {\n\t\t\treturn id\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * On a completed submission create, dispatch the form's post-submit actions and emit `submission.created`.\n * Both are side effects on an already-written row, so the whole body is wrapped: nothing it does can throw\n * past the hook (a failed action or sink must never fail the submission write). Dispatch is itself bounded\n * and non-throwing; the inline fallback is awaited so the bound caps the added latency, while the queued\n * path returns immediately. The form's `actions` are null-guarded for legacy rows created before the field\n * existed.\n */\nconst makeAfterChange =\n\t(args: {\n\t\tactionRegistry: ActionRegistry\n\t\tevents?: FormEventSink\n\t\thasRunner: boolean\n\t}): CollectionAfterChangeHook =>\n\tasync ({ doc, operation, req }) => {\n\t\tif (operation !== 'create' || (doc.status != null && doc.status !== 'complete')) {\n\t\t\treturn doc\n\t\t}\n\t\tconst { payload } = req\n\t\ttry {\n\t\t\tconst formId = formIdOf(doc.form)\n\t\t\tif (formId == null) {\n\t\t\t\treturn doc\n\t\t\t}\n\t\t\tconst form = await payload\n\t\t\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0, overrideAccess: true, req })\n\t\t\t\t.catch(() => null)\n\n\t\t\tawait dispatchActions({\n\t\t\t\tactions: (form?.actions ?? null) as ActionInstance[] | null,\n\t\t\t\tformId,\n\t\t\t\tsubmissionId: doc.id as number | string,\n\t\t\t\tregistry: args.actionRegistry,\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\thasRunner: args.hasRunner,\n\t\t\t})\n\n\t\t\ttry {\n\t\t\t\tawait resolveEventSink(args.events).emit({\n\t\t\t\t\ttype: 'submission.created',\n\t\t\t\t\tformId: String(formId),\n\t\t\t\t\tsubmissionId: String(doc.id),\n\t\t\t\t\tat: new Date().toISOString(),\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tpayload.logger?.error(\n\t\t\t\t\t`@10x-media/form-builder: submission.created sink threw: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`\n\t\t\t\t)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tpayload.logger?.error(\n\t\t\t\t`@10x-media/form-builder: afterChange dispatch failed for submission ${String(doc.id)}: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`\n\t\t\t)\n\t\t}\n\t\treturn doc\n\t}\n\nexport const buildSubmissionsCollection = ({\n\tregistry,\n\truleRegistry,\n\tconsentRegistry,\n\tactionRegistry = new Map(),\n\tevents,\n\thasRunner = false,\n\tuploadSlug,\n\tspam,\n}: BuildSubmissionsCollectionArgs): CollectionConfig => ({\n\tslug: FORM_SUBMISSIONS_SLUG,\n\tlabels: { singular: 'Submission', plural: 'Submissions' },\n\tadmin: { group: 'Forms' },\n\taccess: {\n\t\tcreate: () => true,\n\t\tread: isLoggedIn,\n\t\tupdate: () => false,\n\t},\n\thooks: {\n\t\tbeforeValidate: [\n\t\t\t...(spam ? [buildSpamGuard(spam)] : []),\n\t\t\tvalidateSubmission({ registry, ruleRegistry, consentRegistry, uploadSlug }),\n\t\t],\n\t\tafterChange: [makeAfterChange({ actionRegistry, events, hasRunner })],\n\t},\n\tfields: [\n\t\t{ name: 'form', type: 'relationship', relationTo: FORMS_SLUG, required: true },\n\t\t{\n\t\t\tname: 'status',\n\t\t\ttype: 'select',\n\t\t\tdefaultValue: 'complete',\n\t\t\toptions: [\n\t\t\t\t{ label: 'Complete', value: 'complete' },\n\t\t\t\t{ label: 'Partial', value: 'partial' },\n\t\t\t],\n\t\t\t// Defense-in-depth at the REST layer: anonymous clients cannot set status via the API.\n\t\t\t// The validateSubmission hook also forces 'complete' server-side, so this covers both paths.\n\t\t\taccess: { create: isLoggedIn, update: isLoggedIn },\n\t\t},\n\t\t{ name: 'locale', type: 'text' },\n\t\t{ name: 'values', type: 'json' },\n\t\t{ name: 'descriptors', type: 'json' },\n\t\t{ name: 'consent', type: 'json' },\n\t\t{ name: 'meta', type: 'json' },\n\t\t{\n\t\t\tname: 'answers',\n\t\t\ttype: 'ui',\n\t\t\tadmin: {\n\t\t\t\tcomponents: { Field: '@10x-media/form-builder/rsc#SubmissionAnswers' },\n\t\t\t},\n\t\t},\n\t],\n})\n"],"mappings":";;;;;;;AAeA,MAAa,wBAAwB;AAgBrC,MAAM,YAAY,SAA+C;CAChE,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAC/C,OAAO;CAER,IAAI,QAAQ,OAAO,SAAS,YAAY,QAAQ,MAAM;EACrD,MAAM,KAAM,KAAyB;EACrC,IAAI,OAAO,OAAO,YAAY,OAAO,OAAO,UAC3C,OAAO;CAET;AAED;;;;;;;;;AAUA,MAAM,mBACJ,SAKD,OAAO,EAAE,KAAK,WAAW,UAAU;CAClC,IAAI,cAAc,YAAa,IAAI,UAAU,QAAQ,IAAI,WAAW,YACnE,OAAO;CAER,MAAM,EAAE,YAAY;CACpB,IAAI;EACH,MAAM,SAAS,SAAS,IAAI,IAAI;EAChC,IAAI,UAAU,MACb,OAAO;EAMR,MAAM,gBAAgB;GACrB,UAAU,MALQ,QACjB,SAAS;IAAE,YAAA;IAAwB,IAAI;IAAQ,OAAO;IAAG,gBAAgB;IAAM;GAAI,CAAC,EACpF,YAAY,IAAI,IAGD,WAAW;GAC3B;GACA,cAAc,IAAI;GAClB,UAAU,KAAK;GACf;GACA;GACA,WAAW,KAAK;EACjB,CAAC;EAED,IAAI;GACH,MAAM,iBAAiB,KAAK,MAAM,EAAE,KAAK;IACxC,MAAM;IACN,QAAQ,OAAO,MAAM;IACrB,cAAc,OAAO,IAAI,EAAE;IAC3B,qBAAI,IAAI,KAAK,GAAE,YAAY;GAC5B,CAAC;EACF,SAAS,OAAO;GACf,QAAQ,QAAQ,MACf,2DACC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAEvD;EACD;CACD,SAAS,OAAO;EACf,QAAQ,QAAQ,MACf,uEAAuE,OAAO,IAAI,EAAE,EAAE,IACrF,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAEvD;CACD;CACA,OAAO;AACR;AAED,MAAa,8BAA8B,EAC1C,UACA,cACA,iBACA,iCAAiB,IAAI,IAAI,GACzB,QACA,YAAY,OACZ,YACA,YACwD;CACxD,MAAM;CACN,QAAQ;EAAE,UAAU;EAAc,QAAQ;CAAc;CACxD,OAAO,EAAE,OAAO,QAAQ;CACxB,QAAQ;EACP,cAAc;EACd,MAAM;EACN,cAAc;CACf;CACA,OAAO;EACN,gBAAgB,CACf,GAAI,OAAO,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,GACrC,mBAAmB;GAAE;GAAU;GAAc;GAAiB;EAAW,CAAC,CAC3E;EACA,aAAa,CAAC,gBAAgB;GAAE;GAAgB;GAAQ;EAAU,CAAC,CAAC;CACrE;CACA,QAAQ;EACP;GAAE,MAAM;GAAQ,MAAM;GAAgB,YAAY;GAAY,UAAU;EAAK;EAC7E;GACC,MAAM;GACN,MAAM;GACN,cAAc;GACd,SAAS,CACR;IAAE,OAAO;IAAY,OAAO;GAAW,GACvC;IAAE,OAAO;IAAW,OAAO;GAAU,CACtC;GAGA,QAAQ;IAAE,QAAQ;IAAY,QAAQ;GAAW;EAClD;EACA;GAAE,MAAM;GAAU,MAAM;EAAO;EAC/B;GAAE,MAAM;GAAU,MAAM;EAAO;EAC/B;GAAE,MAAM;GAAe,MAAM;EAAO;EACpC;GAAE,MAAM;GAAW,MAAM;EAAO;EAChC;GAAE,MAAM;GAAQ,MAAM;EAAO;EAC7B;GACC,MAAM;GACN,MAAM;GACN,OAAO,EACN,YAAY,EAAE,OAAO,gDAAgD,EACtE;EACD;CACD;AACD"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { keys } from "../translations/keys.js";
|
|
2
|
+
import { labelFor, labelForKey } from "../translations/server.js";
|
|
3
|
+
import { isLoggedIn } from "../plugin/access.js";
|
|
4
|
+
import { buildActionBlocks } from "../actions/buildActionBlocks.js";
|
|
5
|
+
import { resolveFormResultsRequest } from "../aggregation/resolveResultsRequest.js";
|
|
6
|
+
import { normalizeCalc } from "../calc/normalizeCalc.js";
|
|
7
|
+
import { buildConditionTypeMap } from "../conditions/conditionType.js";
|
|
8
|
+
import { normalizeFormConditions } from "../conditions/normalizeConditions.js";
|
|
9
|
+
import { buildFieldBlocks } from "../fields/buildFieldBlocks.js";
|
|
10
|
+
import { normalizeFlow } from "../flow/normalizeFlow.js";
|
|
11
|
+
import { DEFAULT_PRESENTATION_NAME, defaultPresentationDescriptors } from "../presentations/defaults.js";
|
|
12
|
+
import { ValidationError } from "payload";
|
|
13
|
+
//#region src/collections/forms.ts
|
|
14
|
+
const FORMS_SLUG = "forms";
|
|
15
|
+
const validateFlow = (raw) => {
|
|
16
|
+
if (raw === null || raw === void 0) return true;
|
|
17
|
+
const r = raw;
|
|
18
|
+
if (!Array.isArray(r.steps)) return true;
|
|
19
|
+
const steps = r.steps;
|
|
20
|
+
if (steps.find((s) => typeof s?.id !== "string" || s.id.length === 0)) return "Flow: every step must have a non-empty ID";
|
|
21
|
+
const ids = steps.map((s) => s.id);
|
|
22
|
+
if (new Set(ids).size !== ids.length) return "Flow: duplicate step IDs found";
|
|
23
|
+
const idSet = new Set(ids);
|
|
24
|
+
for (const step of steps) {
|
|
25
|
+
const id = step.id;
|
|
26
|
+
if (typeof step.next === "string" && step.next.length > 0 && !idSet.has(step.next)) return `Flow: step "${id}" references unknown next step "${step.next}"`;
|
|
27
|
+
if (Array.isArray(step.transitions)) {
|
|
28
|
+
for (const t of step.transitions) if (typeof t?.to === "string" && t.to.length > 0 && !idSet.has(t.to)) return `Flow: step "${id}" has a transition to unknown step "${t.to}"`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
};
|
|
33
|
+
/** How many steps the caller actually submitted, before normalization strips/collapses the flow. */
|
|
34
|
+
const providedFlowStepCount = (raw) => {
|
|
35
|
+
if (raw === null || typeof raw !== "object") return 0;
|
|
36
|
+
const steps = raw.steps;
|
|
37
|
+
return Array.isArray(steps) ? steps.length : 0;
|
|
38
|
+
};
|
|
39
|
+
const buildFormsCollection = ({ registry, ruleRegistry, consentRegistry, presentationRegistry = new Map(Object.entries(defaultPresentationDescriptors)), actionRegistry = /* @__PURE__ */ new Map() }) => {
|
|
40
|
+
const conditionTypes = buildConditionTypeMap(registry);
|
|
41
|
+
const FLOW_BUILDER_REF = "@10x-media/form-builder/client#FlowBuilder";
|
|
42
|
+
const beforeValidate = ({ data, req }) => {
|
|
43
|
+
if (data && typeof data.defaultPresentation === "string" && !presentationRegistry.has(data.defaultPresentation)) data.defaultPresentation = DEFAULT_PRESENTATION_NAME;
|
|
44
|
+
if (data && Array.isArray(data.fields)) {
|
|
45
|
+
const normalized = normalizeFormConditions(data.fields, conditionTypes);
|
|
46
|
+
for (const field of normalized) if ("expression" in field) field.expression = normalizeCalc(field.expression);
|
|
47
|
+
data.fields = normalized;
|
|
48
|
+
const fieldNames = normalized.map((field) => typeof field.name === "string" ? field.name : void 0).filter((name) => name !== void 0);
|
|
49
|
+
const normalizedFlow = normalizeFlow(data.flow, fieldNames);
|
|
50
|
+
if (providedFlowStepCount(data.flow) > 0 && normalizedFlow === void 0) throw new ValidationError({
|
|
51
|
+
collection: FORMS_SLUG,
|
|
52
|
+
errors: [{
|
|
53
|
+
path: "flow",
|
|
54
|
+
message: "A flow needs at least two steps with unique, non-empty IDs. Add another step or remove the flow."
|
|
55
|
+
}]
|
|
56
|
+
}, req.t);
|
|
57
|
+
data.flow = normalizedFlow;
|
|
58
|
+
}
|
|
59
|
+
return data;
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
slug: FORMS_SLUG,
|
|
63
|
+
labels: {
|
|
64
|
+
singular: "Form",
|
|
65
|
+
plural: "Forms"
|
|
66
|
+
},
|
|
67
|
+
admin: {
|
|
68
|
+
group: "Forms",
|
|
69
|
+
useAsTitle: "title"
|
|
70
|
+
},
|
|
71
|
+
access: { read: () => true },
|
|
72
|
+
hooks: { beforeValidate: [beforeValidate] },
|
|
73
|
+
fields: [
|
|
74
|
+
{
|
|
75
|
+
name: "title",
|
|
76
|
+
type: "text",
|
|
77
|
+
required: true,
|
|
78
|
+
label: labelForKey(keys.fieldTitle)
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "fields",
|
|
82
|
+
type: "blocks",
|
|
83
|
+
blocks: buildFieldBlocks(registry, ruleRegistry, consentRegistry)
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "flow",
|
|
87
|
+
type: "json",
|
|
88
|
+
validate: validateFlow,
|
|
89
|
+
admin: { components: { Field: {
|
|
90
|
+
path: FLOW_BUILDER_REF,
|
|
91
|
+
clientProps: { conditionTypes }
|
|
92
|
+
} } },
|
|
93
|
+
typescriptSchema: [() => ({
|
|
94
|
+
type: "object",
|
|
95
|
+
required: ["steps"],
|
|
96
|
+
additionalProperties: false,
|
|
97
|
+
properties: { steps: {
|
|
98
|
+
type: "array",
|
|
99
|
+
items: {
|
|
100
|
+
type: "object",
|
|
101
|
+
required: ["id"],
|
|
102
|
+
additionalProperties: true,
|
|
103
|
+
properties: {
|
|
104
|
+
id: { type: "string" },
|
|
105
|
+
title: { type: "string" },
|
|
106
|
+
fields: {
|
|
107
|
+
type: "array",
|
|
108
|
+
items: { type: "string" }
|
|
109
|
+
},
|
|
110
|
+
next: { type: "string" },
|
|
111
|
+
transitions: {
|
|
112
|
+
type: "array",
|
|
113
|
+
items: {
|
|
114
|
+
type: "object",
|
|
115
|
+
required: ["to"],
|
|
116
|
+
additionalProperties: true,
|
|
117
|
+
properties: {
|
|
118
|
+
to: { type: "string" },
|
|
119
|
+
when: {
|
|
120
|
+
type: "object",
|
|
121
|
+
additionalProperties: true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} }
|
|
129
|
+
})]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "actions",
|
|
133
|
+
type: "blocks",
|
|
134
|
+
blocks: buildActionBlocks(actionRegistry),
|
|
135
|
+
label: labelForKey(keys.configActions),
|
|
136
|
+
access: { read: isLoggedIn }
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "defaultPresentation",
|
|
140
|
+
type: "select",
|
|
141
|
+
defaultValue: DEFAULT_PRESENTATION_NAME,
|
|
142
|
+
options: [...presentationRegistry.values()].map((descriptor) => ({
|
|
143
|
+
label: labelFor(descriptor.label),
|
|
144
|
+
value: descriptor.name
|
|
145
|
+
})),
|
|
146
|
+
label: labelForKey(keys.configDefaultPresentation),
|
|
147
|
+
admin: { position: "sidebar" }
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "showResults",
|
|
151
|
+
type: "checkbox",
|
|
152
|
+
defaultValue: false,
|
|
153
|
+
label: labelForKey(keys.configShowResults),
|
|
154
|
+
admin: { position: "sidebar" }
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "resultsField",
|
|
158
|
+
type: "text",
|
|
159
|
+
label: labelForKey(keys.configResultsField),
|
|
160
|
+
admin: {
|
|
161
|
+
position: "sidebar",
|
|
162
|
+
description: "Field whose aggregate results are public when \"Show results publicly\" is on. Use a choice field, never a free-text or PII field.",
|
|
163
|
+
condition: (data) => Boolean(data?.showResults)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
endpoints: [{
|
|
168
|
+
path: "/:id/results",
|
|
169
|
+
method: "get",
|
|
170
|
+
handler: async (req) => {
|
|
171
|
+
const field = typeof req.query?.field === "string" ? req.query.field : void 0;
|
|
172
|
+
const { status, body } = await resolveFormResultsRequest({
|
|
173
|
+
payload: req.payload,
|
|
174
|
+
formId: req.routeParams?.id,
|
|
175
|
+
field,
|
|
176
|
+
isAuthed: Boolean(req.user),
|
|
177
|
+
req
|
|
178
|
+
});
|
|
179
|
+
return Response.json(body, { status });
|
|
180
|
+
}
|
|
181
|
+
}]
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
//#endregion
|
|
185
|
+
export { FORMS_SLUG, buildFormsCollection };
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=forms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forms.js","names":[],"sources":["../../src/collections/forms.ts"],"sourcesContent":["import {\n\ttype CollectionBeforeValidateHook,\n\ttype CollectionConfig,\n\ttype PayloadRequest,\n\tValidationError,\n} from 'payload'\nimport { buildActionBlocks } from '../actions/buildActionBlocks'\nimport type { ActionRegistry } from '../actions/registry'\nimport { resolveFormResultsRequest } from '../aggregation/resolveResultsRequest'\nimport { normalizeCalc } from '../calc/normalizeCalc'\nimport { buildConditionTypeMap } from '../conditions/conditionType'\nimport { type FieldRow, normalizeFormConditions } from '../conditions/normalizeConditions'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport { buildFieldBlocks } from '../fields/buildFieldBlocks'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { normalizeFlow } from '../flow/normalizeFlow'\nimport { isLoggedIn } from '../plugin/access'\nimport {\n\tDEFAULT_PRESENTATION_NAME,\n\tdefaultPresentationDescriptors,\n} from '../presentations/defaults'\nimport type { PresentationDescriptorRegistry } from '../presentations/registry'\nimport { keys } from '../translations/keys'\nimport { labelFor, labelForKey } from '../translations/server'\nimport type { ValidationRuleRegistry } from '../validation/registry'\n\nexport const FORMS_SLUG = 'forms'\n\nconst validateFlow = (raw: unknown): string | true => {\n\tif (raw === null || raw === undefined) return true\n\tconst r = raw as Record<string, unknown>\n\tif (!Array.isArray(r.steps)) return true\n\tconst steps = r.steps as Array<Record<string, unknown>>\n\tconst emptyIdStep = steps.find((s) => typeof s?.id !== 'string' || s.id.length === 0)\n\tif (emptyIdStep) return 'Flow: every step must have a non-empty ID'\n\tconst ids = steps.map((s) => s.id as string)\n\tif (new Set(ids).size !== ids.length) {\n\t\treturn 'Flow: duplicate step IDs found'\n\t}\n\tconst idSet = new Set(ids)\n\tfor (const step of steps) {\n\t\tconst id = step.id as string\n\t\tif (typeof step.next === 'string' && step.next.length > 0 && !idSet.has(step.next)) {\n\t\t\treturn `Flow: step \"${id}\" references unknown next step \"${step.next}\"`\n\t\t}\n\t\tif (Array.isArray(step.transitions)) {\n\t\t\tfor (const t of step.transitions as Array<Record<string, unknown>>) {\n\t\t\t\tif (typeof t?.to === 'string' && t.to.length > 0 && !idSet.has(t.to)) {\n\t\t\t\t\treturn `Flow: step \"${id}\" has a transition to unknown step \"${t.to}\"`\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\n/** How many steps the caller actually submitted, before normalization strips/collapses the flow. */\nconst providedFlowStepCount = (raw: unknown): number => {\n\tif (raw === null || typeof raw !== 'object') return 0\n\tconst steps = (raw as { steps?: unknown }).steps\n\treturn Array.isArray(steps) ? steps.length : 0\n}\n\ntype BuildFormsCollectionArgs = {\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry?: ConsentSourceRegistry\n\tpresentationRegistry?: PresentationDescriptorRegistry\n\tactionRegistry?: ActionRegistry\n}\n\nexport const buildFormsCollection = ({\n\tregistry,\n\truleRegistry,\n\tconsentRegistry,\n\tpresentationRegistry = new Map(Object.entries(defaultPresentationDescriptors)),\n\tactionRegistry = new Map(),\n}: BuildFormsCollectionArgs): CollectionConfig => {\n\tconst conditionTypes = buildConditionTypeMap(registry)\n\tconst FLOW_BUILDER_REF = '@10x-media/form-builder/client#FlowBuilder'\n\n\tconst beforeValidate: CollectionBeforeValidateHook = ({ data, req }) => {\n\t\tif (\n\t\t\tdata &&\n\t\t\ttypeof data.defaultPresentation === 'string' &&\n\t\t\t!presentationRegistry.has(data.defaultPresentation)\n\t\t) {\n\t\t\tdata.defaultPresentation = DEFAULT_PRESENTATION_NAME\n\t\t}\n\t\tif (data && Array.isArray(data.fields)) {\n\t\t\tconst normalized: FieldRow[] = normalizeFormConditions(\n\t\t\t\tdata.fields as FieldRow[],\n\t\t\t\tconditionTypes\n\t\t\t)\n\t\t\tfor (const field of normalized) {\n\t\t\t\tif ('expression' in field) {\n\t\t\t\t\tfield.expression = normalizeCalc(field.expression)\n\t\t\t\t}\n\t\t\t}\n\t\t\tdata.fields = normalized\n\t\t\tconst fieldNames = normalized\n\t\t\t\t.map((field: FieldRow) => (typeof field.name === 'string' ? field.name : undefined))\n\t\t\t\t.filter((name): name is string => name !== undefined)\n\t\t\tconst normalizedFlow = normalizeFlow(data.flow, fieldNames)\n\t\t\t// A flow the author built but that collapses to fewer than two valid steps would\n\t\t\t// otherwise vanish silently. Surface it instead of discarding their work.\n\t\t\tif (providedFlowStepCount(data.flow) > 0 && normalizedFlow === undefined) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t{\n\t\t\t\t\t\tcollection: FORMS_SLUG,\n\t\t\t\t\t\terrors: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: 'flow',\n\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t'A flow needs at least two steps with unique, non-empty IDs. Add another step or remove the flow.',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\treq.t\n\t\t\t\t)\n\t\t\t}\n\t\t\tdata.flow = normalizedFlow\n\t\t}\n\t\treturn data\n\t}\n\n\treturn {\n\t\tslug: FORMS_SLUG,\n\t\tlabels: { singular: 'Form', plural: 'Forms' },\n\t\tadmin: { group: 'Forms', useAsTitle: 'title' },\n\t\taccess: { read: () => true },\n\t\thooks: {\n\t\t\tbeforeValidate: [beforeValidate],\n\t\t},\n\t\tfields: [\n\t\t\t{ name: 'title', type: 'text', required: true, label: labelForKey(keys.fieldTitle) },\n\t\t\t{\n\t\t\t\tname: 'fields',\n\t\t\t\ttype: 'blocks',\n\t\t\t\tblocks: buildFieldBlocks(registry, ruleRegistry, consentRegistry),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'flow',\n\t\t\t\ttype: 'json',\n\t\t\t\tvalidate: validateFlow,\n\t\t\t\tadmin: {\n\t\t\t\t\tcomponents: {\n\t\t\t\t\t\tField: { path: FLOW_BUILDER_REF, clientProps: { conditionTypes } },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t// Narrows the generated TypeScript type from opaque JSON to FormFlow so callers\n\t\t\t\t// don't need a cast. Keep this in sync with src/flow/types.ts.\n\t\t\t\ttypescriptSchema: [\n\t\t\t\t\t() => ({\n\t\t\t\t\t\ttype: 'object' as const,\n\t\t\t\t\t\trequired: ['steps'],\n\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tsteps: {\n\t\t\t\t\t\t\t\ttype: 'array' as const,\n\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\ttype: 'object' as const,\n\t\t\t\t\t\t\t\t\trequired: ['id'],\n\t\t\t\t\t\t\t\t\tadditionalProperties: true,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tid: { type: 'string' as const },\n\t\t\t\t\t\t\t\t\t\ttitle: { type: 'string' as const },\n\t\t\t\t\t\t\t\t\t\tfields: { type: 'array' as const, items: { type: 'string' as const } },\n\t\t\t\t\t\t\t\t\t\tnext: { type: 'string' as const },\n\t\t\t\t\t\t\t\t\t\ttransitions: {\n\t\t\t\t\t\t\t\t\t\t\ttype: 'array' as const,\n\t\t\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: 'object' as const,\n\t\t\t\t\t\t\t\t\t\t\t\trequired: ['to'],\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: true,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tto: { type: 'string' as const },\n\t\t\t\t\t\t\t\t\t\t\t\t\twhen: { type: 'object' as const, additionalProperties: true },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'actions',\n\t\t\t\ttype: 'blocks',\n\t\t\t\tblocks: buildActionBlocks(actionRegistry),\n\t\t\t\tlabel: labelForKey(keys.configActions),\n\t\t\t\t// Action config can contain secrets (e.g. signedWebhook.secret). The collection\n\t\t\t\t// itself is publicly readable so forms can be rendered by anonymous clients, but\n\t\t\t\t// action config must never be exposed to anonymous callers.\n\t\t\t\taccess: { read: isLoggedIn },\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'defaultPresentation',\n\t\t\t\ttype: 'select',\n\t\t\t\tdefaultValue: DEFAULT_PRESENTATION_NAME,\n\t\t\t\toptions: [...presentationRegistry.values()].map((descriptor) => ({\n\t\t\t\t\tlabel: labelFor(descriptor.label),\n\t\t\t\t\tvalue: descriptor.name,\n\t\t\t\t})),\n\t\t\t\tlabel: labelForKey(keys.configDefaultPresentation),\n\t\t\t\tadmin: { position: 'sidebar' },\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'showResults',\n\t\t\t\ttype: 'checkbox',\n\t\t\t\tdefaultValue: false,\n\t\t\t\tlabel: labelForKey(keys.configShowResults),\n\t\t\t\tadmin: { position: 'sidebar' },\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'resultsField',\n\t\t\t\ttype: 'text',\n\t\t\t\tlabel: labelForKey(keys.configResultsField),\n\t\t\t\tadmin: {\n\t\t\t\t\tposition: 'sidebar',\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'Field whose aggregate results are public when \"Show results publicly\" is on. Use a choice field, never a free-text or PII field.',\n\t\t\t\t\tcondition: (data) => Boolean(data?.showResults),\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\tendpoints: [\n\t\t\t{\n\t\t\t\tpath: '/:id/results',\n\t\t\t\tmethod: 'get',\n\t\t\t\thandler: async (req: PayloadRequest) => {\n\t\t\t\t\tconst field = typeof req.query?.field === 'string' ? req.query.field : undefined\n\t\t\t\t\tconst { status, body } = await resolveFormResultsRequest({\n\t\t\t\t\t\tpayload: req.payload,\n\t\t\t\t\t\tformId: req.routeParams?.id as number | string | undefined,\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tisAuthed: Boolean(req.user),\n\t\t\t\t\t\treq,\n\t\t\t\t\t})\n\t\t\t\t\treturn Response.json(body, { status })\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CollectionConfig } from "payload";
|
|
2
|
+
|
|
3
|
+
//#region src/collections/uploads.d.ts
|
|
4
|
+
declare const FORM_UPLOADS_SLUG = "form-uploads";
|
|
5
|
+
/** Override surface for the built-in upload collection. */
|
|
6
|
+
type UploadsCollectionConfig = {
|
|
7
|
+
slug?: string; /** Merged onto the collection's `upload` config (e.g. `staticDir`, `mimeTypes`). `true` uses Payload defaults. */
|
|
8
|
+
upload?: NonNullable<CollectionConfig['upload']> | true; /** Replace access (defaults: anonymous create, authed read/update/delete). */
|
|
9
|
+
access?: CollectionConfig['access']; /** Append extra fields. */
|
|
10
|
+
fields?: CollectionConfig['fields'];
|
|
11
|
+
};
|
|
12
|
+
type UploadsOption = boolean | UploadsCollectionConfig;
|
|
13
|
+
/**
|
|
14
|
+
* The built-in upload collection backing the `file` field. Anonymous create (public forms upload here),
|
|
15
|
+
* authed read/update/delete (only admins read stored files; submitters cannot enumerate others' uploads).
|
|
16
|
+
* Per-field MIME/size is re-enforced at submit, so the collection stays permissive by default. No
|
|
17
|
+
* `imageSizes`, so it needs no `sharp`. Projects with their own upload collection set `uploads: false` and
|
|
18
|
+
* point the file field's `relationTo` at theirs.
|
|
19
|
+
*/
|
|
20
|
+
declare const buildUploadsCollection: (config?: UploadsCollectionConfig) => CollectionConfig;
|
|
21
|
+
/** Resolve the `uploads` plugin option: `false` disables, `true`/object enables the built-in collection. */
|
|
22
|
+
declare const resolveUploads: (option: UploadsOption | undefined) => {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
slug: string;
|
|
25
|
+
collection?: CollectionConfig;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { FORM_UPLOADS_SLUG, UploadsCollectionConfig, UploadsOption, buildUploadsCollection, resolveUploads };
|
|
29
|
+
//# sourceMappingURL=uploads.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { isLoggedIn } from "../plugin/access.js";
|
|
2
|
+
//#region src/collections/uploads.ts
|
|
3
|
+
const FORM_UPLOADS_SLUG = "form-uploads";
|
|
4
|
+
/**
|
|
5
|
+
* The built-in upload collection backing the `file` field. Anonymous create (public forms upload here),
|
|
6
|
+
* authed read/update/delete (only admins read stored files; submitters cannot enumerate others' uploads).
|
|
7
|
+
* Per-field MIME/size is re-enforced at submit, so the collection stays permissive by default. No
|
|
8
|
+
* `imageSizes`, so it needs no `sharp`. Projects with their own upload collection set `uploads: false` and
|
|
9
|
+
* point the file field's `relationTo` at theirs.
|
|
10
|
+
*/
|
|
11
|
+
const buildUploadsCollection = (config = {}) => ({
|
|
12
|
+
slug: config.slug ?? "form-uploads",
|
|
13
|
+
access: config.access ?? {
|
|
14
|
+
create: () => true,
|
|
15
|
+
read: isLoggedIn,
|
|
16
|
+
update: isLoggedIn,
|
|
17
|
+
delete: isLoggedIn
|
|
18
|
+
},
|
|
19
|
+
admin: { group: "Forms" },
|
|
20
|
+
upload: config.upload && config.upload !== true ? config.upload : true,
|
|
21
|
+
fields: [{
|
|
22
|
+
name: "owner",
|
|
23
|
+
type: "text",
|
|
24
|
+
admin: {
|
|
25
|
+
readOnly: true,
|
|
26
|
+
hidden: true
|
|
27
|
+
}
|
|
28
|
+
}, ...config.fields ?? []]
|
|
29
|
+
});
|
|
30
|
+
/** Resolve the `uploads` plugin option: `false` disables, `true`/object enables the built-in collection. */
|
|
31
|
+
const resolveUploads = (option) => {
|
|
32
|
+
if (option === false) return {
|
|
33
|
+
enabled: false,
|
|
34
|
+
slug: FORM_UPLOADS_SLUG
|
|
35
|
+
};
|
|
36
|
+
const collection = buildUploadsCollection(option && option !== true ? option : {});
|
|
37
|
+
return {
|
|
38
|
+
enabled: true,
|
|
39
|
+
slug: collection.slug,
|
|
40
|
+
collection
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { FORM_UPLOADS_SLUG, buildUploadsCollection, resolveUploads };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=uploads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploads.js","names":[],"sources":["../../src/collections/uploads.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\nimport { isLoggedIn } from '../plugin/access'\n\nexport const FORM_UPLOADS_SLUG = 'form-uploads'\n\n/** Override surface for the built-in upload collection. */\nexport type UploadsCollectionConfig = {\n\tslug?: string\n\t/** Merged onto the collection's `upload` config (e.g. `staticDir`, `mimeTypes`). `true` uses Payload defaults. */\n\tupload?: NonNullable<CollectionConfig['upload']> | true\n\t/** Replace access (defaults: anonymous create, authed read/update/delete). */\n\taccess?: CollectionConfig['access']\n\t/** Append extra fields. */\n\tfields?: CollectionConfig['fields']\n}\n\nexport type UploadsOption = boolean | UploadsCollectionConfig\n\n/**\n * The built-in upload collection backing the `file` field. Anonymous create (public forms upload here),\n * authed read/update/delete (only admins read stored files; submitters cannot enumerate others' uploads).\n * Per-field MIME/size is re-enforced at submit, so the collection stays permissive by default. No\n * `imageSizes`, so it needs no `sharp`. Projects with their own upload collection set `uploads: false` and\n * point the file field's `relationTo` at theirs.\n */\nexport const buildUploadsCollection = (config: UploadsCollectionConfig = {}): CollectionConfig => ({\n\tslug: config.slug ?? FORM_UPLOADS_SLUG,\n\taccess: config.access ?? {\n\t\tcreate: () => true,\n\t\tread: isLoggedIn,\n\t\tupdate: isLoggedIn,\n\t\tdelete: isLoggedIn,\n\t},\n\tadmin: { group: 'Forms' },\n\tupload: config.upload && config.upload !== true ? config.upload : true,\n\tfields: [\n\t\t{ name: 'owner', type: 'text', admin: { readOnly: true, hidden: true } },\n\t\t...(config.fields ?? []),\n\t],\n})\n\n/** Resolve the `uploads` plugin option: `false` disables, `true`/object enables the built-in collection. */\nexport const resolveUploads = (\n\toption: UploadsOption | undefined\n): { enabled: boolean; slug: string; collection?: CollectionConfig } => {\n\tif (option === false) {\n\t\treturn { enabled: false, slug: FORM_UPLOADS_SLUG }\n\t}\n\tconst config = option && option !== true ? option : {}\n\tconst collection = buildUploadsCollection(config)\n\treturn { enabled: true, slug: collection.slug, collection }\n}\n"],"mappings":";;AAGA,MAAa,oBAAoB;;;;;;;;AAsBjC,MAAa,0BAA0B,SAAkC,CAAC,OAAyB;CAClG,MAAM,OAAO,QAAA;CACb,QAAQ,OAAO,UAAU;EACxB,cAAc;EACd,MAAM;EACN,QAAQ;EACR,QAAQ;CACT;CACA,OAAO,EAAE,OAAO,QAAQ;CACxB,QAAQ,OAAO,UAAU,OAAO,WAAW,OAAO,OAAO,SAAS;CAClE,QAAQ,CACP;EAAE,MAAM;EAAS,MAAM;EAAQ,OAAO;GAAE,UAAU;GAAM,QAAQ;EAAK;CAAE,GACvE,GAAI,OAAO,UAAU,CAAC,CACvB;AACD;;AAGA,MAAa,kBACZ,WACuE;CACvE,IAAI,WAAW,OACd,OAAO;EAAE,SAAS;EAAO,MAAM;CAAkB;CAGlD,MAAM,aAAa,uBADJ,UAAU,WAAW,OAAO,SAAS,CAAC,CACL;CAChD,OAAO;EAAE,SAAS;EAAM,MAAM,WAAW;EAAM;CAAW;AAC3D"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defaultConditionType } from "./fieldTypes.js";
|
|
2
|
+
//#region src/conditions/conditionType.ts
|
|
3
|
+
/** The condition input a field type uses in the builder: its declared `conditionType` or the value-kind default. */
|
|
4
|
+
const conditionTypeForDefinition = (definition) => definition.conditionType ?? defaultConditionType(definition.value);
|
|
5
|
+
/** A serializable slug -> condition type map for every registered field type, handed to the client builder. */
|
|
6
|
+
const buildConditionTypeMap = (registry) => {
|
|
7
|
+
const map = {};
|
|
8
|
+
for (const definition of registry.values()) map[definition.type] = conditionTypeForDefinition(definition);
|
|
9
|
+
return map;
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
export { buildConditionTypeMap };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=conditionType.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditionType.js","names":[],"sources":["../../src/conditions/conditionType.ts"],"sourcesContent":["import type { AnyFormFieldDefinition } from '../fields/types'\nimport { type ConditionFieldType, defaultConditionType } from './fieldTypes'\n\n/** The condition input a field type uses in the builder: its declared `conditionType` or the value-kind default. */\nexport const conditionTypeForDefinition = (\n\tdefinition: Pick<AnyFormFieldDefinition, 'value' | 'conditionType'>\n): ConditionFieldType => definition.conditionType ?? defaultConditionType(definition.value)\n\n/** A serializable slug -> condition type map for every registered field type, handed to the client builder. */\nexport const buildConditionTypeMap = (\n\tregistry: Map<string, AnyFormFieldDefinition>\n): Record<string, ConditionFieldType> => {\n\tconst map: Record<string, ConditionFieldType> = {}\n\tfor (const definition of registry.values()) {\n\t\tmap[definition.type] = conditionTypeForDefinition(definition)\n\t}\n\treturn map\n}\n"],"mappings":";;;AAIA,MAAa,8BACZ,eACwB,WAAW,iBAAiB,qBAAqB,WAAW,KAAK;;AAG1F,MAAa,yBACZ,aACwC;CACxC,MAAM,MAA0C,CAAC;CACjD,KAAK,MAAM,cAAc,SAAS,OAAO,GACxC,IAAI,WAAW,QAAQ,2BAA2B,UAAU;CAE7D,OAAO;AACR"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Where } from "payload";
|
|
2
|
+
|
|
3
|
+
//#region src/conditions/evaluate.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Evaluate a serializable `Where`-shaped condition against a flat map of (already coerced) form answers.
|
|
6
|
+
* An absent or empty condition matches (returns true). Operator semantics mirror Payload's query
|
|
7
|
+
* adapters: coerce then compare; `not_equals`/`not_in` are null-inclusive; `exists` treats `''`/absent
|
|
8
|
+
* as not-existing; `like` is case-insensitive and space-splits (every word must be a substring), and
|
|
9
|
+
* `not_like` is its logical negation (at least one word absent); `contains` is a single case-insensitive
|
|
10
|
+
* substring; comma-delimited `in`/`not_in` string values are split and trimmed. Geo (`near`/`within`/
|
|
11
|
+
* `intersects`) and `all` are out of scope and evaluate to false. Isomorphic: no `req`/DB access, so the
|
|
12
|
+
* renderer reuses it client-side.
|
|
13
|
+
*/
|
|
14
|
+
declare const evaluateCondition: (where: Where | null | undefined, answers: Record<string, unknown>) => boolean;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { evaluateCondition };
|
|
17
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { transformWhereQuery } from "payload/shared";
|
|
2
|
+
//#region src/conditions/evaluate.ts
|
|
3
|
+
const isNil = (value) => value === null || value === void 0;
|
|
4
|
+
const toNumber = (value) => {
|
|
5
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
6
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
7
|
+
const next = Number(value);
|
|
8
|
+
return Number.isNaN(next) ? void 0 : next;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const toTime = (value) => {
|
|
12
|
+
if (value instanceof Date) return value.getTime();
|
|
13
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
14
|
+
const time = Date.parse(value);
|
|
15
|
+
return Number.isNaN(time) ? void 0 : time;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/** Strict, then numeric, then string equality, mirroring Payload's coerce-by-type-then-compare. */
|
|
19
|
+
const valuesEqual = (answer, value) => {
|
|
20
|
+
if (answer === value) return true;
|
|
21
|
+
if (isNil(answer) || isNil(value)) return false;
|
|
22
|
+
const a = toNumber(answer);
|
|
23
|
+
const b = toNumber(value);
|
|
24
|
+
if (a !== void 0 && b !== void 0) return a === b;
|
|
25
|
+
return String(answer) === String(value);
|
|
26
|
+
};
|
|
27
|
+
const toList = (value) => {
|
|
28
|
+
if (Array.isArray(value)) return value;
|
|
29
|
+
if (typeof value === "string") return value.split(",").map((entry) => entry.trim());
|
|
30
|
+
return [value];
|
|
31
|
+
};
|
|
32
|
+
const ordered = (answer, value, compare) => {
|
|
33
|
+
const a = toNumber(answer) ?? toTime(answer);
|
|
34
|
+
const b = toNumber(value) ?? toTime(value);
|
|
35
|
+
if (a === void 0 || b === void 0) return false;
|
|
36
|
+
return compare(a, b);
|
|
37
|
+
};
|
|
38
|
+
/** Case-insensitive: every space-separated word in `value` is a substring of `answer` (Payload `like`). */
|
|
39
|
+
const everyWord = (answer, value) => {
|
|
40
|
+
const haystack = String(answer).toLowerCase();
|
|
41
|
+
return String(value).toLowerCase().split(" ").filter((word) => word.length > 0).every((word) => haystack.includes(word));
|
|
42
|
+
};
|
|
43
|
+
const evaluateOperator = (operator, answer, value) => {
|
|
44
|
+
switch (operator) {
|
|
45
|
+
case "equals": return isNil(value) ? isNil(answer) : valuesEqual(answer, value);
|
|
46
|
+
case "not_equals": return isNil(value) ? !isNil(answer) : isNil(answer) || !valuesEqual(answer, value);
|
|
47
|
+
case "in": return toList(value).some((entry) => valuesEqual(answer, entry));
|
|
48
|
+
case "not_in": return isNil(answer) || !toList(value).some((entry) => valuesEqual(answer, entry));
|
|
49
|
+
case "exists": {
|
|
50
|
+
const present = !isNil(answer) && answer !== "";
|
|
51
|
+
return value === true || value === "true" ? present : !present;
|
|
52
|
+
}
|
|
53
|
+
case "greater_than": return ordered(answer, value, (a, b) => a > b);
|
|
54
|
+
case "greater_than_equal": return ordered(answer, value, (a, b) => a >= b);
|
|
55
|
+
case "less_than": return ordered(answer, value, (a, b) => a < b);
|
|
56
|
+
case "less_than_equal": return ordered(answer, value, (a, b) => a <= b);
|
|
57
|
+
case "like": return isNil(answer) ? false : everyWord(answer, value);
|
|
58
|
+
case "not_like": return isNil(answer) ? true : !everyWord(answer, value);
|
|
59
|
+
case "contains": return isNil(answer) ? false : String(answer).toLowerCase().includes(String(value).toLowerCase());
|
|
60
|
+
default: return false;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Evaluate one `Where` node: every top-level key is ANDed. `or` is a disjunction of sub-conditions
|
|
65
|
+
* (an empty `or` matches), `and` is a conjunction, and any other key is a field path whose
|
|
66
|
+
* `{ operator: value }` constraints are ANDed. Recurses, so nested `or`/`and` groups are supported.
|
|
67
|
+
*/
|
|
68
|
+
const evaluateWhere = (where, answers) => Object.entries(where).every(([key, constraint]) => {
|
|
69
|
+
if (key === "or") {
|
|
70
|
+
const groups = Array.isArray(constraint) ? constraint : [];
|
|
71
|
+
return groups.length === 0 || groups.some((group) => evaluateWhere(group, answers));
|
|
72
|
+
}
|
|
73
|
+
if (key === "and") return (Array.isArray(constraint) ? constraint : []).every((group) => evaluateWhere(group, answers));
|
|
74
|
+
if (constraint == null || typeof constraint !== "object" || Array.isArray(constraint)) return false;
|
|
75
|
+
return Object.entries(constraint).every(([operator, value]) => evaluateOperator(operator, answers[key], value));
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* Evaluate a serializable `Where`-shaped condition against a flat map of (already coerced) form answers.
|
|
79
|
+
* An absent or empty condition matches (returns true). Operator semantics mirror Payload's query
|
|
80
|
+
* adapters: coerce then compare; `not_equals`/`not_in` are null-inclusive; `exists` treats `''`/absent
|
|
81
|
+
* as not-existing; `like` is case-insensitive and space-splits (every word must be a substring), and
|
|
82
|
+
* `not_like` is its logical negation (at least one word absent); `contains` is a single case-insensitive
|
|
83
|
+
* substring; comma-delimited `in`/`not_in` string values are split and trimmed. Geo (`near`/`within`/
|
|
84
|
+
* `intersects`) and `all` are out of scope and evaluate to false. Isomorphic: no `req`/DB access, so the
|
|
85
|
+
* renderer reuses it client-side.
|
|
86
|
+
*/
|
|
87
|
+
const evaluateCondition = (where, answers) => {
|
|
88
|
+
if (!where || Object.keys(where).length === 0) return true;
|
|
89
|
+
return evaluateWhere(transformWhereQuery(where), answers);
|
|
90
|
+
};
|
|
91
|
+
//#endregion
|
|
92
|
+
export { evaluateCondition };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=evaluate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.js","names":[],"sources":["../../src/conditions/evaluate.ts"],"sourcesContent":["import type { Operator, Where } from 'payload'\nimport { transformWhereQuery } from 'payload/shared'\n\nconst isNil = (value: unknown): boolean => value === null || value === undefined\n\nconst toNumber = (value: unknown): number | undefined => {\n\tif (typeof value === 'number') {\n\t\treturn Number.isFinite(value) ? value : undefined\n\t}\n\tif (typeof value === 'string' && value.trim() !== '') {\n\t\tconst next = Number(value)\n\t\treturn Number.isNaN(next) ? undefined : next\n\t}\n\treturn undefined\n}\n\nconst toTime = (value: unknown): number | undefined => {\n\tif (value instanceof Date) {\n\t\treturn value.getTime()\n\t}\n\tif (typeof value === 'string' && value.trim() !== '') {\n\t\tconst time = Date.parse(value)\n\t\treturn Number.isNaN(time) ? undefined : time\n\t}\n\treturn undefined\n}\n\n/** Strict, then numeric, then string equality, mirroring Payload's coerce-by-type-then-compare. */\nconst valuesEqual = (answer: unknown, value: unknown): boolean => {\n\tif (answer === value) {\n\t\treturn true\n\t}\n\tif (isNil(answer) || isNil(value)) {\n\t\treturn false\n\t}\n\tconst a = toNumber(answer)\n\tconst b = toNumber(value)\n\tif (a !== undefined && b !== undefined) {\n\t\treturn a === b\n\t}\n\treturn String(answer) === String(value)\n}\n\nconst toList = (value: unknown): unknown[] => {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t}\n\tif (typeof value === 'string') {\n\t\treturn value.split(',').map((entry) => entry.trim())\n\t}\n\treturn [value]\n}\n\nconst ordered = (\n\tanswer: unknown,\n\tvalue: unknown,\n\tcompare: (a: number, b: number) => boolean\n): boolean => {\n\tconst a = toNumber(answer) ?? toTime(answer)\n\tconst b = toNumber(value) ?? toTime(value)\n\tif (a === undefined || b === undefined) {\n\t\treturn false\n\t}\n\treturn compare(a, b)\n}\n\n/** Case-insensitive: every space-separated word in `value` is a substring of `answer` (Payload `like`). */\nconst everyWord = (answer: unknown, value: unknown): boolean => {\n\tconst haystack = String(answer).toLowerCase()\n\treturn String(value)\n\t\t.toLowerCase()\n\t\t.split(' ')\n\t\t.filter((word) => word.length > 0)\n\t\t.every((word) => haystack.includes(word))\n}\n\nconst evaluateOperator = (operator: Operator, answer: unknown, value: unknown): boolean => {\n\tswitch (operator) {\n\t\tcase 'equals':\n\t\t\treturn isNil(value) ? isNil(answer) : valuesEqual(answer, value)\n\t\tcase 'not_equals':\n\t\t\treturn isNil(value) ? !isNil(answer) : isNil(answer) || !valuesEqual(answer, value)\n\t\tcase 'in':\n\t\t\treturn toList(value).some((entry) => valuesEqual(answer, entry))\n\t\tcase 'not_in':\n\t\t\treturn isNil(answer) || !toList(value).some((entry) => valuesEqual(answer, entry))\n\t\tcase 'exists': {\n\t\t\tconst present = !isNil(answer) && answer !== ''\n\t\t\treturn value === true || value === 'true' ? present : !present\n\t\t}\n\t\tcase 'greater_than':\n\t\t\treturn ordered(answer, value, (a, b) => a > b)\n\t\tcase 'greater_than_equal':\n\t\t\treturn ordered(answer, value, (a, b) => a >= b)\n\t\tcase 'less_than':\n\t\t\treturn ordered(answer, value, (a, b) => a < b)\n\t\tcase 'less_than_equal':\n\t\t\treturn ordered(answer, value, (a, b) => a <= b)\n\t\tcase 'like':\n\t\t\treturn isNil(answer) ? false : everyWord(answer, value)\n\t\tcase 'not_like':\n\t\t\treturn isNil(answer) ? true : !everyWord(answer, value)\n\t\tcase 'contains':\n\t\t\treturn isNil(answer)\n\t\t\t\t? false\n\t\t\t\t: String(answer).toLowerCase().includes(String(value).toLowerCase())\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\n/**\n * Evaluate one `Where` node: every top-level key is ANDed. `or` is a disjunction of sub-conditions\n * (an empty `or` matches), `and` is a conjunction, and any other key is a field path whose\n * `{ operator: value }` constraints are ANDed. Recurses, so nested `or`/`and` groups are supported.\n */\nconst evaluateWhere = (where: Where, answers: Record<string, unknown>): boolean =>\n\tObject.entries(where).every(([key, constraint]) => {\n\t\tif (key === 'or') {\n\t\t\tconst groups = (Array.isArray(constraint) ? constraint : []) as Where[]\n\t\t\treturn groups.length === 0 || groups.some((group) => evaluateWhere(group, answers))\n\t\t}\n\t\tif (key === 'and') {\n\t\t\tconst groups = (Array.isArray(constraint) ? constraint : []) as Where[]\n\t\t\treturn groups.every((group) => evaluateWhere(group, answers))\n\t\t}\n\t\tif (constraint == null || typeof constraint !== 'object' || Array.isArray(constraint)) {\n\t\t\treturn false\n\t\t}\n\t\treturn Object.entries(constraint as Record<string, unknown>).every(([operator, value]) =>\n\t\t\tevaluateOperator(operator as Operator, answers[key], value)\n\t\t)\n\t})\n\n/**\n * Evaluate a serializable `Where`-shaped condition against a flat map of (already coerced) form answers.\n * An absent or empty condition matches (returns true). Operator semantics mirror Payload's query\n * adapters: coerce then compare; `not_equals`/`not_in` are null-inclusive; `exists` treats `''`/absent\n * as not-existing; `like` is case-insensitive and space-splits (every word must be a substring), and\n * `not_like` is its logical negation (at least one word absent); `contains` is a single case-insensitive\n * substring; comma-delimited `in`/`not_in` string values are split and trimmed. Geo (`near`/`within`/\n * `intersects`) and `all` are out of scope and evaluate to false. Isomorphic: no `req`/DB access, so the\n * renderer reuses it client-side.\n */\nexport const evaluateCondition = (\n\twhere: Where | null | undefined,\n\tanswers: Record<string, unknown>\n): boolean => {\n\tif (!where || Object.keys(where).length === 0) {\n\t\treturn true\n\t}\n\treturn evaluateWhere(transformWhereQuery(where), answers)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/conditions/fieldTypes.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The condition input families the builder supports. A deliberate subset of Payload's field-condition
|
|
4
|
+
* types, restricted to the operators `evaluateCondition` implements (no geo/relationship in v1).
|
|
5
|
+
*/
|
|
6
|
+
type ConditionFieldType = 'text' | 'number' | 'select' | 'checkbox' | 'date';
|
|
7
|
+
//#endregion
|
|
8
|
+
export { ConditionFieldType };
|
|
9
|
+
//# sourceMappingURL=fieldTypes.d.ts.map
|
|
@@ -83,6 +83,6 @@ const operatorValueShape = (operator) => {
|
|
|
83
83
|
return "scalar";
|
|
84
84
|
};
|
|
85
85
|
//#endregion
|
|
86
|
-
export {
|
|
86
|
+
export { conditionOperators, defaultConditionType, firstOperatorFor, operatorLabelKey, operatorValueShape };
|
|
87
87
|
|
|
88
|
-
//# sourceMappingURL=fieldTypes
|
|
88
|
+
//# sourceMappingURL=fieldTypes.js.map
|