@saastro/forms 0.4.0 → 0.6.0
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/index.d.ts +64 -65
- package/dist/index.js +177 -168
- package/dist/index.js.map +1 -1
- package/package.json +10 -11
package/dist/index.d.ts
CHANGED
|
@@ -3025,6 +3025,69 @@ declare global {
|
|
|
3025
3025
|
*/
|
|
3026
3026
|
declare function recaptchaPlugin(config: RecaptchaPluginConfig): FormPlugin;
|
|
3027
3027
|
|
|
3028
|
+
/**
|
|
3029
|
+
* ============================================
|
|
3030
|
+
* TURNSTILE PLUGIN - Cloudflare Turnstile
|
|
3031
|
+
* ============================================
|
|
3032
|
+
*
|
|
3033
|
+
* Mirrors `recaptchaPlugin` but for Cloudflare Turnstile. Unlike
|
|
3034
|
+
* reCAPTCHA v3 (purely programmatic), Turnstile is widget-based: it must
|
|
3035
|
+
* be rendered into a DOM element. This plugin renders the widget in
|
|
3036
|
+
* explicit + `execution: 'execute'` mode (so it only challenges at submit
|
|
3037
|
+
* time) into a container it manages, captures the token via the success
|
|
3038
|
+
* callback, and attaches it to every submission via `transformValues`.
|
|
3039
|
+
*
|
|
3040
|
+
* The token field defaults to `_captchaToken` — the canonical field the
|
|
3041
|
+
* Hub submit helper (`createHubFormSubmit`) extracts and forwards to the
|
|
3042
|
+
* worker as `captchaToken`.
|
|
3043
|
+
*/
|
|
3044
|
+
interface TurnstilePluginConfig {
|
|
3045
|
+
/** Cloudflare Turnstile site key. */
|
|
3046
|
+
siteKey: string;
|
|
3047
|
+
/** Field name for the token in submitted values (default: '_captchaToken'). */
|
|
3048
|
+
tokenField?: string;
|
|
3049
|
+
/**
|
|
3050
|
+
* Widget appearance. 'interaction-only' (default) keeps it invisible
|
|
3051
|
+
* unless Cloudflare decides a challenge is needed — closest to the
|
|
3052
|
+
* reCAPTCHA v3 UX. 'always' renders a visible widget.
|
|
3053
|
+
*/
|
|
3054
|
+
appearance?: 'always' | 'execute' | 'interaction-only';
|
|
3055
|
+
/**
|
|
3056
|
+
* CSS selector for the element to render the widget into. Defaults to
|
|
3057
|
+
* `[data-saastro-turnstile]` (rendered inline by `HubForm`). If no such
|
|
3058
|
+
* element exists, the plugin creates a fixed, bottom-right container so
|
|
3059
|
+
* an interactive challenge can still surface.
|
|
3060
|
+
*/
|
|
3061
|
+
container?: string;
|
|
3062
|
+
}
|
|
3063
|
+
interface TurnstileApi {
|
|
3064
|
+
render: (el: HTMLElement | string, options: Record<string, unknown>) => string;
|
|
3065
|
+
execute: (widgetId?: string, options?: Record<string, unknown>) => void;
|
|
3066
|
+
reset: (widgetId?: string) => void;
|
|
3067
|
+
remove: (widgetId?: string) => void;
|
|
3068
|
+
getResponse: (widgetId?: string) => string | undefined;
|
|
3069
|
+
}
|
|
3070
|
+
declare global {
|
|
3071
|
+
interface Window {
|
|
3072
|
+
turnstile?: TurnstileApi;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
/**
|
|
3076
|
+
* Cloudflare Turnstile plugin.
|
|
3077
|
+
*
|
|
3078
|
+
* @example
|
|
3079
|
+
* ```tsx
|
|
3080
|
+
* import { turnstilePlugin, PluginManager, FormBuilder } from '@saastro/forms';
|
|
3081
|
+
*
|
|
3082
|
+
* const pm = new PluginManager();
|
|
3083
|
+
* pm.register(turnstilePlugin({ siteKey: '0x4AAAAAAA...' }));
|
|
3084
|
+
* ```
|
|
3085
|
+
*
|
|
3086
|
+
* With `HubForm`, this is wired automatically when the form schema
|
|
3087
|
+
* declares `meta.captchaProvider === 'turnstile'`.
|
|
3088
|
+
*/
|
|
3089
|
+
declare function turnstilePlugin(config: TurnstilePluginConfig): FormPlugin;
|
|
3090
|
+
|
|
3028
3091
|
/**
|
|
3029
3092
|
* ============================================
|
|
3030
3093
|
* SHADCN/UI PRESET - Component Registry
|
|
@@ -3716,70 +3779,6 @@ declare function executeSubmitActions(actions: SubmitActionNode[], values: Recor
|
|
|
3716
3779
|
*/
|
|
3717
3780
|
declare function executeSubmitActionsByTrigger(config: FormConfig, triggerType: SubmitTriggerType, values: Record<string, unknown>, triggerValue?: string): Promise<SubmitActionsResult>;
|
|
3718
3781
|
|
|
3719
|
-
/**
|
|
3720
|
-
* Contact form template — name, email, message.
|
|
3721
|
-
*
|
|
3722
|
-
* The classic three-field contact form: full name (required), email
|
|
3723
|
-
* (required + format), and a message textarea (required, min 10 chars).
|
|
3724
|
-
* Single step, single column.
|
|
3725
|
-
*/
|
|
3726
|
-
declare function createContactTemplate(): FormConfig;
|
|
3727
|
-
|
|
3728
|
-
/**
|
|
3729
|
-
* Newsletter signup template — email + consent.
|
|
3730
|
-
*
|
|
3731
|
-
* GDPR-friendly: a single email field plus an explicit checkbox the
|
|
3732
|
-
* subscriber has to tick (validation: must be true). Single step.
|
|
3733
|
-
*/
|
|
3734
|
-
declare function createNewsletterTemplate(): FormConfig;
|
|
3735
|
-
|
|
3736
|
-
/**
|
|
3737
|
-
* Lead-gen template — name, email, phone, company, message.
|
|
3738
|
-
*
|
|
3739
|
-
* Standard B2B lead-capture: required name + email, optional phone +
|
|
3740
|
-
* company (for context), and a message textarea. Two-column layout on
|
|
3741
|
-
* wide screens so the form doesn't feel huge.
|
|
3742
|
-
*/
|
|
3743
|
-
declare function createLeadGenTemplate(): FormConfig;
|
|
3744
|
-
|
|
3745
|
-
/**
|
|
3746
|
-
* Event RSVP template — name, email, attending, guests, dietary notes.
|
|
3747
|
-
*
|
|
3748
|
-
* For wedding-/conference-style RSVPs: required name + email + answer
|
|
3749
|
-
* to "will you attend?", optional guest count and dietary notes.
|
|
3750
|
-
*/
|
|
3751
|
-
declare function createEventRsvpTemplate(): FormConfig;
|
|
3752
|
-
|
|
3753
|
-
/**
|
|
3754
|
-
* Quote-request template — name, email, company, service, budget, details.
|
|
3755
|
-
*
|
|
3756
|
-
* Captures qualified inbound: required contact info plus a service
|
|
3757
|
-
* type and budget range so the sales team can route the lead before
|
|
3758
|
-
* replying. The free-text "details" field is required so we never get
|
|
3759
|
-
* empty leads.
|
|
3760
|
-
*/
|
|
3761
|
-
declare function createQuoteRequestTemplate(): FormConfig;
|
|
3762
|
-
|
|
3763
|
-
/**
|
|
3764
|
-
* Starter templates for common form patterns. Each factory returns a
|
|
3765
|
-
* fresh `FormConfig` so callers can safely mutate the result (e.g.
|
|
3766
|
-
* append fields) without polluting a shared instance.
|
|
3767
|
-
*
|
|
3768
|
-
* Used by:
|
|
3769
|
-
* - `saastro-hub`'s "New form → Start from" dropdown.
|
|
3770
|
-
* - The form-builder's template picker.
|
|
3771
|
-
*/
|
|
3772
|
-
declare const TEMPLATES: Record<TemplateId, () => FormConfig>;
|
|
3773
|
-
type TemplateId = 'contact' | 'newsletter' | 'lead-gen' | 'event-rsvp' | 'quote-request';
|
|
3774
|
-
/** Static metadata for surfacing templates in pickers without invoking the factory. */
|
|
3775
|
-
interface TemplateMeta {
|
|
3776
|
-
id: TemplateId;
|
|
3777
|
-
label: string;
|
|
3778
|
-
description: string;
|
|
3779
|
-
fieldCount: number;
|
|
3780
|
-
}
|
|
3781
|
-
declare const TEMPLATE_META: readonly TemplateMeta[];
|
|
3782
|
-
|
|
3783
3782
|
/**
|
|
3784
3783
|
* Build a `CustomSubmitConfig` that routes form submissions to the
|
|
3785
3784
|
* Saastro ingestion endpoint (`submit.saastro.io/v1` by default).
|
|
@@ -3934,4 +3933,4 @@ declare function getFieldClass(formLayout?: {
|
|
|
3934
3933
|
*/
|
|
3935
3934
|
declare function getHiddenClasses(hidden?: Partial<Record<Breakpoint, 'visible' | 'hidden'>>): string;
|
|
3936
3935
|
|
|
3937
|
-
export { type AccordionContentProps, type AccordionItemProps, type AccordionProps, type AccordionTriggerProps, BUILD_METHODS, BUILTIN_RESOLVERS, type BaseFieldProps, type Breakpoint$1 as Breakpoint, type BuiltinTransform, type ButtonCardFieldProps, type ButtonCardOption, type ButtonCheckboxFieldProps, type ButtonConfig, type ButtonProps, type ButtonRadioFieldProps, COMMON_CLASSES, COMMON_METHODS, COMMON_STRINGS, type CalendarProps, type CheckboxFieldProps, type CheckboxGroupFieldProps, type CheckboxProps, type ColumnsValue, type ComboboxFieldProps, type CommandEmptyProps, type CommandFieldProps, type CommandGroupProps, type CommandInputProps, type CommandItemProps, type CommandListProps, type CommandProps, type ComponentName, type ComponentOverrides, ComponentProvider, type ComponentProviderProps, type ComponentRegistry, type ComponentRegistryInput, ComponentResolver, type ComponentResolverConfig, type Condition, type ConditionGroup, type ConditionOperator, type CreateHubFormSubmitOptions, type CurrencyFieldProps, type CustomSubmitAction, type CustomSubmitConfig, DEFAULT_HUB_URL, type DatabowlConfig, type DateFieldProps, type DateRangeFieldProps, type DefaultSubmitConfig, type DefinePlugin, type DialogContentProps, type DialogDescriptionProps, type DialogHeaderProps, type DialogProps, type DialogTitleProps, type DialogTriggerProps, type EmailProvider, type EmailSubmitAction, type EndpointConfig, FIELD_DEFAULTS, FieldBuilder, type FieldConfig, type FieldDescriptionProps, type FieldErrorProps, type FieldLabelProps, type FieldLayoutConfig, type FieldMapEntry, type FieldMapping, type FieldMappingConfig, type FieldProps, FieldRenderer, type FieldResolver, type FieldTransformFn, type Fields, type FileFieldProps, Form, FormBuilder, type FormButtonProps, type FormButtons, FormComponentsProvider, type FormComponentsProviderProps, type FormConfig, type FormControlProps, type FormFieldProps, type FormLayout, type FormPlugin, type FormProps, type FormRef, type FormStepsInfo, type GapValue, type GlobModules, type HiddenFieldProps, type HtmlFieldProps, type HttpAuthConfig, type HttpBodyConfig, type HttpEndpointConfig, type HttpRetryConfig, type HttpSubmitAction, HubForm, type HubFormProps, type InputGroupFieldProps, type InputOTPGroupProps, type InputOTPProps, type InputOTPSlotProps, type InputProps, type InputSize, type IntegrationSubmitAction, InternalComponentProvider, type InternalComponentProviderProps, type LabelProps, MissingComponentFallback, type MissingComponentFallbackProps, type NativeSelectFieldProps, type NativeSelectProps, OPTION_BASED_TYPES, type Option, type OtpFieldProps, type PartialComponentRegistry, PluginManager, type PopoverContentProps, type PopoverProps, type PopoverTriggerProps, type PropertyMapping, type RadioFieldProps, type RadioGroupItemProps, type RadioGroupProps, type RangeFieldProps, type RecaptchaConfig, type RecaptchaPluginConfig, type RepeaterFieldProps, type ResolvedComponents, SUSPENSE_CONFIG, type SchemaType, type SelectContentProps, type SelectFieldProps, type SelectItemProps, type SelectProps, type SelectTriggerProps, type SelectValueProps, type SeparatorProps, type SerializableFieldResolver, type SerializationHint, type SliderFieldProps, type SliderProps, type Step, type StepCondition, type StepInfo, type StepStatus, type Steps, StepsAccordion, StepsNavigation, StepsProgress, type SubmitAction, type SubmitActionCondition, type SubmitActionNode, type SubmitActionResult, type SubmitActionType, type SubmitActionsResult, type SubmitConfig, type SubmitConfirmationConfig, type SubmitExecutionConfig, type SubmitTrigger, type SubmitTriggerType, type SubmitType, type SwitchFieldProps, type SwitchGroupFieldProps, type SwitchProps,
|
|
3936
|
+
export { type AccordionContentProps, type AccordionItemProps, type AccordionProps, type AccordionTriggerProps, BUILD_METHODS, BUILTIN_RESOLVERS, type BaseFieldProps, type Breakpoint$1 as Breakpoint, type BuiltinTransform, type ButtonCardFieldProps, type ButtonCardOption, type ButtonCheckboxFieldProps, type ButtonConfig, type ButtonProps, type ButtonRadioFieldProps, COMMON_CLASSES, COMMON_METHODS, COMMON_STRINGS, type CalendarProps, type CheckboxFieldProps, type CheckboxGroupFieldProps, type CheckboxProps, type ColumnsValue, type ComboboxFieldProps, type CommandEmptyProps, type CommandFieldProps, type CommandGroupProps, type CommandInputProps, type CommandItemProps, type CommandListProps, type CommandProps, type ComponentName, type ComponentOverrides, ComponentProvider, type ComponentProviderProps, type ComponentRegistry, type ComponentRegistryInput, ComponentResolver, type ComponentResolverConfig, type Condition, type ConditionGroup, type ConditionOperator, type CreateHubFormSubmitOptions, type CurrencyFieldProps, type CustomSubmitAction, type CustomSubmitConfig, DEFAULT_HUB_URL, type DatabowlConfig, type DateFieldProps, type DateRangeFieldProps, type DefaultSubmitConfig, type DefinePlugin, type DialogContentProps, type DialogDescriptionProps, type DialogHeaderProps, type DialogProps, type DialogTitleProps, type DialogTriggerProps, type EmailProvider, type EmailSubmitAction, type EndpointConfig, FIELD_DEFAULTS, FieldBuilder, type FieldConfig, type FieldDescriptionProps, type FieldErrorProps, type FieldLabelProps, type FieldLayoutConfig, type FieldMapEntry, type FieldMapping, type FieldMappingConfig, type FieldProps, FieldRenderer, type FieldResolver, type FieldTransformFn, type Fields, type FileFieldProps, Form, FormBuilder, type FormButtonProps, type FormButtons, FormComponentsProvider, type FormComponentsProviderProps, type FormConfig, type FormControlProps, type FormFieldProps, type FormLayout, type FormPlugin, type FormProps, type FormRef, type FormStepsInfo, type GapValue, type GlobModules, type HiddenFieldProps, type HtmlFieldProps, type HttpAuthConfig, type HttpBodyConfig, type HttpEndpointConfig, type HttpRetryConfig, type HttpSubmitAction, HubForm, type HubFormProps, type InputGroupFieldProps, type InputOTPGroupProps, type InputOTPProps, type InputOTPSlotProps, type InputProps, type InputSize, type IntegrationSubmitAction, InternalComponentProvider, type InternalComponentProviderProps, type LabelProps, MissingComponentFallback, type MissingComponentFallbackProps, type NativeSelectFieldProps, type NativeSelectProps, OPTION_BASED_TYPES, type Option, type OtpFieldProps, type PartialComponentRegistry, PluginManager, type PopoverContentProps, type PopoverProps, type PopoverTriggerProps, type PropertyMapping, type RadioFieldProps, type RadioGroupItemProps, type RadioGroupProps, type RangeFieldProps, type RecaptchaConfig, type RecaptchaPluginConfig, type RepeaterFieldProps, type ResolvedComponents, SUSPENSE_CONFIG, type SchemaType, type SelectContentProps, type SelectFieldProps, type SelectItemProps, type SelectProps, type SelectTriggerProps, type SelectValueProps, type SeparatorProps, type SerializableFieldResolver, type SerializationHint, type SliderFieldProps, type SliderProps, type Step, type StepCondition, type StepInfo, type StepStatus, type Steps, StepsAccordion, StepsNavigation, StepsProgress, type SubmitAction, type SubmitActionCondition, type SubmitActionNode, type SubmitActionResult, type SubmitActionType, type SubmitActionsResult, type SubmitConfig, type SubmitConfirmationConfig, type SubmitExecutionConfig, type SubmitTrigger, type SubmitTriggerType, type SubmitType, type SwitchFieldProps, type SwitchGroupFieldProps, type SwitchProps, TYPE_SPECIFIC_PROPERTIES, type TestDataLocale, type TestDataOptions, type TextFieldProps, type TextareaFieldProps, type TextareaProps, type TooltipContentProps, type TooltipProps, type TooltipProviderProps, type TooltipTriggerProps, type TurnstilePluginConfig, type UseSubmitConfirmationReturn, VALIDATION_METHODS, type ValidateComponentRegistry, type ValidationContext, type ValidationPresetMeta, type ValidationRules, type WebhookSubmitAction, analyticsPlugin, applyBuiltinTransform, applyFieldMapping, applyFieldMappingSync, applyFieldTransforms, applyTransform, autosavePlugin, compileValidationRules, configureComponents, coreComponents, createComponentRegistry, createHubFormSubmit, createMissingComponentPlaceholder, createShadcnRegistry, databowlAction, databowlPlugin, defaultSubmit, definePlugin, detectLocale, executeCustomAction, executeEmailAction, executeHttpAction, executeSubmitAction, executeSubmitActions, executeSubmitActionsByTrigger, executeWebhookAction, fieldTypeComponents, generateFieldValue, generateTestData, getActionsByTrigger, getAllKnownMethods, getAvailablePresets, getComponentResolver, getFieldClass, getFormGridClass, getHiddenClasses, getInstallCommand, getMinDelayMs, getMissingComponents, getRequiredComponents, globalPluginManager, groupMissingByPackage, iconVariants, iconVariantsConfig, inputVariants, inputVariantsConfig, isAdvancedMapping, isValidationRules, isZodSchema, localStoragePlugin, mergeComponentRegistries, parseGlobModules, pxToRem, recaptchaPlugin, registerPreset, resolvePreset, resolveValue, resolveValueSync, textareaVariants, textareaVariantsConfig, turnstilePlugin, useComponentMode, useComponents, useComputedFields, useFormLayout, useFormState, useFormStepsInfo, useHasComponentProvider, useHiddenFieldResolvers, usePartialComponents, useRecaptcha, useSubmitActionTriggers, useSubmitConfirmation, withComponents };
|
package/dist/index.js
CHANGED
|
@@ -4422,6 +4422,7 @@ var FieldBuilder = class {
|
|
|
4422
4422
|
constructor(name) {
|
|
4423
4423
|
this.name = name;
|
|
4424
4424
|
}
|
|
4425
|
+
name;
|
|
4425
4426
|
config = {};
|
|
4426
4427
|
/**
|
|
4427
4428
|
* Configura el tipo de campo.
|
|
@@ -6070,6 +6071,156 @@ function recaptchaPlugin(config) {
|
|
|
6070
6071
|
});
|
|
6071
6072
|
}
|
|
6072
6073
|
|
|
6074
|
+
// src/plugins/turnstile.ts
|
|
6075
|
+
var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
6076
|
+
var TOKEN_TIMEOUT_MS = 2e4;
|
|
6077
|
+
function waitForTurnstile(timeoutMs = 1e4) {
|
|
6078
|
+
return new Promise((resolve, reject) => {
|
|
6079
|
+
if (typeof window === "undefined") {
|
|
6080
|
+
reject(new Error("turnstile: no window"));
|
|
6081
|
+
return;
|
|
6082
|
+
}
|
|
6083
|
+
if (window.turnstile) {
|
|
6084
|
+
resolve(window.turnstile);
|
|
6085
|
+
return;
|
|
6086
|
+
}
|
|
6087
|
+
const start = Date.now();
|
|
6088
|
+
const tick = () => {
|
|
6089
|
+
if (window.turnstile) {
|
|
6090
|
+
resolve(window.turnstile);
|
|
6091
|
+
} else if (Date.now() - start > timeoutMs) {
|
|
6092
|
+
reject(new Error("turnstile: script did not load"));
|
|
6093
|
+
} else {
|
|
6094
|
+
setTimeout(tick, 100);
|
|
6095
|
+
}
|
|
6096
|
+
};
|
|
6097
|
+
tick();
|
|
6098
|
+
});
|
|
6099
|
+
}
|
|
6100
|
+
function turnstilePlugin(config) {
|
|
6101
|
+
const {
|
|
6102
|
+
siteKey,
|
|
6103
|
+
tokenField = "_captchaToken",
|
|
6104
|
+
appearance = "interaction-only",
|
|
6105
|
+
container
|
|
6106
|
+
} = config;
|
|
6107
|
+
let scriptElement = null;
|
|
6108
|
+
let createdContainer = null;
|
|
6109
|
+
let widgetId;
|
|
6110
|
+
let lastToken = null;
|
|
6111
|
+
let pending = null;
|
|
6112
|
+
const resolveContainer = () => {
|
|
6113
|
+
if (container) {
|
|
6114
|
+
const found = document.querySelector(container);
|
|
6115
|
+
if (found) return found;
|
|
6116
|
+
}
|
|
6117
|
+
const byData = document.querySelector("[data-saastro-turnstile]");
|
|
6118
|
+
if (byData) return byData;
|
|
6119
|
+
if (!createdContainer) {
|
|
6120
|
+
createdContainer = document.createElement("div");
|
|
6121
|
+
createdContainer.setAttribute("data-saastro-turnstile-fallback", "");
|
|
6122
|
+
createdContainer.style.position = "fixed";
|
|
6123
|
+
createdContainer.style.bottom = "12px";
|
|
6124
|
+
createdContainer.style.right = "12px";
|
|
6125
|
+
createdContainer.style.zIndex = "2147483647";
|
|
6126
|
+
document.body.appendChild(createdContainer);
|
|
6127
|
+
}
|
|
6128
|
+
return createdContainer;
|
|
6129
|
+
};
|
|
6130
|
+
const ensureWidget = async () => {
|
|
6131
|
+
const api = await waitForTurnstile();
|
|
6132
|
+
if (widgetId === void 0) {
|
|
6133
|
+
const el = resolveContainer();
|
|
6134
|
+
widgetId = api.render(el, {
|
|
6135
|
+
sitekey: siteKey,
|
|
6136
|
+
appearance,
|
|
6137
|
+
execution: "execute",
|
|
6138
|
+
callback: (token) => {
|
|
6139
|
+
lastToken = token;
|
|
6140
|
+
pending?.resolve(token);
|
|
6141
|
+
pending = null;
|
|
6142
|
+
},
|
|
6143
|
+
"error-callback": () => {
|
|
6144
|
+
pending?.reject(new Error("turnstile: challenge failed"));
|
|
6145
|
+
pending = null;
|
|
6146
|
+
return true;
|
|
6147
|
+
},
|
|
6148
|
+
"expired-callback": () => {
|
|
6149
|
+
lastToken = null;
|
|
6150
|
+
}
|
|
6151
|
+
});
|
|
6152
|
+
}
|
|
6153
|
+
return api;
|
|
6154
|
+
};
|
|
6155
|
+
return definePlugin({
|
|
6156
|
+
name: "turnstile",
|
|
6157
|
+
version: "1.0.0",
|
|
6158
|
+
description: "Cloudflare Turnstile \u2014 widget render and token generation",
|
|
6159
|
+
onFormInit() {
|
|
6160
|
+
if (typeof window === "undefined") return;
|
|
6161
|
+
if (!window.turnstile && !document.querySelector('script[src*="turnstile/v0/api.js"]')) {
|
|
6162
|
+
scriptElement = document.createElement("script");
|
|
6163
|
+
scriptElement.src = SCRIPT_SRC;
|
|
6164
|
+
scriptElement.async = true;
|
|
6165
|
+
scriptElement.defer = true;
|
|
6166
|
+
document.body.appendChild(scriptElement);
|
|
6167
|
+
}
|
|
6168
|
+
void ensureWidget().catch(() => {
|
|
6169
|
+
});
|
|
6170
|
+
},
|
|
6171
|
+
async transformValues(values) {
|
|
6172
|
+
if (typeof window === "undefined") return values;
|
|
6173
|
+
try {
|
|
6174
|
+
const api = await ensureWidget();
|
|
6175
|
+
const token = await new Promise((resolve, reject) => {
|
|
6176
|
+
pending = { resolve, reject };
|
|
6177
|
+
const timer = setTimeout(() => {
|
|
6178
|
+
if (pending) {
|
|
6179
|
+
pending = null;
|
|
6180
|
+
reject(new Error("turnstile: token timeout"));
|
|
6181
|
+
}
|
|
6182
|
+
}, TOKEN_TIMEOUT_MS);
|
|
6183
|
+
const wrappedResolve = (t) => {
|
|
6184
|
+
clearTimeout(timer);
|
|
6185
|
+
resolve(t);
|
|
6186
|
+
};
|
|
6187
|
+
const wrappedReject = (e) => {
|
|
6188
|
+
clearTimeout(timer);
|
|
6189
|
+
reject(e);
|
|
6190
|
+
};
|
|
6191
|
+
pending = { resolve: wrappedResolve, reject: wrappedReject };
|
|
6192
|
+
lastToken = null;
|
|
6193
|
+
api.reset(widgetId);
|
|
6194
|
+
api.execute(widgetId);
|
|
6195
|
+
});
|
|
6196
|
+
return { ...values, [tokenField]: token };
|
|
6197
|
+
} catch (error) {
|
|
6198
|
+
console.error("turnstilePlugin: failed to get token:", error);
|
|
6199
|
+
return values;
|
|
6200
|
+
}
|
|
6201
|
+
},
|
|
6202
|
+
cleanup() {
|
|
6203
|
+
if (typeof window !== "undefined" && window.turnstile && widgetId !== void 0) {
|
|
6204
|
+
try {
|
|
6205
|
+
window.turnstile.remove(widgetId);
|
|
6206
|
+
} catch {
|
|
6207
|
+
}
|
|
6208
|
+
}
|
|
6209
|
+
widgetId = void 0;
|
|
6210
|
+
lastToken = null;
|
|
6211
|
+
pending = null;
|
|
6212
|
+
if (createdContainer?.parentNode) {
|
|
6213
|
+
createdContainer.parentNode.removeChild(createdContainer);
|
|
6214
|
+
createdContainer = null;
|
|
6215
|
+
}
|
|
6216
|
+
if (scriptElement?.parentNode) {
|
|
6217
|
+
scriptElement.parentNode.removeChild(scriptElement);
|
|
6218
|
+
scriptElement = null;
|
|
6219
|
+
}
|
|
6220
|
+
}
|
|
6221
|
+
});
|
|
6222
|
+
}
|
|
6223
|
+
|
|
6073
6224
|
// src/presets/shadcn.ts
|
|
6074
6225
|
function createComponentRegistry(components) {
|
|
6075
6226
|
return {
|
|
@@ -6799,165 +6950,6 @@ var BUILTIN_RESOLVERS = [
|
|
|
6799
6950
|
{ id: "urlParam", label: "URL Parameter", description: "Value from a URL query parameter" }
|
|
6800
6951
|
];
|
|
6801
6952
|
|
|
6802
|
-
// src/templates/contact.ts
|
|
6803
|
-
function createContactTemplate() {
|
|
6804
|
-
return FormBuilder.create("contact").layout("auto").columns(1).gap(4).addField(
|
|
6805
|
-
"name",
|
|
6806
|
-
(f) => f.type("text").label("Name").placeholder("Your full name").required("Please enter your name").minLength(2, "Name must be at least 2 characters")
|
|
6807
|
-
).addField(
|
|
6808
|
-
"email",
|
|
6809
|
-
(f) => f.type("email").label("Email").placeholder("you@example.com").required("Please enter your email").email("Please enter a valid email address")
|
|
6810
|
-
).addField(
|
|
6811
|
-
"message",
|
|
6812
|
-
(f) => f.type("textarea").label("Message").placeholder("How can we help?").required("Please write a short message").minLength(10, "Message must be at least 10 characters")
|
|
6813
|
-
).addStep("main", ["name", "email", "message"]).initialStep("main").buttons({
|
|
6814
|
-
submit: { type: "submit", label: "Send message" },
|
|
6815
|
-
align: "end"
|
|
6816
|
-
}).build();
|
|
6817
|
-
}
|
|
6818
|
-
|
|
6819
|
-
// src/templates/newsletter.ts
|
|
6820
|
-
function createNewsletterTemplate() {
|
|
6821
|
-
return FormBuilder.create("newsletter").layout("auto").columns(1).gap(4).addField(
|
|
6822
|
-
"email",
|
|
6823
|
-
(f) => f.type("email").label("Email").placeholder("you@example.com").required("Please enter your email").email("Please enter a valid email address")
|
|
6824
|
-
).addField(
|
|
6825
|
-
"consent",
|
|
6826
|
-
(f) => f.type("checkbox").label("I agree to receive newsletter emails").required("You must agree to receive the newsletter")
|
|
6827
|
-
).addStep("main", ["email", "consent"]).initialStep("main").buttons({
|
|
6828
|
-
submit: { type: "submit", label: "Subscribe" },
|
|
6829
|
-
align: "end"
|
|
6830
|
-
}).build();
|
|
6831
|
-
}
|
|
6832
|
-
|
|
6833
|
-
// src/templates/lead-gen.ts
|
|
6834
|
-
function createLeadGenTemplate() {
|
|
6835
|
-
return FormBuilder.create("lead-gen").layout("auto").columns(2).gap(4).addField(
|
|
6836
|
-
"name",
|
|
6837
|
-
(f) => f.type("text").label("Full name").placeholder("Jane Doe").required("Please enter your name").minLength(2, "Name must be at least 2 characters").columns({ default: 1 })
|
|
6838
|
-
).addField(
|
|
6839
|
-
"email",
|
|
6840
|
-
(f) => f.type("email").label("Work email").placeholder("jane@acme.com").required("Please enter your email").email("Please enter a valid email address").columns({ default: 1 })
|
|
6841
|
-
).addField(
|
|
6842
|
-
"phone",
|
|
6843
|
-
(f) => f.type("tel").label("Phone").placeholder("+1 555 0123").optional().columns({ default: 1 })
|
|
6844
|
-
).addField(
|
|
6845
|
-
"company",
|
|
6846
|
-
(f) => f.type("text").label("Company").placeholder("Acme Inc.").optional().columns({ default: 1 })
|
|
6847
|
-
).addField(
|
|
6848
|
-
"message",
|
|
6849
|
-
(f) => f.type("textarea").label("What are you looking to solve?").placeholder("A few words about your project or use case").required("Please describe your need briefly").minLength(10, "Message must be at least 10 characters").columns({ default: 2 })
|
|
6850
|
-
).addStep("main", ["name", "email", "phone", "company", "message"]).initialStep("main").buttons({
|
|
6851
|
-
submit: { type: "submit", label: "Request a call" },
|
|
6852
|
-
align: "end"
|
|
6853
|
-
}).build();
|
|
6854
|
-
}
|
|
6855
|
-
|
|
6856
|
-
// src/templates/event-rsvp.ts
|
|
6857
|
-
function createEventRsvpTemplate() {
|
|
6858
|
-
return FormBuilder.create("event-rsvp").layout("auto").columns(2).gap(4).addField(
|
|
6859
|
-
"name",
|
|
6860
|
-
(f) => f.type("text").label("Full name").placeholder("Jane Doe").required("Please enter your name").minLength(2, "Name must be at least 2 characters").columns({ default: 1 })
|
|
6861
|
-
).addField(
|
|
6862
|
-
"email",
|
|
6863
|
-
(f) => f.type("email").label("Email").placeholder("jane@example.com").required("Please enter your email").email("Please enter a valid email address").columns({ default: 1 })
|
|
6864
|
-
).addField(
|
|
6865
|
-
"attending",
|
|
6866
|
-
(f) => f.type("radio").label("Will you attend?").options([
|
|
6867
|
-
{ label: "Yes, I will be there", value: "yes" },
|
|
6868
|
-
{ label: "No, I cannot make it", value: "no" },
|
|
6869
|
-
{ label: "Not sure yet", value: "maybe" }
|
|
6870
|
-
]).required("Please pick one").columns({ default: 2 })
|
|
6871
|
-
).addField(
|
|
6872
|
-
"guests",
|
|
6873
|
-
(f) => f.type("number").label("Number of guests (including yourself)").min(1).max(10).optional().columns({ default: 1 })
|
|
6874
|
-
).addField(
|
|
6875
|
-
"dietary",
|
|
6876
|
-
(f) => f.type("textarea").label("Dietary notes").placeholder("Allergies, preferences, etc.").optional().columns({ default: 2 })
|
|
6877
|
-
).addStep("main", ["name", "email", "attending", "guests", "dietary"]).initialStep("main").buttons({
|
|
6878
|
-
submit: { type: "submit", label: "Send RSVP" },
|
|
6879
|
-
align: "end"
|
|
6880
|
-
}).build();
|
|
6881
|
-
}
|
|
6882
|
-
|
|
6883
|
-
// src/templates/quote-request.ts
|
|
6884
|
-
function createQuoteRequestTemplate() {
|
|
6885
|
-
return FormBuilder.create("quote-request").layout("auto").columns(2).gap(4).addField(
|
|
6886
|
-
"name",
|
|
6887
|
-
(f) => f.type("text").label("Full name").placeholder("Jane Doe").required("Please enter your name").minLength(2, "Name must be at least 2 characters").columns({ default: 1 })
|
|
6888
|
-
).addField(
|
|
6889
|
-
"email",
|
|
6890
|
-
(f) => f.type("email").label("Work email").placeholder("jane@acme.com").required("Please enter your email").email("Please enter a valid email address").columns({ default: 1 })
|
|
6891
|
-
).addField(
|
|
6892
|
-
"company",
|
|
6893
|
-
(f) => f.type("text").label("Company").placeholder("Acme Inc.").required("Please enter your company name").columns({ default: 1 })
|
|
6894
|
-
).addField(
|
|
6895
|
-
"service",
|
|
6896
|
-
(f) => f.type("select").label("Service type").placeholder("Pick the closest match").options([
|
|
6897
|
-
{ label: "Design", value: "design" },
|
|
6898
|
-
{ label: "Development", value: "development" },
|
|
6899
|
-
{ label: "Consulting", value: "consulting" },
|
|
6900
|
-
{ label: "Other", value: "other" }
|
|
6901
|
-
]).required("Please pick a service").columns({ default: 1 })
|
|
6902
|
-
).addField(
|
|
6903
|
-
"budget",
|
|
6904
|
-
(f) => f.type("select").label("Estimated budget").placeholder("Pick a range").options([
|
|
6905
|
-
{ label: "Under $5k", value: "under-5k" },
|
|
6906
|
-
{ label: "$5k \u2013 $25k", value: "5k-25k" },
|
|
6907
|
-
{ label: "$25k \u2013 $100k", value: "25k-100k" },
|
|
6908
|
-
{ label: "$100k+", value: "100k+" },
|
|
6909
|
-
{ label: "Not sure yet", value: "unknown" }
|
|
6910
|
-
]).required("Please pick a budget range").columns({ default: 2 })
|
|
6911
|
-
).addField(
|
|
6912
|
-
"details",
|
|
6913
|
-
(f) => f.type("textarea").label("Project details").placeholder("Goals, timeline, anything we should know").required("Please share a few sentences about the project").minLength(20, "Project details must be at least 20 characters").columns({ default: 2 })
|
|
6914
|
-
).addStep("main", ["name", "email", "company", "service", "budget", "details"]).initialStep("main").buttons({
|
|
6915
|
-
submit: { type: "submit", label: "Request a quote" },
|
|
6916
|
-
align: "end"
|
|
6917
|
-
}).build();
|
|
6918
|
-
}
|
|
6919
|
-
|
|
6920
|
-
// src/templates/index.ts
|
|
6921
|
-
var TEMPLATES = {
|
|
6922
|
-
contact: createContactTemplate,
|
|
6923
|
-
newsletter: createNewsletterTemplate,
|
|
6924
|
-
"lead-gen": createLeadGenTemplate,
|
|
6925
|
-
"event-rsvp": createEventRsvpTemplate,
|
|
6926
|
-
"quote-request": createQuoteRequestTemplate
|
|
6927
|
-
};
|
|
6928
|
-
var TEMPLATE_META = [
|
|
6929
|
-
{
|
|
6930
|
-
id: "contact",
|
|
6931
|
-
label: "Contact",
|
|
6932
|
-
description: "Name, email, and a message. The classic three-field form.",
|
|
6933
|
-
fieldCount: 3
|
|
6934
|
-
},
|
|
6935
|
-
{
|
|
6936
|
-
id: "newsletter",
|
|
6937
|
-
label: "Newsletter",
|
|
6938
|
-
description: "Email plus a GDPR-friendly consent checkbox.",
|
|
6939
|
-
fieldCount: 2
|
|
6940
|
-
},
|
|
6941
|
-
{
|
|
6942
|
-
id: "lead-gen",
|
|
6943
|
-
label: "Lead generation",
|
|
6944
|
-
description: "Name, work email, phone, company, and a brief message.",
|
|
6945
|
-
fieldCount: 5
|
|
6946
|
-
},
|
|
6947
|
-
{
|
|
6948
|
-
id: "event-rsvp",
|
|
6949
|
-
label: "Event RSVP",
|
|
6950
|
-
description: "Name, email, attendance answer, guest count, and dietary notes.",
|
|
6951
|
-
fieldCount: 5
|
|
6952
|
-
},
|
|
6953
|
-
{
|
|
6954
|
-
id: "quote-request",
|
|
6955
|
-
label: "Quote request",
|
|
6956
|
-
description: "Contact info plus service type, budget range, and a details field.",
|
|
6957
|
-
fieldCount: 6
|
|
6958
|
-
}
|
|
6959
|
-
];
|
|
6960
|
-
|
|
6961
6953
|
// src/lib/createHubFormSubmit.ts
|
|
6962
6954
|
var DEFAULT_HUB_URL = "https://submit.saastro.io/v1";
|
|
6963
6955
|
function publicBase({ hubUrl, siteId, formSlug }) {
|
|
@@ -7059,6 +7051,13 @@ function createHubFormSubmit(opts) {
|
|
|
7059
7051
|
// src/components/HubForm.tsx
|
|
7060
7052
|
import { useEffect as useEffect8, useMemo as useMemo7, useState as useState5 } from "react";
|
|
7061
7053
|
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
7054
|
+
function readCaptchaMeta(schema) {
|
|
7055
|
+
const meta = schema.meta;
|
|
7056
|
+
return {
|
|
7057
|
+
captchaProvider: meta?.captchaProvider,
|
|
7058
|
+
captchaSiteKey: meta?.captchaSiteKey
|
|
7059
|
+
};
|
|
7060
|
+
}
|
|
7062
7061
|
function isRenderableSchema(value) {
|
|
7063
7062
|
if (!value || typeof value !== "object") return false;
|
|
7064
7063
|
const schema = value;
|
|
@@ -7128,6 +7127,18 @@ function HubForm({
|
|
|
7128
7127
|
}
|
|
7129
7128
|
};
|
|
7130
7129
|
}, [hubUrl, siteId, formSlug, onSuccess, onError]);
|
|
7130
|
+
const captcha = useMemo7(() => {
|
|
7131
|
+
if (state.status !== "ready") return void 0;
|
|
7132
|
+
const { captchaProvider, captchaSiteKey } = readCaptchaMeta(state.schema);
|
|
7133
|
+
if (!captchaProvider || !captchaSiteKey) return void 0;
|
|
7134
|
+
const pm = new PluginManager();
|
|
7135
|
+
if (captchaProvider === "turnstile") {
|
|
7136
|
+
pm.register(turnstilePlugin({ siteKey: captchaSiteKey, tokenField: "_captchaToken" }));
|
|
7137
|
+
} else {
|
|
7138
|
+
pm.register(recaptchaPlugin({ siteKey: captchaSiteKey, tokenField: "_captchaToken" }));
|
|
7139
|
+
}
|
|
7140
|
+
return { pm, provider: captchaProvider };
|
|
7141
|
+
}, [state]);
|
|
7131
7142
|
if (state.status === "loading") {
|
|
7132
7143
|
return loadingFallback ?? /* @__PURE__ */ jsx14("div", { "data-saastro-hubform-loading": true, style: loadingStyle, children: "Loading\u2026" });
|
|
7133
7144
|
}
|
|
@@ -7148,9 +7159,13 @@ function HubForm({
|
|
|
7148
7159
|
}
|
|
7149
7160
|
const config = {
|
|
7150
7161
|
...state.schema,
|
|
7151
|
-
submit: wrappedSubmit
|
|
7162
|
+
submit: wrappedSubmit,
|
|
7163
|
+
...captcha ? { pluginManager: captcha.pm } : {}
|
|
7152
7164
|
};
|
|
7153
|
-
return /* @__PURE__ */
|
|
7165
|
+
return /* @__PURE__ */ jsxs11(Fragment3, { children: [
|
|
7166
|
+
/* @__PURE__ */ jsx14(Form, { ...formProps, config }),
|
|
7167
|
+
captcha?.provider === "turnstile" ? /* @__PURE__ */ jsx14("div", { "data-saastro-turnstile": true }) : null
|
|
7168
|
+
] });
|
|
7154
7169
|
}
|
|
7155
7170
|
var loadingStyle = {
|
|
7156
7171
|
padding: "2rem 1rem",
|
|
@@ -7428,8 +7443,6 @@ export {
|
|
|
7428
7443
|
StepsAccordion,
|
|
7429
7444
|
StepsNavigation,
|
|
7430
7445
|
StepsProgress,
|
|
7431
|
-
TEMPLATES,
|
|
7432
|
-
TEMPLATE_META,
|
|
7433
7446
|
TYPE_SPECIFIC_PROPERTIES,
|
|
7434
7447
|
VALIDATION_METHODS,
|
|
7435
7448
|
analyticsPlugin,
|
|
@@ -7443,13 +7456,8 @@ export {
|
|
|
7443
7456
|
configureComponents,
|
|
7444
7457
|
coreComponents,
|
|
7445
7458
|
createComponentRegistry,
|
|
7446
|
-
createContactTemplate,
|
|
7447
|
-
createEventRsvpTemplate,
|
|
7448
7459
|
createHubFormSubmit,
|
|
7449
|
-
createLeadGenTemplate,
|
|
7450
7460
|
createMissingComponentPlaceholder,
|
|
7451
|
-
createNewsletterTemplate,
|
|
7452
|
-
createQuoteRequestTemplate,
|
|
7453
7461
|
createShadcnRegistry,
|
|
7454
7462
|
databowlAction,
|
|
7455
7463
|
databowlPlugin,
|
|
@@ -7497,6 +7505,7 @@ export {
|
|
|
7497
7505
|
resolveValueSync,
|
|
7498
7506
|
textareaVariants,
|
|
7499
7507
|
textareaVariantsConfig,
|
|
7508
|
+
turnstilePlugin,
|
|
7500
7509
|
useComponentMode,
|
|
7501
7510
|
useComponents,
|
|
7502
7511
|
useComputedFields,
|