@contract-kit/events
v1.0.0
Published
Event, listener, and job primitives for Contract Kit applications.
Downloads
212
Maintainers
Readme
@contract-kit/events
Typed events, listeners, and jobs for Contract Kit applications.
Installation
bun add @contract-kit/eventsEvents
Use defineEvent(...) for facts that happened in your domain or application:
import { defineEvent } from "@contract-kit/events";
import { z } from "zod";
export const PostPublished = defineEvent("post.published", {
payload: z.object({
postId: z.string().uuid(),
slug: z.string(),
}),
});Publish through an event bus port:
import { publishEvent } from "@contract-kit/events";
await publishEvent(ctx.ports.eventBus, PostPublished, {
postId: post.id,
slug: post.slug,
});publishEvent(...) validates and parses the payload before publishing. If you
are inside a Unit of Work, record the event on tx.events and flush after
commit; the recorder validates each buffered event before publishing it.
Listeners
Listeners react to events. Bind your app context once so handler ctx is typed
without per-handler annotations:
import { createEventHandlers } from "@contract-kit/events";
import { PostPublished } from "@/domain/events";
import type { AppContext } from "@/app-context";
const events = createEventHandlers<AppContext>();
export const indexPublishedPost = events.defineListener(PostPublished, {
name: "posts.index-published",
async handle({ payload, ctx }) {
await ctx.ports.search.indexPost(payload.postId);
},
});Register listeners during application setup:
const unregister = events.registerListeners(eventBus, [indexPublishedPost], {
ctx: appContext,
onError(error, listener) {
logger.error({ error, listener: listener.name }, "Event listener failed");
},
});Jobs
Use jobs for explicit work that usually has one handler:
import { z } from "zod";
export const SendEmailJob = events.defineJob("mail.send", {
payload: z.object({
to: z.string().email(),
subject: z.string(),
text: z.string(),
}),
retry: {
attempts: 3,
},
async handle({ payload, ctx }) {
await ctx.ports.mailer.send(payload);
},
});For tests and local development, use the inline dispatcher:
const jobs = events.createInlineJobDispatcher({ ctx });
await jobs.dispatch(SendEmailJob, {
to: "[email protected]",
subject: "Welcome",
text: "Hello",
});Durable providers implement the same dispatch shape. For example,
@contract-kit/provider-inngest enqueues the job through Inngest and provides
helpers to run the same JobDef in an Inngest function. Providers may map
retry.attempts into their own retry configuration.
License
MIT
