@saastro/forms 0.3.0 → 0.5.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 CHANGED
@@ -3717,100 +3717,48 @@ declare function executeSubmitActions(actions: SubmitActionNode[], values: Recor
3717
3717
  declare function executeSubmitActionsByTrigger(config: FormConfig, triggerType: SubmitTriggerType, values: Record<string, unknown>, triggerValue?: string): Promise<SubmitActionsResult>;
3718
3718
 
3719
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
- /**
3784
- * Build a `CustomSubmitConfig` that routes form submissions to a
3785
- * Saastro Hub site. Used by Astro / React sites whose forms are
3786
- * authored in Hub (`builder.saastro.io` embed mode) and stored there.
3720
+ * Build a `CustomSubmitConfig` that routes form submissions to the
3721
+ * Saastro ingestion endpoint (`submit.saastro.io/v1` by default).
3722
+ * Used by Astro / React sites whose forms are authored in Hub
3723
+ * (`builder.saastro.io` embed mode) and stored there.
3787
3724
  *
3788
3725
  * The flow is:
3789
3726
  *
3790
3727
  * 1. Walk the field values and split into `payload` (scalar/JSON-safe)
3791
- * and `files` (instances of File). Hub keeps text fields together;
3792
- * files take a separate path.
3793
- * 2. For each file, POST `/api/public/forms/:siteId/:slug/upload-url`
3794
- * to get a presigned R2 PUT URL, then PUT the binary directly to
3795
- * that URL. Hub never touches the file body — R2 receives it.
3796
- * 3. POST `/api/public/forms/:siteId/:slug/submit` with the textual
3797
- * payload + an `attachments` array describing the uploaded R2
3798
- * keys. Hub inserts the submission row and dispatches integrations
3799
- * (Sheets / Resend / etc.) per the form's schema.
3800
- *
3801
- * Turnstile: if the form schema declares `meta.requireCaptcha=true`,
3802
- * the host app must render the Turnstile widget and pass the token
3803
- * via the special `_turnstile` field in the form values. The helper
3804
- * extracts it before sending.
3728
+ * and `files` (instances of File). The endpoint keeps text fields
3729
+ * together; files take a separate path.
3730
+ * 2. For each file, POST `/:siteId/:slug/upload-url` to get a
3731
+ * presigned R2 PUT URL, then PUT the binary directly to that URL.
3732
+ * The Worker never touches the file body — R2 receives it.
3733
+ * 3. POST `/:siteId/:slug/submit` with the textual payload + an
3734
+ * `attachments` array describing the uploaded R2 keys. The Worker
3735
+ * inserts the submission row and dispatches integrations (Sheets
3736
+ * / Resend / etc.) per the form's schema.
3737
+ *
3738
+ * Captcha: if the form schema declares `meta.captchaProvider`
3739
+ * ('turnstile' or 'recaptcha-v3'), the host app must render the widget
3740
+ * and pass the token via the special `_captchaToken` field (legacy
3741
+ * `_turnstile` is accepted too). The helper extracts it before sending.
3805
3742
  *
3806
3743
  * Honeypot: pass via `_hp` (or whatever `meta.honeypotField` says).
3807
- * The Hub will silently 200 the request and not store anything if
3744
+ * The endpoint will silently 200 the request and not store anything if
3808
3745
  * the honeypot is non-empty.
3746
+ *
3747
+ * Endpoint history:
3748
+ * - v0.3.x and earlier: `https://hub.saastro.io/api/public/forms/...`
3749
+ * - v0.4.0+: `https://submit.saastro.io/v1/...` (dedicated worker)
3750
+ * See https://docs.forms.saastro.io/migration/v0.4 for details.
3809
3751
  */
3810
3752
 
3753
+ /** Default canonical ingestion endpoint. Override via `hubUrl` for self-hosting. */
3754
+ declare const DEFAULT_HUB_URL = "https://submit.saastro.io/v1";
3811
3755
  interface CreateHubFormSubmitOptions {
3812
- /** Hub origin, e.g. `https://hub.saastro.io`. No trailing slash. */
3813
- hubUrl: string;
3756
+ /**
3757
+ * Ingestion endpoint base URL (no trailing slash). Defaults to
3758
+ * `https://submit.saastro.io/v1` — the canonical CF Worker.
3759
+ * Override for self-hosted Hub deployments or test envs.
3760
+ */
3761
+ hubUrl?: string;
3814
3762
  siteId: string;
3815
3763
  formSlug: string;
3816
3764
  }
@@ -3826,8 +3774,12 @@ interface CreateHubFormSubmitOptions {
3826
3774
  declare function createHubFormSubmit(opts: CreateHubFormSubmitOptions): CustomSubmitConfig;
3827
3775
 
3828
3776
  interface HubFormProps {
3829
- /** Origin of the Hub API. e.g. `https://hub.saastro.io`. No trailing slash. */
3830
- hubUrl: string;
3777
+ /**
3778
+ * Ingestion endpoint base URL (no trailing slash). Defaults to
3779
+ * `https://submit.saastro.io/v1` — the canonical hosted endpoint.
3780
+ * Override for self-hosted Hub or test envs.
3781
+ */
3782
+ hubUrl?: string;
3831
3783
  siteId: string;
3832
3784
  formSlug: string;
3833
3785
  /** Custom UI while the schema is being fetched. Defaults to a skeleton. */
@@ -3918,4 +3870,4 @@ declare function getFieldClass(formLayout?: {
3918
3870
  */
3919
3871
  declare function getHiddenClasses(hidden?: Partial<Record<Breakpoint, 'visible' | 'hidden'>>): string;
3920
3872
 
3921
- 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, 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, TEMPLATES, TEMPLATE_META, TYPE_SPECIFIC_PROPERTIES, type TemplateId, type TemplateMeta, type TestDataLocale, type TestDataOptions, type TextFieldProps, type TextareaFieldProps, type TextareaProps, type TooltipContentProps, type TooltipProps, type TooltipProviderProps, type TooltipTriggerProps, 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, createContactTemplate, createEventRsvpTemplate, createHubFormSubmit, createLeadGenTemplate, createMissingComponentPlaceholder, createNewsletterTemplate, createQuoteRequestTemplate, 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, useComponentMode, useComponents, useComputedFields, useFormLayout, useFormState, useFormStepsInfo, useHasComponentProvider, useHiddenFieldResolvers, usePartialComponents, useRecaptcha, useSubmitActionTriggers, useSubmitConfirmation, withComponents };
3873
+ 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 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, 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.
@@ -6799,169 +6800,11 @@ var BUILTIN_RESOLVERS = [
6799
6800
  { id: "urlParam", label: "URL Parameter", description: "Value from a URL query parameter" }
6800
6801
  ];
6801
6802
 
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
6803
  // src/lib/createHubFormSubmit.ts
6804
+ var DEFAULT_HUB_URL = "https://submit.saastro.io/v1";
6962
6805
  function publicBase({ hubUrl, siteId, formSlug }) {
6963
- const cleanHub = hubUrl.replace(/\/+$/, "");
6964
- return `${cleanHub}/api/public/forms/${encodeURIComponent(siteId)}/${encodeURIComponent(formSlug)}`;
6806
+ const base = (hubUrl ?? DEFAULT_HUB_URL).replace(/\/+$/, "");
6807
+ return `${base}/${encodeURIComponent(siteId)}/${encodeURIComponent(formSlug)}`;
6965
6808
  }
6966
6809
  async function presignAndUpload(base, file) {
6967
6810
  const presignRes = await fetch(`${base}/upload-url`, {
@@ -6999,7 +6842,7 @@ function partitionValues(values) {
6999
6842
  let turnstileToken = null;
7000
6843
  const honeypotEntries = [];
7001
6844
  for (const [key, value] of Object.entries(values)) {
7002
- if (key === "_turnstile") {
6845
+ if (key === "_captchaToken" || key === "_turnstile") {
7003
6846
  if (typeof value === "string") turnstileToken = value;
7004
6847
  continue;
7005
6848
  }
@@ -7039,7 +6882,10 @@ function createHubFormSubmit(opts) {
7039
6882
  body: JSON.stringify({
7040
6883
  payload,
7041
6884
  attachments,
7042
- turnstile: turnstileToken ?? void 0,
6885
+ // Send under the canonical name. The worker also accepts the
6886
+ // legacy `turnstile` field for old SDK callers, but new sends
6887
+ // should use `captchaToken`.
6888
+ captchaToken: turnstileToken ?? void 0,
7043
6889
  ...honeypotEntry
7044
6890
  })
7045
6891
  });
@@ -7083,8 +6929,8 @@ function HubForm({
7083
6929
  const [state, setState] = useState5({ status: "loading" });
7084
6930
  useEffect8(() => {
7085
6931
  let cancelled = false;
7086
- const cleanHub = hubUrl.replace(/\/+$/, "");
7087
- const url = `${cleanHub}/api/public/forms/${encodeURIComponent(siteId)}/${encodeURIComponent(formSlug)}.json`;
6932
+ const cleanHub = (hubUrl ?? DEFAULT_HUB_URL).replace(/\/+$/, "");
6933
+ const url = `${cleanHub}/${encodeURIComponent(siteId)}/${encodeURIComponent(formSlug)}.json`;
7088
6934
  fetch(url, { headers: { accept: "application/json" } }).then(async (res) => {
7089
6935
  if (!res.ok) throw new Error(`schema fetch failed: ${res.status} ${res.statusText}`);
7090
6936
  return res.json();
@@ -7408,6 +7254,7 @@ export {
7408
7254
  COMMON_STRINGS,
7409
7255
  ComponentProvider,
7410
7256
  ComponentResolver,
7257
+ DEFAULT_HUB_URL,
7411
7258
  FIELD_DEFAULTS,
7412
7259
  FieldBuilder,
7413
7260
  FieldRenderer,
@@ -7423,8 +7270,6 @@ export {
7423
7270
  StepsAccordion,
7424
7271
  StepsNavigation,
7425
7272
  StepsProgress,
7426
- TEMPLATES,
7427
- TEMPLATE_META,
7428
7273
  TYPE_SPECIFIC_PROPERTIES,
7429
7274
  VALIDATION_METHODS,
7430
7275
  analyticsPlugin,
@@ -7438,13 +7283,8 @@ export {
7438
7283
  configureComponents,
7439
7284
  coreComponents,
7440
7285
  createComponentRegistry,
7441
- createContactTemplate,
7442
- createEventRsvpTemplate,
7443
7286
  createHubFormSubmit,
7444
- createLeadGenTemplate,
7445
7287
  createMissingComponentPlaceholder,
7446
- createNewsletterTemplate,
7447
- createQuoteRequestTemplate,
7448
7288
  createShadcnRegistry,
7449
7289
  databowlAction,
7450
7290
  databowlPlugin,