@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.
- package/LICENSE +22 -0
- package/README.md +122 -0
- package/dist/CheckboxFieldEditor-K4T6QX52.js +121 -0
- package/dist/CheckboxFieldEditor-K4T6QX52.js.map +1 -0
- package/dist/DateFieldEditor-JFJFAVOH.js +187 -0
- package/dist/DateFieldEditor-JFJFAVOH.js.map +1 -0
- package/dist/EmailFieldEditor-MMOXXL7P.js +138 -0
- package/dist/EmailFieldEditor-MMOXXL7P.js.map +1 -0
- package/dist/NumberFieldEditor-UISAYR6Z.js +175 -0
- package/dist/NumberFieldEditor-UISAYR6Z.js.map +1 -0
- package/dist/RadioFieldEditor-6UQFH4WT.js +165 -0
- package/dist/RadioFieldEditor-6UQFH4WT.js.map +1 -0
- package/dist/SelectFieldEditor-PSO4KHV4.js +213 -0
- package/dist/SelectFieldEditor-PSO4KHV4.js.map +1 -0
- package/dist/TextFieldEditor-5EU64EA5.js +187 -0
- package/dist/TextFieldEditor-5EU64EA5.js.map +1 -0
- package/dist/TextareaFieldEditor-773G3LNM.js +190 -0
- package/dist/TextareaFieldEditor-773G3LNM.js.map +1 -0
- package/dist/admin/index.css +934 -0
- package/dist/admin/index.css.map +1 -0
- package/dist/admin/index.d.ts +547 -0
- package/dist/admin/index.js +4030 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/chunk-ABNZFTQK.js +586 -0
- package/dist/chunk-ABNZFTQK.js.map +1 -0
- package/dist/chunk-XTCEPJ7B.js +256 -0
- package/dist/chunk-XTCEPJ7B.js.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +9 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index-C0kBvYjm.d.ts +1012 -0
- package/dist/index.d.ts +1663 -0
- package/dist/index.js +2470 -0
- package/dist/index.js.map +1 -0
- package/dist/styles/form-builder.css +1150 -0
- package/dist/styles/submissions-filter.css +75 -0
- package/package.json +96 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|