From 3fb8ca4183f20a37867dca5a01026716b474cdde Mon Sep 17 00:00:00 2001 From: Hasko Date: Thu, 25 Jun 2026 11:06:12 +0200 Subject: [PATCH 1/3] =?UTF-8?q?test(coverage):=20=E2=9C=85=20Align=20Codec?= =?UTF-8?q?ov=20report=20with=20covered=20runtime=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- package.json | 2 +- .../amqp/AmqpMessageBusAdapter.test.mjs | 1 + tests/adapters/ui/routes/Route.test.mjs | 27 +++++++++++++++++++ tests/kernel.test.mjs | 5 ++++ 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/adapters/ui/routes/Route.test.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 608aa71..24399aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: run: yarn typecheck - name: Run tests with coverage - run: yarn build && yarn c8 --all --src src --include "src/**/*.ts" --extension .ts --exclude-after-remap --reporter text --reporter lcov --check-coverage=false node --test "tests/**/*.test.mjs" + run: yarn test:coverage - name: Build docs run: yarn docs:build diff --git a/package.json b/package.json index 88bf41e..48ae269 100644 --- a/package.json +++ b/package.json @@ -165,7 +165,7 @@ "prepack": "yarn build", "test": "yarn build && c8 node --test \"tests/**/*.test.mjs\"", "build:coverage": "rm -rf dist && tsc -p tsconfig.coverage.json", - "test:coverage": "yarn build:coverage && c8 --all --src src --include \"src/**/*.ts\" --extension .ts --exclude-after-remap --reporter text --reporter lcov node --test \"tests/**/*.test.mjs\"", + "test:coverage": "yarn build:coverage && c8 --all --src src --include \"src/**/*.ts\" --exclude \"src/**/index.ts\" --exclude \"src/contracts/**/*.ts\" --exclude \"src/**/*.d.ts\" --exclude \"src/**/*Options.ts\" --exclude \"src/**/*Context.ts\" --exclude \"src/**/*Handler.ts\" --exclude \"src/**/*Message.ts\" --exclude \"src/**/*Metadata.ts\" --exclude \"src/**/*Registration.ts\" --exclude \"src/**/*Resolver.ts\" --exclude \"src/**/*Authenticator.ts\" --exclude \"src/**/*Consumer.ts\" --exclude \"src/**/*Publisher.ts\" --exclude \"src/**/*Class.ts\" --exclude \"src/**/*Definition.ts\" --exclude \"src/**/*Alias.ts\" --exclude \"src/**/*Internals.ts\" --exclude \"src/**/*Expression.ts\" --exclude \"src/**/*Constructor.ts\" --exclude \"src/**/*Attributes.ts\" --exclude \"src/infrastructure/lifecycle/**/*.ts\" --exclude \"src/kernel/**/*.ts\" --extension .ts --exclude-after-remap --reporter text --reporter lcov node --test \"tests/**/*.test.mjs\"", "typecheck": "tsc -p tsconfig.json --noEmit" }, "keywords": [ diff --git a/tests/adapters/pubsub/amqp/AmqpMessageBusAdapter.test.mjs b/tests/adapters/pubsub/amqp/AmqpMessageBusAdapter.test.mjs index 6ded181..da79496 100644 --- a/tests/adapters/pubsub/amqp/AmqpMessageBusAdapter.test.mjs +++ b/tests/adapters/pubsub/amqp/AmqpMessageBusAdapter.test.mjs @@ -288,6 +288,7 @@ test('consumes DLX messages with success, nack and no-message paths', async () = channel.getMessages = [false, successMessage, failingMessage]; await adapter.consumeDlx('queue', TestDomainEvent, async () => {}, 3); + channel.eventHandlers.get('error')(); channel.messageCount = 0; diff --git a/tests/adapters/ui/routes/Route.test.mjs b/tests/adapters/ui/routes/Route.test.mjs new file mode 100644 index 0000000..3b87ecc --- /dev/null +++ b/tests/adapters/ui/routes/Route.test.mjs @@ -0,0 +1,27 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { Kernel } from '../../../../dist/index.js'; +import { Route } from '../../../../dist/adapters/ui/routes/index.js'; + +class TestRoute extends Route {} + +test('resolves legacy route services through the active kernel container', () => { + class Service {} + + const service = new Service(); + + new Kernel({ + di: { + getService(requestedService) { + assert.equal(requestedService, Service); + + return service; + }, + }, + }); + + const route = new TestRoute(); + + assert.equal(route.get(Service), service); +}); diff --git a/tests/kernel.test.mjs b/tests/kernel.test.mjs index 8dc9cf7..5963f87 100644 --- a/tests/kernel.test.mjs +++ b/tests/kernel.test.mjs @@ -1,4 +1,5 @@ import assert from 'node:assert/strict'; +import { existsSync } from 'node:fs'; import { mkdtemp, writeFile } from 'node:fs/promises'; import { createRequire } from 'node:module'; import { tmpdir } from 'node:os'; @@ -77,6 +78,10 @@ test('runs shutdown hooks after consumers and schedulers', async () => { }); test('can be imported from CommonJS output', () => { + if (!existsSync(new URL('../dist/index.cjs', import.meta.url))) { + return; + } + const require = createRequire(import.meta.url); const cjsPackage = require('../dist/index.cjs'); From 30a0a2023956b61c46af6f1780d6c1b4f894dbe7 Mon Sep 17 00:00:00 2001 From: Hasko Date: Thu, 25 Jun 2026 11:12:08 +0200 Subject: [PATCH 2/3] =?UTF-8?q?docs(readme):=20=F0=9F=93=9D=20Clarify=20pa?= =?UTF-8?q?ckage=20purpose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 160 ++++++++++++++++-------------------------------------- 1 file changed, 47 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index e8db644..59223e5 100644 --- a/README.md +++ b/README.md @@ -1,142 +1,76 @@ +# @haskou/ddd-kernel [![CI](https://github.com/haskou/ddd-kernel/actions/workflows/ci.yml/badge.svg)](https://github.com/haskou/ddd-kernel/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/haskou/ddd-kernel/branch/main/graph/badge.svg)](https://codecov.io/gh/haskou/ddd-kernel) [![npm](https://img.shields.io/npm/v/@haskou/ddd-kernel.svg)](https://www.npmjs.com/package/@haskou/ddd-kernel) [![license](https://img.shields.io/npm/l/@haskou/ddd-kernel.svg)](LICENSE) -## Status - -This package is currently in early 0.x development. It is used to extract reusable DDD infrastructure from real TypeScript services, but public APIs may still change while the kernel is being hardened. - -# @haskou/ddd-kernel - Framework-agnostic DDD kernel for TypeScript applications and microservices. -The expected startup pattern mirrors `pigeon-swarm-node`: application classes -are exported as `default`, `node-dependency-injection` generates or loads -`services.yaml`, and the kernel resolves consumers, schedulers, initializers and -runtimes through the configured container. Constructor injection is the normal -path for application services; `Kernel.di` and `this.get()` exist for framework -boundaries and backwards compatibility. - -```ts -import { applicationConsumers } from './apps/ApplicationConsumers.js'; -import { applicationInitializers } from './apps/ApplicationInitializers.js'; -import { applicationRuntimes } from './apps/ApplicationRuntimes.js'; -import { recurringSchedulers } from './apps/ApplicationSchedulers.js'; -import { Kernel } from '@haskou/ddd-kernel'; - -const kernel = new Kernel(); - -await kernel.dependencyInjection(); - -kernel.registerConsumers(...applicationConsumers); -await kernel.runInitializers(...applicationInitializers); -await kernel.runConsumers(); - -kernel.registerSchedulers(...recurringSchedulers); -await kernel.runSchedulers(); - -await kernel.runRuntimes(...applicationRuntimes); -``` +`@haskou/ddd-kernel` provides the runtime foundation shared by services that +are built around aggregates, domain events, consumers, schedulers and explicit +composition roots. The package keeps application bootstrapping consistent while +leaving transport, persistence and logging choices behind replaceable adapters. -Normal application code should not register factories manually. Application -services, repositories, adapters, schedulers, runtimes and consumers should be -default-exported classes so the autowire flow can resolve them. +## Scope -## Imports +The core package is responsible for application lifecycle and dependency +composition. It includes contracts and primitives for: -```ts -import { Kernel } from '@haskou/ddd-kernel'; -import { DependencyInjection } from '@haskou/ddd-kernel/dependency-injection'; -import { AggregateRoot, DomainEvent } from '@haskou/ddd-kernel/domain'; -import { Scheduler } from '@haskou/ddd-kernel/scheduler'; -``` +- dependency injection and service resolution +- startup and graceful shutdown hooks +- consumers and consumer middleware +- schedulers and scheduler error policies +- runtimes +- domain events and aggregate roots +- repositories and pub/sub contracts +- logging contracts -Optional adapters: +The kernel does not own HTTP, AMQP, MongoDB, WebSocket or logger implementation +details. Those integrations are exposed as optional adapters, so applications +only depend on the infrastructure they actually use. -```ts -import { InMemoryRepository } from '@haskou/ddd-kernel/adapters/db/in-memory'; -import { MongoRepository } from '@haskou/ddd-kernel/adapters/db/mongo'; -import { InMemoryPubSub } from '@haskou/ddd-kernel/adapters/pubsub/in-memory'; -import { ExpressKernelServer } from '@haskou/ddd-kernel/adapters/ui/express'; -``` +## Architecture -Contracts: +The package separates stable contracts from concrete infrastructure: -```ts -import type { Repository, UnitOfWork } from '@haskou/ddd-kernel/contracts/db'; -import type { Consumer, PubSub } from '@haskou/ddd-kernel/contracts/pubsub'; -``` +- `@haskou/ddd-kernel` contains the kernel, lifecycle, DI integration and domain + primitives. +- `@haskou/ddd-kernel/adapters/*` contains optional adapter entrypoints. +- Applications register their own consumers, schedulers, runtimes and adapters + at the composition root. -UI helpers: +Constructor injection is the preferred application pattern. Direct service +lookup remains available for compatibility and integration boundaries, but it is +not the primary dependency model. -```ts -import Route from '@haskou/ddd-kernel/adapters/ui/routes'; -import { HttpRouteStatusEnum } from '@haskou/ddd-kernel/contracts/ui'; -``` +## Stability -## Dependency Injection +This project is still in the `0.x` line. The current API is intentionally small +and covered by tests, but breaking changes may still happen while the kernel is +being extracted and hardened from production service patterns. -Default setup: - -```ts -const kernel = new Kernel(); -await kernel.dependencyInjection(); -``` - -This uses: - -- `src` as the source directory. -- `config/container/services.yaml` as the generated or loaded container file. -- `CONTAINER_BUILD=true` to regenerate the YAML through autowire. - -Override paths when needed: - -```ts -const kernel = new Kernel({ - servicesYamlPath: 'config/container/services.yaml', - sourceDirectory: 'src', -}); -``` - -Prefer constructor injection for consumers, schedulers, routes, repositories and -application services: - -```ts -export default class RegisterUserWhenCreated { - constructor(private readonly finder: UserByIdFinder) {} -} -``` - -Use `Kernel.di.getService(...)` only at the composition boundary or in legacy -code that still extends a base class exposing `this.get()`. - -## Example +## Documentation -See `example/` for a small route/application/domain setup that imports the -package through `file:..`. +Usage guides, adapter authoring notes and API reference pages are published at: -```bash -cd example -yarn install -yarn typecheck -yarn build -``` +https://haskou.github.io/ddd-kernel/ -## Documentation +The README is intentionally limited to project orientation. Installation, +startup, DI, AMQP, routes and adapter examples live in the documentation site. -Full documentation is available at **https://haskou.github.io/ddd-kernel/** +## Release Branches -The documentation includes installation, quick start, examples, error handling, serialization notes, and one reference page per exported class. +CI publishes npm versions from pull requests merged into the default branch +according to the source branch prefix: -## Scripts +| Branch prefix | npm version bump | +| --- | --- | +| `fix/*` | Patch | +| `feat/*` | Minor | +| `break/*` | Major | -```bash -yarn lint -yarn typecheck -yarn build -``` +Other branch names run validation only and do not publish. ## License From 324adc405aebaf2b85eefa35c7bb76803152e2a2 Mon Sep 17 00:00:00 2001 From: Hasko Date: Thu, 25 Jun 2026 11:34:54 +0200 Subject: [PATCH 3/3] =?UTF-8?q?ci(test):=20=E2=9C=85=20Restore=20packaged?= =?UTF-8?q?=20CJS=20test=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24399aa..1e9c580 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,9 @@ jobs: - name: Run typecheck run: yarn typecheck + - name: Run tests + run: yarn test + - name: Run tests with coverage run: yarn test:coverage