YAML Manifests
Declarative product definitions for z0 entities, configs, and subscriptions.
Prerequisites: schema-builders.md
Overview
Section titled “Overview”z0 supports declarative product definitions using YAML manifests. Instead of (or alongside) TypeScript schema builders, you can define your entire domain model in a YAML file.
YAML manifests are ideal for:
- Configuration-driven products where domain models change frequently
- Multi-language teams who prefer declarative configs over TypeScript
- Automated code generation from design tools or product specifications
- Runtime schema loading without code deployments
The YAML manifest system parses YAML into the same DomainManifest and builder objects used by the TypeScript schema system, ensuring full compatibility.
name: my-productversion: 1.0.0
entities: account: description: Financial account with balance tracking fields: email: type: string indexed: true balance: type: number default: 0 facts: deposit: data: amount: type: number withdrawal: data: amount: type: numberThe Four Core Functions
Section titled “The Four Core Functions”| Function | Purpose |
|---|---|
parseManifest() | Parse YAML string → validated manifest + DomainManifest |
manifestToBuilders() | Convert manifest → EntityBuilder/ConfigBuilder instances |
parseManifestToBuilders() | Convenience: parse + convert in one step |
validateManifest() | Validate YAML without fully parsing |
parseManifest
Section titled “parseManifest”Parse and validate a YAML manifest string.
Returns both the validated YAML structure (YamlManifest) and a DomainManifest ready for LedgerRegistry.
Signature
Section titled “Signature”function parseManifest(yamlString: string): ManifestParseResult
interface ManifestParseResult { /** The validated YAML manifest */ manifest: YamlManifest; /** Converted DomainManifest for LedgerRegistry */ domainManifest: DomainManifest;}Example
Section titled “Example”import { parseManifest } from '@z0-app/sdk';
const yaml = `name: edge-analyticsversion: 1.0.0
entities: pageview: description: Single page view event fields: url: type: string indexed: true duration_ms: type: number facts: - viewed - engaged`;
const { manifest, domainManifest } = parseManifest(yaml);
// manifest: validated YamlManifestconsole.log(manifest.name); // "edge-analytics"console.log(manifest.entities.pageview.description); // "Single page view event"
// domainManifest: ready for LedgerRegistryconst registry = new LedgerRegistry(domainManifest);Error Handling
Section titled “Error Handling”import { parseManifest, ManifestParseError } from '@z0-app/sdk';
try { const result = parseManifest(yamlString);} catch (err) { if (err instanceof ManifestParseError) { console.error('Manifest validation failed:', err.message); console.error('Issues:', err.issues); // Array of validation errors }}manifestToBuilders
Section titled “manifestToBuilders”Convert a parsed YAML manifest into schema builder instances.
Returns EntityBuilder and ConfigBuilder objects compatible with the TypeScript schema system. Use this when you want to:
- Register YAML-defined entities with
registerEntity() - Generate Zod validators with
entityToValidators() - Combine YAML and TypeScript schemas
Signature
Section titled “Signature”function manifestToBuilders(manifest: YamlManifest): ManifestBuilders
interface ManifestBuilders { /** EntityBuilder instances keyed by entity name */ entities: Record<string, EntityBuilder<any, any>>; /** ConfigBuilder instances keyed by config name */ configs: Record<string, ConfigBuilder<any>>;}Example
Section titled “Example”import { parseManifest, manifestToBuilders, registerEntity } from '@z0-app/sdk';
const yaml = `name: my-appversion: 1.0.0
entities: account: fields: balance: type: number default: 0 facts: - deposit - withdrawal
configs: rate_limits: scope: tenant category: security settings: max_requests_per_minute: type: number default: 100`;
const { manifest } = parseManifest(yaml);const { entities, configs } = manifestToBuilders(manifest);
// Register with schema systemregisterEntity(entities.account);
// Use like TypeScript-built schemasconst validators = entityToValidators(entities.account);const result = validators.fields.safeParse({ balance: 100 });parseManifestToBuilders
Section titled “parseManifestToBuilders”Convenience function combining parseManifest + manifestToBuilders in one step.
Signature
Section titled “Signature”function parseManifestToBuilders(yamlString: string): ManifestBuilders & { domainManifest: DomainManifest }Example
Section titled “Example”import { parseManifestToBuilders } from '@z0-app/sdk';
const yaml = `name: my-appversion: 1.0.0
entities: user: fields: email: type: string indexed: true`;
const { entities, configs, domainManifest } = parseManifestToBuilders(yaml);
// entities: { user: EntityBuilder<...> }// domainManifest: ready for LedgerRegistryvalidateManifest
Section titled “validateManifest”Validate a YAML manifest string without fully parsing it.
Returns an array of validation issues (empty if valid). Useful for:
- Pre-flight validation in APIs or CLIs
- Quick syntax checks without creating builder objects
Signature
Section titled “Signature”function validateManifest(yamlString: string): string[]Example
Section titled “Example”import { validateManifest } from '@z0-app/sdk';
const yaml = `name: testversion: 1.0.0entities: account: fields: status: type: enum values: [] # Invalid: enum requires at least one value`;
const issues = validateManifest(yaml);
if (issues.length > 0) { console.error('Validation failed:'); issues.forEach((issue) => console.error(` - ${issue}`)); // Output: // - entities.account.fields.status.values: Array must contain at least 1 element(s)} else { console.log('Manifest is valid!');}Complete YAML Manifest Example
Section titled “Complete YAML Manifest Example”A real-world manifest showcasing all features:
name: edge-analyticsversion: 1.0.0
# ============================================================================# Entities# ============================================================================
entities: session: description: User session with pageviews and engagement tracking ledger: SessionLedger fields: user_id: type: string indexed: true started_at: type: date status: type: enum values: [active, expired, abandoned] indexed: true default: active metadata: type: object properties: user_agent: type: string ip: type: string required: false facts: started: description: Session initiated data: source: type: string pageview_recorded: data: url: type: string duration_ms: type: number ended: data: reason: type: enum values: [user_logout, timeout, system] hooks: - trigger: type: ended mode: async action: type: method target: archiveSession
pageview: description: Individual page view event fields: url: type: string indexed: true referrer: type: string required: false duration_ms: type: number default: 0 is_bot: type: boolean default: false facts: - viewed - engaged - bounced
# ============================================================================# Configs# ============================================================================
configs: sampling: description: Analytics sampling configuration scope: tenant category: analytics settings: sample_rate: type: number default: 1.0 description: Fraction of events to collect (0.0 to 1.0) exclude_bots: type: boolean default: true include_debug_events: type: boolean default: false
session_timeout: scope: platform category: analytics settings: idle_timeout_ms: type: number default: 1800000 # 30 minutes max_duration_ms: type: number default: 86400000 # 24 hours
# ============================================================================# Subscriptions (Event Handlers)# ============================================================================
subscriptions: - name: update_dashboards patterns: ["session.ended", "pageview.engaged"] handler: updateAnalyticsDashboard continueOnMatch: true
- name: track_conversions patterns: ["*.conversion"] handler: handleConversionEventYAML Field Types
Section titled “YAML Field Types”Primitive Types
Section titled “Primitive Types”fields: name: type: string age: type: number active: type: boolean created_at: type: dateEnum Fields
Section titled “Enum Fields”fields: status: type: enum values: [pending, active, closed] indexed: trueObject Fields
Section titled “Object Fields”fields: metadata: type: object properties: key1: type: string key2: type: numberField Modifiers
Section titled “Field Modifiers”fields: email: type: string indexed: true # Create generated column for queries required: false # Optional field (default: true) default: "guest" # Default value description: "User email address"Facts: Short Form vs Long Form
Section titled “Facts: Short Form vs Long Form”Short Form (Array of Names)
Section titled “Short Form (Array of Names)”facts: - created - updated - deletedUse when facts have no data schema.
Long Form (Record with Data)
Section titled “Long Form (Record with Data)”facts: deposit: description: Money added to account data: amount: type: number reference: type: string required: false withdrawal: data: amount: type: numberUse when facts have typed data payloads.
Hooks define event-driven actions triggered by facts:
hooks: - trigger: type: deposit # Fact type subtype: recurring # Optional: only recurring deposits mode: async # 'sync' or 'async' action: type: method target: updateCreditScore metadata: notify_user: trueMigration from TypeScript to YAML
Section titled “Migration from TypeScript to YAML”You can gradually migrate from TypeScript schemas to YAML:
Before (TypeScript)
Section titled “Before (TypeScript)”import { z0 } from '@z0-app/sdk';
const Account = z0.entity('account', { balance: z0.number().default(0), status: z0.enum(['active', 'frozen']),}).facts({ deposit: { data: { amount: z0.number() } },});
z0.registerEntity(Account);After (YAML + TypeScript)
Section titled “After (YAML + TypeScript)”import { parseManifestToBuilders, registerEntity } from '@z0-app/sdk';import { readFileSync } from 'fs';
const yaml = readFileSync('./manifest.yaml', 'utf-8');const { entities, domainManifest } = parseManifestToBuilders(yaml);
// Register YAML-defined entitiesObject.values(entities).forEach(registerEntity);
// Mix with TypeScript schemas if neededimport { CustomEntity } from './custom-schemas.js';registerEntity(CustomEntity);manifest.yaml:
name: my-appversion: 1.0.0
entities: account: fields: balance: type: number default: 0 status: type: enum values: [active, frozen] facts: deposit: data: amount: type: numberBest Practices
Section titled “Best Practices”1. Version Your Manifests
Section titled “1. Version Your Manifests”name: my-appversion: 1.2.0 # Bump when making breaking changes2. Use Descriptions Liberally
Section titled “2. Use Descriptions Liberally”entities: account: description: Financial account with FDIC insurance tracking fields: balance: type: number description: Current balance in USD cents (not dollars)3. Keep Manifests Close to Code
Section titled “3. Keep Manifests Close to Code”src/ schemas/ manifest.yaml # Domain model custom-entities.ts # TypeScript-only schemas ledgers/ AccountLedger.ts4. Validate Early
Section titled “4. Validate Early”import { validateManifest } from '@z0-app/sdk';import { readFileSync } from 'fs';
const yaml = readFileSync('./manifest.yaml', 'utf-8');const issues = validateManifest(yaml);
if (issues.length > 0) { console.error('Manifest validation failed:'); issues.forEach((issue) => console.error(` - ${issue}`)); process.exit(1);}5. Combine with TypeScript for Complex Logic
Section titled “5. Combine with TypeScript for Complex Logic”Use YAML for straightforward entities and configs. Use TypeScript schema builders for:
- Complex validation logic (cross-field constraints)
- Dynamic schema generation
- Advanced type inference
Summary
Section titled “Summary”| Function | Input | Output | Use Case |
|---|---|---|---|
parseManifest | YAML string | { manifest, domainManifest } | Parse and validate, get both formats |
manifestToBuilders | YamlManifest | { entities, configs } | Convert to builder objects for schema system |
parseManifestToBuilders | YAML string | { entities, configs, domainManifest } | One-step parse and convert |
validateManifest | YAML string | string[] (issues) | Pre-flight validation without parsing |
YAML manifests integrate seamlessly with the TypeScript schema system, enabling declarative product definitions without sacrificing type safety or runtime validation.