Skip to content

YAML Manifests

Declarative product definitions for z0 entities, configs, and subscriptions.

Prerequisites: schema-builders.md


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-product
version: 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: number

FunctionPurpose
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

Parse and validate a YAML manifest string.

Returns both the validated YAML structure (YamlManifest) and a DomainManifest ready for LedgerRegistry.

function parseManifest(yamlString: string): ManifestParseResult
interface ManifestParseResult {
/** The validated YAML manifest */
manifest: YamlManifest;
/** Converted DomainManifest for LedgerRegistry */
domainManifest: DomainManifest;
}
import { parseManifest } from '@z0-app/sdk';
const yaml = `
name: edge-analytics
version: 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 YamlManifest
console.log(manifest.name); // "edge-analytics"
console.log(manifest.entities.pageview.description); // "Single page view event"
// domainManifest: ready for LedgerRegistry
const registry = new LedgerRegistry(domainManifest);
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
}
}

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
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>>;
}
import { parseManifest, manifestToBuilders, registerEntity } from '@z0-app/sdk';
const yaml = `
name: my-app
version: 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 system
registerEntity(entities.account);
// Use like TypeScript-built schemas
const validators = entityToValidators(entities.account);
const result = validators.fields.safeParse({ balance: 100 });

Convenience function combining parseManifest + manifestToBuilders in one step.

function parseManifestToBuilders(yamlString: string): ManifestBuilders & { domainManifest: DomainManifest }
import { parseManifestToBuilders } from '@z0-app/sdk';
const yaml = `
name: my-app
version: 1.0.0
entities:
user:
fields:
email:
type: string
indexed: true
`;
const { entities, configs, domainManifest } = parseManifestToBuilders(yaml);
// entities: { user: EntityBuilder<...> }
// domainManifest: ready for LedgerRegistry

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
function validateManifest(yamlString: string): string[]
import { validateManifest } from '@z0-app/sdk';
const yaml = `
name: test
version: 1.0.0
entities:
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!');
}

A real-world manifest showcasing all features:

name: edge-analytics
version: 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: handleConversionEvent

fields:
name:
type: string
age:
type: number
active:
type: boolean
created_at:
type: date
fields:
status:
type: enum
values: [pending, active, closed]
indexed: true
fields:
metadata:
type: object
properties:
key1:
type: string
key2:
type: number
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:
- created
- updated
- deleted

Use when facts have no data schema.

facts:
deposit:
description: Money added to account
data:
amount:
type: number
reference:
type: string
required: false
withdrawal:
data:
amount:
type: number

Use 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: true

You can gradually migrate from TypeScript schemas to YAML:

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);
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 entities
Object.values(entities).forEach(registerEntity);
// Mix with TypeScript schemas if needed
import { CustomEntity } from './custom-schemas.js';
registerEntity(CustomEntity);

manifest.yaml:

name: my-app
version: 1.0.0
entities:
account:
fields:
balance:
type: number
default: 0
status:
type: enum
values: [active, frozen]
facts:
deposit:
data:
amount:
type: number

name: my-app
version: 1.2.0 # Bump when making breaking changes
entities:
account:
description: Financial account with FDIC insurance tracking
fields:
balance:
type: number
description: Current balance in USD cents (not dollars)
src/
schemas/
manifest.yaml # Domain model
custom-entities.ts # TypeScript-only schemas
ledgers/
AccountLedger.ts
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

FunctionInputOutputUse Case
parseManifestYAML string{ manifest, domainManifest }Parse and validate, get both formats
manifestToBuildersYamlManifest{ entities, configs }Convert to builder objects for schema system
parseManifestToBuildersYAML string{ entities, configs, domainManifest }One-step parse and convert
validateManifestYAML stringstring[] (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.