@verydia/contracts
v0.1.0
Published
Contract-first event specification for Verydia workflows and frontend streaming
Readme
@verydia/contracts
Contract-first event specification for Verydia workflows and frontend streaming.
Overview
@verydia/contracts provides a type-safe way to define event contracts that can be shared between workflow implementations and frontend consumers. This eliminates type duplication, casting, and ensures full type safety across your entire application.
Installation
npm install @verydia/contracts
# or
pnpm add @verydia/contracts
# or
yarn add @verydia/contractsQuick Start
1. Define Your Contract
import { defineEventContract, z } from "@verydia/contracts";
export const MyWorkflowContract = defineEventContract({
started: z.object({
timestamp: z.string(),
}),
progress: z.object({
percent: z.number().min(0).max(100),
message: z.string(),
}),
completed: z.object({
result: z.string(),
duration: z.number(),
}),
});2. Use in Workflow (Backend)
import { MyWorkflowContract } from "./contracts";
import type { InferEventPayload } from "@verydia/contracts";
async function myWorkflow(emit: (event: string, data: any) => void) {
// Emit with full type safety
emit("started", { timestamp: new Date().toISOString() });
emit("progress", { percent: 50, message: "Processing..." });
emit("completed", { result: "Success!", duration: 1234 });
}3. Use in Frontend (React)
import { MyWorkflowContract } from "./contracts";
import type { InferEventMap } from "@verydia/contracts";
type WorkflowEvent = InferEventMap<typeof MyWorkflowContract>;
function handleEvent(event: WorkflowEvent) {
if (event.type === "progress") {
console.log(event.data.percent); // Fully typed!
} else if (event.type === "completed") {
console.log(event.data.result); // Fully typed!
}
}API Reference
defineEventContract(spec)
Define an event contract for a workflow.
Parameters:
spec- An object mapping event names to Zod schemas
Returns:
- The same spec, but with enhanced type inference
Example:
const contract = defineEventContract({
progress: z.object({ percent: z.number() }),
complete: z.object({ result: z.string() }),
});EventContract
Type representing a contract mapping event names to their Zod schemas.
type EventContract = Record<string, z.ZodType<any, any, any>>;InferEventPayload<TContract, K>
Infer the payload type for a specific event in a contract.
Type Parameters:
TContract- The event contract typeK- The event name (key) in the contract
Example:
const contract = defineEventContract({
progress: z.object({ percent: z.number() }),
});
type ProgressPayload = InferEventPayload<typeof contract, "progress">;
// Result: { percent: number }InferEventMap<TContract>
Infer a discriminated union of all events in a contract.
Type Parameters:
TContract- The event contract type
Returns:
- A union of event objects with
type(discriminator) anddata(payload)
Example:
const contract = defineEventContract({
progress: z.object({ percent: z.number() }),
complete: z.object({ result: z.string() }),
});
type Events = InferEventMap<typeof contract>;
// Result:
// | { type: "progress"; data: { percent: number } }
// | { type: "complete"; data: { result: string } }Benefits
✅ Single Source of Truth
Define your events once, use them everywhere.
✅ Full Type Safety
No more casts, no more any, no more runtime surprises.
✅ Runtime Validation
Zod schemas provide both TypeScript types and runtime validation.
✅ Discriminated Unions
Frontend event handlers get perfect type narrowing.
✅ Refactoring Confidence
Change a contract and TypeScript will catch all affected code.
Advanced Usage
Complex Contracts
const contract = defineEventContract({
userAction: z.object({
userId: z.string().uuid(),
action: z.enum(["click", "scroll", "submit"]),
metadata: z.record(z.unknown()).optional(),
}),
error: z.object({
code: z.string(),
message: z.string(),
stack: z.string().optional(),
}),
});Nested Objects
const contract = defineEventContract({
dataUpdate: z.object({
user: z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
}),
changes: z.array(z.object({
field: z.string(),
oldValue: z.unknown(),
newValue: z.unknown(),
})),
}),
});Arrays and Unions
const contract = defineEventContract({
batchUpdate: z.object({
items: z.array(z.string()),
status: z.union([
z.literal("pending"),
z.literal("processing"),
z.literal("complete"),
]),
}),
});Related Packages
@verydia/dev-server- Local development server for workflows@verydia/client-sdk- Client SDK for calling workflows@verydia/tooling-core- Core utilities and types
License
MIT
Support
For issues and questions, please visit our GitHub repository.
