Leyman
An Nx monorepo of TypeScript libraries and tools for Node.js ESM environments.
First time here? Open this repo in VSCode — it will prompt you to reopen in the DevContainer, which sets up Node, PNPM, and all tooling automatically. Day-to-day work happens in git worktrees; see the worktrees skill.
Packages
Published to npm:
| Package | Description | Source |
|---|---|---|
barrelify |
Generate and CI-verify TypeScript barrel (index.ts) files with the correct ESM/CJS import extensions. | apps/barrelify |
common-proxy |
Wrap an ESM import so it is synchronously available as a promise-returning function in CommonJS. | tools/common-proxy |
constant-consistent-hash |
Consistent hashing in constant time with no loops: stateless, evenly balanced, and only moves keys to new buckets. Portable across languages. | tools/constant-consistent-hash/constant-consistent-hash |
default-import |
Properly handle CJS imports in ESM. | tools/default-import |
entry-script |
Modular control for entry script execution. | tools/entry-script |
enum-to-array |
Convert Typescript Enums to a strongly typed array. | tools/enum-to-array |
find-import |
Find and load first instance of js/json in parent directories. | tools/find-import |
format-file |
Utility API to format files via biome | tools/format-file |
generated-signature |
Sign generated files so hand edits are detected, while keeping manual sections editable | tools/generated-signature/generated-signature |
genever |
Generate TypeScript types from JSON Schema files and OpenAPI documents, with strict schema checks and the same types Juniper infers. | apps/genever |
haywire |
Compile-time checked dependency injection for TypeScript — no decorators, no reflect-metadata, no global state. | tools/haywire |
haywire-launcher |
Instantiate and execute your script in one line. | tools/haywire-launcher |
iso-crypto |
Isomorphic cryptography for browsers and Node.js — one API over WebCrypto and node:crypto for hashing, AES (including GCM), PBKDF2/HKDF key derivation and ECDH. | tools/iso-crypto |
juniper |
Build JSON Schemas in TypeScript with inferred static types — strict, Ajv-ready, JSON Schema 2020-12 and OpenAPI 3.0 output. | apps/juniper |
juniper-validator |
StandardSchema compliance for juniper. | apps/juniper-validator |
lease-cache |
Read-through caching on Valkey or Redis with leases: one load per key across every instance, and a load that finishes after an invalidation never caches its outdated value. | tools/lease-cache |
load-populate-files |
Load and dynamically populate file content | apps/populate-files/load-populate-files |
mocha-chain |
Chain mocha BDD methods together for deterministic and type safe tests. | tools/test-chain/mocha-chain |
normalized-react-query |
Type-safe query definitions for TanStack Query — define each key and fetcher once, and link related queries so they prefetch together. | tools/normalized-react-query |
nx-lifecycle |
Nx plugin that derives every dependsOn from declared lifecycle stages (build, test, …) and the targets bound to them. | apps/nx-lifecycle |
parse-cwd |
Convert relative path or URL to full path | tools/parse-cwd |
pg-dolly |
A fresh Postgres database for every test, cloned from a template built once from your schema's SQL. | tools/pg-dolly |
pg-dolly-schema |
Keep a schema.sql of what your Postgres migrations build: migrate an empty database, dump it, and fail CI when the committed file is stale. | apps/pg-dolly-schema |
populate-files |
Dynamically populate file content | apps/populate-files/populate-files |
punycode-esm |
Typescript + ESM version of punycode.js | tools/punycode-esm |
static-emitter |
Statically typed event emitter. | tools/static-emitter |
test-chain-core |
Framework-agnostic internals for chaining test hooks with typed context | tools/test-chain/test-chain-core |
unknown-error |
Turn any caught unknown into a real Error without throwing, even for hostile values, other realms and deep or circular cause chains. |
tools/unknown-error |
vitest-chain |
Chain vitest methods together for deterministic and type safe tests | tools/test-chain/vitest-chain |
weighted-rendezvous-hash |
Weighted rendezvous hashing: any set of nodes, in any order, sized by proportion, moving few keys when the set changes. | tools/constant-consistent-hash/weighted-rendezvous-hash |
Private workspace packages (shared configs and tooling) are listed in the projects skill.
Packages that have been removed from the repo but are still on npm are listed in DEPRECATED_PACKAGES.md.
A documentation site, with every code example type-checked, is in docs/ (barrelify, entry-script, generated-signature, genever, haywire, haywire-launcher, juniper, juniper-validator, lease-cache, mocha-chain, normalized-react-query, nx-lifecycle, populate-files, unknown-error and vitest-chain so far), plus the demos.
Demos
Worked examples of putting open-source projects and services together well, in demos/. They are never published: the code and its write-up are the point. Each is tested against real services (devcontainer sidecars, and the same images in CI) with 100% coverage, with every feature behind a haywire binding so each can be tested on its own. Also on the docs site.
| Demo | What it shows | Source |
|---|---|---|
| OpenAPI, spec to server | A spec built with Juniper, a client and a server typed by genever, the server validating every request and response against openapi.json with bearer auth, on Postgres with Drizzle, tested with pg-dolly. |
demos/openapi |
| Read-through cache | BentoCache on Valkey: a memory tier kept in sync, stale-while-revalidate, one load per key across instances with lease-cache, and sharding with weighted-rendezvous-hash. |
demos/read-through-cache |
Getting Started
# Install dependencies
pnpm i
# Build and test everything
test-ci
Nx is not globally installed. It ships with
@leyman/mainand is onPATHautomatically. Usenxdirectly — notnpx nxorpnpm exec nx.
See AGENTS.md for the full skill index.
CI
This repo uses Dagger for CI. To replicate CI locally:
dagger-test
# or equivalently:
dagger call --mod ./dagger test
Run test-ci first — it's faster for iteration. Use Dagger to confirm before pushing.
See all aliased (added to PATH by devcontainer.json) scripts in the /scripts/commands directory.
Coding Agents
Agent docs start at AGENTS.md (also linked as CLAUDE.md). In the devcontainer, Claude Code config (credentials, settings, memory) lives in <main checkout>-worktrees/.shared/claude-config, outside version control and shared by every worktree. See the worktrees skill.
(If you use a different coding agent, PRs are welcome to support those directories).
Contributing
Contributions are welcome! See CONTRIBUTING.md for setup, conventions and how to submit a pull request. In short:
- Make your changes
- Run
test-cito verify - Run
changesetand follow the prompts to document your changes - Submit a PR — GitHub Actions runs Dagger automatically
This project follows the Contributor Covenant Code of Conduct. To report a security vulnerability, see SECURITY.md.
Tooling
| Tool | Role |
|---|---|
| PNPM | Package manager with workspace support |
| Nx | Task runner with caching and dependency graph |
| TypeScript | Language (ESM, strict mode) |
| SWC | Fast TypeScript compiler |
| ESLint | Linting (opinionated, 15+ plugins) |
| Biome | Formatting |
Mocha + Chai (via @leyman/expect) |
Testing |
| C8 | Coverage (100% required) |
| Dagger | CI pipeline |
| Changesets | Versioning and changelogs |
License
MIT Jacob Ley