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

@othrys-core/event-bus

v0.1.0

Published

The Othrys domain event bus: immutable domain events, dispatcher, subscribers, in-memory store. The one package products consume.

Downloads

27

Readme

@othrys-core/event-bus

The Othrys domain event bus: immutable domain events, a dispatcher, subscribers, and an append-only store. This is the package products consume — the first supported seam between othrys-core and the product repositories (othrys-web, vtc-platform).

Standalone, pure domain. No UI, no React, no Supabase, no HTTP, no vendor brand (Article 8).

Every significant platform action becomes an immutable domain event — a past-tense fact, frozen at construction, appended to a log and delivered to subscribers. The goal: every subsystem communicates through events instead of holding a direct dependency on another. A module publishes PhotoUploaded; rewards / search / audit subscribe. Neither imports the other.

Why this package exists

Before it, othrys-core was not distributed at all. Every consumer had to copy the source or hand-write a shim, and both happened: vtc-docs/event-bus was a byte-identical fork, and vtc-platform/src/titan/eventBusCompat.js is a hand-written adapter whose own comment says "swap this import for @othrys-core/event-bus once it is distributed as a package."

This package is that swap. Products must not copy Othrys source or maintain compatibility shims — they import this.

Install and use

npm install @othrys-core/event-bus
import { InMemoryEventBus, LocalEventRepository, createEvent } from "@othrys-core/event-bus";

const bus = new InMemoryEventBus(new LocalEventRepository());

bus.subscribe("PhotoUploaded", (event) => {
  console.log(event.type, event.payload);
});

await bus.publish(createEvent("PhotoUploaded", { photoId: "p-1" }));

Events (10 defined)

CollectibleCreated · CollectibleUpdated · PhotoUploaded · PriceImported · CollectionCreated · WishlistChanged · MarketplaceListingCreated · BadgeAwarded · CreditGranted · SearchPerformed

Each is a DomainEvent<T>id, type, occurredAt, a typed payload, and metadata (actorId, aggregateId, correlationId, causationId, source) for tracing and choreography. Adding an event is one key in EventPayloads + EVENT_TYPES; nothing in the dispatcher, store, or subscribers changes.

Dispatch contract

publish(event):

  1. Persist first — append to the store; if it can't be stored, nothing is delivered and the publish rejects. The log is the source of truth.
  2. Deliver — to every matching handler, in subscription order, awaiting async handlers.
  3. Isolate failures — a throwing handler never stops the others and never fails the publisher; failures come back in PublishResult.errors. A bus fault must never disturb the application.

Subscribers

A subsystem ships an EventSubscriber (name, subscribedTo, handle) and is wired once with registerSubscriber(bus, subscriber). Two references are included: an audit trail (wildcard observer) and a rewards subscriber that answers PhotoUploaded by publishing a CreditGranted (with causationId back to the upload) — choreography, not a direct call.

Layout

src/
  domain/
    events.ts            # payloads, DomainEvent envelope, createEvent (deep-frozen)
    EventBus.ts          # EventBus + EventHandler + Subscription + EventSubscriber interfaces
    EventRepository.ts   # append-only event-store interface
  infra/
    InMemoryEventBus.ts     # the dispatcher (persist-first, isolate failures) — 100% covered
    LocalEventRepository.ts # in-memory append-only log (the only shipped store)
    repositoryProvider.ts   # the single store swap point
    busProvider.ts          # the single shared-bus access point
  subscribers/
    registerSubscriber.ts   # wire a declarative EventSubscriber onto the bus
    exampleSubscribers.ts   # audit trail (wildcard) + rewards (reacts by publishing)
  index.ts                  # public API barrel

Commands

npm run build          # rm -rf dist && tsc -p tsconfig.build.json  → dist/ (ESM + .d.ts)
npm test               # vitest run — 32 tests
npm run typecheck      # tsc --noEmit (strict)
npm run test:coverage  # dispatcher at 100% (hard gate in vitest.config.ts)

The dev tsconfig.json is noEmit (typecheck + vitest only). tsconfig.build.json is the only thing that produces a consumable artifact.

Source imports carry explicit .js extensions so the emitted ESM resolves in plain Node as well as in bundlers (Vite for vtc-platform, Next.js for othrys-web). TypeScript maps "./foo.js" back to foo.ts at compile time. Keep the extensions — dropping them silently produces output Node cannot resolve. dist/ is generated and git-ignored; prepack rebuilds it, so a publish can never ship a stale artifact.

Verified

  • Dispatching is 100% covered — statements / branches / functions / lines on InMemoryEventBus.ts, enforced as a threshold gate.
  • Immutable eventsevents.test.ts proves an event (and its payload) cannot be mutated after creation.
  • Consumable — the built dist/ has been imported and exercised from plain Node.

Boundary

Othrys owns capability, never a customer's world. This package never depends on a product, and a product never reaches past this surface into othrys-core internals.


Part of othrys-core. Roadmap: MASTER-PLAN.md. The record never lies.