@anarchitects/forms-ts
Public HTTP authority belongs to anarchitecture-contracts
under ADR-0016.
This package consumes @anarchitects/forms-contracts@0.2.0 and Zod 4.5.0
through derived TypeBox compatibility bindings. Domain models remain local.
Use export-aware TypeScript resolution (NodeNext, supported Node16, or
bundler); legacy node10 resolution is no longer supported. Existing DTO names
and ESM/CommonJS entry points remain. See the adoption record
for exact scope, migration requirements and remaining host-conformance gates.
The next minor includes explicit .js relative declaration specifiers for strict NodeNext consumers. Existing public exports and ESM/CJS bundles are preserved. Packed Nest 11/12 consumers verify this contract through release-tools:test-forms-identity-nest-compatibility (#466). The declaration correction can also be backported to the previous maintenance minor.
Schema-first form configuration and validation library built with TypeBox. Define forms declaratively and generate runtime validation schemas automatically.
Developer + AI Agent Start Here
- Read this README before generating DTO/model code that depends on
@anarchitects/forms-ts. - Keep Forms models/renderer semantics here; existing DTOs remain transitional consumers of the canonical public HTTP ownership rules above.
- Prefer public exports (
@anarchitects/forms-ts,/builders,/dtos,/models) and avoid internal path imports. - Keep framework-specific behavior outside this package.
Features
- 🏗️ Declarative Form Configuration - Define forms with field types, validation rules, and UI metadata
- ⚡ Runtime Schema Generation - Automatically generate TypeBox schemas from form configs
- 🔒 Type Safety - Full TypeScript support with inferred types
- 🎯 Field Validation - Built-in support for strings, emails, textareas, booleans, selects, and file uploads
- 🔁 Cross-Field Validation Rules - Declarative transport-safe rules such as password confirmation
- 🛡️ Security Features - Honeypot and CAPTCHA integration options
- 📧 Delivery Configuration - Email notifications and webhook support
Installation
npm install @anarchitects/forms-ts
@sinclair/typebox is installed transitively for the package runtime. Install it explicitly in your app only if you also import TypeBox helpers directly, for example @sinclair/typebox/value.
Usage
import { FormConfig, contactForm } from '@anarchitects/forms-ts';
import { schemaFromConfig } from '@anarchitects/forms-ts/builders';
import { Value } from '@sinclair/typebox/value';
// Use the predefined contact form configuration
console.log(contactForm);
// {
// id: 'contact_default',
// version: 1,
// fields: [
// { name: 'name', kind: 'string', required: true, minLength: 2, maxLength: 100, ui: { label: 'Name' } },
// { name: 'email', kind: 'email', required: true, ui: { label: 'Email' } },
// { name: 'message', kind: 'textarea', required: true, minLength: 10, maxLength: 3000, ui: { label: 'Message', rows: 6 } },
// { name: 'consent', kind: 'boolean', required: true, ui: { label: 'I agree' } }
// ],
// security: { honeypot: 'website', captcha: 'none' },
// delivery: { adminEmail: 'admin@site.tld', autoReply: { enabled: true, templateId: 'contact_autoreply' } }
// }
// Generate validation schema from form config
const schema = schemaFromConfig(contactForm);
// Validate form submission data
const formData = {
name: 'John Doe',
email: 'john@example.com',
message: 'Hello world! This is a test message.',
consent: true,
};
const isValid = Value.Check(schema, formData);
console.log(isValid); // true
// Get validation errors
const errors = [...Value.Errors(schema, formData)];
console.log(errors); // []
Submissions read contracts
The /dtos entry point exports the backend read contracts:
SubmissionsQueryDTO/SubmissionsQuerySchema: optional nonemptyformIdand positive-integerformVersionfilters; omit both to list all submissions.SubmissionIdParamsDTO/SubmissionIdParamsSchema: a UUIDsubmissionIdpath parameter.SubmissionsResponseDTO/SubmissionsResponseSchema: an array of the existingSubmissionResponseDTO/SubmissionResponseSchema, also used for detail responses.
Transport responses contain ISO date strings. Use fromSubmissionResponseDTO from
/mappers to obtain the Submission model with Date fields. Domain repository filters
use SubmissionFilters from /models. HTTP query strings are coerced and validated by
the Fastify route schema; direct TypeBox value checks expect a numeric formVersion.
Field Types
String Field
{
name: 'username',
kind: 'string',
required: true,
minLength: 3,
maxLength: 20,
pattern: '^[a-zA-Z0-9_]+$',
ui: { label: 'Username', placeholder: 'Enter username' }
}
Email Field
{
name: 'email',
kind: 'email',
required: true,
ui: { label: 'Email Address' }
}
Textarea Field
{
name: 'description',
kind: 'textarea',
required: false,
maxLength: 500,
ui: { label: 'Description', rows: 4, help: 'Optional description' }
}
Boolean Field
{
name: 'subscribe',
kind: 'boolean',
required: true,
ui: { label: 'Subscribe to newsletter' }
}
Select Field
{
name: 'country',
kind: 'select',
required: true,
options: [
{ value: 'us', label: 'United States' },
{ value: 'uk', label: 'United Kingdom' },
{ value: 'ca', label: 'Canada' }
],
ui: { label: 'Country' }
}
File Upload Field
{
name: 'attachment',
kind: 'file',
required: false,
ui: { label: 'Upload File', help: 'Max 10MB' }
}
Using DTOs for API Integration
import { FormDefinitionRequestDTO, SubmissionRequestDTO } from '@anarchitects/forms-ts/dtos';
// Request a form definition
const formRequest: FormDefinitionRequestDTO = {
formId: 'contact_default',
formVersion: 1,
};
// Submit form data
const submission: SubmissionRequestDTO = {
formId: 'contact_default',
formVersion: 1,
payload: {
name: 'John Doe',
email: 'john@example.com',
message: 'Hello world!',
consent: true,
},
};
Security Configuration
const secureForm: FormConfig = {
// ... other config
validationRules: [
{
kind: 'matchFields',
sourceField: 'password',
targetField: 'confirmPassword',
message: 'Passwords must match.',
},
],
security: {
honeypot: 'website', // Honeypot field name
captcha: 'turnstile', // 'turnstile' | 'hcaptcha' | 'none'
},
};
Delivery Configuration
const formWithDelivery: FormConfig = {
// ... other config
delivery: {
adminEmail: 'admin@example.com',
subject: 'New Contact Form Submission',
templateId: 'contact_template',
autoReply: {
enabled: true,
subject: 'Thank you for contacting us',
templateId: 'contact_autoreply',
},
webhooks: [
{
url: 'https://api.example.com/webhook',
secret: 'webhook-secret',
},
],
},
};
API Reference
Types
FormConfig- Main form configuration interfaceFormField- Individual field configurationFieldKind- Union of supported field types ('string' | 'email' | 'textarea' | 'boolean' | 'select' | 'file')FormDefinitionRequestDTO- Request DTO for fetching form definitionsFormDefinitionResponseDTO- Response DTO containing complete form configurationSubmissionRequestDTO- Request DTO for form submissionsSubmissionResponseDTO- Response DTO for submission results
Functions
schemaFromConfig(config: FormConfig)- Generate TypeBox schema from form configuration
Pre-defined Forms
contactForm- Ready-to-use contact form configuration with name, email, message, and consent fields
Exports
The library provides subpath exports for better tree-shaking:
// Main exports (models and predefined forms)
import { FormConfig, FormField, contactForm } from '@anarchitects/forms-ts';
// Models only
import { FormField, FieldKind, FormConfig } from '@anarchitects/forms-ts/models';
// DTOs only
import { FormDefinitionRequestDTO, FormDefinitionResponseDTO, SubmissionRequestDTO, SubmissionResponseDTO } from '@anarchitects/forms-ts/dtos';
// Builders only
import { schemaFromConfig } from '@anarchitects/forms-ts/builders';
Development notes
- Preserve local Forms models and transitional DTO exports; coordinate public HTTP changes in Contracts first.
- Keep builder behavior deterministic and framework-agnostic.
- Regenerate API artifacts and verify contract tests when DTO schemas change.
Contributing
This library is part of the Anarchitecture Bricks monorepo. See the main repository for contribution guidelines.
License
Released under the Apache License 2.0.
Canonical public definition migration
Public HTTP reads use PublicFormDefinitionResponseDTO and
PublicFormDefinitionEnvelopeResponseDTO, derived from the published canonical
safe targets. Use toPublicFormDefinitionEnvelopeResponseDTO from /mappers;
it selects public fields and regenerates the dynamic schema without delivery data.
Legacy definition DTOs/mappers remain compatibility exports and must not be used
to return delivery credentials, including in administrative read endpoints.
toFormDeliveryStatusDTO provides only a configured indicator for restricted host
management views. Internal FormConfig.delivery remains available to delivery code.
The dynamic builder now correctly marks optional fields optional and constrains select values to configured options. Nest validates actual submitted values against the exact definition/version before side effects, including cross-field rules. This is the approved D05/D08 security migration, with consumer and release impact recorded in the adoption audit. Canonical Forms capability blockers are still subject to upstream review; package adoption is not a deployment certification.
Canonical version and verification
This source line consumes @anarchitects/forms-contracts@0.2.0 through
the domain TS compatibility bindings. See the canonical conformance guide
for exact checks, configured-host obligations and remaining capability gates.
Generated implementation OpenAPI and passing fixtures do not approve a deployed
host's security profile. Existing runtime support and TypeBox exports remain.