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

@ebca/core

v0.0.12

Published

Core runtime for Event-Based Component Architecture.

Readme

@ebca/core

Core runtime for Event-Based Component Architecture.

@ebca/core gives a NestJS backend a typed component lifecycle: entities hold components, commands are components, systems react to lifecycle events, and all state changes go through ComponentManager.

It is the foundation package for EBCA. It does not include WebSocket, GraphQL, REST, or application-specific business logic.

Install

npm install @ebca/core

Peer dependencies are intentionally explicit: NestJS, NATS, TypeORM, cache-manager, Redis/Keyv, RxJS, and reflect-metadata are supplied by the consuming application.

What It Provides

  • Base classes: BaseEntity, BaseComponent, BaseCommandComponent.
  • Runtime decorators: @Entity, @Component, @System, @EbcaPattern.
  • Read-side decorators: @EbcaReadRepository, @EbcaQuery, @EbcaQueryParam.
  • IO metadata: @EbcaIO for runtime architecture reports.
  • Inbound component metadata for gateway operations, ownership, roles, and fields.
  • Projection metadata for WebSocket and GraphQL component snapshots/subscriptions.
  • Persistence mapping: @PersistentProperty.
  • ComponentManager as the single component read/write path.
  • TypeORM JSONB persistence and column projection support.
  • NATS lifecycle publication.
  • Delayed streams and opt-in ordered ingress for command paths.

Mental Model

flowchart LR
  Boundary["Boundary writes command component"] --> Manager["ComponentManager"]
  Manager --> State["Redis / DB projection"]
  Manager --> Lifecycle["ebca.entity.id.event.component"]
  Lifecycle --> System["@EbcaPattern handler"]
  System --> Manager

Commands, facts, inputs, and state are all represented as components. A gateway or API writes an intent component. A system owns the domain decision. The domain result is expressed as more component lifecycle, not as hidden side effects.

Example

Use the runnable example in examples/counter as the canonical minimal app.

It shows the real core surface:

  • CounterEntity extends BaseEntity and is registered with @Entity.
  • IncrementCounterCommandComponent extends BaseCommandComponent.
  • CounterValueComponent is a persistent BaseComponent.
  • CounterSystem subscribes with object-form @EbcaPattern.
  • @ebca/rest-gateway exposes REST mutations, Swagger, and read queries over the same ComponentManager path.

The example includes the required NestJS wiring for PostgreSQL, Redis, NATS, and EbcaModule.

Command components track their source (system, websocket, rest, or graphql) so gateways can share the same command base without hiding where an intent entered the system.

Read query transport result contracts are inferred from the method return type when the CLI can read the repository source file. @EbcaQuery({ resultType }) remains an override for unusual signatures, but normal async readWorldMap(): Promise<MapMarkupDocument> methods need no extra DTO or declaration layer. Queries without explicit gates are available to every query transport; use a non-empty gates list when a query should be transport-specific, or gates: [] when it is repository-internal only.

Command Storage

Command components are transient by default. ComponentManager emits their lifecycle event and does not keep the command in Redis, so repeated writes behave like repeated intent events.

For idempotent paths, opt in per command component:

@Component({
  transient: false,
  inbound: {
    expose: true,
    fields: ['commandId', 'orderId', 'amount'],
  },
})
export class PlaceOrderCommandComponent extends BaseCommandComponent {
  readonly orderId!: string;
  readonly amount!: number;
}

transient: false is not JSONB persistence. It stores the current command component in the entity Redis hash, keeps getComponent and hasComponent meaningful for that command, and preserves terminal status when a system calls updateComponent after succeed() or reject(...).

Duplicate add or upsert with the same commandId and same intent payload is ignored. The same commandId with a different intent payload is rejected. A different command instance for the same entity and component name requires explicit removal first, or a different command/order entity if the domain needs multiple command instances.

REST and GraphQL gateways create a random commandId when one is missing. Real idempotent retries need a stable caller-provided commandId exposed through inbound.fields.

Runtime Metadata

EBCA decorators register metadata at runtime. That metadata powers:

  • handler discovery;
  • architecture reports;
  • command workflow graphs;
  • IO coverage checks;
  • generated transport contracts;
  • optional WebSocket and GraphQL adapters.

This is the heart of why EBCA works well with AI-assisted development: the system can explain its own shape before a human or agent edits it.

Ordered Ingress

EBCA can keep default NATS lifecycle behavior for most commands while enabling ordered ingress only where sequence matters.

When a handler pattern opts in, command publication resolves a shard at publish time, writes a JetStream envelope, replays the original EBCA topic through the partition consumer, and acknowledges only after the handler completes.

This avoids global locks and keeps horizontal scaling available.

Boundaries

@ebca/core should stay domain-agnostic.

  • Put business rules in systems.
  • Put transport policy in adapters.
  • Put read-side filtering in repositories.
  • Use ComponentManager for state changes.
  • Keep direct DB writes out of domain lifecycle code unless they are explicit read-model/projection concerns.

License

Apache-2.0.