@nextlyhq/plugin-form-builder 0.0.2-alpha.7 → 0.0.2-alpha.71
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/README.md +29 -17
- package/dist/admin/index.d.ts +251 -454
- package/dist/admin/index.js +4782 -3985
- package/dist/admin/index.js.map +1 -1
- package/dist/field-references-BshlgMcn.js +1048 -0
- package/dist/field-references-BshlgMcn.js.map +1 -0
- package/dist/index.d.ts +341 -615
- package/dist/index.js +3893 -2406
- package/dist/index.js.map +1 -1
- package/dist/types-BHReIA0j.d.ts +986 -0
- package/package.json +40 -39
- package/dist/CheckboxFieldEditor-K4T6QX52.js +0 -121
- package/dist/CheckboxFieldEditor-K4T6QX52.js.map +0 -1
- package/dist/DateFieldEditor-JFJFAVOH.js +0 -187
- package/dist/DateFieldEditor-JFJFAVOH.js.map +0 -1
- package/dist/EmailFieldEditor-MMOXXL7P.js +0 -138
- package/dist/EmailFieldEditor-MMOXXL7P.js.map +0 -1
- package/dist/NumberFieldEditor-UISAYR6Z.js +0 -175
- package/dist/NumberFieldEditor-UISAYR6Z.js.map +0 -1
- package/dist/RadioFieldEditor-6UQFH4WT.js +0 -165
- package/dist/RadioFieldEditor-6UQFH4WT.js.map +0 -1
- package/dist/SelectFieldEditor-PSO4KHV4.js +0 -213
- package/dist/SelectFieldEditor-PSO4KHV4.js.map +0 -1
- package/dist/TextFieldEditor-5EU64EA5.js +0 -187
- package/dist/TextFieldEditor-5EU64EA5.js.map +0 -1
- package/dist/TextareaFieldEditor-773G3LNM.js +0 -190
- package/dist/TextareaFieldEditor-773G3LNM.js.map +0 -1
- package/dist/admin/index.css +0 -934
- package/dist/admin/index.css.map +0 -1
- package/dist/chunk-ABNZFTQK.js +0 -586
- package/dist/chunk-ABNZFTQK.js.map +0 -1
- package/dist/chunk-XTCEPJ7B.js +0 -256
- package/dist/chunk-XTCEPJ7B.js.map +0 -1
- package/dist/components/index.d.ts +0 -3
- package/dist/components/index.js +0 -9
- package/dist/components/index.js.map +0 -1
- package/dist/index-C0kBvYjm.d.ts +0 -1012
- package/dist/styles/form-builder.css +0 -1150
- package/dist/styles/submissions-filter.css +0 -75
|
@@ -0,0 +1,986 @@
|
|
|
1
|
+
import { CollectionConfig, FieldDefinition, RequestContext } from "nextly";
|
|
2
|
+
import { ComponentType } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Reading the document a form's redirect setting points at.
|
|
5
|
+
*
|
|
6
|
+
* Separate from `redirect-target` because this half has no dependencies and
|
|
7
|
+
* the other half imports `nextly/config`: the admin picker needs to read a
|
|
8
|
+
* stored reference to show what is selected, and pulling the config surface
|
|
9
|
+
* into the browser bundle to do it would be a real cost for no gain.
|
|
10
|
+
*/
|
|
11
|
+
/** A document being turned into a URL. `id` is always present; the rest is the row. */
|
|
12
|
+
interface RedirectTargetDocument {
|
|
13
|
+
id: string;
|
|
14
|
+
[field: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* How documents in one collection become URLs.
|
|
18
|
+
*
|
|
19
|
+
* A string is a path with `{field}` placeholders filled from the document.
|
|
20
|
+
* A function receives the document and returns the path itself.
|
|
21
|
+
*/
|
|
22
|
+
type RedirectUrlPattern = string | ((document: RedirectTargetDocument) => string | undefined);
|
|
23
|
+
/** The collection-to-pattern map, after the array shorthand is normalised. */
|
|
24
|
+
type RedirectRelationships = Record<string, RedirectUrlPattern>;
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for customizing field block behavior.
|
|
27
|
+
*/
|
|
28
|
+
interface FieldBlockConfig {
|
|
29
|
+
/** Default label for this field type */
|
|
30
|
+
label?: string;
|
|
31
|
+
/** Default validation settings */
|
|
32
|
+
validation?: Record<string, unknown>;
|
|
33
|
+
/** Custom component to use for this field type */
|
|
34
|
+
component?: ComponentType<unknown>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Email configuration for notifications.
|
|
38
|
+
*/
|
|
39
|
+
interface EmailConfig {
|
|
40
|
+
to: string | string[];
|
|
41
|
+
from?: string;
|
|
42
|
+
subject: string;
|
|
43
|
+
html?: string;
|
|
44
|
+
text?: string;
|
|
45
|
+
replyTo?: string;
|
|
46
|
+
cc?: string | string[];
|
|
47
|
+
bcc?: string | string[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @experimental A resolved outgoing form notification (template-based) as built
|
|
51
|
+
* by the submission handler, before it is sent. This is the value transformed by
|
|
52
|
+
* the `form-builder.beforeEmail` D63 filter seam.
|
|
53
|
+
*/
|
|
54
|
+
interface FormEmailNotification {
|
|
55
|
+
/** Resolved recipient address. */
|
|
56
|
+
to: string;
|
|
57
|
+
/** Email template slug to render. */
|
|
58
|
+
templateSlug: string;
|
|
59
|
+
/** Template variables (submitted data + form metadata). */
|
|
60
|
+
variables: Record<string, unknown>;
|
|
61
|
+
/** Email provider id; undefined = system default. */
|
|
62
|
+
providerId?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Resolved sender address (rule's senderEmail, else the plugin's
|
|
65
|
+
* defaultFrom); undefined = template/provider default.
|
|
66
|
+
*/
|
|
67
|
+
from?: string;
|
|
68
|
+
/** Resolved Reply-To address, if the rule configured one. */
|
|
69
|
+
replyTo?: string;
|
|
70
|
+
/** CC addresses, if any. */
|
|
71
|
+
cc?: string[];
|
|
72
|
+
/** BCC addresses, if any. */
|
|
73
|
+
bcc?: string[];
|
|
74
|
+
/** Source notification id; undefined for filter-injected emails. */
|
|
75
|
+
notificationId?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A single email notification rule stored on a form.
|
|
79
|
+
* Matches the shape saved by the form builder admin UI. This is the ONE
|
|
80
|
+
* notification rule type — the admin context re-exports it rather than
|
|
81
|
+
* keeping a parallel copy.
|
|
82
|
+
*/
|
|
83
|
+
interface FormNotification {
|
|
84
|
+
/** Unique ID for this notification */
|
|
85
|
+
id: string;
|
|
86
|
+
/** Display name for this notification rule */
|
|
87
|
+
name: string;
|
|
88
|
+
/** Whether this notification is active */
|
|
89
|
+
enabled: boolean;
|
|
90
|
+
/** ID of the email provider to use; undefined = system default */
|
|
91
|
+
providerId?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Sender email override. Resolution order at send time:
|
|
94
|
+
* this value, else the plugin's `notifications.defaultFrom` option, else
|
|
95
|
+
* the template/provider default.
|
|
96
|
+
*/
|
|
97
|
+
senderEmail?: string;
|
|
98
|
+
/** How the recipient address is determined */
|
|
99
|
+
recipientType: "static" | "field";
|
|
100
|
+
/** Recipient: static email address or a {{fieldName}} reference */
|
|
101
|
+
to: string;
|
|
102
|
+
/** CC email addresses */
|
|
103
|
+
cc: string[];
|
|
104
|
+
/** BCC email addresses */
|
|
105
|
+
bcc: string[];
|
|
106
|
+
/**
|
|
107
|
+
* Reply-To address: a `{{fieldName}}` reference to one of the form's
|
|
108
|
+
* email fields (resolved against the submission) or a literal address.
|
|
109
|
+
*/
|
|
110
|
+
replyTo?: string;
|
|
111
|
+
/**
|
|
112
|
+
* Optional send condition evaluated against the submitted data. When set
|
|
113
|
+
* and unmet, the rule is skipped for that submission. A single condition
|
|
114
|
+
* covers the common case; the JSON storage leaves room to grow into a
|
|
115
|
+
* multi-condition tree without a migration.
|
|
116
|
+
*/
|
|
117
|
+
condition?: ConditionalLogicCondition;
|
|
118
|
+
/** Slug of the email template to use */
|
|
119
|
+
templateSlug?: string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* @deprecated Use {@link FormNotification}. Alias kept so existing imports
|
|
123
|
+
* keep compiling during the alpha; will be removed before a stable release.
|
|
124
|
+
*/
|
|
125
|
+
type FormNotificationItem = FormNotification;
|
|
126
|
+
/**
|
|
127
|
+
* Form document structure (stored in database).
|
|
128
|
+
*/
|
|
129
|
+
interface FormDocument {
|
|
130
|
+
id: string;
|
|
131
|
+
slug: string;
|
|
132
|
+
name: string;
|
|
133
|
+
fields: AnyFormField[];
|
|
134
|
+
settings: FormSettings;
|
|
135
|
+
notifications: FormNotification[];
|
|
136
|
+
status: "draft" | "published" | "closed";
|
|
137
|
+
/**
|
|
138
|
+
* What a closed form tells a visitor who reaches it.
|
|
139
|
+
*
|
|
140
|
+
* Optional because the field is only written once an author has a reason to
|
|
141
|
+
* give, and a row created before it existed has none. A closed form with
|
|
142
|
+
* nothing here falls back to the generic refusal, which is also what a draft
|
|
143
|
+
* gets — a draft has never been public, so there is no author intent to
|
|
144
|
+
* relay.
|
|
145
|
+
*/
|
|
146
|
+
closedMessage?: string;
|
|
147
|
+
createdAt: Date;
|
|
148
|
+
updatedAt: Date;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Submission document structure (stored in database).
|
|
152
|
+
*/
|
|
153
|
+
interface SubmissionDocument {
|
|
154
|
+
id: string;
|
|
155
|
+
form: string | FormDocument;
|
|
156
|
+
data: Record<string, unknown>;
|
|
157
|
+
/**
|
|
158
|
+
* Where the submission sits in review.
|
|
159
|
+
*
|
|
160
|
+
* `spam` is one of them, not a separate flag: the stored field offers it, the
|
|
161
|
+
* admin has a Spam tab and filters the other views with `not_equals: "spam"`,
|
|
162
|
+
* the notification hook skips it, and marking something "Not spam" moves it
|
|
163
|
+
* back to `new`. `spamReason` records WHICH check flagged it.
|
|
164
|
+
*/
|
|
165
|
+
status: "new" | "read" | "archived" | "spam";
|
|
166
|
+
ipAddress?: string;
|
|
167
|
+
userAgent?: string;
|
|
168
|
+
/** Which spam check flagged this, when one did. */
|
|
169
|
+
spamReason?: string | null;
|
|
170
|
+
submittedAt: Date;
|
|
171
|
+
createdAt: Date;
|
|
172
|
+
updatedAt: Date;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Configuration options for the form builder plugin.
|
|
176
|
+
*/
|
|
177
|
+
/**
|
|
178
|
+
* Which field types the builder offers, and how each one is customised.
|
|
179
|
+
*
|
|
180
|
+
* Declared ONCE and viewed twice: the options surface takes
|
|
181
|
+
* `Partial<FormFieldToggles>` and the resolved config takes it whole, because
|
|
182
|
+
* "resolved" means exactly "every toggle now has a value". Written out twice
|
|
183
|
+
* the two lists agree until someone adds a field type to one of them.
|
|
184
|
+
*/
|
|
185
|
+
interface FormFieldToggles {
|
|
186
|
+
text: boolean | Partial<FieldBlockConfig>;
|
|
187
|
+
email: boolean | Partial<FieldBlockConfig>;
|
|
188
|
+
number: boolean | Partial<FieldBlockConfig>;
|
|
189
|
+
phone: boolean | Partial<FieldBlockConfig>;
|
|
190
|
+
url: boolean | Partial<FieldBlockConfig>;
|
|
191
|
+
textarea: boolean | Partial<FieldBlockConfig>;
|
|
192
|
+
select: boolean | Partial<FieldBlockConfig>;
|
|
193
|
+
checkbox: boolean | Partial<FieldBlockConfig>;
|
|
194
|
+
radio: boolean | Partial<FieldBlockConfig>;
|
|
195
|
+
file: boolean | Partial<FieldBlockConfig>;
|
|
196
|
+
date: boolean | Partial<FieldBlockConfig>;
|
|
197
|
+
time: boolean | Partial<FieldBlockConfig>;
|
|
198
|
+
hidden: boolean | Partial<FieldBlockConfig>;
|
|
199
|
+
}
|
|
200
|
+
/** What a host may override on a collection this plugin contributes. */
|
|
201
|
+
type CollectionOverrides = Partial<CollectionConfig> & {
|
|
202
|
+
slug?: string;
|
|
203
|
+
labels?: {
|
|
204
|
+
singular?: string;
|
|
205
|
+
plural?: string;
|
|
206
|
+
};
|
|
207
|
+
/** Fields can be an array or a function receiving defaultFields */
|
|
208
|
+
fields?: FieldDefinition[] | ((args: {
|
|
209
|
+
defaultFields: FieldDefinition[];
|
|
210
|
+
}) => FieldDefinition[]);
|
|
211
|
+
};
|
|
212
|
+
/** The same, once the plugin's defaults have filled in what a host omitted. */
|
|
213
|
+
type ResolvedCollectionOverrides = {
|
|
214
|
+
slug: string;
|
|
215
|
+
labels: {
|
|
216
|
+
singular: string;
|
|
217
|
+
plural: string;
|
|
218
|
+
};
|
|
219
|
+
} & Partial<CollectionConfig>;
|
|
220
|
+
interface FormBuilderPluginOptions {
|
|
221
|
+
/**
|
|
222
|
+
* Override the default Forms collection configuration.
|
|
223
|
+
*
|
|
224
|
+
* Allows customizing the slug, labels, fields, access control, and other
|
|
225
|
+
* collection options.
|
|
226
|
+
*
|
|
227
|
+
* The `fields` option can be:
|
|
228
|
+
* - An array of fields (merged with defaults)
|
|
229
|
+
* - A function receiving `{ defaultFields }` for full control
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* // Option 1: Array (fields merged with defaults)
|
|
234
|
+
* formOverrides: {
|
|
235
|
+
* slug: 'my-forms',
|
|
236
|
+
* labels: { singular: 'My Form', plural: 'My Forms' },
|
|
237
|
+
* access: {
|
|
238
|
+
* read: () => true,
|
|
239
|
+
* // Narrow the permission, never replace it: the role screen still
|
|
240
|
+
* // decides who holds `create-my-forms`.
|
|
241
|
+
* create: ({ permissions }) =>
|
|
242
|
+
* permissions.includes('create-my-forms') && process.env.CONTENT_FREEZE !== 'on',
|
|
243
|
+
* },
|
|
244
|
+
* fields: [
|
|
245
|
+
* text({ name: 'internalNotes', label: 'Internal Notes' }),
|
|
246
|
+
* ],
|
|
247
|
+
* }
|
|
248
|
+
*
|
|
249
|
+
* // Option 2: Function (full control over fields)
|
|
250
|
+
* formOverrides: {
|
|
251
|
+
* slug: 'contact-forms',
|
|
252
|
+
* access: {
|
|
253
|
+
* read: ({ permissions }) => permissions.includes('read-contact-forms'),
|
|
254
|
+
* update: () => false,
|
|
255
|
+
* },
|
|
256
|
+
* fields: ({ defaultFields }) => {
|
|
257
|
+
* return [
|
|
258
|
+
* ...defaultFields,
|
|
259
|
+
* { name: 'custom', type: 'text' },
|
|
260
|
+
* ];
|
|
261
|
+
* },
|
|
262
|
+
* }
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
formOverrides?: CollectionOverrides;
|
|
266
|
+
/**
|
|
267
|
+
* Override the default Form Submissions collection configuration.
|
|
268
|
+
*
|
|
269
|
+
* Allows customizing the slug, labels, fields, access control, and other
|
|
270
|
+
* collection options.
|
|
271
|
+
*
|
|
272
|
+
* The `fields` option can be:
|
|
273
|
+
* - An array of fields (merged with defaults)
|
|
274
|
+
* - A function receiving `{ defaultFields }` for full control
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* // Option 1: Array (fields merged with defaults)
|
|
279
|
+
* formSubmissionOverrides: {
|
|
280
|
+
* slug: 'my-submissions',
|
|
281
|
+
* labels: { singular: 'Response', plural: 'Responses' },
|
|
282
|
+
* access: {
|
|
283
|
+
* read: ({ permissions }) => permissions.includes('read-my-submissions'),
|
|
284
|
+
* },
|
|
285
|
+
* fields: [
|
|
286
|
+
* select({
|
|
287
|
+
* name: 'priority',
|
|
288
|
+
* label: 'Priority',
|
|
289
|
+
* options: [
|
|
290
|
+
* { label: 'Low', value: 'low' },
|
|
291
|
+
* { label: 'High', value: 'high' },
|
|
292
|
+
* ],
|
|
293
|
+
* }),
|
|
294
|
+
* ],
|
|
295
|
+
* }
|
|
296
|
+
*
|
|
297
|
+
* // Option 2: Function (full control over fields)
|
|
298
|
+
* formSubmissionOverrides: {
|
|
299
|
+
* fields: ({ defaultFields }) => {
|
|
300
|
+
* // Add field after 'form' field
|
|
301
|
+
* const formIndex = defaultFields.findIndex(f => f.name === 'form');
|
|
302
|
+
* return [
|
|
303
|
+
* ...defaultFields.slice(0, formIndex + 1),
|
|
304
|
+
* { name: 'source', type: 'text' },
|
|
305
|
+
* ...defaultFields.slice(formIndex + 1),
|
|
306
|
+
* ];
|
|
307
|
+
* },
|
|
308
|
+
* }
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
formSubmissionOverrides?: CollectionOverrides;
|
|
312
|
+
/**
|
|
313
|
+
* Configure which field types are available in the form builder.
|
|
314
|
+
*
|
|
315
|
+
* Set to `false` to disable a field type, or provide partial configuration
|
|
316
|
+
* to customize default behavior.
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```typescript
|
|
320
|
+
* fields: {
|
|
321
|
+
* // Disable payment field
|
|
322
|
+
* payment: false,
|
|
323
|
+
*
|
|
324
|
+
* // Customize text field defaults
|
|
325
|
+
* text: {
|
|
326
|
+
* maxLength: 500,
|
|
327
|
+
* },
|
|
328
|
+
*
|
|
329
|
+
* // Disable file uploads
|
|
330
|
+
* file: false,
|
|
331
|
+
* }
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
fields?: Partial<FormFieldToggles>;
|
|
335
|
+
/**
|
|
336
|
+
* Collections whose documents a form may redirect to after submission, and
|
|
337
|
+
* how each one's documents become URLs.
|
|
338
|
+
*
|
|
339
|
+
* The mapping is configuration because it cannot be derived: a site's URL
|
|
340
|
+
* structure belongs to the site. `{field}` placeholders are filled from the
|
|
341
|
+
* document; a function covers a path this package cannot express.
|
|
342
|
+
*
|
|
343
|
+
* The array shorthand names the collections and takes `/{slug}` for all of
|
|
344
|
+
* them, which is the routing the page builder ships.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```typescript
|
|
348
|
+
* redirectRelationships: ['pages']
|
|
349
|
+
*
|
|
350
|
+
* redirectRelationships: {
|
|
351
|
+
* pages: '/{slug}',
|
|
352
|
+
* posts: '/blog/{slug}',
|
|
353
|
+
* docs: page => `/docs/${String(page.section)}/${String(page.slug)}`,
|
|
354
|
+
* }
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
redirectRelationships?: string[] | RedirectRelationships;
|
|
358
|
+
/**
|
|
359
|
+
* Hook called before sending email notifications.
|
|
360
|
+
*
|
|
361
|
+
* Runs as the `form-builder.beforeEmail` D63 filter: the resolved outgoing
|
|
362
|
+
* notifications (template + recipient + variables) are threaded through this
|
|
363
|
+
* callback before they are sent. Allows modifying, adding, or filtering the
|
|
364
|
+
* outgoing notifications. Return the modified array.
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* ```typescript
|
|
368
|
+
* beforeEmail: async ({ emails, form, submission }) => {
|
|
369
|
+
* // Add BCC to all outgoing notifications
|
|
370
|
+
* return emails.map((e) => ({ ...e, bcc: ['archive@example.com'] }));
|
|
371
|
+
* }
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
beforeEmail?: (args: {
|
|
375
|
+
emails: FormEmailNotification[];
|
|
376
|
+
form: FormDocument;
|
|
377
|
+
submission: SubmissionDocument;
|
|
378
|
+
}) => Promise<FormEmailNotification[]> | FormEmailNotification[];
|
|
379
|
+
/**
|
|
380
|
+
* Email notification settings.
|
|
381
|
+
*/
|
|
382
|
+
notifications?: {
|
|
383
|
+
/**
|
|
384
|
+
* Default "from" email address.
|
|
385
|
+
*/
|
|
386
|
+
defaultFrom?: string;
|
|
387
|
+
/**
|
|
388
|
+
* Default "to" email address for all form submissions.
|
|
389
|
+
*
|
|
390
|
+
* If specified, this email will receive all form submissions
|
|
391
|
+
* regardless of form-specific notification settings.
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* notifications: {
|
|
395
|
+
* defaultToEmail: 'submissions@example.com',
|
|
396
|
+
* }
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
defaultToEmail?: string;
|
|
400
|
+
/**
|
|
401
|
+
* Enable/disable email notifications globally.
|
|
402
|
+
* @default true
|
|
403
|
+
*/
|
|
404
|
+
enabled?: boolean;
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* Spam protection configuration.
|
|
408
|
+
*/
|
|
409
|
+
spamProtection?: {
|
|
410
|
+
/**
|
|
411
|
+
* Enable honeypot field for spam detection.
|
|
412
|
+
* @default true
|
|
413
|
+
*/
|
|
414
|
+
honeypot?: boolean;
|
|
415
|
+
/**
|
|
416
|
+
* Enable reCAPTCHA v3.
|
|
417
|
+
* @default false
|
|
418
|
+
*/
|
|
419
|
+
recaptcha?: {
|
|
420
|
+
enabled: boolean;
|
|
421
|
+
siteKey?: string;
|
|
422
|
+
secretKey?: string;
|
|
423
|
+
scoreThreshold?: number;
|
|
424
|
+
};
|
|
425
|
+
/**
|
|
426
|
+
* Rate limiting configuration.
|
|
427
|
+
*/
|
|
428
|
+
rateLimit?: {
|
|
429
|
+
/**
|
|
430
|
+
* Maximum submissions per window.
|
|
431
|
+
* @default 10
|
|
432
|
+
*/
|
|
433
|
+
maxSubmissions?: number;
|
|
434
|
+
/**
|
|
435
|
+
* Time window in milliseconds.
|
|
436
|
+
* @default 60000 (1 minute)
|
|
437
|
+
*/
|
|
438
|
+
windowMs?: number;
|
|
439
|
+
};
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* File upload settings.
|
|
443
|
+
*/
|
|
444
|
+
uploads?: {
|
|
445
|
+
/**
|
|
446
|
+
* Maximum file size in bytes.
|
|
447
|
+
* @default 10485760 (10MB)
|
|
448
|
+
*/
|
|
449
|
+
maxFileSize?: number;
|
|
450
|
+
/**
|
|
451
|
+
* Allowed file types (MIME types).
|
|
452
|
+
* @default ['image/*', 'application/pdf', 'text/*']
|
|
453
|
+
*/
|
|
454
|
+
allowedMimeTypes?: string[];
|
|
455
|
+
/**
|
|
456
|
+
* Collection to store uploaded files.
|
|
457
|
+
* @default 'media'
|
|
458
|
+
*/
|
|
459
|
+
uploadCollection?: string;
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* Enable/disable features.
|
|
463
|
+
*/
|
|
464
|
+
features?: {
|
|
465
|
+
/**
|
|
466
|
+
* Enable visual form builder UI.
|
|
467
|
+
* @default true
|
|
468
|
+
*/
|
|
469
|
+
builder?: boolean;
|
|
470
|
+
/**
|
|
471
|
+
* Enable conditional logic.
|
|
472
|
+
* @default true
|
|
473
|
+
*/
|
|
474
|
+
conditionalLogic?: boolean;
|
|
475
|
+
/**
|
|
476
|
+
* Enable file uploads.
|
|
477
|
+
* @default true
|
|
478
|
+
*/
|
|
479
|
+
fileUploads?: boolean;
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Form configuration interface.
|
|
484
|
+
* Similar to CollectionConfig but tailored for forms.
|
|
485
|
+
*/
|
|
486
|
+
interface FormConfig {
|
|
487
|
+
/**
|
|
488
|
+
* Unique identifier for the form.
|
|
489
|
+
*/
|
|
490
|
+
slug: string;
|
|
491
|
+
/**
|
|
492
|
+
* Display labels for the form.
|
|
493
|
+
*/
|
|
494
|
+
labels?: {
|
|
495
|
+
singular?: string;
|
|
496
|
+
plural?: string;
|
|
497
|
+
};
|
|
498
|
+
/**
|
|
499
|
+
* Form fields definition.
|
|
500
|
+
*/
|
|
501
|
+
fields: AnyFormField[];
|
|
502
|
+
/**
|
|
503
|
+
* Form settings and behavior.
|
|
504
|
+
*/
|
|
505
|
+
settings?: FormSettings;
|
|
506
|
+
/**
|
|
507
|
+
* Email notification configuration.
|
|
508
|
+
*/
|
|
509
|
+
notifications?: FormNotifications;
|
|
510
|
+
/**
|
|
511
|
+
* Access control for form submission and viewing.
|
|
512
|
+
*/
|
|
513
|
+
access?: FormAccess;
|
|
514
|
+
/**
|
|
515
|
+
* Lifecycle hooks for form submissions.
|
|
516
|
+
*/
|
|
517
|
+
hooks?: FormHooks;
|
|
518
|
+
/**
|
|
519
|
+
* Custom metadata.
|
|
520
|
+
*/
|
|
521
|
+
custom?: Record<string, unknown>;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Form field type (discriminated union).
|
|
525
|
+
* Uses the `type` property as the discriminant.
|
|
526
|
+
*/
|
|
527
|
+
type FormField = TextFormField | EmailFormField | NumberFormField | PhoneFormField | UrlFormField | TextareaFormField | SelectFormField | CheckboxFormField | RadioFormField | FileFormField | DateFormField | TimeFormField | HiddenFormField;
|
|
528
|
+
/**
|
|
529
|
+
* A form field of a built-in type OR a plugin-contributed type. Stored form
|
|
530
|
+
* data and the builder work with this; `isKnownFormField` narrows it back to
|
|
531
|
+
* the closed `FormField` union so `switch (field.type)` keeps discriminating
|
|
532
|
+
* the built-ins (a `CustomFormField` in the union itself would collapse that).
|
|
533
|
+
*/
|
|
534
|
+
type AnyFormField = FormField | CustomFormField;
|
|
535
|
+
/**
|
|
536
|
+
* All available built-in form field type identifiers.
|
|
537
|
+
*/
|
|
538
|
+
type FormFieldType = "text" | "email" | "number" | "phone" | "url" | "textarea" | "select" | "checkbox" | "radio" | "file" | "date" | "time" | "hidden";
|
|
539
|
+
/**
|
|
540
|
+
* A form field's type on the wire: a built-in or a plugin-contributed type id
|
|
541
|
+
* that opted into the forms surface. The `(string & {})` arm admits the plugin
|
|
542
|
+
* id while keeping built-in autocomplete. Built-in `case` narrowing still works
|
|
543
|
+
* because every field-type switch keeps a `default` for the custom arm.
|
|
544
|
+
*/
|
|
545
|
+
type FormFieldTypeId = FormFieldType | (string & {});
|
|
546
|
+
/** Every built-in form field type, as a value list (the single source shared
|
|
547
|
+
* by the field-type guard and the config validator). */
|
|
548
|
+
declare const BUILT_IN_FORM_FIELD_TYPES: readonly FormFieldType[];
|
|
549
|
+
/**
|
|
550
|
+
* Narrow an `AnyFormField` to the closed built-in `FormField` union. Returns
|
|
551
|
+
* `false` for a plugin-contributed field, so callers handle it generically
|
|
552
|
+
* before switching on the built-in discriminant.
|
|
553
|
+
*/
|
|
554
|
+
declare function isKnownFormField(field: AnyFormField): field is FormField;
|
|
555
|
+
/**
|
|
556
|
+
* Base form field properties.
|
|
557
|
+
* All form field types extend this interface.
|
|
558
|
+
*/
|
|
559
|
+
interface BaseFormField {
|
|
560
|
+
/** Field type identifier (discriminant) */
|
|
561
|
+
type: FormFieldType;
|
|
562
|
+
/** Field name (used as key in submission data) */
|
|
563
|
+
name: string;
|
|
564
|
+
/** Display label */
|
|
565
|
+
label: string;
|
|
566
|
+
/** Placeholder text */
|
|
567
|
+
placeholder?: string;
|
|
568
|
+
/** Help text shown below field */
|
|
569
|
+
helpText?: string;
|
|
570
|
+
/** Default value */
|
|
571
|
+
defaultValue?: unknown;
|
|
572
|
+
/** Whether field is required */
|
|
573
|
+
required?: boolean;
|
|
574
|
+
/** Validation rules */
|
|
575
|
+
validation?: ValidationRules;
|
|
576
|
+
/** Conditional logic configuration */
|
|
577
|
+
conditionalLogic?: ConditionalLogic;
|
|
578
|
+
/** Admin UI configuration */
|
|
579
|
+
admin?: {
|
|
580
|
+
/** CSS classes */
|
|
581
|
+
className?: string;
|
|
582
|
+
/** Field width */
|
|
583
|
+
width?: "25%" | "33%" | "50%" | "66%" | "75%" | "100%";
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* A plugin-contributed form field type (outside the built-in union). Carries
|
|
588
|
+
* only the base properties — the plugin owns its editor component and value
|
|
589
|
+
* semantics — and its `type` is the plugin's registered field-type id. Kept a
|
|
590
|
+
* distinct union member so built-in `case` narrowing is unaffected while a
|
|
591
|
+
* plugin field still type-checks as a `FormField`.
|
|
592
|
+
*/
|
|
593
|
+
interface CustomFormField extends Omit<BaseFormField, "type"> {
|
|
594
|
+
type: string & {};
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Text input field.
|
|
598
|
+
*/
|
|
599
|
+
interface TextFormField extends Omit<BaseFormField, "type"> {
|
|
600
|
+
type: "text";
|
|
601
|
+
defaultValue?: string;
|
|
602
|
+
validation?: ValidationRules & {
|
|
603
|
+
minLength?: number;
|
|
604
|
+
maxLength?: number;
|
|
605
|
+
pattern?: string;
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Email input field.
|
|
610
|
+
*/
|
|
611
|
+
interface EmailFormField extends Omit<BaseFormField, "type"> {
|
|
612
|
+
type: "email";
|
|
613
|
+
defaultValue?: string;
|
|
614
|
+
validation?: ValidationRules & {
|
|
615
|
+
pattern?: string;
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Number input field.
|
|
620
|
+
*/
|
|
621
|
+
interface NumberFormField extends Omit<BaseFormField, "type"> {
|
|
622
|
+
type: "number";
|
|
623
|
+
defaultValue?: number;
|
|
624
|
+
validation?: ValidationRules & {
|
|
625
|
+
min?: number;
|
|
626
|
+
max?: number;
|
|
627
|
+
step?: number;
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Phone input field.
|
|
632
|
+
*/
|
|
633
|
+
interface PhoneFormField extends Omit<BaseFormField, "type"> {
|
|
634
|
+
type: "phone";
|
|
635
|
+
defaultValue?: string;
|
|
636
|
+
validation?: ValidationRules & {
|
|
637
|
+
pattern?: string;
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* URL input field.
|
|
642
|
+
*/
|
|
643
|
+
interface UrlFormField extends Omit<BaseFormField, "type"> {
|
|
644
|
+
type: "url";
|
|
645
|
+
defaultValue?: string;
|
|
646
|
+
validation?: ValidationRules & {
|
|
647
|
+
pattern?: string;
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Textarea field.
|
|
652
|
+
*/
|
|
653
|
+
interface TextareaFormField extends Omit<BaseFormField, "type"> {
|
|
654
|
+
type: "textarea";
|
|
655
|
+
defaultValue?: string;
|
|
656
|
+
rows?: number;
|
|
657
|
+
validation?: ValidationRules & {
|
|
658
|
+
minLength?: number;
|
|
659
|
+
maxLength?: number;
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Select dropdown field.
|
|
664
|
+
*/
|
|
665
|
+
interface SelectFormField extends Omit<BaseFormField, "type"> {
|
|
666
|
+
type: "select";
|
|
667
|
+
options: Array<{
|
|
668
|
+
label: string;
|
|
669
|
+
value: string;
|
|
670
|
+
}>;
|
|
671
|
+
defaultValue?: string | string[];
|
|
672
|
+
allowMultiple?: boolean;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Single checkbox field.
|
|
676
|
+
*/
|
|
677
|
+
interface CheckboxFormField extends Omit<BaseFormField, "type"> {
|
|
678
|
+
type: "checkbox";
|
|
679
|
+
defaultValue?: boolean;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Radio group field.
|
|
683
|
+
*/
|
|
684
|
+
interface RadioFormField extends Omit<BaseFormField, "type"> {
|
|
685
|
+
type: "radio";
|
|
686
|
+
options: Array<{
|
|
687
|
+
label: string;
|
|
688
|
+
value: string;
|
|
689
|
+
}>;
|
|
690
|
+
defaultValue?: string;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* File upload field.
|
|
694
|
+
*/
|
|
695
|
+
interface FileFormField extends Omit<BaseFormField, "type"> {
|
|
696
|
+
type: "file";
|
|
697
|
+
accept?: string;
|
|
698
|
+
multiple?: boolean;
|
|
699
|
+
maxFileSize?: number;
|
|
700
|
+
/**
|
|
701
|
+
* When true, files uploaded through this field are attached to all
|
|
702
|
+
* notification emails for this form. Default false.
|
|
703
|
+
*/
|
|
704
|
+
attachToEmail?: boolean;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Date picker field.
|
|
708
|
+
*/
|
|
709
|
+
interface DateFormField extends Omit<BaseFormField, "type"> {
|
|
710
|
+
type: "date";
|
|
711
|
+
defaultValue?: string;
|
|
712
|
+
min?: string;
|
|
713
|
+
max?: string;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Time picker field.
|
|
717
|
+
*/
|
|
718
|
+
interface TimeFormField extends Omit<BaseFormField, "type"> {
|
|
719
|
+
type: "time";
|
|
720
|
+
defaultValue?: string;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Hidden field.
|
|
724
|
+
*/
|
|
725
|
+
interface HiddenFormField extends Omit<BaseFormField, "type"> {
|
|
726
|
+
type: "hidden";
|
|
727
|
+
defaultValue?: string;
|
|
728
|
+
}
|
|
729
|
+
interface ValidationRules {
|
|
730
|
+
/** Custom error message */
|
|
731
|
+
errorMessage?: string;
|
|
732
|
+
/** Custom validation function */
|
|
733
|
+
validate?: (value: unknown, formData: Record<string, unknown>) => boolean | string | Promise<boolean | string>;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Conditional logic configuration.
|
|
737
|
+
*/
|
|
738
|
+
interface ConditionalLogic {
|
|
739
|
+
/** Whether conditional logic is enabled */
|
|
740
|
+
enabled: boolean;
|
|
741
|
+
/** Action to perform when conditions are met */
|
|
742
|
+
action: "show" | "hide";
|
|
743
|
+
/** Logical operator for multiple conditions */
|
|
744
|
+
operator: "AND" | "OR";
|
|
745
|
+
/** Conditions to evaluate */
|
|
746
|
+
conditions: ConditionalLogicCondition[];
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* A single conditional logic condition.
|
|
750
|
+
*/
|
|
751
|
+
interface ConditionalLogicCondition {
|
|
752
|
+
/** Field name to check */
|
|
753
|
+
field: string;
|
|
754
|
+
/** Comparison operator */
|
|
755
|
+
comparison: "equals" | "notEquals" | "contains" | "isEmpty" | "isNotEmpty" | "greaterThan" | "lessThan";
|
|
756
|
+
/** Value to compare against (not used for isEmpty/isNotEmpty) */
|
|
757
|
+
value?: unknown;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Form settings and behavior.
|
|
761
|
+
*/
|
|
762
|
+
interface FormSettings {
|
|
763
|
+
/** Submit button text */
|
|
764
|
+
submitButtonText?: string;
|
|
765
|
+
/**
|
|
766
|
+
* Confirmation type after successful submission.
|
|
767
|
+
* - "message": Display a success message
|
|
768
|
+
* - "redirect": Redirect to a URL
|
|
769
|
+
* - "relationship": Redirect to a linked document (requires
|
|
770
|
+
* `redirectRelationships` in the plugin options)
|
|
771
|
+
*
|
|
772
|
+
* @default "message"
|
|
773
|
+
*/
|
|
774
|
+
confirmationType?: "message" | "redirect" | "relationship";
|
|
775
|
+
/**
|
|
776
|
+
* Linked document for relationship-based redirects (the collection's
|
|
777
|
+
* `redirectPage` relationship value). Preserved verbatim — resolution
|
|
778
|
+
* happens where the relationship is rendered.
|
|
779
|
+
*/
|
|
780
|
+
redirectPage?: unknown;
|
|
781
|
+
/** Success message (supports HTML) - used when confirmationType is "message" */
|
|
782
|
+
successMessage?: string;
|
|
783
|
+
/** Redirect URL after successful submission - used when confirmationType is "redirect" */
|
|
784
|
+
redirectUrl?: string;
|
|
785
|
+
/**
|
|
786
|
+
* Relationship to redirect document - used when confirmationType is "redirect"
|
|
787
|
+
* and redirectRelationships are configured in plugin options.
|
|
788
|
+
*/
|
|
789
|
+
redirectRelation?: {
|
|
790
|
+
relationTo: string;
|
|
791
|
+
value: string;
|
|
792
|
+
};
|
|
793
|
+
/** Allow multiple submissions from same IP (default true) */
|
|
794
|
+
allowMultipleSubmissions?: boolean;
|
|
795
|
+
/**
|
|
796
|
+
* Per-form honeypot override. Unset inherits the plugin's
|
|
797
|
+
* `spamProtection.honeypot`; the form wins where set.
|
|
798
|
+
*/
|
|
799
|
+
honeypotEnabled?: boolean;
|
|
800
|
+
/**
|
|
801
|
+
* Per-form reCAPTCHA override. Unset inherits the plugin's
|
|
802
|
+
* `spamProtection.recaptcha.enabled`; the form wins where set.
|
|
803
|
+
*/
|
|
804
|
+
captchaEnabled?: boolean;
|
|
805
|
+
/** reCAPTCHA site key (client-facing) when captcha is enabled per-form */
|
|
806
|
+
captchaSiteKey?: string;
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Email notification configuration for a form.
|
|
810
|
+
*/
|
|
811
|
+
interface FormNotifications {
|
|
812
|
+
/** Enable email notifications */
|
|
813
|
+
enabled: boolean;
|
|
814
|
+
/** Recipient email addresses */
|
|
815
|
+
recipients: Array<{
|
|
816
|
+
email: string;
|
|
817
|
+
name?: string;
|
|
818
|
+
}>;
|
|
819
|
+
/** Email subject line */
|
|
820
|
+
subject?: string;
|
|
821
|
+
/** Use submitter's email as reply-to */
|
|
822
|
+
replyTo?: boolean;
|
|
823
|
+
/** Custom email template */
|
|
824
|
+
template?: string;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Form access control.
|
|
828
|
+
*/
|
|
829
|
+
interface FormAccess {
|
|
830
|
+
/** Who can submit the form */
|
|
831
|
+
submit?: (context: {
|
|
832
|
+
req: RequestContext;
|
|
833
|
+
}) => boolean | Promise<boolean>;
|
|
834
|
+
/** Who can view submissions */
|
|
835
|
+
read?: (context: {
|
|
836
|
+
req: RequestContext;
|
|
837
|
+
}) => boolean | Promise<boolean>;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Form lifecycle hooks.
|
|
841
|
+
*/
|
|
842
|
+
interface FormHooks {
|
|
843
|
+
/** Before submission validation */
|
|
844
|
+
beforeValidate?: Array<(context: FormHookContext) => void | Promise<void>>;
|
|
845
|
+
/** Before submission is saved */
|
|
846
|
+
beforeSubmit?: Array<(context: FormHookContext) => void | Promise<void>>;
|
|
847
|
+
/** After submission is saved */
|
|
848
|
+
afterSubmit?: Array<(context: FormHookContext) => void | Promise<void>>;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Hook context for form hooks.
|
|
852
|
+
*/
|
|
853
|
+
interface FormHookContext {
|
|
854
|
+
/** Form configuration */
|
|
855
|
+
form: FormConfig;
|
|
856
|
+
/** Submission data */
|
|
857
|
+
data: Record<string, unknown>;
|
|
858
|
+
/** Request context */
|
|
859
|
+
req: RequestContext;
|
|
860
|
+
/** Submission metadata */
|
|
861
|
+
metadata?: {
|
|
862
|
+
ipAddress?: string;
|
|
863
|
+
userAgent?: string;
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Form submission data structure.
|
|
868
|
+
*/
|
|
869
|
+
interface FormSubmission {
|
|
870
|
+
/** Submission ID */
|
|
871
|
+
id: string;
|
|
872
|
+
/** Form slug */
|
|
873
|
+
formSlug: string;
|
|
874
|
+
/** Submission data (JSON) */
|
|
875
|
+
data: Record<string, unknown>;
|
|
876
|
+
/**
|
|
877
|
+
* Submission status.
|
|
878
|
+
*
|
|
879
|
+
* `spam` is one of them, not a separate flag: the stored field offers it, the
|
|
880
|
+
* admin has a Spam tab and filters its other views with `not_equals: "spam"`,
|
|
881
|
+
* and marking something "Not spam" moves it back to `new`.
|
|
882
|
+
*/
|
|
883
|
+
status: "new" | "read" | "archived" | "spam";
|
|
884
|
+
/** Which spam check flagged this, when one did. */
|
|
885
|
+
spamReason?: string | null;
|
|
886
|
+
/** Internal notes (admin only) */
|
|
887
|
+
notes?: string;
|
|
888
|
+
/** Metadata */
|
|
889
|
+
ipAddress?: string;
|
|
890
|
+
userAgent?: string;
|
|
891
|
+
submittedAt: Date;
|
|
892
|
+
/** Created/updated timestamps */
|
|
893
|
+
createdAt: Date;
|
|
894
|
+
updatedAt: Date;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Resolved form builder configuration after applying defaults.
|
|
898
|
+
* Used internally by the plugin.
|
|
899
|
+
*/
|
|
900
|
+
interface ResolvedFormBuilderConfig {
|
|
901
|
+
formOverrides: ResolvedCollectionOverrides;
|
|
902
|
+
formSubmissionOverrides: ResolvedCollectionOverrides;
|
|
903
|
+
fields: FormFieldToggles;
|
|
904
|
+
/**
|
|
905
|
+
* The collection-to-URL map, with the array shorthand already expanded.
|
|
906
|
+
*
|
|
907
|
+
* ONE shape, so nothing downstream branches on which spelling was written.
|
|
908
|
+
* The list of redirect collections is `Object.keys` of this rather than a
|
|
909
|
+
* second field beside it, which would agree with it today and drift later.
|
|
910
|
+
*/
|
|
911
|
+
redirectRelationships: RedirectRelationships;
|
|
912
|
+
/**
|
|
913
|
+
* Whether each redirect target collection is localized, keyed by slug.
|
|
914
|
+
*
|
|
915
|
+
* Resolved at `init` from the collection's own registered metadata rather
|
|
916
|
+
* than asked for in options, because the collection already declares it and
|
|
917
|
+
* a second declaration drifts.
|
|
918
|
+
*
|
|
919
|
+
* It decides whether a target's main-row `status` answers for the document:
|
|
920
|
+
* a localized collection publishes on a locale companion, so the row reads
|
|
921
|
+
* `draft` while a translation is public. A slug missing here is a collection
|
|
922
|
+
* whose metadata could not be read — undefined, not false, because reading
|
|
923
|
+
* an unknown as "not localized" is what marks a reachable translation as a
|
|
924
|
+
* draft.
|
|
925
|
+
*/
|
|
926
|
+
redirectTargetLocalization: Record<string, boolean>;
|
|
927
|
+
beforeEmail?: FormBuilderPluginOptions["beforeEmail"];
|
|
928
|
+
notifications: {
|
|
929
|
+
defaultFrom?: string;
|
|
930
|
+
defaultToEmail?: string;
|
|
931
|
+
enabled: boolean;
|
|
932
|
+
};
|
|
933
|
+
spamProtection: {
|
|
934
|
+
honeypot: boolean;
|
|
935
|
+
recaptcha?: {
|
|
936
|
+
enabled: boolean;
|
|
937
|
+
siteKey?: string;
|
|
938
|
+
secretKey?: string;
|
|
939
|
+
scoreThreshold?: number;
|
|
940
|
+
};
|
|
941
|
+
rateLimit?: {
|
|
942
|
+
maxSubmissions: number;
|
|
943
|
+
windowMs: number;
|
|
944
|
+
};
|
|
945
|
+
};
|
|
946
|
+
uploads: {
|
|
947
|
+
maxFileSize: number;
|
|
948
|
+
allowedMimeTypes: string[];
|
|
949
|
+
uploadCollection: string;
|
|
950
|
+
};
|
|
951
|
+
features: {
|
|
952
|
+
builder: boolean;
|
|
953
|
+
conditionalLogic: boolean;
|
|
954
|
+
fileUploads: boolean;
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Check if a value is a FormField.
|
|
959
|
+
*/
|
|
960
|
+
declare function isFormField(value: unknown): value is FormField;
|
|
961
|
+
/**
|
|
962
|
+
* Check if a FormField is a specific type.
|
|
963
|
+
*/
|
|
964
|
+
declare function isFormFieldType<T extends FormField>(field: FormField, type: T["type"]): field is T;
|
|
965
|
+
/**
|
|
966
|
+
* Type guard for text field.
|
|
967
|
+
*/
|
|
968
|
+
declare function isTextFormField(field: FormField): field is TextFormField;
|
|
969
|
+
/**
|
|
970
|
+
* Type guard for email field.
|
|
971
|
+
*/
|
|
972
|
+
declare function isEmailFormField(field: FormField): field is EmailFormField;
|
|
973
|
+
/**
|
|
974
|
+
* Type guard for select field.
|
|
975
|
+
*/
|
|
976
|
+
declare function isSelectFormField(field: FormField): field is SelectFormField;
|
|
977
|
+
/**
|
|
978
|
+
* Type guard for checkbox field.
|
|
979
|
+
*/
|
|
980
|
+
declare function isCheckboxFormField(field: FormField): field is CheckboxFormField;
|
|
981
|
+
/**
|
|
982
|
+
* Type guard for file field.
|
|
983
|
+
*/
|
|
984
|
+
declare function isFileFormField(field: FormField): field is FileFormField;
|
|
985
|
+
export { RadioFormField as A, isEmailFormField as B, FormNotificationItem as C, HiddenFormField as D, FormSubmission as E, TextareaFormField as F, isSelectFormField as G, isFormField as H, TimeFormField as I, isTextFormField as K, UrlFormField as L, SelectFormField as M, SubmissionDocument as N, NumberFormField as O, TextFormField as P, ValidationRules as R, FormNotification as S, FormSettings as T, isFormFieldType as U, isFileFormField as V, isKnownFormField as W, FormField as _, ConditionalLogic as a, FormHookContext as b, DateFormField as c, FieldBlockConfig as d, FileFormField as f, FormDocument as g, FormConfig as h, CheckboxFormField as i, ResolvedFormBuilderConfig as j, PhoneFormField as k, EmailConfig as l, FormBuilderPluginOptions as m, BUILT_IN_FORM_FIELD_TYPES as n, ConditionalLogicCondition as o, FormAccess as p, BaseFormField as r, CustomFormField as s, AnyFormField as t, EmailFormField as u, FormFieldType as v, FormNotifications as w, FormHooks as x, FormFieldTypeId as y, isCheckboxFormField as z };
|
|
986
|
+
//# sourceMappingURL=types-BHReIA0j.d.ts.map
|