@stvnprkns/formless-registry
v0.1.1
Published
Semantic form schema registry for Formless - A type-safe registry for form field definitions with metadata
Downloads
5
Maintainers
Readme
@stvnprkns/formless-registry
A type-safe, semantic form field registry for Formless applications. This package provides a centralized registry of form field definitions with rich metadata, validation rules, and type information.
Features
- 🏷️ Type-Safe: Built with TypeScript for excellent developer experience
- 📝 Rich Metadata: Each field includes type information, validation rules, labels, and more
- 🗂️ Categorized: Organize fields into logical groups (Personal, Account, Address, etc.)
- 🔄 Versioned: Track field additions and changes
- ⚠️ Deprecation Support: Mark fields as deprecated while maintaining backward compatibility
- 🧪 Tested: Comprehensive test coverage
- 📦 Tree-shakeable: Only bundle what you use
- 🌐 Universal: Works in both Node.js and browser environments
- 📱 Framework Agnostic: Use with any frontend framework or vanilla JS
Installation
# Using npm
npm install @stvnprkns/formless-registry
# Using yarn
yarn add @stvnprkns/formless-registry
# Using pnpm
pnpm add @stvnprkns/formless-registryTable of Contents
Quick Start
Basic Usage
import { formlessRegistry } from '@stvnprkns/formless-registry';
// Access field definitions
const emailField = formlessRegistry.fields.email;
console.log('Email Field:', {
label: emailField.label,
type: emailField.type,
required: emailField.required,
pattern: emailField.pattern
});
// Check registry version
console.log('Registry Version:', formlessRegistry.version);
// List all available fields
console.log('Available Fields:', Object.keys(formlessRegistry.fields));Accessing Fields by Category
// Get all personal information fields
const personalFields = Object.entries(formlessRegistry.fields)
.filter(([_, field]) => field.category === 'personal')
.map(([key, field]) => ({
key,
label: field.label,
type: field.type
}));
console.log('Personal Information Fields:', personalFields);API Reference
formlessRegistry
The main registry object that contains all field definitions and metadata.
Properties
// Current version of the registry
version: string;
// Object containing all field definitions
fields: Record<string, FieldConfig>;
// Object containing category metadata (if any)
categories?: Record<string, CategoryConfig>;
// Additional metadata about the registry
meta?: {
name?: string;
description?: string;
[key: string]: any;
};FieldConfig
Interface representing a field configuration:
interface FieldConfig {
// The type of the field (e.g., 'text', 'email', 'select')
type: FieldType;
// Human-readable label for the field
label: string;
// Description or help text
description?: string;
// Whether the field is required
required?: boolean;
// Validation pattern (regex string)
pattern?: string;
// Error message when pattern validation fails
patternMessage?: string;
// Category for grouping related fields
category?: string;
// Whether the field is deprecated
deprecated?: boolean;
// Version when this field was added
version?: string;
// Example values for the field
examples?: any[];
// Additional field-specific options
[key: string]: any;
}CategoryConfig
interface CategoryConfig {
// Display name for the category
label: string;
// Optional description
description?: string;
// Additional metadata
[key: string]: any;
}Field Configuration
Each field in the registry is a FieldConfig object with properties like:
{
"email": {
"type": "email",
"label": "Email Address",
"description": "A valid email address",
"required": true,
"pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
"patternMessage": "Please enter a valid email address",
"category": "account",
"examples": ["[email protected]"],
"deprecated": false,
"version": "1.0.0"
}
}Categories
The registry organizes fields into logical categories for easier management:
- personal: Personal Information (name, date of birth, etc.)
- account: Account Information (email, username, password)
- address: Physical Address (street, city, zip code)
- payment: Payment Information (credit card, billing details)
- preferences: User Preferences (language, theme, notifications)
- other: Additional Information
TypeScript Support
The package includes TypeScript type definitions. Import types like this:
import type { FieldConfig, FieldType, FormlessSchema } from '@stvnprkns/formless-registry';
// Example of using types
function validateField(field: FieldConfig, value: any): boolean {
if (field.required && !value) return false;
if (field.pattern && value) {
const regex = new RegExp(field.pattern);
return regex.test(value);
}
return true;
}Examples
1. Form Generation
function renderForm(fields: string[]) {
return fields.map(fieldKey => {
const field = formlessRegistry.fields[fieldKey];
if (!field) return null;
return `
<div class="form-group">
<label for="${fieldKey}">
${field.label}
${field.required ? '<span class="required">*</span>' : ''}
</label>
<input
type="${field.type}"
id="${fieldKey}"
name="${fieldKey}"
${field.required ? 'required' : ''}
${field.pattern ? `pattern="${field.pattern}"` : ''}
placeholder={field.placeholder || ''}
/>
{field.description && (
<div class="help-text">${field.description}</div>
)}
</div>
`;
}).join('\n');
}2. Field Validation
function validateForm(data: Record<string, any>) {
const errors: Record<string, string> = {};
Object.entries(data).forEach(([fieldKey, value]) => {
const field = formlessRegistry.fields[fieldKey];
if (!field) return;
if (field.required && !value) {
errors[fieldKey] = `${field.label} is required`;
return;
}
if (field.pattern && value) {
const regex = new RegExp(field.pattern);
if (!regex.test(value)) {
errors[fieldKey] = field.patternMessage || 'Invalid format';
}
}
});
return Object.keys(errors).length > 0 ? errors : null;
}3. Dynamic Form with React
import React from 'react';
import { formlessRegistry } from '@stvnprkns/formless-registry';
function DynamicForm() {
const [formData, setFormData] = React.useState({});
const [errors, setErrors] = React.useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
const validationErrors = validateForm(formData);
if (validationErrors) {
setErrors(validationErrors);
return;
}
// Submit form...
};
return (
<form onSubmit={handleSubmit}>
{Object.entries(formlessRegistry.fields).map(([key, field]) => (
<div key={key} className="form-group">
<label htmlFor={key}>
{field.label}
{field.required && <span className="required">*</span>}
</label>
<input
type={field.type}
id={key}
name={key}
value={formData[key] || ''}
onChange={handleChange}
required={field.required}
pattern={field.pattern}
placeholder={field.placeholder}
className={errors[key] ? 'error' : ''}
/>
{field.description && (
<div className="help-text">{field.description}</div>
)}
{errors[key] && (
<div className="error-message">{errors[key]}</div>
)}
</div>
))}
<button type="submit">Submit</button>
</form>
);
}Contributing
Contributions are welcome! Please read our Contributing Guide to get started.
License
MIT © [Your Name]
Changelog
0.1.0
- Initial release of formless-registry
- Added core field definitions
- TypeScript support
- Comprehensive documentation
function validateField(fieldName: string, value: string): boolean {
const field = formlessRegistry.fields[fieldName as keyof typeof formlessRegistry.fields];
if (!field) return false;
if (field.required && !value) return false;
if (field.pattern && !new RegExp(field.pattern).test(value)) return false;
if (field.minLength && value.length < field.minLength) return false;
if (field.maxLength && value.length > field.maxLength) return false;
return true;
}
// Example usage
validateField('email', '[email protected]'); // true
validateField('email', 'invalid-email'); // falseGetting Fields by Category
function getFieldsByCategory(category: string) {
return Object.entries(formlessRegistry.fields)
.filter(([_, config]) => config.category === category)
.map(([key, config]) => ({
key,
label: config.label,
type: config.type,
required: config.required,
}));
}
// Get all personal information fields
const personalFields = getFieldsByCategory('personal');Getting Fields by Type
function getFieldsByType(type: FieldType) {
return Object.entries(formlessRegistry.fields)
.filter(([_, config]) => config.type === type)
.map(([key, config]) => ({
key,
label: config.label,
category: config.category,
}));
}
// Get all select fields
const selectFields = getFieldsByType('select');API Reference
formlessRegistry
The main registry object containing all field definitions.
Properties
version: string- The current version of the registryfields: Record<string, FieldConfig>- Object containing all field definitionscategories?: Record<string, CategoryConfig>- Optional categories for organizing fieldsmeta?: Record<string, any>- Additional metadata about the registry
Types
FieldConfig
interface FieldConfig {
// Basic field information
type: FieldType;
label: string;
description?: string;
required?: boolean;
// Validation
pattern?: string;
patternMessage?: string;
minLength?: number;
maxLength?: number;
min?: number | string;
max?: number | string;
// Metadata
category?: string;
deprecated?: boolean;
version?: string;
examples?: any[];
// For select/radio fields
options?: Array<{
label: string;
value: any;
disabled?: boolean;
}>;
}FieldType
type FieldType =
| 'text'
| 'email'
| 'password'
| 'number'
| 'tel'
| 'url'
| 'date'
| 'time'
| 'datetime-local'
| 'month'
| 'week'
| 'color'
| 'checkbox'
| 'radio'
| 'select'
| 'textarea'
| 'hidden';Development
Prerequisites
- Node.js 16+
- npm or yarn
Installation
git clone https://github.com/your-org/formless.git
cd formless/packages/registry
npm installBuilding
# Build the project
npm run build
# Watch for changes
npm run build -- --watchTesting
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Generate test coverage
npm run test:coverageLinting
# Run linter
npm run lint
# Fix linting issues
npm run lint:fixContributing
Contributions are welcome! Please read our contributing guide to get started.
License
MIT © Formless Team description?: string; [key: string]: any; }; }
## Field Configuration
Each field in the registry is a `FieldConfig` object with properties like:
```typescript
{
type: FieldType; // 'text', 'email', 'password', etc.
label: string; // Human-readable label
description?: string; // Help text
required?: boolean; // Whether the field is required
default?: any; // Default value
// ... and many more
}Extending the Registry
You can extend the default registry with your own fields:
import { formlessRegistry } from '@formless/registry';
const customRegistry = {
...formlessRegistry,
fields: {
...formlessRegistry.fields,
customField: {
type: 'text',
label: 'Custom Field',
// ...
}
}
};Building
npm install
npm run buildLicense
MIT
