Primitive serialization package for OpenForm framework
npm install @open-form/serialization

OpenForm is documents as code. It lets developers and AI agents define, validate, and render business documents using typed, composable schemas. This eliminates template drift, broken mappings, and brittle glue code — while giving AI systems a reliable document layer they can safely read, reason over, and generate against in production workflows.
Locale and region-aware serialization of OpenForm primitive types (Money, Address, Phone, Person, Organization) into human-readable string representations.
- Multi-locale support - USA, EU, and international serializers built-in
- Pluggable architecture - Implement custom serializers via SerializerRegistry interface
- Accessibility-ready - Simple patterns for screen reader and accessibility-focused formatting
- Type-safe - Full TypeScript support
``bash`
npm install @open-form/serialization
Import a pre-configured serializer and use them directly:
`typescript
import { usaSerializers } from "@open-form/serialization";
// USA serializers (default - uses USD, US address format, formatted phone)
usaSerializers.money.stringify({ amount: 1500, currency: "USD" });
// → "$1,500.00"
usaSerializers.address.stringify({
line1: "123 Main St",
locality: "New York",
region: "NY",
postalCode: "10001",
country: "USA",
});
// → "123 Main St, New York, NY, 10001, USA"
`
Create serializer instances with specific regional configurations:
`typescript
import { createSerializer } from "@open-form/serialization";
// Create serializers for different regions
const usaSerializers = createSerializer({ regionFormat: "us" });
// Use them to serialize data
const price = usaSerializers.money.stringify({
amount: 99.99,
currency: "USD",
});
// -> "$99.99"
`
Configure fallback values that are used when serialization fails (e.g., when data is null or invalid). The default is an empty string ("").
`typescript
import { createSerializer } from "@open-form/serialization";
const serializers = createSerializer({
regionFormat: "us",
fallbacks: {
money: "N/A",
address: "Address not available",
phone: "–",
person: "Unknown person",
organization: "Unnamed organization",
party: "Unknown party",
coordinate: "No coordinates",
bbox: "No bounds",
duration: "No duration",
identification: "No ID",
},
});
// When serialization fails, the configured fallback is returned
serializers.money.stringify(null);
// → "N/A"
serializers.address.stringify(null);
// → "Address not available"
`
If no fallback is configured for a serializer type, an empty string is used by default:
`typescript
const serializers = createSerializer({ regionFormat: "us" });
serializers.money.stringify(null);
// → ""
`
Each serializer registry provides these stringifiers:
| Type | Stringifier Signature | Returns | Description |
| ---------------- | -------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------- |
| money | stringify(Money \| number \| Partial | string | Serializes monetary amounts with currency symbol and locale-specific grouping |address
| | stringify(Address \| Partial): string | string | Serializes addresses with region-appropriate ordering and punctuation |phone
| | stringify(Phone \| string \| Partial | string | Serializes phone numbers in E.164 format |person
| | stringify(Person \| Partial | string | Serializes person names from full name or name components |organization
| | stringify(Organization \| Partial | string | Serializes organization names with optional tax ID or identifier |party
| | stringify(Party \| Partial | string | Serializes either a person or organization (automatically detects type) |coordinate
| | stringify(Coordinate \| Partial | string | Serializes geographic coordinates as "lat,lon" |bbox
| | stringify(Bbox \| Partial | string | Serializes bounding boxes as "swLat,swLon,neLat,neLon" |duration
| | stringify(Duration \| string): string | string | Serializes ISO 8601 durations (e.g., "P1Y", "PT30M") |identification
| | stringify(Identification \| Partial | string | Serializes identification documents with type, number, and issuer info |
Implement the SerializerRegistry interface to create custom serializers:
`typescript
import type { SerializerRegistry } from "@open-form/serialization";
const customSerializers: SerializerRegistry = {
money: {
stringify: (value) => {
const amount = typeof value === "number" ? value : value.amount ?? 0;
return $${amount.toFixed(2)};
},
},
address: {
stringify: (value) => {
const addr = value as Record
return [addr.line1, addr.locality, addr.country]
.filter(Boolean)
.join(", ");
},
},
// ... other serializers (phone, person, organization, party, coordinate, bbox, duration, identification)
};
// Use custom serializers
customSerializers.money.stringify(100);
// → "$100.00"
customSerializers.address.stringify({
line1: "123 Main St",
locality: "Boston",
country: "USA",
});
// → "123 Main St, Boston, USA"
`
Note: Stringifiers do not accept a fallback parameter. Fallback handling is configured at factory creation time using the createSerializer() function with the fallbacks option.
View the Changelog for updates.
- @open-form/sdk` - OpenForm framework SDK
We're open to all community contributions! If you'd like to contribute in any way, please read our contribution guidelines and code of conduct.
This project is licensed under the MIT license.
See LICENSE for more information.