On this page
  1. Install
  2. Define and compose registries
  3. Bridge configuration
  4. Emit options and envelope properties
  5. Handler and transport bridges
  6. Boundary
  7. API entry points and requirements
  8. Peer dependencies

@playstack/events

Typed in-process domain events for dependency inversion between Playstack packages.

Free. MIT licensed. Check preview availability before installing. See package access.

@playstack/events is the typed seam through which a capability describes what happened without selecting a framework, broker, or event store.

Install

sh
npm install @playstack/core @playstack/events

Define and compose registries

Payload validators use Standard Schema v1, so Zod and other conforming schemas work directly.

ts
import { createEventBridge, defineEvents } from '@playstack/events'
import { z } from 'zod'

const catalogEvents = defineEvents({
  'catalog.product.published': {
    payload: z.object({ productId: z.string(), slug: z.string() }),
    audit: { tier: 'transactional', fields: ['productId'] },
  },
})

export const eventBridge = createEventBridge({
  registries: [catalogEvents, accountsEvents, authEvents],
  clock,
  ids,
  outbox,
  supportedTransactionKinds: ['prisma'],
  routes: [
    { match: 'catalog.product.published', to: [searchIndexer], retry: 3 },
  ],
  failureRecorder,
})

eventBridge.assertReady()

Event names require at least three lowercase dot-separated segments. defineEventSchema(parse) adapts a small hand-written parser when a schema library is unnecessary.

Bridge configuration

EventBridgeOptions propertyRequiredPurpose
registriesYesPackage and application registries merged into one typed contract.
clock, idsYesEnvelope timestamps and identifiers.
outboxFor durable routesTransactional observational delivery bridge.
routesNoMaps event names to observational handlers and retry counts.
failureRecorderNoCaptures isolated observational dispatch failures.
supportedTransactionKindsNoAllowlist of transaction handles accepted by internal handlers/outbox.
maximumDepthNoLimits recursive caused-event chains.

The returned bridge exposes the merged registry, typed events emitter, and assertReady() completeness check. It owns no application lifecycle.

Emit options and envelope properties

ts
await events.emit(
  'catalog.product.published',
  () => ({ productId, slug }),
  { transaction, context: operationContext },
)
Emit optionMeaning
transactionApplication transaction used by same-kind internal handlers and the outbox.
causeSource envelope; inherits correlation, actor, scope, and trace and sets causation.
contextRoot OperationContext for a new chain.
bestEffortAllows explicitly non-critical observational work to fail without rejecting emission.

Every immutable EventEnvelope carries id, name, validated JSON payload, occurredAt, correlationId, and optional causation, actor, scope, and W3C trace properties.

Handler and transport bridges

ContractResponsibility
InternalEventHandlerDeclares transactionKind and handles an event inside the emitting transaction.
ObservationalEventHandlerNamed handler for isolated, retryable application reactions.
OutboxWriterDeclares transactionKind and enqueues the envelope atomically.
DispatchFailureRecorderRecords event, handler, error, and attempt count.

Internal handlers implement package invariants and may fail the originating mutation. Observational handlers belong behind the outbox when they cross process or infrastructure boundaries.

emit is a no-op that returns null when nothing is registered for the event: no internal handler, no observational handler, and no route. An outbox row is written only when an observational handler or route will consume it, so a host that wants every event of a package in the outbox, for a projection or an external consumer, registers an observational handler for it. Transactional audit handlers bound with @playstack/audit write audit rows on the emitting transaction and do not by themselves produce outbox rows.

Boundary

Events is not an event-sourced aggregate log, replay system, or general broker abstraction. Stateful packages expose their persistence seams and publish integration events rather than importing one another; the application chooses durable transport and consumer lifecycle.

API entry points and requirements

Reference snapshot: @playstack/events@0.1.0-beta.1. Import only the entry point your runtime needs. Paths below are relative to the installed package; use Go to Definition in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

Public entry pointDeclaration file
@playstack/events./dist/index.d.ts
@playstack/events/errors./dist/errors.d.ts
@playstack/events/testing./dist/testing.d.ts
@playstack/events/playstack.integration.jsonNo TypeScript declaration (asset or metadata export).
@playstack/events/package.jsonNo TypeScript declaration (asset or metadata export).

Node.js engine requirement: >=20. This is not a claim that every entry point works in browsers or Workers.

Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with Getting started. For API lookup and partial-example conventions, see Reading the reference. Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

Go

Playstack Pro tag
OriginsPricingBlogNewsletterChangelogStatusRoadmap
ContributorsCommunityIn Use ShowcaseCase StudiesPartnersSponsors
FAQsSupportContact

© 2026 Playstack. All rights reserved.

With OSS
Terms of ServicePrivacy PolicyCookie PolicyImprint

By

Commune Software