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

domain-toolkit

v0.2.0

Published

A small, dependency-free toolkit of TypeScript building blocks for domain-driven design

Readme

domain-toolkit

The published package: TypeScript building blocks for domain-driven design.

For installation and the one compiler option @Handle needs, see the repo-root README. For every exported signature, see the API reference, regenerated from these sources on each push to main.

The building blocks

| Export | What it is | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Entity | A domain object defined by who it is, not what it holds. Records events; never publishes them. | | AggregateRoot | An Entity that is also a consistency boundary — the only way in, and the unit of persistence and publication. | | DomainIdGenerator | The port through which the domain obtains a new id. The adapter decides how; the model never looks inside one. | | ValueObject | Defined entirely by its attributes. No identity, no lifecycle, frozen on construction. | | DomainEvent | Something that has already happened, in the ubiquitous language. Past tense, immutable, carries ids. | | @Handle / HandleRegistry | Binds an event to the aggregate method that reacts to it, keyed by eventName. | | Repository | A collection-like interface over whole aggregates — one per root, never per entity. | | DomainError and friends | Errors the business refuses, as distinct from bugs. | | State, RequiredKeys, RequiredState | Re-exported from @domain-toolkit/state, which is bundled into this package's dist. |

Design notes

Each source file keeps the contract in its doc comments — what the thing is and how to call it, which is what the API reference renders. The reasoning behind the implementation lives here, one file per source file:

| Notes | Covers | | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | docs/entity.md | Why an id is a string and where it comes from; why an entity holds a state manager instead of extending one; the machinery behind create; why mutate wraps the operation. | | docs/aggregate-root.md | The cycle guard; why hasPendingEvents is overridden at all; fromEvents and the abstractness check that was traded away. | | docs/value-object.md | Why the freeze is enforced rather than recommended, and why equals is deliberately shallow. | | docs/domain-event.md | Why ordering uses a counter and not a timestamp; why eventName is static and cannot be checked. | | docs/handle-decorator.md | Why registration keys on eventName rather than the class name, and what broke when it did not. | | docs/handle-registry.md | Dropping reflect-metadata for a WeakMap; the prototype walk; why AggregateRootClass avoids a construct signature. |

Repo-root docs/ holds the investigation records these refer back to — a change that was made, what it cost, and what it fixed: inheritance-to-composition.md, state-manager-init.md, operation-atomicity.md, abstract-base-construction.md.

It also holds two reference notes on how the building blocks divide up:

  • domain-event-ownership.md — where Entity stops and AggregateRoot starts, event-wise. Start there if you are unsure whether something should record or apply.
  • entity-aggregate-membership.md — which entities may exist where. Start there if you are unsure whether something should be a child entity, its own aggregate root, or a value object.

Tests

test/ mirrors the concerns rather than the files: construction.test.ts, identity.test.ts, events.test.ts, boundary.test.ts, atomicity.test.ts, and state.test.ts — which holds the Entity/StateManager seam, the part of the state contract that cannot be tested inside @domain-toolkit/state itself.

Models the suites are written against live in test/models/.

Releasing

Two commands, from this directory. Each refuses to run on a bad state, so the order is the only thing to remember.

yarn release --minor              # or --patch, --major, --new-version 0.3.0-rc.1
npm publish

yarn release is yarn version. It writes the new version into package.json, commits that one file as chore: release 0.3.0 — the message comes from the repo-root .yarnrc — and creates the annotated tag v0.3.0 carrying the same message. Three lifecycle hooks in package.json hang off it and off npm publish:

| Hook | Runs | Gates | | ---------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | | preversion | before the version is written | check-staged, then lint, typecheck, test — staged changes or a red suite stop the release before any commit or tag exists. | | postversion | after the commit and tag | git push --follow-tags, so the tag reaches origin with its commit. | | prepublishOnly | before npm publish, and only there | build, then check:dist — a publish always ships a fresh dist that has passed the check. |

Both checks are one-screen shell scripts in scripts/. check-staged.sh exists because yarn version stages only the manifest and never looks at the index, so anything already staged would be swept into the release commit. check-dist.sh is the same line CI runs after every build: it refuses a bundle in which the private @domain-toolkit/state survived as an import specifier.

Why yarn for the version and npm for the publish

Yarn 1's version is the right tool here and its publish is not. yarn version walks up to the git root, stages this package's manifest alone, and leaves yarn.lock untouched; npm version in a workspace member creates no commit and no tag, and rewrites the lockfiles with the wrong package manager. yarn publish, on the other hand, prompts for a new version and bumps and tags as a side effect of publishing — exactly the coupling to avoid. npm publish does one thing, honours files and exports as written, and touches nothing else.