@nextlyhq/plugin-form-builder 0.0.2-alpha.65 → 0.0.2-alpha.66
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/dist/admin/index.js +1 -1
- package/dist/{chunk-W6NLZFHH.js → chunk-VIGKSRSH.js} +117 -117
- package/dist/chunk-VIGKSRSH.js.map +1 -0
- package/dist/index.d.ts +130 -48
- package/dist/index.js +1244 -935
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
- package/dist/chunk-W6NLZFHH.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -936,6 +936,20 @@ declare function validateFormData(data: Record<string, unknown>, fields: AnyForm
|
|
|
936
936
|
* @param result - Zod safe parse result
|
|
937
937
|
* @returns Object mapping field names to error messages
|
|
938
938
|
*/
|
|
939
|
+
/** A field that failed, with the machine code a wire error reports. */
|
|
940
|
+
interface ValidationIssue {
|
|
941
|
+
path: string;
|
|
942
|
+
/** `REQUIRED` for an answer that is missing or empty, else `INVALID`. */
|
|
943
|
+
code: "REQUIRED" | "INVALID";
|
|
944
|
+
message: string;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* The same failures as a field-to-message map.
|
|
948
|
+
*
|
|
949
|
+
* Derived from {@link getValidationIssues} rather than walking the issues a
|
|
950
|
+
* second time, so the two cannot disagree about which message belongs to a
|
|
951
|
+
* field or which failure won when one field had several.
|
|
952
|
+
*/
|
|
939
953
|
declare function getValidationErrors(result: z.ZodSafeParseResult<unknown>): Record<string, string>;
|
|
940
954
|
|
|
941
955
|
/**
|
|
@@ -1166,21 +1180,126 @@ interface SubmitFormOptions {
|
|
|
1166
1180
|
/** Submitter's user agent string */
|
|
1167
1181
|
userAgent?: string;
|
|
1168
1182
|
};
|
|
1183
|
+
/**
|
|
1184
|
+
* The HTTP request this submission arrived on.
|
|
1185
|
+
*
|
|
1186
|
+
* A route calling this helper is its own route, outside everything Nextly
|
|
1187
|
+
* pins a request around, so nothing else can supply it. Without it the write
|
|
1188
|
+
* reaches the submissions hooks looking like a seed or a job, and every rule
|
|
1189
|
+
* scoped to a visitor stands down: the rate limit above all.
|
|
1190
|
+
*
|
|
1191
|
+
* The request rather than an address, because the core resolves the client
|
|
1192
|
+
* from it under this deployment's proxy-trust settings. `metadata.ipAddress`
|
|
1193
|
+
* is a string the caller chose, which is the right shape for the audit column
|
|
1194
|
+
* it fills and the wrong shape for deciding who to throttle.
|
|
1195
|
+
*/
|
|
1196
|
+
request?: Request;
|
|
1197
|
+
/**
|
|
1198
|
+
* Whose permissions the reads and the write are judged against.
|
|
1199
|
+
*
|
|
1200
|
+
* Defaults to `system`, so every existing caller behaves as before.
|
|
1201
|
+
*/
|
|
1202
|
+
access?: SubmissionAccess;
|
|
1203
|
+
/**
|
|
1204
|
+
* The language the visitor submitted in.
|
|
1205
|
+
*
|
|
1206
|
+
* Decides which translation a picked redirect page is read in, and so which
|
|
1207
|
+
* URL the visitor is sent to: a page whose slug is `thanks` in English and
|
|
1208
|
+
* `merci` in French answered `/thanks` to everyone, because the target was
|
|
1209
|
+
* read with no locale and the default answered. The built-in route takes it
|
|
1210
|
+
* from `?locale=` on the submit URL, the same way core's own routes take a
|
|
1211
|
+
* locale on the wire; a host route passes whatever it resolved for the page
|
|
1212
|
+
* the form was on.
|
|
1213
|
+
*
|
|
1214
|
+
* A code that is not a configured locale reads as the default rather than
|
|
1215
|
+
* failing the submission, which the collection services decide. Absent means
|
|
1216
|
+
* the default, as it always has. The read wildcards (`all`, `*`) are not a
|
|
1217
|
+
* language a visitor can have submitted in and are treated as absent: a read
|
|
1218
|
+
* in "every language" answers a localized URL field with one value per
|
|
1219
|
+
* language, which no URL pattern can be filled from.
|
|
1220
|
+
*/
|
|
1221
|
+
locale?: string;
|
|
1169
1222
|
}
|
|
1223
|
+
/**
|
|
1224
|
+
* How this submission reaches the collections it touches.
|
|
1225
|
+
*
|
|
1226
|
+
* `system` is the default and is right for host code: the caller already
|
|
1227
|
+
* decided, and there is no visitor whose permissions could be consulted.
|
|
1228
|
+
*
|
|
1229
|
+
* `public` is for a route serving an anonymous visitor. It keeps the
|
|
1230
|
+
* collection's configured `access` rules in force, so a host that closed
|
|
1231
|
+
* submissions or restricted reading forms is obeyed rather than bypassed. The
|
|
1232
|
+
* former core endpoint ran this way, and elevating a public route to `system`
|
|
1233
|
+
* silently overrode that configuration.
|
|
1234
|
+
*/
|
|
1235
|
+
type SubmissionAccess = {
|
|
1236
|
+
as: "system";
|
|
1237
|
+
} | {
|
|
1238
|
+
as: "public";
|
|
1239
|
+
};
|
|
1240
|
+
/**
|
|
1241
|
+
* Why a submission ended the way it did.
|
|
1242
|
+
*
|
|
1243
|
+
* `success` alone cannot carry this. An HTTP route has to answer 404 for a slug
|
|
1244
|
+
* nobody used, 409 for a form its author closed and 400 for data that did not
|
|
1245
|
+
* validate, and a boolean plus a human sentence cannot tell those apart without
|
|
1246
|
+
* matching on the sentence.
|
|
1247
|
+
*
|
|
1248
|
+
* `accepted` covers every ending a visitor is told succeeded, INCLUDING the two
|
|
1249
|
+
* that are deliberately indistinguishable from success: a submission the
|
|
1250
|
+
* honeypot flagged, and one the rate limiter refused. A caller must not try to
|
|
1251
|
+
* separate them, which is why they do not appear here as their own names.
|
|
1252
|
+
*/
|
|
1253
|
+
type SubmitFormOutcome = "accepted" | "no-such-form" | "closed" | "invalid" | "duplicate" | "failed";
|
|
1170
1254
|
/**
|
|
1171
1255
|
* Result of form submission.
|
|
1172
1256
|
*/
|
|
1173
1257
|
interface SubmitFormResult {
|
|
1174
1258
|
/** Whether the submission was successful */
|
|
1175
1259
|
success: boolean;
|
|
1260
|
+
/**
|
|
1261
|
+
* Which ending this was, for a caller that has to map it to a status code.
|
|
1262
|
+
*
|
|
1263
|
+
* Required rather than optional, so a return path added later cannot omit it
|
|
1264
|
+
* and leave the HTTP route guessing from the message text.
|
|
1265
|
+
*/
|
|
1266
|
+
outcome: SubmitFormOutcome;
|
|
1176
1267
|
/** The created submission document (on success) */
|
|
1177
1268
|
submission?: SubmissionDocument;
|
|
1178
1269
|
/** Error message (on failure) */
|
|
1179
1270
|
error?: string;
|
|
1180
1271
|
/** Field-level validation errors */
|
|
1181
1272
|
validationErrors?: Record<string, string>;
|
|
1273
|
+
/**
|
|
1274
|
+
* The same failures with their machine codes, for a caller building an HTTP
|
|
1275
|
+
* error. `validationErrors` above cannot carry one, so a route reading only
|
|
1276
|
+
* that has to invent a code and reports every blank answer as `INVALID`.
|
|
1277
|
+
*/
|
|
1278
|
+
validationIssues?: ValidationIssue[];
|
|
1182
1279
|
/** Redirect URL (if form configured for redirect on success) */
|
|
1183
1280
|
redirect?: string;
|
|
1281
|
+
/**
|
|
1282
|
+
* The error that ended a `failed` submission, kept rather than flattened.
|
|
1283
|
+
*
|
|
1284
|
+
* A collection can refuse a write for reasons its host chose: a
|
|
1285
|
+
* `beforeValidate` hook throwing `NextlyError.validation`, a uniqueness
|
|
1286
|
+
* conflict. Those carry their own status and field detail, and reporting them
|
|
1287
|
+
* all as one internal error tells a visitor the server broke when in fact
|
|
1288
|
+
* their submission was answered deliberately.
|
|
1289
|
+
*/
|
|
1290
|
+
cause?: unknown;
|
|
1291
|
+
/**
|
|
1292
|
+
* The toast the form's author wrote for a successful submission.
|
|
1293
|
+
*
|
|
1294
|
+
* Carried on the result rather than read from the form again by whoever
|
|
1295
|
+
* presents it: the HTTP route would otherwise have to fetch the form a second
|
|
1296
|
+
* time purely to learn a string this function already had in hand, and a
|
|
1297
|
+
* second read is a second chance to disagree about which form was submitted.
|
|
1298
|
+
*
|
|
1299
|
+
* Present on every `accepted` ending, including the ones a bot caused, so the
|
|
1300
|
+
* message cannot be used to tell an accepted submission from a flagged one.
|
|
1301
|
+
*/
|
|
1302
|
+
successMessage?: string;
|
|
1184
1303
|
}
|
|
1185
1304
|
/**
|
|
1186
1305
|
* Context for the submission handler.
|
|
@@ -1289,8 +1408,16 @@ declare function getFormSubmissionStats(formSlug: string, context: SubmitFormCon
|
|
|
1289
1408
|
/**
|
|
1290
1409
|
* Spam Detection
|
|
1291
1410
|
*
|
|
1292
|
-
*
|
|
1293
|
-
*
|
|
1411
|
+
* Honeypot and rate limiting for form submissions.
|
|
1412
|
+
*
|
|
1413
|
+
* The honeypot is a pure read of the submitted payload. The rate limit is not:
|
|
1414
|
+
* it counts, and where the count lives decides whether the limit means
|
|
1415
|
+
* anything. It lives in the deployment's own {@link RateLimitStore}, the one
|
|
1416
|
+
* the REST and auth limiters already share, so `rateLimit.store` is configured
|
|
1417
|
+
* once and all three agree about what they are counting. A store private to
|
|
1418
|
+
* this module would count per process, and on a deployment that spans several
|
|
1419
|
+
* the effective limit becomes `configured x instances` -- a number the operator
|
|
1420
|
+
* never chose, that fails open, and only under load.
|
|
1294
1421
|
*
|
|
1295
1422
|
* @module handlers/spam-detection
|
|
1296
1423
|
* @since 0.1.0
|
|
@@ -1376,51 +1503,6 @@ interface SpamCheckResult {
|
|
|
1376
1503
|
* ```
|
|
1377
1504
|
*/
|
|
1378
1505
|
declare function checkSpam(options: SpamCheckOptions): Promise<SpamCheckResult>;
|
|
1379
|
-
/**
|
|
1380
|
-
* Clean up expired rate limit entries.
|
|
1381
|
-
*
|
|
1382
|
-
* Call this periodically (e.g., every 5 minutes) to prevent
|
|
1383
|
-
* memory leaks from accumulated rate limit entries.
|
|
1384
|
-
*
|
|
1385
|
-
* @param maxAgeMs - Maximum age of entries to keep (default: 5 minutes)
|
|
1386
|
-
* @returns Number of entries removed
|
|
1387
|
-
*
|
|
1388
|
-
* @example
|
|
1389
|
-
* ```typescript
|
|
1390
|
-
* // Set up periodic cleanup
|
|
1391
|
-
* setInterval(() => {
|
|
1392
|
-
* const removed = cleanupRateLimitStore();
|
|
1393
|
-
* console.log(`Cleaned up ${removed} expired rate limit entries`);
|
|
1394
|
-
* }, 5 * 60 * 1000); // Every 5 minutes
|
|
1395
|
-
* ```
|
|
1396
|
-
*/
|
|
1397
|
-
declare function cleanupRateLimitStore(maxAgeMs?: number): number;
|
|
1398
|
-
/**
|
|
1399
|
-
* Get the current size of the rate limit store.
|
|
1400
|
-
*
|
|
1401
|
-
* Useful for monitoring and debugging.
|
|
1402
|
-
*
|
|
1403
|
-
* @returns Number of entries in the store
|
|
1404
|
-
*/
|
|
1405
|
-
declare function getRateLimitStoreSize(): number;
|
|
1406
|
-
/**
|
|
1407
|
-
* Clear all rate limit entries.
|
|
1408
|
-
*
|
|
1409
|
-
* Useful for testing or resetting state.
|
|
1410
|
-
*/
|
|
1411
|
-
declare function clearRateLimitStore(): void;
|
|
1412
|
-
/**
|
|
1413
|
-
* Check if a specific IP/form is currently rate limited.
|
|
1414
|
-
*
|
|
1415
|
-
* @param ipAddress - IP address to check
|
|
1416
|
-
* @param formSlug - Form slug
|
|
1417
|
-
* @param config - Rate limit configuration
|
|
1418
|
-
* @returns Whether the IP is currently rate limited
|
|
1419
|
-
*/
|
|
1420
|
-
declare function isRateLimited(ipAddress: string, formSlug: string, config: {
|
|
1421
|
-
maxSubmissions: number;
|
|
1422
|
-
windowMs: number;
|
|
1423
|
-
}): boolean;
|
|
1424
1506
|
|
|
1425
1507
|
/**
|
|
1426
1508
|
* Webhook Handler
|
|
@@ -1646,4 +1728,4 @@ declare function submissionsCollection(pluginConfig: ResolvedFormBuilderConfig):
|
|
|
1646
1728
|
|
|
1647
1729
|
declare const formBuilderPlugin: nextly.PluginDefinition;
|
|
1648
1730
|
|
|
1649
|
-
export { AnyFormField, type CSVExportOptions, CheckboxFormField, type ComparisonOperator, ConditionalLogic, DEFAULT_FORM_SETTINGS, DEFAULT_NOTIFICATION_SETTINGS, DateFormField, EmailFormField, type ExportedJSON, FileFormField, FormBuilderPluginOptions, type FormBuilderPluginResult, FormConfig, FormDocument, 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,
|
|
1731
|
+
export { AnyFormField, type CSVExportOptions, CheckboxFormField, type ComparisonOperator, ConditionalLogic, DEFAULT_FORM_SETTINGS, DEFAULT_NOTIFICATION_SETTINGS, DateFormField, EmailFormField, type ExportedJSON, FileFormField, FormBuilderPluginOptions, type FormBuilderPluginResult, FormConfig, FormDocument, 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, createFormConfig, date, downloadFile, email, evaluateConditions, exportAndDownload, exportToCSV, exportToJSON, file, fireWebhooks, formBuilder, formBuilderPlugin, formatExportValue, formsCollection, generateExportFilename, generateZodSchema, getFormBuilderConfig, getFormSubmissionStats, getSupportedComparisonOperators, getSupportedWebhookEvents, getValidationErrors, hidden, isFormAcceptingSubmissions, isValidComparisonOperator, isValidWebhookUrl, number, option, phone, pluralize, radio, select, submissionsCollection, submitForm, text, textarea, time, toTitleCase, transformFormData, triggerWebhooks, url, validateFormConfig, validateFormData, validateSubmission };
|