Schema Builders
TypeScript-first schema definitions for z0 entities, facts, and configs.
Prerequisites: core-concepts.md
Overview
Section titled “Overview”z0 provides a fluent TypeScript API for defining schemas. Instead of writing JSON manifests, define your domain model using TypeScript with full type inference.
import { z0 } from '@z0-app/sdk';
// Define an entityconst Account = z0.entity('account', { email: z0.string().indexed(), balance: z0.number().default(0), status: z0.enum(['active', 'suspended', 'closed']),}).facts({ deposit: { data: { amount: z0.number(), reference: z0.string().optional() } }, withdrawal: { data: { amount: z0.number() } },});
// Type inference works automaticallytype AccountFields = z0.infer<typeof Account>;// => { email: string; balance: number; status: 'active' | 'suspended' | 'closed' }The Three Primitives
Section titled “The Three Primitives”z0 has three core primitives. Each has a corresponding schema builder:
| Primitive | Builder | Purpose |
|---|---|---|
| Entity | z0.entity() | Identity/state container with fields and facts |
| Fact | z0.fact() | Immutable event with typed data |
| Config | z0.config() | Versioned configuration with scope |
Entity Schema
Section titled “Entity Schema”Entities are the core state containers. Define fields and the facts that can modify them.
Basic Entity
Section titled “Basic Entity”const User = z0.entity('user', { email: z0.string().indexed(), name: z0.string(), createdAt: z0.date(),});Entity with Facts
Section titled “Entity with Facts”const Account = z0.entity('account', { balance: z0.number().default(0), status: z0.enum(['active', 'frozen']),}).facts({ deposit: { data: { amount: z0.number(), reference: z0.string().optional() } }, withdrawal: { data: { amount: z0.number() } }, freeze: { data: { reason: z0.string() } },}).description('Financial account with balance tracking');Entity Validation
Section titled “Entity Validation”Generate Zod validators for runtime validation:
const validators = z0.entityToValidators(Account);
// Validate field dataconst result = validators.fields.safeParse({ balance: 100, status: 'active' });if (!result.success) { console.error(result.error);}
// Validate fact dataconst factResult = validators.validateFactData('deposit', { amount: 50 });Fact Schema
Section titled “Fact Schema”For standalone facts not tied to a specific entity definition:
const AuditEvent = z0.fact('audit', { action: z0.string(), userId: z0.string(), resourceId: z0.string(), metadata: z0.object({ ip: z0.string(), userAgent: z0.string().optional(), }),}).subtype('security') .description('Security audit trail event');
// Type inferencetype AuditData = z0.infer<typeof AuditEvent>;// => { action: string; userId: string; resourceId: string; metadata: { ip: string; userAgent?: string } }
// Validationconst validators = z0.factSchemaToValidators(AuditEvent);const result = validators.validate({ action: 'login', userId: 'user_123', resourceId: 'session_456', metadata: { ip: '192.168.1.1' }});Fact Methods
Section titled “Fact Methods”| Method | Description |
|---|---|
.subtype(string) | Set the fact subtype (e.g., ‘manual’, ‘recurring’) |
.description(string) | Add human-readable description |
Config Schema
Section titled “Config Schema”Configuration schemas define settings that can vary by scope:
const RateLimits = z0.config('rate_limits', 'security', { maxRequestsPerMinute: z0.number().default(100), windowMs: z0.number().default(60000), blockDurationMs: z0.number().default(300000),}).scope('tenant') .description('Per-tenant API rate limiting configuration');
// Type inferencetype RateLimitSettings = z0.infer<typeof RateLimits>;// => { maxRequestsPerMinute: number; windowMs: number; blockDurationMs: number }
// Validationconst validators = z0.configToValidators(RateLimits);const result = validators.validate({ maxRequestsPerMinute: 50, windowMs: 30000, blockDurationMs: 600000});Config Scopes
Section titled “Config Scopes”| Scope | Description |
|---|---|
tenant | Configuration specific to a single tenant |
platform | Configuration shared across all tenants |
global | System-wide configuration |
Config Methods
Section titled “Config Methods”| Method | Description |
|---|---|
.scope(scope) | Set default scope (‘tenant’, ‘platform’, ‘global’) |
.description(string) | Add human-readable description |
Field Types
Section titled “Field Types”Primitive Fields
Section titled “Primitive Fields”z0.string() // String valuesz0.number() // Numeric valuesz0.boolean() // Boolean valuesz0.date() // Date/timestamp valuesEnum Fields
Section titled “Enum Fields”z0.enum(['active', 'inactive', 'pending'])// Type inferred as: 'active' | 'inactive' | 'pending'Object Fields
Section titled “Object Fields”z0.object({ street: z0.string(), city: z0.string(), zip: z0.string(),})// Stored as JSON in SQLiteField Modifiers
Section titled “Field Modifiers”Chain modifiers to customize field behavior:
z0.string() .optional() // Field is not required .default('pending') // Default value .indexed() // Create generated column for queries .description('...') // Human-readable descriptionModifier Reference
Section titled “Modifier Reference”| Modifier | Description |
|---|---|
.optional() | Field can be undefined |
.required() | Field must be present (default) |
.default(value) | Default value when not provided |
.indexed() | Generate SQLite column for efficient queries |
.indexed(storage) | Use custom storage key for the index |
.description(desc) | Documentation string |
Indexed Fields
Section titled “Indexed Fields”Indexed fields create generated columns in SQLite for efficient querying:
const User = z0.entity('user', { email: z0.string().indexed(), // Creates ix_s_1 column age: z0.number().indexed(), // Creates ix_n_1 column status: z0.enum(['a', 'b']).indexed(), // Creates ix_s_2 column});The storage keys are auto-generated but can be customized:
z0.string().indexed('email_idx')Type Inference
Section titled “Type Inference”The z0.infer<> helper extracts TypeScript types from schemas:
// Entity fieldsconst Account = z0.entity('account', { email: z0.string(), balance: z0.number(),});type AccountData = z0.infer<typeof Account>;// => { email: string; balance: number }
// Standalone factconst Deposit = z0.fact('deposit', { amount: z0.number(), reference: z0.string().optional(),});type DepositData = z0.infer<typeof Deposit>;// => { amount: number; reference?: string }
// Config settingsconst Settings = z0.config('settings', 'app', { darkMode: z0.boolean().default(false),});type SettingsData = z0.infer<typeof Settings>;// => { darkMode: boolean }Manifest Generation
Section titled “Manifest Generation”Convert entity schemas to DomainManifest for the registry:
import { z0 } from '@z0-app/sdk';
// Register entitiesz0.registerEntity(Account);z0.registerEntity(User);
// Build manifestconst manifest = z0.buildManifest({ name: 'my-app', version: '1.0.0', ledgers: { account: AccountLedger, user: UserLedger, },});
// Or for a single entityconst simpleManifest = z0.entityToManifest(Account, { name: 'my-app', version: '1.0.0', ledger: AccountLedger,});Security: Freezing Schemas
Section titled “Security: Freezing Schemas”After boot, freeze the schema registry to prevent runtime tampering:
const platform = z0.boot(env, manifest);z0.freezeSchemas(); // No more schema registrations allowed
// Check if frozenif (z0.isSchemaFrozen()) { // Safe to proceed}Complete Example
Section titled “Complete Example”import { z0 } from '@z0-app/sdk';
// =========================================// Define Domain Schema// =========================================
// Entity with factsconst Account = z0.entity('account', { email: z0.string().indexed(), balance: z0.number().default(0), status: z0.enum(['active', 'suspended', 'closed']).indexed(), profile: z0.object({ firstName: z0.string(), lastName: z0.string(), avatar: z0.string().optional(), }),}).facts({ deposit: { data: { amount: z0.number(), reference: z0.string().optional() } }, withdrawal: { data: { amount: z0.number() } }, statusChange: { data: { newStatus: z0.string(), reason: z0.string() } },}).description('User financial account');
// Standalone fact for cross-entity eventsconst Transfer = z0.fact('transfer', { fromAccountId: z0.string(), toAccountId: z0.string(), amount: z0.number(), reference: z0.string(),}).description('Account-to-account transfer');
// Config for rate limitingconst RateLimits = z0.config('rate_limits', 'security', { maxTransfersPerDay: z0.number().default(10), maxTransferAmount: z0.number().default(10000),}).scope('tenant');
// =========================================// Type Inference (compile-time)// =========================================
type AccountFields = z0.infer<typeof Account>;type TransferData = z0.infer<typeof Transfer>;type RateLimitSettings = z0.infer<typeof RateLimits>;
// =========================================// Runtime Validation// =========================================
const accountValidators = z0.entityToValidators(Account);const transferValidators = z0.factSchemaToValidators(Transfer);const rateLimitValidators = z0.configToValidators(RateLimits);
// Validate incoming datafunction processDeposit(data: unknown) { const result = accountValidators.validateFactData('deposit', data); if (!result.success) { throw new Error('Invalid deposit data'); } return result.data; // Typed as { amount: number; reference?: string }}
// =========================================// Build Manifest// =========================================
z0.registerEntity(Account);
const manifest = z0.buildManifest({ name: 'banking-app', version: '1.0.0', ledgers: { account: AccountLedger, },});
// Boot and freezeconst platform = z0.boot(env, manifest);z0.freezeSchemas();Summary
Section titled “Summary”| Concept | API |
|---|---|
| Define entity | z0.entity(name, fields).facts({...}) |
| Define fact | z0.fact(type, data).subtype(...).description(...) |
| Define config | z0.config(type, category, settings).scope(...) |
| Infer types | z0.infer<typeof Schema> |
| Validate entity | z0.entityToValidators(entity) |
| Validate fact | z0.factSchemaToValidators(fact) |
| Validate config | z0.configToValidators(config) |
| Build manifest | z0.buildManifest({ name, version, ledgers }) |
| Freeze registry | z0.freezeSchemas() |
The schema builder API provides compile-time type safety and runtime validation from a single source of truth.