@alufie/form
v0.1.1
Published
Verified submission workflows for contact forms, comments, reviews, and feedback with tree-shakeable subpath exports.
Maintainers
Readme
@alufie/form
@alufie/form is a reusable verified submission system for contact forms, comments, reviews, feedback, and similar user-generated workflows.
It extracts an existing production contact confirmation pattern into a configurable, packageable lifecycle:
- accept a submission
- run anti-spam checks
- hold it in a pending state
- send a confirmation email
- confirm once with a signed token
- notify, queue for approval, or publish
This workspace currently provides a working headless core plus first-party adapters for Drizzle, Resend, Turnstile, and SvelteKit.
Install
npm install @alufie/formImport only the parts you use:
import { createFormSystem } from '@alufie/form/core';
import { createSubmitAction } from '@alufie/form/sveltekit';
import { createDrizzleSubmissionStorage } from '@alufie/form/drizzle';This package is exposed through subpath exports so consumers can avoid pulling in adapters they do not use.
Table of contents
- What this is for
- What is included
- Architecture
- Submission lifecycle
- Package guide
- Core API
- Configuration reference
- Storage model
- SvelteKit integration
- Example workflows
- Security model
- Workspace structure
- Development
- Current limitations
- Roadmap ideas
What this is for
Use @alufie/form when you want:
- email-confirmed contact forms
- verified comments before moderation
- verified reviews before publication
- feedback or lead forms with anti-spam protection
- a reusable backend lifecycle that can be plugged into multiple apps
This is not yet a full public comment platform with:
- threaded discussion
- moderation dashboard UI
- end-user account management
- permanent public rendering layer
Instead, this package family focuses on the verified submission workflow that sits before those higher-level product features.
What is included
The public package currently includes these subpath exports:
@alufie/form/coreHeadless workflow engine, status transitions, contracts, hooks, token lifecycle, and orchestration.@alufie/form/drizzleDrizzle schema and storage adapter for persisted submissions.@alufie/form/resendResend email transport adapter.@alufie/form/turnstileCloudflare Turnstile captcha verifier.@alufie/form/sveltekitSvelteKit form action and route helpers.@alufie/form/ui-svelteMinimal Svelte UI helpers for status display and honeypot fields.
Architecture
The project is intentionally split into a headless core and optional integrations.
Core responsibilities
@alufie/form/core owns:
- submission state machine
- confirmation token generation and hashing
- transition rules
- orchestration of validation, anti-spam, confirmation, and notification
- moderation actions
- lifecycle hooks
Adapter responsibilities
Adapters plug infrastructure into the core:
- storage
- email transport
- captcha verification
- framework-specific request/response helpers
- optional UI primitives
Design goal
The core package must not depend on:
- SvelteKit
- Drizzle
- Resend
- Turnstile
- framework env modules
That keeps the lifecycle portable while still allowing opinionated first-party integrations.
Submission lifecycle
The current implementation supports these statuses:
submittedpending_confirmationconfirmedpending_approvalapprovedrejectedpublishedexpired
Typical flow
Default contact-style flow:
- user submits the form
- validator checks the payload
- honeypot, rate limiting, and captcha checks run if configured
- payload is sanitized if a sanitizer is configured
- system stores the submission
- system sends a confirmation email if confirmation is required
- user clicks the verification link
- token is consumed exactly once
- submission transitions and post-confirmation behavior runs
Policy options
The current policy knobs are:
- confirmation mode:
requiredordisabled - post-confirmation action:
notify,await_approval, orpublish - moderation mode:
disabledorrequired - token expiry hours
Default behavior
If no policies are overridden:
- confirmation is enabled
- moderation is disabled
- post-confirmation action is
notify - token expiry is 24 hours
Package guide
@alufie/form/core
Use this when you want the lifecycle engine without any framework lock-in.
Main exports:
createFormSystemgenerateTokenhashToken- types for storage, validation, transport, policies, and results
See:
packages/core/src/index.tspackages/core/src/system.tspackages/core/src/types.ts
@alufie/form/drizzle
Use this when you want a Drizzle-backed submission store.
It currently exports:
formSubmissionTablecreateDrizzleSubmissionStorage
See:
packages/drizzle/src/index.ts
@alufie/form/resend
Use this when Resend is your email provider.
It exports:
createResendTransport
See:
packages/resend/src/index.ts
@alufie/form/turnstile
Use this when Cloudflare Turnstile is your captcha provider.
It exports:
createTurnstileVerifier
See:
packages/turnstile/src/index.ts
@alufie/form/sveltekit
Use this when your app is built with SvelteKit.
It currently exports:
createSubmitActioncreateConfirmationHandlercreateModerationHandler
See:
packages/sveltekit/src/index.ts
@alufie/form/ui-svelte
Optional small UI helpers for Svelte apps:
FormStatusHiddenHoneypot
See:
packages/ui-svelte/src/index.ts
Core API
The main entrypoint is:
import { createFormSystem } from '@alufie/form/core';createFormSystem(config)
Creates a configured submission system instance.
The returned object currently exposes:
submit({ data }, context?)confirm(token, context?)approve(submissionId, context?)reject(submissionId, context?)publish(submissionId, context?)resendConfirmation(submissionId, context?)cleanupExpired(context?)
Example
import { createFormSystem } from '@alufie/form/core';
const system = createFormSystem({
type: 'contact',
storage,
validator,
templates,
emailTransport,
generateConfirmationUrl: (token, submission) =>
`https://example.com/contact/verify/${token}`,
});Configuration reference
The most important config fields are below.
Required config
typeLogical submission type likecontact,comment, orreview.storageA storage adapter implementing theSubmissionStoragecontract.validatorValidates raw input and returns typed payload data.templatesRenders confirmation and internal notification emails.emailTransportSends email messages generated by templates.generateConfirmationUrlConverts a raw confirmation token into a user-facing URL.
Optional config
sanitizerSanitizes validated payload data before persistence.captchaVerifierVerifies captcha tokens.rateLimiterApplies request rate limits.duplicateCheckerRejects near-duplicate submissions if desired.hooksLifecycle hooks for submit/confirm/approve/reject/publish.loggerStructured logging hooks.policiesWorkflow policy overrides.generateIdCustom ID generation.getCaptchaTokenExtracts the captcha token from raw input.getHoneypotValueExtracts the honeypot field value from raw input.getRateLimitKeyChooses a key for rate limiting.getInternalRecipientsOverrides internal email recipients.
Validator contract
Your validator should return:
{
success: true,
data: typedPayload
}or:
{
success: false,
message: 'Validation failed',
fieldErrors: {
email: ['Valid email is required']
}
}Template contract
Templates must implement:
renderConfirmationEmailrenderInternalNotification
Optional:
renderApprovalRequestrenderDecisionEmail
Example config
const system = createFormSystem({
type: 'contact',
storage,
validator: {
validate(input) {
return {
success: true,
data: {
name: String(input.name ?? ''),
email: String(input.email ?? ''),
message: String(input.message ?? ''),
},
};
},
},
sanitizer: {
sanitize(payload) {
return {
...payload,
email: payload.email.trim().toLowerCase(),
message: payload.message.trim(),
};
},
},
templates: {
renderConfirmationEmail({ submission, confirmationUrl }) {
return {
to: submission.submitterEmail,
subject: 'Confirm your submission',
text: `Confirm here: ${confirmationUrl}`,
};
},
renderInternalNotification({ submission }) {
return {
to: '[email protected]',
replyTo: submission.submitterEmail,
subject: `New verified ${submission.type}`,
text: JSON.stringify(submission.payload, null, 2),
};
},
},
emailTransport,
generateConfirmationUrl: (token) =>
`https://example.com/contact/verify/${token}`,
});Storage model
The generalized data model is a reusable form_submission table.
The current Drizzle adapter stores:
idtypestatuspayloadsubmitter_emailsubmitter_nameconfirmation_token_hashconfirmation_token_expires_atconfirmed_atapproved_atpublished_atrejected_atmetacreated_atupdated_at
Important storage notes
- raw confirmation tokens are never stored
- only the token hash is persisted
- confirmation is one-time by design
- the Drizzle adapter consumes confirmation tokens atomically
SvelteKit integration
Submit action
Use createSubmitAction for form actions:
import { createSubmitAction } from '@alufie/form/sveltekit';
import { system } from '$lib/server/system';
export const actions = {
default: createSubmitAction({ system }),
};Confirmation route
Use createConfirmationHandler for token verification:
import { createConfirmationHandler } from '@alufie/form/sveltekit';
import { system } from '$lib/server/system';
export const GET = createConfirmationHandler({
system,
getToken: ({ params }) => params.token ?? '',
successRedirectTo: '/contact?verified=true',
});Moderation routes
Use createModerationHandler for admin transitions:
import { createModerationHandler } from '@alufie/form/sveltekit';
import { system } from '$lib/server/system';
export const POST = createModerationHandler({
system,
action: 'approve',
getSubmissionId: ({ params }) => params.id ?? '',
successRedirectTo: '/admin/submissions',
});Example workflows
1. Contact form
Recommended policy:
- confirmation:
required - post-confirmation action:
notify - moderation:
disabled
Meaning:
- user must confirm email
- after confirmation, internal admin notification is sent
2. Comment submission
Recommended policy:
- confirmation:
required - post-confirmation action:
await_approval - moderation:
disabledorrequired
Meaning:
- user confirms ownership of email
- comment enters approval queue
- admin can approve, reject, or publish
3. Review submission
Recommended policy:
- confirmation:
required - post-confirmation action:
publish
Meaning:
- user confirms email
- review is published immediately after confirmation
4. Internal feedback form
Recommended policy:
- confirmation:
disabled - post-confirmation action:
notify
Meaning:
- skip email confirmation
- use the same validation, storage, and anti-spam pipeline
Security model
The current implementation includes these safeguards:
- honeypot support
- pluggable rate limiting
- pluggable captcha verification
- validator boundary before persistence
- optional sanitizer boundary before persistence
- hashed confirmation tokens
- one-time confirmation token consumption
- expiry-based cleanup
Security defaults
- token hashing uses SHA-256
- tokens are URL-safe
- raw confirmation tokens are only sent to the user
- expired pending confirmations can be moved to
expired
Recommended production hardening
For production apps, also add:
- request logging
- duplicate submission detection
- stronger audit trail persistence
- provider retry strategy
- admin authentication around moderation routes
- rate limiting keyed by IP and email when appropriate
Workspace structure
alufie-form/
packages/
core/
drizzle/
resend/
sveltekit/
turnstile/
ui-svelte/Useful files:
packages/core/src/system.ts- [packages/core/test/system.test.ts]
Development
Install
pnpm installRun checks
pnpm test
pnpm check
pnpm buildCurrent limitations
This workspace is functional, but still intentionally lightweight.
Current limitations include:
- no packaged moderation dashboard UI
- no built-in database migrations folder yet beyond exported schema/adapter
- no webhook adapter yet
- no job queue adapter yet
- no first-party React/Next.js adapter yet
- no built-in duplicate detection adapter yet
- no localization framework for templates yet
- no publish script or release automation yet
Roadmap ideas
Natural next steps:
- add a more complete production integration example in docs
- add webhook and queue adapters
- add moderation dashboard primitives
- add richer template composition helpers
- add first-party Next.js and generic Express/Fetch adapters
- add audit log persistence and metrics hooks
- add retry and dead-letter strategies for email delivery
Reference files
- Workspace root: [alufie-form]
- Core API:
packages/core/src/index.ts - SvelteKit adapter:
packages/sveltekit/src/index.ts
