npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arki/event-sourcing

v0.2.2

Published

Event sourcing primitives — event store, message bus, projections, process managers — built on Emmett for ARKI.

Readme

@arki/event-sourcing

Event sourcing primitives for the ARKI package family — event store, message bus, command handlers, projections, and process managers, built on top of Emmett with @event-driven-io/emmett-postgresql.

Installation

npm install @arki/event-sourcing
# or
bun add @arki/event-sourcing
# or
pnpm add @arki/event-sourcing

Peer-installs:

  • @event-driven-io/emmett (re-exported core types)
  • @event-driven-io/emmett-postgresql (PostgreSQL event store)
  • drizzle-orm (for projections that write through a Drizzle handle)

What you get

  • Event storegetEventStore(connectionString, options) opens a PostgreSQL event store with a clean close() that releases the pool.
  • Message buseventSourcingFeatures.initMessageBus(eventStore, handlers) builds an in-memory message bus, registers command handlers, and wraps each handler with stream-name resolution + logging.
  • DOT adaptereventSourcing() opens the store, publishes eventStore + messageBus, collects feature-local ES bundles, and registers the event-catalog projection.
  • Process managerscreateProcessManager (per-batch), createStatefulProcessManager (per-batch with state), and createSimpleProcessManager (per-message with a pre-bound context).
  • BuildersdefineCommand, defineEvent, defineDecider, defineProjection, defineCommandHandler, defineProcessManager, defineStatefulProcessManager for type-safe authoring.
  • Drizzle projection helpersdrizzleProjection / drizzleWithRepoProjection so projection handlers receive a typed Drizzle database (or repository registry) instead of a raw client.

Quick start

DOT app setup

import { z } from '@arki/contracts';
import { defineApp, plugin, provide, token } from '@arki/dot';
import { eventSourcing, es, type EsBundle } from '@arki/event-sourcing/dot';
import { defineCommand, defineCommandHandler, defineEvent } from '@arki/event-sourcing/builders';

const placeOrder = defineCommand({
  type: 'PlaceOrder',
  inputSchema: z.object({ orderId: z.string(), total: z.number() }),
});

const orderPlaced = defineEvent({
  type: 'OrderPlaced',
  dataSchema: z.object({ orderId: z.string(), total: z.number() }),
});

const placeOrderHandler = defineCommandHandler({
  initialState: () => ({ placed: false }),
  evolve: (state, event) => (event.type === 'OrderPlaced' ? { placed: true } : state),
  decide: (command, state) => {
    if (state.placed) throw new Error('Order already placed');
    return [orderPlaced({ orderId: command.data.orderId, total: command.data.total })];
  },
});

export const OrdersEs = token<EsBundle>()('orders.es');

export const ordersEsPlugin = plugin({
  name: 'orders-es',
  actions: [placeOrder, orderPlaced],
  boot: () =>
    provide(
      OrdersEs,
      es.bundle({
        handlers: [es.handle(placeOrder, placeOrderHandler, command => `order-${command.data.orderId}`)],
        readModels: [],
      }),
    ),
});

export const app = defineApp('shop-api')
  .use(ordersEsPlugin)
  .use(eventSourcing({ bundles: [OrdersEs], dbUrl: process.env.DB_URL }));

eventSourcing() may be mounted with no options for append-only mode. It resolves the store URL from EVENT_STORE_URL, EVENTSTORE_URL, or EVENT_DB_URL; apps that use a different env name should pass dbUrl explicitly.

Commands and events produced by defineCommand / defineEvent contribute DOT actions. dot explain --as event-catalog renders their JSON Schemas without booting the app. Deprecated dynamic commandHandlers still run, but are intentionally absent from the manifest and catalog.

Composition order

Feature plugins that handle commands publish ES bundles. Feature plugins that send commands need the messageBus service. Mount them in this order:

es-feature plugins -> eventSourcing() -> http/queue feature plugins that need messageBus -> http()/queue runtime

A feature that both handles PlaceOrder and exposes POST /orders should be split into two plugins: orders-es publishes OrdersEs; orders-http needs messageBus and binds the HTTP route.

HTTP and queue recipes

Expose a command over HTTP explicitly:

routes().bind(placeOrderRoute, async ({ body }, { messageBus }) => {
  await messageBus.send(placeOrder(body));
  return { ok: true };
});

Use @arki/queue for durable/async command transport:

jobs.worker('orders.place', async ({ payload }, { messageBus }) => {
  await messageBus.send(placeOrder(payload));
});

The message bus published by this package is strictly in-process synchronous dispatch. It is not durable, does not distribute work across instances, and does not survive restarts or rolling deploys.

Programmatic bootstrap

Non-DOT composition roots can still use the lower-level helpers:

import { eventSourcingFeatures } from '@arki/event-sourcing';

const { eventStore, close } = eventSourcingFeatures.initEventSourcing(
  [
    /* read-model projections produced by defineProjection / drizzleProjection / postgreSQLProjection */
  ],
  process.env.EVENT_STORE_URL,
);

const messageBus = eventSourcingFeatures.initMessageBus(eventStore, [
  /* { commandType, handler, getStreamName } registrations */
]);

await close();

Process managers

Use createProcessManager for batch processing, or createSimpleProcessManager when the handler closes over a fixed context and processes one event at a time:

import { createSimpleProcessManager } from '@arki/event-sourcing';

const sendOrderEmails = createSimpleProcessManager(
  { emailer, repo },
  'order-emails',
  ['OrderPlaced', 'OrderCancelled'],
  async (event, ctx) => {
    if (event.type === 'OrderPlaced') {
      await ctx.emailer.send('order-placed', event.data);
    } else {
      await ctx.emailer.send('order-cancelled', event.data);
    }
  },
);

await eventSourcingFeatures.setupProcessManagers(eventStore, [sendOrderEmails]);

setupProcessManagers starts each process manager as a consumer of the event store and installs a SIGTERM hook that stops and closes the consumers before exiting.

Subpath exports

  • @arki/event-sourcing — main surface (commands, events, deciders, process managers, features).
  • @arki/event-sourcing/builders — fluent builders only.
  • @arki/event-sourcing/storegetEventStore, drizzleProjection, postgreSQLProjection, projection context types.
  • @arki/event-sourcing/busgetInMemoryMessageBus and message bus types re-exported from Emmett.
  • @arki/event-sourcing/dot — DOT plugin, ES bundle helpers, and ES plugin error codes.
  • @arki/event-sourcing/projection — pure event-catalog projection for DOT.

Documentation

For an overview of the package's design principles, see docs/design.md. For command flow patterns, see docs/command-flows.md.

License

MIT © ARKI Contributors