@nextlyhq/plugin-form-builder 0.0.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.
Files changed (37) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +122 -0
  3. package/dist/CheckboxFieldEditor-K4T6QX52.js +121 -0
  4. package/dist/CheckboxFieldEditor-K4T6QX52.js.map +1 -0
  5. package/dist/DateFieldEditor-JFJFAVOH.js +187 -0
  6. package/dist/DateFieldEditor-JFJFAVOH.js.map +1 -0
  7. package/dist/EmailFieldEditor-MMOXXL7P.js +138 -0
  8. package/dist/EmailFieldEditor-MMOXXL7P.js.map +1 -0
  9. package/dist/NumberFieldEditor-UISAYR6Z.js +175 -0
  10. package/dist/NumberFieldEditor-UISAYR6Z.js.map +1 -0
  11. package/dist/RadioFieldEditor-6UQFH4WT.js +165 -0
  12. package/dist/RadioFieldEditor-6UQFH4WT.js.map +1 -0
  13. package/dist/SelectFieldEditor-PSO4KHV4.js +213 -0
  14. package/dist/SelectFieldEditor-PSO4KHV4.js.map +1 -0
  15. package/dist/TextFieldEditor-5EU64EA5.js +187 -0
  16. package/dist/TextFieldEditor-5EU64EA5.js.map +1 -0
  17. package/dist/TextareaFieldEditor-773G3LNM.js +190 -0
  18. package/dist/TextareaFieldEditor-773G3LNM.js.map +1 -0
  19. package/dist/admin/index.css +934 -0
  20. package/dist/admin/index.css.map +1 -0
  21. package/dist/admin/index.d.ts +547 -0
  22. package/dist/admin/index.js +4030 -0
  23. package/dist/admin/index.js.map +1 -0
  24. package/dist/chunk-ABNZFTQK.js +586 -0
  25. package/dist/chunk-ABNZFTQK.js.map +1 -0
  26. package/dist/chunk-XTCEPJ7B.js +256 -0
  27. package/dist/chunk-XTCEPJ7B.js.map +1 -0
  28. package/dist/components/index.d.ts +3 -0
  29. package/dist/components/index.js +9 -0
  30. package/dist/components/index.js.map +1 -0
  31. package/dist/index-C0kBvYjm.d.ts +1012 -0
  32. package/dist/index.d.ts +1663 -0
  33. package/dist/index.js +2470 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/styles/form-builder.css +1150 -0
  36. package/dist/styles/submissions-filter.css +75 -0
  37. package/package.json +96 -0
@@ -0,0 +1,1663 @@
1
+ import * as nextly from 'nextly';
2
+ import { PluginDefinition, CollectionConfig, PluginContext } from 'nextly';
3
+ import { R as ResolvedFormBuilderConfig, F as FormBuilderPluginOptions, a as FormConfig, b as FormSettings, c as FormNotifications, C as CheckboxFormField, D as DateFormField, E as EmailFormField, d as FileFormField, H as HiddenFormField, N as NumberFormField, P as PhoneFormField, e as RadioFormField, S as SelectFormField, T as TextFormField, f as TextareaFormField, g as TimeFormField, U as UrlFormField, h as ConditionalLogic, i as FormField, j as SubmissionDocument, k as FormDocument, W as WebhookEvent } from './index-C0kBvYjm.js';
4
+ export { B as BaseFormField, l as ConditionalLogicCondition, m as EmailConfig, n as FieldBlockConfig, o as FormAccess, p as FormFieldType, q as FormHookContext, r as FormHooks, s as FormSubmission, t as SubmissionDetailProps, u as SubmissionListProps, V as ValidationRules, v as WebhookConfig, w as isCheckboxFormField, x as isEmailFormField, y as isFileFormField, z as isFormField, A as isFormFieldType, G as isSelectFormField, I as isTextFormField } from './index-C0kBvYjm.js';
5
+ import { z } from 'zod';
6
+ import 'react';
7
+
8
+ /**
9
+ * Form Builder Plugin
10
+ *
11
+ * Visual form builder with drag-and-drop UI, submission management,
12
+ * email notifications, and spam protection.
13
+ *
14
+ * @module plugin
15
+ * @since 0.1.0
16
+ */
17
+
18
+ type NextlyPlugin = PluginDefinition;
19
+ interface FormBuilderPluginResult {
20
+ /** Plugin definition — pass to `defineConfig({ plugins: [...] })`. */
21
+ plugin: NextlyPlugin;
22
+ /** Forms & Submissions collections (also auto-added by the plugin). */
23
+ collections: CollectionConfig[];
24
+ /** Resolved configuration with all defaults applied. */
25
+ config: ResolvedFormBuilderConfig;
26
+ }
27
+ /**
28
+ * Create a Form Builder plugin instance.
29
+ *
30
+ * @example Basic usage
31
+ * ```ts
32
+ * import { defineConfig } from "nextly/config";
33
+ * import { formBuilder } from "@nextlyhq/plugin-form-builder";
34
+ *
35
+ * const fb = formBuilder();
36
+ *
37
+ * export default defineConfig({
38
+ * plugins: [fb.plugin],
39
+ * });
40
+ * ```
41
+ *
42
+ * @example With options
43
+ * ```ts
44
+ * const fb = formBuilder({
45
+ * notifications: { defaultFrom: "noreply@example.com" },
46
+ * spamProtection: { honeypot: true },
47
+ * formOverrides: {
48
+ * slug: "contact-forms",
49
+ * labels: { singular: "Contact Form", plural: "Contact Forms" },
50
+ * },
51
+ * });
52
+ * ```
53
+ */
54
+ declare function formBuilder(options?: FormBuilderPluginOptions): FormBuilderPluginResult;
55
+ /**
56
+ * Retrieve the resolved Form Builder config from a Nextly plugin context.
57
+ */
58
+ declare function getFormBuilderConfig(nextly: unknown): ResolvedFormBuilderConfig | undefined;
59
+
60
+ /**
61
+ * Form Configuration Validator
62
+ *
63
+ * Provides comprehensive validation for form configurations including:
64
+ * - Slug validation (format, reserved names)
65
+ * - Field name validation (format, duplicates)
66
+ * - Field-specific validation (select/radio options, min/max constraints)
67
+ * - Conditional logic validation
68
+ * - Nested field validation
69
+ *
70
+ * @module config/validate-form
71
+ * @since 1.0.0
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * import { validateFormConfig } from '@nextly/plugin-form-builder';
76
+ *
77
+ * const result = validateFormConfig(config);
78
+ * if (!result.valid) {
79
+ * console.error('Validation errors:', result.errors);
80
+ * }
81
+ * ```
82
+ */
83
+
84
+ /**
85
+ * Error codes for form validation failures.
86
+ * Used for programmatic error handling.
87
+ */
88
+ type FormValidationErrorCode = "SLUG_REQUIRED" | "SLUG_INVALID_TYPE" | "SLUG_TOO_SHORT" | "SLUG_TOO_LONG" | "SLUG_INVALID_FORMAT" | "SLUG_RESERVED" | "FIELDS_REQUIRED" | "FIELDS_INVALID_TYPE" | "FIELDS_EMPTY" | "FIELD_NAME_REQUIRED" | "FIELD_NAME_INVALID_FORMAT" | "FIELD_NAME_DUPLICATE" | "FIELD_TYPE_REQUIRED" | "FIELD_TYPE_INVALID" | "FIELD_LABEL_REQUIRED" | "OPTIONS_REQUIRED" | "OPTIONS_EMPTY" | "MIN_GREATER_THAN_MAX" | "MIN_LENGTH_GREATER_THAN_MAX" | "FILE_MAX_SIZE_INVALID" | "CONDITIONS_REQUIRED" | "CONDITION_FIELD_REQUIRED" | "CONDITION_COMPARISON_REQUIRED" | "SETTINGS_INVALID_TYPE" | "REDIRECT_URL_REQUIRED" | "ACCESS_INVALID_TYPE" | "ACCESS_FUNCTION_INVALID";
89
+ /**
90
+ * A single validation error with path and context.
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const error: FormValidationError = {
95
+ * path: 'fields.email.validation',
96
+ * message: "Min length cannot be greater than max length",
97
+ * code: 'MIN_LENGTH_GREATER_THAN_MAX',
98
+ * };
99
+ * ```
100
+ */
101
+ interface FormValidationError {
102
+ /**
103
+ * Dot-notation path to the invalid property.
104
+ * @example 'slug', 'fields.0.name', 'fields.email.options'
105
+ */
106
+ path: string;
107
+ /**
108
+ * Human-readable error message.
109
+ */
110
+ message: string;
111
+ /**
112
+ * Machine-readable error code for programmatic handling.
113
+ */
114
+ code: FormValidationErrorCode;
115
+ }
116
+ /**
117
+ * Result of form config validation.
118
+ */
119
+ interface FormValidationResult {
120
+ /**
121
+ * Whether the configuration is valid.
122
+ */
123
+ valid: boolean;
124
+ /**
125
+ * Array of validation errors (empty if valid).
126
+ */
127
+ errors: FormValidationError[];
128
+ }
129
+ /**
130
+ * Reserved form slugs that cannot be used.
131
+ * These are used by the system or have special meaning.
132
+ */
133
+ declare const RESERVED_FORM_SLUGS: readonly ["api", "submit", "submissions", "export", "admin", "new", "create", "edit", "delete"];
134
+ /**
135
+ * Validates a complete form configuration.
136
+ *
137
+ * This is the main entry point for form validation. It performs
138
+ * comprehensive validation including:
139
+ * - Slug format and reserved name checking
140
+ * - Field name validation
141
+ * - Field-specific validation (options, min/max, etc.)
142
+ * - Duplicate field name detection
143
+ * - Conditional logic validation
144
+ * - Settings validation
145
+ * - Access function type validation
146
+ *
147
+ * @param config - The form configuration to validate
148
+ * @returns Validation result with any errors found
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * import { validateFormConfig } from '@nextly/plugin-form-builder';
153
+ *
154
+ * const config = {
155
+ * slug: 'contact-form',
156
+ * fields: [
157
+ * { type: 'text', name: 'firstName', label: 'First Name' },
158
+ * { type: 'email', name: 'email', label: 'Email', required: true },
159
+ * ],
160
+ * };
161
+ *
162
+ * const result = validateFormConfig(config);
163
+ *
164
+ * if (!result.valid) {
165
+ * result.errors.forEach(err => {
166
+ * console.error(`[${err.code}] ${err.path}: ${err.message}`);
167
+ * });
168
+ * }
169
+ * ```
170
+ */
171
+ declare function validateFormConfig(config: FormConfig): FormValidationResult;
172
+ /**
173
+ * Throws an error if the form configuration is invalid.
174
+ *
175
+ * This is a convenience wrapper around `validateFormConfig` that
176
+ * throws a descriptive error instead of returning a result object.
177
+ *
178
+ * @param config - The form configuration to validate
179
+ * @throws Error if validation fails with all error messages
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * import { assertValidFormConfig } from '@nextly/plugin-form-builder';
184
+ *
185
+ * // Throws if invalid
186
+ * assertValidFormConfig(config);
187
+ * ```
188
+ */
189
+ declare function assertValidFormConfig(config: FormConfig): void;
190
+
191
+ /**
192
+ * Form Configuration Defaults
193
+ *
194
+ * Applies default values to form configurations, ensuring all required
195
+ * properties have sensible defaults while preserving user-specified values.
196
+ *
197
+ * @module config/defaults
198
+ * @since 1.0.0
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * import { applyFormDefaults } from '@nextly/plugin-form-builder';
203
+ *
204
+ * const config = {
205
+ * slug: 'contact-form',
206
+ * fields: [
207
+ * { type: 'text', name: 'name', label: 'Name' },
208
+ * { type: 'email', name: 'email', label: 'Email' },
209
+ * ],
210
+ * };
211
+ *
212
+ * const configWithDefaults = applyFormDefaults(config);
213
+ * // configWithDefaults now has labels, settings, etc. with defaults applied
214
+ * ```
215
+ */
216
+
217
+ /**
218
+ * Default form settings.
219
+ */
220
+ declare const DEFAULT_FORM_SETTINGS: Required<Pick<FormSettings, "submitButtonText" | "confirmationType" | "successMessage" | "allowMultipleSubmissions">>;
221
+ /**
222
+ * Default notification settings.
223
+ */
224
+ declare const DEFAULT_NOTIFICATION_SETTINGS: Pick<FormNotifications, "enabled" | "replyTo">;
225
+ /**
226
+ * Converts a slug to title case.
227
+ *
228
+ * @param str - The slug string to convert
229
+ * @returns Title case string
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * toTitleCase('contact-form'); // 'Contact Form'
234
+ * toTitleCase('newsletter_signup'); // 'Newsletter Signup'
235
+ * ```
236
+ */
237
+ declare function toTitleCase(str: string): string;
238
+ /**
239
+ * Generates a plural form from a singular label.
240
+ * Handles common English pluralization rules.
241
+ *
242
+ * @param singular - The singular form
243
+ * @returns The plural form
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * pluralize('Form'); // 'Forms'
248
+ * pluralize('Category'); // 'Categories'
249
+ * pluralize('Quiz'); // 'Quizzes'
250
+ * ```
251
+ */
252
+ declare function pluralize(singular: string): string;
253
+ /**
254
+ * Apply default values to form configuration.
255
+ *
256
+ * This function takes a partial form configuration and returns a complete
257
+ * configuration with all default values applied. User-specified values
258
+ * are preserved and take precedence over defaults.
259
+ *
260
+ * @param config - Form configuration (may be partial)
261
+ * @returns Configuration with defaults applied
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * import { applyFormDefaults } from '@nextly/plugin-form-builder';
266
+ *
267
+ * const config = {
268
+ * slug: 'contact-form',
269
+ * fields: [
270
+ * { type: 'text', name: 'name', label: 'Name' },
271
+ * { type: 'email', name: 'email', label: 'Email' },
272
+ * ],
273
+ * };
274
+ *
275
+ * const result = applyFormDefaults(config);
276
+ *
277
+ * // Result:
278
+ * // {
279
+ * // slug: 'contact-form',
280
+ * // labels: { singular: 'Contact Form', plural: 'Contact Forms' },
281
+ * // fields: [...],
282
+ * // settings: {
283
+ * // submitButtonText: 'Submit',
284
+ * // confirmationType: 'message',
285
+ * // successMessage: 'Thank you for your submission!',
286
+ * // allowMultipleSubmissions: true,
287
+ * // },
288
+ * // access: {
289
+ * // submit: () => true,
290
+ * // read: ({ req }) => !!req.user,
291
+ * // },
292
+ * // }
293
+ * ```
294
+ */
295
+ declare function applyFormDefaults(config: FormConfig): FormConfig;
296
+ /**
297
+ * Apply default values to individual form field.
298
+ *
299
+ * This is a utility function for applying defaults to a single field,
300
+ * useful when processing fields individually (e.g., in the Schema Builder).
301
+ *
302
+ * @param field - The field to apply defaults to
303
+ * @returns Field with defaults applied
304
+ *
305
+ * @example
306
+ * ```typescript
307
+ * import { applyFieldDefaults } from '@nextly/plugin-form-builder';
308
+ *
309
+ * const field = { type: 'text', name: 'firstName' };
310
+ * const fieldWithDefaults = applyFieldDefaults(field);
311
+ * // { type: 'text', name: 'firstName', label: 'First Name', required: false }
312
+ * ```
313
+ */
314
+ declare function applyFieldDefaults<T extends {
315
+ type: string;
316
+ name?: string;
317
+ label?: string;
318
+ required?: boolean;
319
+ }>(field: T): T;
320
+ /**
321
+ * Creates a complete form config with minimal input.
322
+ *
323
+ * This is a convenience function for quickly creating form configs
324
+ * with sensible defaults. Only the slug and fields are required.
325
+ *
326
+ * @param slug - The form slug
327
+ * @param fields - Array of form fields
328
+ * @param options - Optional additional configuration
329
+ * @returns Complete form configuration
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * import { createFormConfig } from '@nextly/plugin-form-builder';
334
+ *
335
+ * const config = createFormConfig('contact', [
336
+ * { type: 'text', name: 'name', label: 'Name', required: true },
337
+ * { type: 'email', name: 'email', label: 'Email', required: true },
338
+ * { type: 'textarea', name: 'message', label: 'Message' },
339
+ * ]);
340
+ * ```
341
+ */
342
+ declare function createFormConfig(slug: string, fields: FormConfig["fields"], options?: Partial<Omit<FormConfig, "slug" | "fields">>): FormConfig;
343
+
344
+ /**
345
+ * Form Field Helpers
346
+ *
347
+ * Convenient factory functions for creating form field configurations.
348
+ * These helpers eliminate the need to manually specify the `type` property,
349
+ * providing a cleaner, more ergonomic API for form definitions.
350
+ *
351
+ * @module config/field-helpers
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * import {
356
+ * text,
357
+ * email,
358
+ * select,
359
+ * checkbox,
360
+ * option,
361
+ * } from '@nextly/plugin-form-builder';
362
+ *
363
+ * const contactFormFields = [
364
+ * text({ name: 'firstName', label: 'First Name', required: true }),
365
+ * text({ name: 'lastName', label: 'Last Name', required: true }),
366
+ * email({ name: 'email', label: 'Email Address', required: true }),
367
+ * select({
368
+ * name: 'subject',
369
+ * label: 'Subject',
370
+ * options: [
371
+ * option('General Inquiry'),
372
+ * option('Support Request'),
373
+ * option('Feedback'),
374
+ * ],
375
+ * }),
376
+ * checkbox({ name: 'subscribe', label: 'Subscribe to newsletter' }),
377
+ * ];
378
+ * ```
379
+ */
380
+
381
+ /**
382
+ * Option for select and radio fields.
383
+ */
384
+ interface FormFieldOption {
385
+ label: string;
386
+ value: string;
387
+ }
388
+ /**
389
+ * Creates an option for select or radio fields.
390
+ *
391
+ * If no value is provided, the label is converted to lowercase with
392
+ * spaces replaced by underscores.
393
+ *
394
+ * @param label - Display label for the option
395
+ * @param value - Optional value (defaults to lowercase label with underscores)
396
+ * @returns Option object with label and value
397
+ *
398
+ * @example
399
+ * ```typescript
400
+ * // Value derived from label
401
+ * option('United States') // { label: 'United States', value: 'united_states' }
402
+ *
403
+ * // Explicit value
404
+ * option('United States', 'us') // { label: 'United States', value: 'us' }
405
+ *
406
+ * // Usage in select field
407
+ * select({
408
+ * name: 'country',
409
+ * label: 'Country',
410
+ * options: [
411
+ * option('United States', 'us'),
412
+ * option('Canada', 'ca'),
413
+ * option('Mexico', 'mx'),
414
+ * ],
415
+ * })
416
+ * ```
417
+ */
418
+ declare const option: (label: string, value?: string) => FormFieldOption;
419
+ /**
420
+ * Creates a text input field configuration.
421
+ *
422
+ * Text fields store simple string values with optional constraints
423
+ * like min/max length and pattern validation.
424
+ *
425
+ * @param config - Field configuration without the `type` property
426
+ * @returns Complete text field configuration
427
+ *
428
+ * @example
429
+ * ```typescript
430
+ * // Basic text field
431
+ * text({ name: 'firstName', label: 'First Name', required: true })
432
+ *
433
+ * // Text with validation
434
+ * text({
435
+ * name: 'username',
436
+ * label: 'Username',
437
+ * required: true,
438
+ * placeholder: 'Enter username',
439
+ * validation: {
440
+ * minLength: 3,
441
+ * maxLength: 20,
442
+ * pattern: '^[a-zA-Z0-9_]+$',
443
+ * },
444
+ * })
445
+ * ```
446
+ */
447
+ declare const text: (config: Omit<TextFormField, "type">) => TextFormField;
448
+ /**
449
+ * Creates an email input field configuration.
450
+ *
451
+ * Email fields provide built-in email format validation.
452
+ *
453
+ * @param config - Field configuration without the `type` property
454
+ * @returns Complete email field configuration
455
+ *
456
+ * @example
457
+ * ```typescript
458
+ * // Basic email field
459
+ * email({ name: 'email', label: 'Email Address', required: true })
460
+ *
461
+ * // Email with placeholder
462
+ * email({
463
+ * name: 'workEmail',
464
+ * label: 'Work Email',
465
+ * required: true,
466
+ * placeholder: 'you@company.com',
467
+ * })
468
+ * ```
469
+ */
470
+ declare const email: (config: Omit<EmailFormField, "type">) => EmailFormField;
471
+ /**
472
+ * Creates a phone input field configuration.
473
+ *
474
+ * Phone fields are optimized for phone number entry with
475
+ * appropriate keyboard on mobile devices.
476
+ *
477
+ * @param config - Field configuration without the `type` property
478
+ * @returns Complete phone field configuration
479
+ *
480
+ * @example
481
+ * ```typescript
482
+ * // Basic phone field
483
+ * phone({ name: 'phone', label: 'Phone Number', required: true })
484
+ *
485
+ * // Phone with placeholder
486
+ * phone({
487
+ * name: 'mobile',
488
+ * label: 'Mobile Number',
489
+ * placeholder: '+1 (555) 123-4567',
490
+ * })
491
+ * ```
492
+ */
493
+ declare const phone: (config: Omit<PhoneFormField, "type">) => PhoneFormField;
494
+ /**
495
+ * Creates a URL input field configuration.
496
+ *
497
+ * URL fields provide built-in URL format validation.
498
+ *
499
+ * @param config - Field configuration without the `type` property
500
+ * @returns Complete URL field configuration
501
+ *
502
+ * @example
503
+ * ```typescript
504
+ * // Basic URL field
505
+ * url({ name: 'website', label: 'Website URL' })
506
+ *
507
+ * // URL with placeholder
508
+ * url({
509
+ * name: 'portfolio',
510
+ * label: 'Portfolio URL',
511
+ * placeholder: 'https://example.com',
512
+ * })
513
+ * ```
514
+ */
515
+ declare const url: (config: Omit<UrlFormField, "type">) => UrlFormField;
516
+ /**
517
+ * Creates a textarea field configuration.
518
+ *
519
+ * Textarea fields provide multi-line text input with configurable
520
+ * row count and length validation.
521
+ *
522
+ * @param config - Field configuration without the `type` property
523
+ * @returns Complete textarea field configuration
524
+ *
525
+ * @example
526
+ * ```typescript
527
+ * // Basic textarea
528
+ * textarea({ name: 'message', label: 'Message', required: true })
529
+ *
530
+ * // Textarea with rows and validation
531
+ * textarea({
532
+ * name: 'description',
533
+ * label: 'Description',
534
+ * rows: 5,
535
+ * validation: {
536
+ * minLength: 10,
537
+ * maxLength: 500,
538
+ * },
539
+ * })
540
+ * ```
541
+ */
542
+ declare const textarea: (config: Omit<TextareaFormField, "type">) => TextareaFormField;
543
+ /**
544
+ * Creates a number input field configuration.
545
+ *
546
+ * Number fields store numeric values with optional min/max/step
547
+ * constraints.
548
+ *
549
+ * @param config - Field configuration without the `type` property
550
+ * @returns Complete number field configuration
551
+ *
552
+ * @example
553
+ * ```typescript
554
+ * // Basic number field
555
+ * number({ name: 'age', label: 'Age', required: true })
556
+ *
557
+ * // Number with range validation
558
+ * number({
559
+ * name: 'quantity',
560
+ * label: 'Quantity',
561
+ * required: true,
562
+ * validation: {
563
+ * min: 1,
564
+ * max: 100,
565
+ * step: 1,
566
+ * },
567
+ * })
568
+ * ```
569
+ */
570
+ declare const number: (config: Omit<NumberFormField, "type">) => NumberFormField;
571
+ /**
572
+ * Creates a select dropdown field configuration.
573
+ *
574
+ * Select fields display a dropdown list of options. Use the `option()`
575
+ * helper to create options.
576
+ *
577
+ * @param config - Field configuration without the `type` property
578
+ * @returns Complete select field configuration
579
+ *
580
+ * @example
581
+ * ```typescript
582
+ * // Basic select
583
+ * select({
584
+ * name: 'country',
585
+ * label: 'Country',
586
+ * required: true,
587
+ * options: [
588
+ * option('United States', 'us'),
589
+ * option('Canada', 'ca'),
590
+ * option('Mexico', 'mx'),
591
+ * ],
592
+ * })
593
+ *
594
+ * // Multi-select
595
+ * select({
596
+ * name: 'interests',
597
+ * label: 'Interests',
598
+ * allowMultiple: true,
599
+ * options: [
600
+ * option('Technology'),
601
+ * option('Design'),
602
+ * option('Business'),
603
+ * ],
604
+ * })
605
+ * ```
606
+ */
607
+ declare const select: (config: Omit<SelectFormField, "type">) => SelectFormField;
608
+ /**
609
+ * Creates a single checkbox field configuration.
610
+ *
611
+ * Checkbox fields store boolean values (true/false).
612
+ *
613
+ * @param config - Field configuration without the `type` property
614
+ * @returns Complete checkbox field configuration
615
+ *
616
+ * @example
617
+ * ```typescript
618
+ * // Terms agreement checkbox
619
+ * checkbox({
620
+ * name: 'agreeToTerms',
621
+ * label: 'I agree to the terms and conditions',
622
+ * required: true,
623
+ * })
624
+ *
625
+ * // Newsletter subscription
626
+ * checkbox({
627
+ * name: 'subscribe',
628
+ * label: 'Subscribe to our newsletter',
629
+ * defaultValue: true,
630
+ * })
631
+ * ```
632
+ */
633
+ declare const checkbox: (config: Omit<CheckboxFormField, "type">) => CheckboxFormField;
634
+ /**
635
+ * Creates a radio group field configuration.
636
+ *
637
+ * Radio group fields allow selecting exactly one option from a list.
638
+ * Use the `option()` helper to create options.
639
+ *
640
+ * @param config - Field configuration without the `type` property
641
+ * @returns Complete radio field configuration
642
+ *
643
+ * @example
644
+ * ```typescript
645
+ * radio({
646
+ * name: 'contactMethod',
647
+ * label: 'Preferred Contact Method',
648
+ * required: true,
649
+ * options: [
650
+ * option('Email', 'email'),
651
+ * option('Phone', 'phone'),
652
+ * option('SMS', 'sms'),
653
+ * ],
654
+ * })
655
+ * ```
656
+ */
657
+ declare const radio: (config: Omit<RadioFormField, "type">) => RadioFormField;
658
+ /**
659
+ * Creates a file upload field configuration.
660
+ *
661
+ * File fields allow users to upload files with configurable
662
+ * accepted types, size limits, and multiple file support.
663
+ *
664
+ * @param config - Field configuration without the `type` property
665
+ * @returns Complete file field configuration
666
+ *
667
+ * @example
668
+ * ```typescript
669
+ * // Single file upload
670
+ * file({
671
+ * name: 'resume',
672
+ * label: 'Upload Resume',
673
+ * required: true,
674
+ * accept: 'application/pdf,.doc,.docx',
675
+ * maxFileSize: 5242880, // 5MB
676
+ * })
677
+ *
678
+ * // Multiple images
679
+ * file({
680
+ * name: 'photos',
681
+ * label: 'Upload Photos',
682
+ * accept: 'image/*',
683
+ * multiple: true,
684
+ * maxFileSize: 10485760, // 10MB
685
+ * })
686
+ * ```
687
+ */
688
+ declare const file: (config: Omit<FileFormField, "type">) => FileFormField;
689
+ /**
690
+ * Creates a date picker field configuration.
691
+ *
692
+ * Date fields provide a date picker with optional min/max constraints.
693
+ *
694
+ * @param config - Field configuration without the `type` property
695
+ * @returns Complete date field configuration
696
+ *
697
+ * @example
698
+ * ```typescript
699
+ * // Basic date field
700
+ * date({ name: 'birthdate', label: 'Date of Birth', required: true })
701
+ *
702
+ * // Date with range constraints
703
+ * date({
704
+ * name: 'eventDate',
705
+ * label: 'Event Date',
706
+ * required: true,
707
+ * min: '2024-01-01',
708
+ * max: '2024-12-31',
709
+ * })
710
+ * ```
711
+ */
712
+ declare const date: (config: Omit<DateFormField, "type">) => DateFormField;
713
+ /**
714
+ * Creates a time picker field configuration.
715
+ *
716
+ * Time fields provide a time picker for selecting time values.
717
+ *
718
+ * @param config - Field configuration without the `type` property
719
+ * @returns Complete time field configuration
720
+ *
721
+ * @example
722
+ * ```typescript
723
+ * // Basic time field
724
+ * time({ name: 'appointmentTime', label: 'Preferred Time', required: true })
725
+ *
726
+ * // Time with default
727
+ * time({
728
+ * name: 'meetingTime',
729
+ * label: 'Meeting Time',
730
+ * defaultValue: '09:00',
731
+ * })
732
+ * ```
733
+ */
734
+ declare const time: (config: Omit<TimeFormField, "type">) => TimeFormField;
735
+ /**
736
+ * Creates a hidden field configuration.
737
+ *
738
+ * Hidden fields store values that are not visible to users but
739
+ * are included in the submission data. Useful for tracking
740
+ * metadata like source, campaign, or referrer.
741
+ *
742
+ * @param config - Field configuration without the `type` property
743
+ * @returns Complete hidden field configuration
744
+ *
745
+ * @example
746
+ * ```typescript
747
+ * // Track form source
748
+ * hidden({ name: 'source', label: 'Source', defaultValue: 'website' })
749
+ *
750
+ * // Campaign tracking
751
+ * hidden({ name: 'campaign', label: 'Campaign', defaultValue: 'summer-sale' })
752
+ * ```
753
+ */
754
+ declare const hidden: (config: Omit<HiddenFormField, "type">) => HiddenFormField;
755
+
756
+ /**
757
+ * Conditional Logic Evaluator
758
+ *
759
+ * Evaluates conditional logic rules to determine field visibility in forms.
760
+ * Used by the FormRenderer component to show/hide fields based on user input.
761
+ *
762
+ * @module utils/evaluate-conditions
763
+ * @since 1.0.0
764
+ *
765
+ * @example
766
+ * ```typescript
767
+ * import { evaluateConditions } from '@nextly/plugin-form-builder';
768
+ *
769
+ * const conditionalLogic = {
770
+ * enabled: true,
771
+ * action: 'show',
772
+ * operator: 'AND',
773
+ * conditions: [
774
+ * { field: 'country', comparison: 'equals', value: 'US' },
775
+ * { field: 'age', comparison: 'greaterThan', value: 18 },
776
+ * ],
777
+ * };
778
+ *
779
+ * const formData = { country: 'US', age: 25 };
780
+ * const isVisible = evaluateConditions(conditionalLogic, formData);
781
+ * // isVisible = true (all conditions met, action is 'show')
782
+ * ```
783
+ */
784
+
785
+ /**
786
+ * Comparison operators supported by the conditional logic system.
787
+ */
788
+ type ComparisonOperator = "equals" | "notEquals" | "contains" | "isEmpty" | "isNotEmpty" | "greaterThan" | "lessThan";
789
+ /**
790
+ * Evaluates conditional logic to determine field visibility.
791
+ *
792
+ * This is the main entry point for conditional logic evaluation.
793
+ * It evaluates all conditions according to the logical operator (AND/OR)
794
+ * and returns whether the field should be visible based on the action
795
+ * (show/hide).
796
+ *
797
+ * @param logic - The conditional logic configuration
798
+ * @param formData - The current form data (field values)
799
+ * @returns Whether the field should be visible
800
+ *
801
+ * @example
802
+ * ```typescript
803
+ * // Show field when country is US AND age is over 18
804
+ * const logic: ConditionalLogic = {
805
+ * enabled: true,
806
+ * action: 'show',
807
+ * operator: 'AND',
808
+ * conditions: [
809
+ * { field: 'country', comparison: 'equals', value: 'US' },
810
+ * { field: 'age', comparison: 'greaterThan', value: 18 },
811
+ * ],
812
+ * };
813
+ *
814
+ * evaluateConditions(logic, { country: 'US', age: 25 }); // true
815
+ * evaluateConditions(logic, { country: 'US', age: 15 }); // false
816
+ * evaluateConditions(logic, { country: 'CA', age: 25 }); // false
817
+ *
818
+ * // Hide field when status is "completed"
819
+ * const hideLogic: ConditionalLogic = {
820
+ * enabled: true,
821
+ * action: 'hide',
822
+ * operator: 'AND',
823
+ * conditions: [
824
+ * { field: 'status', comparison: 'equals', value: 'completed' },
825
+ * ],
826
+ * };
827
+ *
828
+ * evaluateConditions(hideLogic, { status: 'completed' }); // false (hidden)
829
+ * evaluateConditions(hideLogic, { status: 'pending' }); // true (visible)
830
+ * ```
831
+ *
832
+ * @example
833
+ * ```typescript
834
+ * // Show field when either condition is met (OR)
835
+ * const orLogic: ConditionalLogic = {
836
+ * enabled: true,
837
+ * action: 'show',
838
+ * operator: 'OR',
839
+ * conditions: [
840
+ * { field: 'role', comparison: 'equals', value: 'admin' },
841
+ * { field: 'role', comparison: 'equals', value: 'editor' },
842
+ * ],
843
+ * };
844
+ *
845
+ * evaluateConditions(orLogic, { role: 'admin' }); // true
846
+ * evaluateConditions(orLogic, { role: 'editor' }); // true
847
+ * evaluateConditions(orLogic, { role: 'viewer' }); // false
848
+ * ```
849
+ */
850
+ declare function evaluateConditions(logic: ConditionalLogic | undefined, formData: Record<string, unknown>): boolean;
851
+ /**
852
+ * Checks if a comparison operator is valid.
853
+ *
854
+ * @param operator - The operator to check
855
+ * @returns Whether the operator is valid
856
+ */
857
+ declare function isValidComparisonOperator(operator: string): operator is ComparisonOperator;
858
+ /**
859
+ * Gets the list of supported comparison operators.
860
+ *
861
+ * @returns Array of supported operator names
862
+ */
863
+ declare function getSupportedComparisonOperators(): ComparisonOperator[];
864
+
865
+ /**
866
+ * Dynamic Zod Schema Generator
867
+ *
868
+ * Converts FormField[] configuration to Zod validation schemas.
869
+ * Supports all form field types with proper validation rules.
870
+ *
871
+ * @module utils/generate-schema
872
+ * @since 0.1.0
873
+ */
874
+
875
+ /**
876
+ * Generate a Zod validation schema from form field configuration.
877
+ *
878
+ * Creates a dynamic Zod schema based on the form fields array,
879
+ * applying appropriate validation rules for each field type.
880
+ *
881
+ * @param fields - Array of form field configurations
882
+ * @returns Zod object schema for validating submission data
883
+ *
884
+ * @example
885
+ * ```typescript
886
+ * const fields: FormField[] = [
887
+ * { type: 'text', name: 'firstName', label: 'First Name', required: true },
888
+ * { type: 'email', name: 'email', label: 'Email', required: true },
889
+ * { type: 'number', name: 'age', label: 'Age', validation: { min: 18, max: 120 } },
890
+ * ];
891
+ *
892
+ * const schema = generateZodSchema(fields);
893
+ * const result = schema.safeParse({ firstName: 'John', email: 'john@example.com', age: 25 });
894
+ * ```
895
+ */
896
+ declare function generateZodSchema(fields: FormField[]): z.ZodObject<Record<string, z.ZodTypeAny>>;
897
+ /**
898
+ * Transform form data before validation.
899
+ *
900
+ * Handles type coercion and normalization:
901
+ * - Converts string numbers to actual numbers
902
+ * - Converts string booleans to actual booleans
903
+ * - Trims string values
904
+ * - Handles empty strings
905
+ *
906
+ * @param data - Raw form data
907
+ * @param fields - Form field configurations
908
+ * @returns Transformed data ready for validation
909
+ */
910
+ declare function transformFormData(data: Record<string, unknown>, fields: FormField[]): Record<string, unknown>;
911
+ /**
912
+ * Validate form data against a form's field configuration.
913
+ *
914
+ * Combines transformation and validation in one step.
915
+ *
916
+ * @param data - Raw form data
917
+ * @param fields - Form field configurations
918
+ * @returns Zod safe parse result
919
+ *
920
+ * @example
921
+ * ```typescript
922
+ * const result = validateFormData({ name: 'John', email: 'john@example.com' }, fields);
923
+ *
924
+ * if (result.success) {
925
+ * console.log('Valid data:', result.data);
926
+ * } else {
927
+ * console.log('Validation errors:', result.error.flatten());
928
+ * }
929
+ * ```
930
+ */
931
+ declare function validateFormData(data: Record<string, unknown>, fields: FormField[]): z.SafeParseReturnType<Record<string, unknown>, Record<string, unknown>>;
932
+ /**
933
+ * Get validation errors as a flat object.
934
+ *
935
+ * @param result - Zod safe parse result
936
+ * @returns Object mapping field names to error messages
937
+ */
938
+ declare function getValidationErrors(result: z.SafeParseReturnType<unknown, unknown>): Record<string, string>;
939
+
940
+ /**
941
+ * Export Formats Utilities
942
+ *
943
+ * Pure utility functions for exporting form submissions to CSV and JSON formats.
944
+ * These functions generate formatted strings that can be used for file downloads
945
+ * or API responses.
946
+ *
947
+ * @module utils/export-formats
948
+ * @since 0.1.0
949
+ */
950
+
951
+ /**
952
+ * Options for CSV export.
953
+ */
954
+ interface CSVExportOptions {
955
+ /**
956
+ * Whether to include metadata columns (_id, _status, _submittedAt, etc.).
957
+ * @default true
958
+ */
959
+ includeMetadata?: boolean;
960
+ /**
961
+ * Delimiter character for CSV.
962
+ * @default ","
963
+ */
964
+ delimiter?: string;
965
+ /**
966
+ * Whether to include UTF-8 BOM for Excel compatibility.
967
+ * @default true
968
+ */
969
+ includeBOM?: boolean;
970
+ /**
971
+ * Date format for timestamps.
972
+ * @default "iso" (ISO 8601)
973
+ */
974
+ dateFormat?: "iso" | "locale";
975
+ }
976
+ /**
977
+ * Options for JSON export.
978
+ */
979
+ interface JSONExportOptions {
980
+ /**
981
+ * Whether to include metadata in each submission.
982
+ * @default true
983
+ */
984
+ includeMetadata?: boolean;
985
+ /**
986
+ * Whether to include form definition in the export.
987
+ * @default true
988
+ */
989
+ includeFormDefinition?: boolean;
990
+ /**
991
+ * Number of spaces for indentation (0 for minified).
992
+ * @default 2
993
+ */
994
+ indent?: number;
995
+ }
996
+ /**
997
+ * Exported JSON structure.
998
+ */
999
+ interface ExportedJSON {
1000
+ /** Export metadata */
1001
+ exportedAt: string;
1002
+ /** Form information (if includeFormDefinition is true) */
1003
+ form?: {
1004
+ id: string;
1005
+ name: string;
1006
+ slug: string;
1007
+ fields: Array<{
1008
+ name: string;
1009
+ label: string;
1010
+ type: string;
1011
+ }>;
1012
+ };
1013
+ /** Total number of submissions in export */
1014
+ totalSubmissions: number;
1015
+ /** Array of submission data */
1016
+ submissions: Array<Record<string, unknown>>;
1017
+ }
1018
+ /**
1019
+ * Format a field value for export based on field type.
1020
+ *
1021
+ * @param value - The raw value from submission data
1022
+ * @param field - The field definition (optional, for type-specific formatting)
1023
+ * @returns Formatted string representation of the value
1024
+ *
1025
+ * @example
1026
+ * ```typescript
1027
+ * formatExportValue(true, { type: 'checkbox' }); // "Yes"
1028
+ * formatExportValue(['a', 'b'], { type: 'select' }); // "a, b"
1029
+ * formatExportValue(new Date(), { type: 'date' }); // "2024-01-15"
1030
+ * ```
1031
+ */
1032
+ declare function formatExportValue(value: unknown, field?: FormField): string;
1033
+ /**
1034
+ * Export submissions to CSV format.
1035
+ *
1036
+ * Generates an RFC 4180 compliant CSV string with:
1037
+ * - Form field values as columns (in form definition order)
1038
+ * - Optional metadata columns at the end (_id, _status, _submittedAt, etc.)
1039
+ * - Proper escaping of special characters
1040
+ * - Optional UTF-8 BOM for Excel compatibility
1041
+ *
1042
+ * @param submissions - Array of submission documents to export
1043
+ * @param form - Form document containing field definitions
1044
+ * @param options - Export options
1045
+ * @returns CSV formatted string
1046
+ *
1047
+ * @example
1048
+ * ```typescript
1049
+ * const csv = exportToCSV(submissions, form, {
1050
+ * includeMetadata: true,
1051
+ * includeBOM: true,
1052
+ * });
1053
+ *
1054
+ * // Download in browser
1055
+ * downloadFile(csv, 'submissions.csv', 'text/csv;charset=utf-8');
1056
+ * ```
1057
+ */
1058
+ declare function exportToCSV(submissions: SubmissionDocument[], form: FormDocument, options?: CSVExportOptions): string;
1059
+ /**
1060
+ * Export submissions to JSON format.
1061
+ *
1062
+ * Generates a structured JSON export with:
1063
+ * - Export metadata (timestamp)
1064
+ * - Optional form definition
1065
+ * - Array of submissions with field values
1066
+ * - Optional submission metadata (_id, _status, etc.)
1067
+ *
1068
+ * @param submissions - Array of submission documents to export
1069
+ * @param form - Form document containing field definitions
1070
+ * @param options - Export options
1071
+ * @returns JSON formatted string
1072
+ *
1073
+ * @example
1074
+ * ```typescript
1075
+ * const json = exportToJSON(submissions, form, {
1076
+ * includeMetadata: true,
1077
+ * includeFormDefinition: true,
1078
+ * indent: 2,
1079
+ * });
1080
+ *
1081
+ * // Download in browser
1082
+ * downloadFile(json, 'submissions.json', 'application/json');
1083
+ * ```
1084
+ */
1085
+ declare function exportToJSON(submissions: SubmissionDocument[], form: FormDocument, options?: JSONExportOptions): string;
1086
+ /**
1087
+ * Trigger a file download in the browser.
1088
+ *
1089
+ * Creates a Blob from the content and triggers a download using a temporary
1090
+ * anchor element. This function is browser-only and will throw in Node.js.
1091
+ *
1092
+ * @param content - File content as string
1093
+ * @param filename - Name for the downloaded file
1094
+ * @param mimeType - MIME type of the file
1095
+ *
1096
+ * @example
1097
+ * ```typescript
1098
+ * const csv = exportToCSV(submissions, form);
1099
+ * downloadFile(csv, 'contact-form-submissions.csv', 'text/csv;charset=utf-8');
1100
+ *
1101
+ * const json = exportToJSON(submissions, form);
1102
+ * downloadFile(json, 'submissions.json', 'application/json');
1103
+ * ```
1104
+ */
1105
+ declare function downloadFile(content: string, filename: string, mimeType: string): void;
1106
+ /**
1107
+ * Generate a filename for export.
1108
+ *
1109
+ * Creates a filename with form slug and current timestamp.
1110
+ *
1111
+ * @param formSlug - The form's slug
1112
+ * @param format - Export format ('csv' or 'json')
1113
+ * @returns Generated filename
1114
+ *
1115
+ * @example
1116
+ * ```typescript
1117
+ * generateExportFilename('contact-form', 'csv');
1118
+ * // Returns: "contact-form-submissions-2024-01-15.csv"
1119
+ * ```
1120
+ */
1121
+ declare function generateExportFilename(formSlug: string, format: "csv" | "json"): string;
1122
+ /**
1123
+ * Export submissions and trigger browser download.
1124
+ *
1125
+ * Convenience function that combines export and download in one call.
1126
+ * Browser-only.
1127
+ *
1128
+ * @param submissions - Array of submission documents
1129
+ * @param form - Form document
1130
+ * @param format - Export format ('csv' or 'json')
1131
+ * @param options - Format-specific options
1132
+ *
1133
+ * @example
1134
+ * ```typescript
1135
+ * // In a React component
1136
+ * const handleExport = (format: 'csv' | 'json') => {
1137
+ * exportAndDownload(submissions, form, format);
1138
+ * };
1139
+ * ```
1140
+ */
1141
+ declare function exportAndDownload(submissions: SubmissionDocument[], form: FormDocument, format: "csv" | "json", options?: CSVExportOptions | JSONExportOptions): void;
1142
+
1143
+ /**
1144
+ * Form Submission Handler
1145
+ *
1146
+ * Processes form submissions including validation, spam detection,
1147
+ * storage, and notification triggering.
1148
+ *
1149
+ * @module handlers/submit-form
1150
+ * @since 0.1.0
1151
+ */
1152
+
1153
+ /**
1154
+ * Options for submitting a form.
1155
+ */
1156
+ interface SubmitFormOptions {
1157
+ /** Form slug (URL-friendly identifier) */
1158
+ formSlug: string;
1159
+ /** Form submission data */
1160
+ data: Record<string, unknown>;
1161
+ /** Optional metadata about the submission */
1162
+ metadata?: {
1163
+ /** Submitter's IP address */
1164
+ ipAddress?: string;
1165
+ /** Submitter's user agent string */
1166
+ userAgent?: string;
1167
+ };
1168
+ }
1169
+ /**
1170
+ * Result of form submission.
1171
+ */
1172
+ interface SubmitFormResult {
1173
+ /** Whether the submission was successful */
1174
+ success: boolean;
1175
+ /** The created submission document (on success) */
1176
+ submission?: SubmissionDocument;
1177
+ /** Error message (on failure) */
1178
+ error?: string;
1179
+ /** Field-level validation errors */
1180
+ validationErrors?: Record<string, string>;
1181
+ /** Redirect URL (if form configured for redirect on success) */
1182
+ redirect?: string;
1183
+ }
1184
+ /**
1185
+ * Context for the submission handler.
1186
+ */
1187
+ interface SubmitFormContext {
1188
+ /** Plugin context with access to services */
1189
+ pluginContext: PluginContext;
1190
+ /** Resolved plugin configuration */
1191
+ pluginConfig: ResolvedFormBuilderConfig;
1192
+ }
1193
+ /**
1194
+ * Process a form submission.
1195
+ *
1196
+ * This is the main entry point for handling form submissions.
1197
+ * It performs the following steps:
1198
+ *
1199
+ * 1. Fetch form configuration by slug
1200
+ * 2. Verify form status (must be "published")
1201
+ * 3. Transform and validate submission data
1202
+ * 4. Check for spam (honeypot, rate limiting)
1203
+ * 5. Create submission record in database
1204
+ * 6. Return result with redirect info if configured
1205
+ *
1206
+ * Note: Email notifications are triggered separately via collection
1207
+ * hooks in the submissions collection (see Phase 4.2).
1208
+ *
1209
+ * @param options - Submission options
1210
+ * @param context - Handler context with services
1211
+ * @returns Submission result
1212
+ *
1213
+ * @example
1214
+ * ```typescript
1215
+ * const result = await submitForm(
1216
+ * {
1217
+ * formSlug: 'contact-form',
1218
+ * data: { name: 'John', email: 'john@example.com', message: 'Hello!' },
1219
+ * metadata: { ipAddress: '127.0.0.1', userAgent: 'Mozilla/5.0...' },
1220
+ * },
1221
+ * { pluginContext, pluginConfig }
1222
+ * );
1223
+ *
1224
+ * if (result.success) {
1225
+ * console.log('Submission created:', result.submission?.id);
1226
+ * if (result.redirect) {
1227
+ * // Redirect user
1228
+ * }
1229
+ * } else {
1230
+ * console.error('Submission failed:', result.error);
1231
+ * }
1232
+ * ```
1233
+ */
1234
+ declare function submitForm(options: SubmitFormOptions, context: SubmitFormContext): Promise<SubmitFormResult>;
1235
+ /**
1236
+ * Validate form data without creating a submission.
1237
+ *
1238
+ * Useful for real-time validation in the frontend.
1239
+ *
1240
+ * @param formSlug - Form slug
1241
+ * @param data - Form data to validate
1242
+ * @param context - Handler context
1243
+ * @returns Validation result
1244
+ *
1245
+ * @example
1246
+ * ```typescript
1247
+ * const result = await validateSubmission(
1248
+ * 'contact-form',
1249
+ * { name: 'John', email: 'invalid-email' },
1250
+ * { pluginContext, pluginConfig }
1251
+ * );
1252
+ *
1253
+ * if (!result.valid) {
1254
+ * console.log('Validation errors:', result.errors);
1255
+ * }
1256
+ * ```
1257
+ */
1258
+ declare function validateSubmission(formSlug: string, data: Record<string, unknown>, context: SubmitFormContext): Promise<{
1259
+ valid: boolean;
1260
+ errors?: Record<string, string>;
1261
+ }>;
1262
+ /**
1263
+ * Check if a form is accepting submissions.
1264
+ *
1265
+ * @param formSlug - Form slug
1266
+ * @param context - Handler context
1267
+ * @returns Whether the form accepts submissions
1268
+ */
1269
+ declare function isFormAcceptingSubmissions(formSlug: string, context: SubmitFormContext): Promise<boolean>;
1270
+ /**
1271
+ * Get submission statistics for a form.
1272
+ *
1273
+ * Note: This function uses client-side filtering which works for
1274
+ * small-medium submission counts. For large-scale deployments,
1275
+ * consider using direct database queries with COUNT aggregation.
1276
+ *
1277
+ * @param formSlug - Form slug
1278
+ * @param context - Handler context
1279
+ * @returns Submission statistics
1280
+ */
1281
+ declare function getFormSubmissionStats(formSlug: string, context: SubmitFormContext): Promise<{
1282
+ total: number;
1283
+ new: number;
1284
+ read: number;
1285
+ archived: number;
1286
+ } | null>;
1287
+
1288
+ /**
1289
+ * Spam Detection
1290
+ *
1291
+ * Implements honeypot and rate limiting spam protection for form submissions.
1292
+ * Provides a pluggable approach to spam detection that can be extended.
1293
+ *
1294
+ * @module handlers/spam-detection
1295
+ * @since 0.1.0
1296
+ */
1297
+ /**
1298
+ * Configuration for spam detection.
1299
+ */
1300
+ interface SpamCheckConfig {
1301
+ /**
1302
+ * Enable honeypot field detection.
1303
+ * When enabled, submissions with filled honeypot fields are marked as spam.
1304
+ */
1305
+ honeypot?: boolean;
1306
+ /**
1307
+ * Rate limiting configuration.
1308
+ */
1309
+ rateLimit?: {
1310
+ /** Maximum submissions allowed per window */
1311
+ maxSubmissions: number;
1312
+ /** Time window in milliseconds */
1313
+ windowMs: number;
1314
+ };
1315
+ /**
1316
+ * reCAPTCHA configuration (for future implementation).
1317
+ */
1318
+ recaptcha?: {
1319
+ enabled: boolean;
1320
+ secretKey?: string;
1321
+ scoreThreshold?: number;
1322
+ };
1323
+ }
1324
+ /**
1325
+ * Options for spam check.
1326
+ */
1327
+ interface SpamCheckOptions {
1328
+ /** Form submission data */
1329
+ data: Record<string, unknown>;
1330
+ /** Submitter's IP address */
1331
+ ipAddress?: string;
1332
+ /** Form slug for rate limiting key */
1333
+ formSlug: string;
1334
+ /** Spam protection configuration */
1335
+ config: SpamCheckConfig;
1336
+ }
1337
+ /**
1338
+ * Result of spam check.
1339
+ */
1340
+ interface SpamCheckResult {
1341
+ /** Whether the submission is detected as spam */
1342
+ isSpam: boolean;
1343
+ /** Reason for spam detection (for logging, not exposed to user) */
1344
+ reason?: "honeypot" | "rate_limit" | "recaptcha";
1345
+ /** Additional details (for logging) */
1346
+ details?: string;
1347
+ }
1348
+ /**
1349
+ * Check a form submission for spam indicators.
1350
+ *
1351
+ * Performs multiple spam checks based on configuration:
1352
+ * 1. Honeypot field detection
1353
+ * 2. Rate limiting by IP/form
1354
+ * 3. reCAPTCHA verification (future)
1355
+ *
1356
+ * @param options - Spam check options
1357
+ * @returns Spam check result
1358
+ *
1359
+ * @example
1360
+ * ```typescript
1361
+ * const result = await checkSpam({
1362
+ * data: formData,
1363
+ * ipAddress: req.ip,
1364
+ * formSlug: 'contact-form',
1365
+ * config: {
1366
+ * honeypot: true,
1367
+ * rateLimit: { maxSubmissions: 5, windowMs: 60000 },
1368
+ * },
1369
+ * });
1370
+ *
1371
+ * if (result.isSpam) {
1372
+ * // Silently reject (return fake success to bot)
1373
+ * return { success: true };
1374
+ * }
1375
+ * ```
1376
+ */
1377
+ declare function checkSpam(options: SpamCheckOptions): Promise<SpamCheckResult>;
1378
+ /**
1379
+ * Clean up expired rate limit entries.
1380
+ *
1381
+ * Call this periodically (e.g., every 5 minutes) to prevent
1382
+ * memory leaks from accumulated rate limit entries.
1383
+ *
1384
+ * @param maxAgeMs - Maximum age of entries to keep (default: 5 minutes)
1385
+ * @returns Number of entries removed
1386
+ *
1387
+ * @example
1388
+ * ```typescript
1389
+ * // Set up periodic cleanup
1390
+ * setInterval(() => {
1391
+ * const removed = cleanupRateLimitStore();
1392
+ * console.log(`Cleaned up ${removed} expired rate limit entries`);
1393
+ * }, 5 * 60 * 1000); // Every 5 minutes
1394
+ * ```
1395
+ */
1396
+ declare function cleanupRateLimitStore(maxAgeMs?: number): number;
1397
+ /**
1398
+ * Get the current size of the rate limit store.
1399
+ *
1400
+ * Useful for monitoring and debugging.
1401
+ *
1402
+ * @returns Number of entries in the store
1403
+ */
1404
+ declare function getRateLimitStoreSize(): number;
1405
+ /**
1406
+ * Clear all rate limit entries.
1407
+ *
1408
+ * Useful for testing or resetting state.
1409
+ */
1410
+ declare function clearRateLimitStore(): void;
1411
+ /**
1412
+ * Check if a specific IP/form is currently rate limited.
1413
+ *
1414
+ * @param ipAddress - IP address to check
1415
+ * @param formSlug - Form slug
1416
+ * @param config - Rate limit configuration
1417
+ * @returns Whether the IP is currently rate limited
1418
+ */
1419
+ declare function isRateLimited(ipAddress: string, formSlug: string, config: {
1420
+ maxSubmissions: number;
1421
+ windowMs: number;
1422
+ }): boolean;
1423
+
1424
+ /**
1425
+ * Webhook Handler
1426
+ *
1427
+ * Triggers webhook notifications for form events.
1428
+ * Webhooks are fired asynchronously and do not block form submission.
1429
+ *
1430
+ * @module handlers/webhooks
1431
+ * @since 0.1.0
1432
+ */
1433
+
1434
+ /**
1435
+ * Webhook payload sent to configured endpoints.
1436
+ */
1437
+ interface WebhookPayload {
1438
+ /** Event type that triggered the webhook */
1439
+ event: WebhookEvent;
1440
+ /** ISO 8601 timestamp when the event occurred */
1441
+ timestamp: string;
1442
+ /** Form information */
1443
+ form: {
1444
+ id: string;
1445
+ slug: string;
1446
+ name: string;
1447
+ };
1448
+ /** Submission information (full data or minimal based on config) */
1449
+ submission: SubmissionDocument | {
1450
+ id: string;
1451
+ submittedAt: Date;
1452
+ };
1453
+ }
1454
+ /**
1455
+ * Options for triggering webhooks.
1456
+ */
1457
+ interface TriggerWebhooksOptions {
1458
+ /** Event type */
1459
+ event: WebhookEvent;
1460
+ /** Form document */
1461
+ form: FormDocument;
1462
+ /** Submission document */
1463
+ submission: SubmissionDocument;
1464
+ }
1465
+ /**
1466
+ * Result of a single webhook delivery attempt.
1467
+ */
1468
+ interface WebhookDeliveryResult {
1469
+ /** Webhook URL */
1470
+ url: string;
1471
+ /** Whether the delivery was successful */
1472
+ success: boolean;
1473
+ /** HTTP status code (if request completed) */
1474
+ statusCode?: number;
1475
+ /** Error message (if delivery failed) */
1476
+ error?: string;
1477
+ /** Time taken in milliseconds */
1478
+ durationMs: number;
1479
+ }
1480
+ /**
1481
+ * Result of triggering webhooks for an event.
1482
+ */
1483
+ interface TriggerWebhooksResult {
1484
+ /** Number of webhooks triggered */
1485
+ triggered: number;
1486
+ /** Number of successful deliveries */
1487
+ successful: number;
1488
+ /** Number of failed deliveries */
1489
+ failed: number;
1490
+ /** Individual delivery results */
1491
+ results: WebhookDeliveryResult[];
1492
+ }
1493
+ /**
1494
+ * Trigger webhooks for a form event.
1495
+ *
1496
+ * This function fires all matching webhooks asynchronously and does not
1497
+ * block form submission. Failed webhooks are logged but do not cause
1498
+ * submission failures.
1499
+ *
1500
+ * @param options - Trigger options
1501
+ * @returns Result of webhook triggering
1502
+ *
1503
+ * @example
1504
+ * ```typescript
1505
+ * // After creating a submission
1506
+ * const result = await triggerWebhooks({
1507
+ * event: 'submission.created',
1508
+ * form,
1509
+ * submission,
1510
+ * });
1511
+ *
1512
+ * console.log(`Triggered ${result.triggered} webhooks`);
1513
+ * console.log(`Successful: ${result.successful}, Failed: ${result.failed}`);
1514
+ * ```
1515
+ */
1516
+ declare function triggerWebhooks(options: TriggerWebhooksOptions): Promise<TriggerWebhooksResult>;
1517
+ /**
1518
+ * Trigger webhooks without waiting for completion.
1519
+ *
1520
+ * This is the recommended way to call webhooks from submission handlers
1521
+ * as it doesn't block the response. Errors are logged but not thrown.
1522
+ *
1523
+ * @param options - Trigger options
1524
+ * @param logger - Optional logger for error reporting
1525
+ *
1526
+ * @example
1527
+ * ```typescript
1528
+ * // In submission handler - fire and forget
1529
+ * fireWebhooks(
1530
+ * { event: 'submission.created', form, submission },
1531
+ * pluginContext.infra.logger
1532
+ * );
1533
+ *
1534
+ * // Continue with response immediately
1535
+ * return { success: true, submission };
1536
+ * ```
1537
+ */
1538
+ declare function fireWebhooks(options: TriggerWebhooksOptions, logger?: {
1539
+ info?: (message: string, data?: Record<string, unknown>) => void;
1540
+ warn?: (message: string, data?: Record<string, unknown>) => void;
1541
+ error?: (message: string, data?: Record<string, unknown>) => void;
1542
+ }): void;
1543
+ /**
1544
+ * Validate a webhook URL.
1545
+ *
1546
+ * @param url - URL to validate
1547
+ * @returns Whether the URL is valid for webhooks
1548
+ */
1549
+ declare function isValidWebhookUrl(url: string): boolean;
1550
+ /**
1551
+ * Get the list of supported webhook events.
1552
+ *
1553
+ * @returns Array of supported event types
1554
+ */
1555
+ declare function getSupportedWebhookEvents(): WebhookEvent[];
1556
+
1557
+ /**
1558
+ * Forms Collection
1559
+ *
1560
+ * Stores form definitions created via Code-First or Schema Builder.
1561
+ * Each form contains field definitions, settings, notifications, and access control.
1562
+ *
1563
+ * @module collections/forms
1564
+ * @since 0.1.0
1565
+ */
1566
+
1567
+ /**
1568
+ * Component path string format.
1569
+ * Format: `"package-name/path#ExportName"`
1570
+ */
1571
+ type ComponentPath = string;
1572
+ /**
1573
+ * Custom view configuration for replacing default admin views.
1574
+ */
1575
+ interface CollectionAdminViewConfig {
1576
+ /** Component path to the custom view component */
1577
+ Component: ComponentPath;
1578
+ }
1579
+ /**
1580
+ * Custom components configuration for collection admin UI.
1581
+ */
1582
+ interface CollectionAdminComponents {
1583
+ views?: {
1584
+ Edit?: CollectionAdminViewConfig;
1585
+ List?: CollectionAdminViewConfig;
1586
+ };
1587
+ BeforeListTable?: ComponentPath;
1588
+ AfterListTable?: ComponentPath;
1589
+ BeforeEdit?: ComponentPath;
1590
+ AfterEdit?: ComponentPath;
1591
+ }
1592
+ /**
1593
+ * Extended CollectionConfig with custom admin components support.
1594
+ * This extends the base CollectionConfig to include the components property.
1595
+ */
1596
+ interface ExtendedCollectionConfig$1 extends Omit<CollectionConfig, "admin"> {
1597
+ admin?: CollectionConfig["admin"] & {
1598
+ /** Whether this collection is provided by a plugin */
1599
+ isPlugin?: boolean;
1600
+ components?: CollectionAdminComponents;
1601
+ };
1602
+ }
1603
+ /**
1604
+ * Generate the Forms collection configuration.
1605
+ *
1606
+ * Creates a collection for storing form definitions with fields,
1607
+ * settings, notifications, and access control.
1608
+ *
1609
+ * @param pluginConfig - Resolved plugin configuration
1610
+ * @returns ExtendedCollectionConfig for the Forms collection (with custom admin components)
1611
+ *
1612
+ * @example
1613
+ * ```typescript
1614
+ * const formsCol = formsCollection(resolvedConfig);
1615
+ * // Returns a collection with slug "forms" (or custom slug from config)
1616
+ * ```
1617
+ */
1618
+ declare function formsCollection(pluginConfig: ResolvedFormBuilderConfig): ExtendedCollectionConfig$1;
1619
+
1620
+ /**
1621
+ * Form Submissions Collection
1622
+ *
1623
+ * Stores form submission data from user-submitted forms.
1624
+ * Each submission contains the submitted data, metadata, and status.
1625
+ *
1626
+ * @module collections/submissions
1627
+ * @since 0.1.0
1628
+ */
1629
+
1630
+ /**
1631
+ * Extended CollectionConfig with isPlugin support.
1632
+ * This extends the base CollectionConfig to include the isPlugin property.
1633
+ */
1634
+ interface ExtendedCollectionConfig extends Omit<CollectionConfig, "admin"> {
1635
+ admin?: CollectionConfig["admin"] & {
1636
+ /** Whether this collection is provided by a plugin */
1637
+ isPlugin?: boolean;
1638
+ };
1639
+ }
1640
+ /**
1641
+ * Generate the Form Submissions collection configuration.
1642
+ *
1643
+ * Creates a collection for storing form submissions with:
1644
+ * - Reference to the parent form
1645
+ * - Submitted data (JSON)
1646
+ * - Status tracking (new, read, archived)
1647
+ * - Metadata (IP, user agent, timestamp)
1648
+ * - Internal notes for admin use
1649
+ *
1650
+ * @param pluginConfig - Resolved plugin configuration
1651
+ * @returns CollectionConfig for the Submissions collection
1652
+ *
1653
+ * @example
1654
+ * ```typescript
1655
+ * const submissionsCol = submissionsCollection(resolvedConfig);
1656
+ * // Returns a collection with slug "form-submissions" (or custom slug from config)
1657
+ * ```
1658
+ */
1659
+ declare function submissionsCollection(pluginConfig: ResolvedFormBuilderConfig): ExtendedCollectionConfig;
1660
+
1661
+ declare const formBuilderPlugin: nextly.PluginDefinition;
1662
+
1663
+ export { type CSVExportOptions, CheckboxFormField, type ComparisonOperator, ConditionalLogic, DEFAULT_FORM_SETTINGS, DEFAULT_NOTIFICATION_SETTINGS, DateFormField, EmailFormField, type ExportedJSON, FileFormField, FormBuilderPluginOptions, type FormBuilderPluginResult, FormConfig, FormDocument, FormField, type FormFieldOption, FormNotifications, FormSettings, type FormValidationError, type FormValidationErrorCode, type FormValidationResult, HiddenFormField, type JSONExportOptions, type NextlyPlugin, NumberFormField, PhoneFormField, RESERVED_FORM_SLUGS, RadioFormField, ResolvedFormBuilderConfig, SelectFormField, type SpamCheckConfig, type SpamCheckOptions, type SpamCheckResult, SubmissionDocument, type SubmitFormContext, type SubmitFormOptions, type SubmitFormResult, TextFormField, TextareaFormField, TimeFormField, type TriggerWebhooksOptions, type TriggerWebhooksResult, UrlFormField, type WebhookDeliveryResult, WebhookEvent, type WebhookPayload, applyFieldDefaults, applyFormDefaults, assertValidFormConfig, checkSpam, checkbox, cleanupRateLimitStore, clearRateLimitStore, createFormConfig, date, downloadFile, email, evaluateConditions, exportAndDownload, exportToCSV, exportToJSON, file, fireWebhooks, formBuilder, formBuilderPlugin, formatExportValue, formsCollection, generateExportFilename, generateZodSchema, getFormBuilderConfig, getFormSubmissionStats, getRateLimitStoreSize, getSupportedComparisonOperators, getSupportedWebhookEvents, getValidationErrors, hidden, isFormAcceptingSubmissions, isRateLimited, isValidComparisonOperator, isValidWebhookUrl, number, option, phone, pluralize, radio, select, submissionsCollection, submitForm, text, textarea, time, toTitleCase, transformFormData, triggerWebhooks, url, validateFormConfig, validateFormData, validateSubmission };