archstone 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -10
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/domain/application/index.d.ts +1 -1
- package/dist/domain/enterprise/index.d.ts +1 -1
- package/dist/domain/enterprise/index.js +1 -1
- package/dist/domain/index.d.ts +1 -1
- package/dist/domain/index.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-z9br464b.d.ts → chunk-ghvxv2w6.d.ts} +9 -0
- package/dist/shared/chunk-k4yvnajk.js +2 -0
- package/dist/shared/{chunk-k39ksk62.js → chunk-ph10w7xb.js} +1 -1
- package/package.json +1 -1
- package/dist/shared/chunk-8hx7pxm3.js +0 -2
package/README.md
CHANGED
|
@@ -224,9 +224,9 @@ export interface AuditRepository extends Creatable<AuditLog> {}
|
|
|
224
224
|
| Import | Contents |
|
|
225
225
|
|---|---|
|
|
226
226
|
| `archstone` | Everything |
|
|
227
|
-
| `archstone/core` | `Either`, `ValueObject`, `UniqueEntityId`, `WatchedList`, `Optional`, `EventHandler` |
|
|
227
|
+
| `archstone/core` | `Either`, `ValueObject`, `UniqueEntityId`, `WatchedList`, `Optional`, `DomainEvent`, `DomainEvents`, `EventHandler` |
|
|
228
228
|
| `archstone/domain` | All domain exports |
|
|
229
|
-
| `archstone/domain/enterprise` | `Entity`, `AggregateRoot
|
|
229
|
+
| `archstone/domain/enterprise` | `Entity`, `AggregateRoot` |
|
|
230
230
|
| `archstone/domain/application` | `UseCase`, `UseCaseError`, repository contracts |
|
|
231
231
|
|
|
232
232
|
All sub-paths share type declarations via a common chunk — mixing imports from multiple sub-paths is fully type-safe with no duplicate declaration conflicts.
|
|
@@ -242,18 +242,18 @@ src/
|
|
|
242
242
|
│ ├── value-object.ts # Value equality base class
|
|
243
243
|
│ ├── unique-entity-id.ts # UUID v7 identity wrapper
|
|
244
244
|
│ ├── watched-list.ts # Change-tracked collection
|
|
245
|
+
│ ├── events/
|
|
246
|
+
│ │ ├── domain-event.ts # Marker interface for all domain events
|
|
247
|
+
│ │ ├── domain-events.ts # Central registry and dispatcher (singleton)
|
|
248
|
+
│ │ └── event-handler.ts # Generic handler interface EventHandler<T>
|
|
245
249
|
│ └── types/
|
|
246
250
|
│ └── optional.ts # Optional<T, K> helper type
|
|
247
251
|
│
|
|
248
252
|
└── domain/
|
|
249
253
|
├── enterprise/ # Pure domain model — zero framework dependencies
|
|
250
|
-
│
|
|
251
|
-
│
|
|
252
|
-
│
|
|
253
|
-
│ └── events/
|
|
254
|
-
│ ├── domain-event.ts
|
|
255
|
-
│ ├── domain-events.ts
|
|
256
|
-
│ └── event-handler.ts
|
|
254
|
+
│ └── entities/
|
|
255
|
+
│ ├── entity.ts
|
|
256
|
+
│ └── aggregate-root.ts
|
|
257
257
|
│
|
|
258
258
|
└── application/ # Orchestration — use cases & repository contracts
|
|
259
259
|
├── use-cases/
|
|
@@ -269,6 +269,73 @@ src/
|
|
|
269
269
|
|
|
270
270
|
---
|
|
271
271
|
|
|
272
|
+
## Technology Stack
|
|
273
|
+
|
|
274
|
+
| | |
|
|
275
|
+
|---|---|
|
|
276
|
+
| **Language** | TypeScript 5+ |
|
|
277
|
+
| **Runtime / Package Manager** | [Bun](https://bun.sh) (required) |
|
|
278
|
+
| **Test Framework** | `bun:test` (built-in) |
|
|
279
|
+
| **Build Tool** | [bunup](https://github.com/nicepkg/bunup) |
|
|
280
|
+
| **Linter / Formatter** | [Biome](https://biomejs.dev) via [Ultracite](https://ultracite.dev) |
|
|
281
|
+
| **Dependencies** | None (zero runtime dependencies) |
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Development Workflow
|
|
286
|
+
|
|
287
|
+
Requirements: **Bun >= 1.0**
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
bun install # install dev dependencies
|
|
291
|
+
bun test # run tests
|
|
292
|
+
bun run build # compile to dist/
|
|
293
|
+
bun x ultracite fix # lint + format
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Branch naming: `feat/<name>`, `fix/<name>`, `docs/<name>`, `chore/<name>`
|
|
297
|
+
|
|
298
|
+
Every commit must pass `bun test` and `bun x ultracite fix` before pushing.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Coding Standards
|
|
303
|
+
|
|
304
|
+
- **Error handling:** never throw inside use cases — always return `left(error)` with `Either`
|
|
305
|
+
- **Imports:** always import from a layer's index (`archstone/core`, `archstone/domain/enterprise`), never from deep paths
|
|
306
|
+
- **Layer boundaries:** inner layers never import outer ones — `core` has zero domain knowledge, `enterprise` never imports `application`
|
|
307
|
+
- **Factories:** always provide a static `create()` factory on entities and value objects — never expose constructors directly
|
|
308
|
+
- **Style:** no semicolons, 2-space indent, double quotes (enforced by Biome via Ultracite)
|
|
309
|
+
- **Commits:** [Conventional Commits](https://www.conventionalcommits.org/) — `feat`, `fix`, `chore`, `docs`, `refactor`, `test`
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Testing
|
|
314
|
+
|
|
315
|
+
Framework: `bun:test`. Test files are `*.spec.ts` co-located with the source they test.
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
import { test, expect } from "bun:test"
|
|
319
|
+
|
|
320
|
+
test("example", () => {
|
|
321
|
+
expect(1).toBe(1)
|
|
322
|
+
})
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Use **in-memory repository implementations** for use case tests — never couple tests to a real database. Isolate domain event state between tests:
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
import { DomainEvents } from "archstone/core"
|
|
329
|
+
import { beforeEach } from "bun:test"
|
|
330
|
+
|
|
331
|
+
beforeEach(() => {
|
|
332
|
+
DomainEvents.clearHandlers()
|
|
333
|
+
DomainEvents.clearMarkedAggregates()
|
|
334
|
+
})
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
272
339
|
## Agent Skills — new in v1.1.0
|
|
273
340
|
|
|
274
341
|
Archstone ships with a built-in skill for AI coding agents. Once installed, your agent understands every DDD convention, layer boundary, and usage pattern — without you ever having to explain them.
|
|
@@ -297,7 +364,7 @@ cp -r node_modules/archstone/skills/use-archstone .claude/skills/
|
|
|
297
364
|
|
|
298
365
|
<div align="center">
|
|
299
366
|
|
|
300
|
-
**Built with
|
|
367
|
+
**Built with ❤️ for the TypeScript community.**
|
|
301
368
|
|
|
302
369
|
[Contributing](./CONTRIBUTING.md) · [Code of Conduct](./CODE_OF_CONDUCT.md) · [MIT License](./LICENSE)
|
|
303
370
|
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DomainEvent, DomainEvents, Either, EventHandler1 as EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "../shared/chunk-
|
|
1
|
+
import { DomainEvent, DomainEvents, Either, EventHandler1 as EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "../shared/chunk-ghvxv2w6.js";
|
|
2
2
|
export { right, left, WatchedList, ValueObject, UniqueEntityId, Optional, EventHandler, Either, DomainEvents, DomainEvent };
|
package/dist/core/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{c as a,d as b,e as c,f as d,g as e,h as f}from"../shared/chunk-
|
|
2
|
+
import{c as a,d as b,e as c,f as d,g as e,h as f}from"../shared/chunk-k4yvnajk.js";export{b as right,a as left,f as WatchedList,e as ValueObject,d as UniqueEntityId,c as DomainEvents};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "../../shared/chunk-
|
|
1
|
+
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "../../shared/chunk-ghvxv2w6.js";
|
|
2
2
|
export { UseCaseError, UseCase, Saveable, Repository, Findable, Deletable, Creatable };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AggregateRoot, DomainEvent, DomainEvents, Entity, EventHandler } from "../../shared/chunk-
|
|
1
|
+
import { AggregateRoot, DomainEvent, DomainEvents, Entity, EventHandler } from "../../shared/chunk-ghvxv2w6.js";
|
|
2
2
|
export { EventHandler, Entity, DomainEvents, DomainEvent, AggregateRoot };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{a as b,b as c}from"../../shared/chunk-
|
|
2
|
+
import{a as b,b as c}from"../../shared/chunk-ph10w7xb.js";import{e as a}from"../../shared/chunk-k4yvnajk.js";export{b as Entity,a as DomainEvents,c as AggregateRoot};
|
package/dist/domain/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Entity, EventHandler, Findable, Repository, Saveable, UseCase, UseCaseError } from "../shared/chunk-
|
|
1
|
+
import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Entity, EventHandler, Findable, Repository, Saveable, UseCase, UseCaseError } from "../shared/chunk-ghvxv2w6.js";
|
|
2
2
|
export { UseCaseError, UseCase, Saveable, Repository, Findable, EventHandler, Entity, DomainEvents, DomainEvent, Deletable, Creatable, AggregateRoot };
|
package/dist/domain/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"../shared/chunk-x296z7h1.js";import"../shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"../shared/chunk-
|
|
2
|
+
import"../shared/chunk-x296z7h1.js";import"../shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"../shared/chunk-ph10w7xb.js";import{e as a}from"../shared/chunk-k4yvnajk.js";export{b as Entity,a as DomainEvents,c as AggregateRoot};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Either, Entity, Findable, Optional, Repository, Saveable, UniqueEntityId, UseCase, UseCaseError, ValueObject, WatchedList, left, right } from "./shared/chunk-
|
|
1
|
+
import { AggregateRoot, Creatable, Deletable, DomainEvent, DomainEvents, Either, Entity, Findable, Optional, Repository, Saveable, UniqueEntityId, UseCase, UseCaseError, ValueObject, WatchedList, left, right } from "./shared/chunk-ghvxv2w6.js";
|
|
2
2
|
export { right, left, WatchedList, ValueObject, UseCaseError, UseCase, UniqueEntityId, Saveable, Repository, Optional, Findable, Entity, Either, DomainEvents, DomainEvent, Deletable, Creatable, AggregateRoot };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"./shared/chunk-x296z7h1.js";import"./shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"./shared/chunk-
|
|
2
|
+
import"./shared/chunk-x296z7h1.js";import"./shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"./shared/chunk-ph10w7xb.js";import{c as f,d as m,e as p,f as t,g as x,h as a}from"./shared/chunk-k4yvnajk.js";export{m as right,f as left,a as WatchedList,x as ValueObject,t as UniqueEntityId,b as Entity,p as DomainEvents,c as AggregateRoot};
|
|
@@ -346,6 +346,15 @@ declare class DomainEventsImplementation {
|
|
|
346
346
|
private readonly handlersMap;
|
|
347
347
|
private readonly markedAggregates;
|
|
348
348
|
/**
|
|
349
|
+
* Controls whether event dispatching is active.
|
|
350
|
+
*
|
|
351
|
+
* Set to `false` in tests that construct aggregates but do not want
|
|
352
|
+
* side-effects to run, without having to clear and re-register handlers.
|
|
353
|
+
*
|
|
354
|
+
* @default true
|
|
355
|
+
*/
|
|
356
|
+
shouldRun: boolean;
|
|
357
|
+
/**
|
|
349
358
|
* Marks an aggregate root to have its events dispatched.
|
|
350
359
|
* Called automatically by {@link AggregateRoot.addDomainEvent}.
|
|
351
360
|
*
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
class H{value;constructor(x){this.value=x}isRight(){return!1}isLeft(){return!0}}class V{value;constructor(x){this.value=x}isRight(){return!0}isLeft(){return!1}}var B=(x)=>new H(x),F=(x)=>new V(x);class W{handlersMap=new Map;markedAggregates=new Set;shouldRun=!0;markAggregateForDispatch(x){if(!this.findMarkedAggregateByID(x.id))this.markedAggregates.add(x)}dispatchEventsForAggregate(x){let E=this.findMarkedAggregateByID(x);if(E)this.dispatchAggregateEvents(E),E.clearEvents(),this.removeAggregateFromMarkedDispatchList(E)}register(x,E){if(!this.handlersMap.has(E))this.handlersMap.set(E,[]);this.handlersMap.get(E)?.push(x)}clearHandlers(){this.handlersMap.clear()}clearMarkedAggregates(){this.markedAggregates.clear()}dispatchAggregateEvents(x){for(let E of x.domainEvents)this.dispatch(E)}removeAggregateFromMarkedDispatchList(x){this.markedAggregates.delete(x)}findMarkedAggregateByID(x){return[...this.markedAggregates].find((E)=>E.id.equals(x))}dispatch(x){if(!this.shouldRun)return;let E=this.handlersMap.get(x.constructor.name)??[];for(let O of E)O(x)}}var G=new W;var{randomUUIDv7:J}=globalThis.Bun;class f{value;constructor(x){this.value=x??J()}toValue(){return this.value}toString(){return this.value}equals(x){return x.toValue()===this.value}}class z{props;constructor(x){this.props=x}equals(x){return JSON.stringify(this.props)===JSON.stringify(x.props)}}class A{currentItems;initial;new;removed;constructor(x){this.currentItems=x??[],this.initial=x??[],this.new=[],this.removed=[]}getItems(){return this.currentItems}getNewItems(){return this.new}getRemovedItems(){return this.removed}exists(x){return this.isCurrentItem(x)}add(x){if(this.isRemovedItem(x))this.removeFromRemoved(x);if(!(this.isNewItem(x)||this.wasAddedInitially(x)))this.new.push(x);if(!this.isCurrentItem(x))this.currentItems.push(x)}remove(x){if(this.removeFromCurrent(x),this.isNewItem(x)){this.removeFromNew(x);return}if(!this.isRemovedItem(x))this.removed.push(x)}update(x){this.new=x.filter((E)=>!this.getItems().some((O)=>this.compareItems(E,O))),this.removed=this.getItems().filter((E)=>!x.some((O)=>this.compareItems(E,O))),this.currentItems=x}isCurrentItem(x){return this.currentItems.some((E)=>this.compareItems(x,E))}isNewItem(x){return this.new.some((E)=>this.compareItems(x,E))}isRemovedItem(x){return this.removed.some((E)=>this.compareItems(x,E))}removeFromNew(x){this.new=this.new.filter((E)=>!this.compareItems(E,x))}removeFromCurrent(x){this.currentItems=this.currentItems.filter((E)=>!this.compareItems(x,E))}removeFromRemoved(x){this.removed=this.removed.filter((E)=>!this.compareItems(x,E))}wasAddedInitially(x){return this.initial.some((E)=>this.compareItems(x,E))}}export{B as c,F as d,G as e,f,z as g,A as h};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{e as j,f as k}from"./chunk-
|
|
2
|
+
import{e as j,f as k}from"./chunk-k4yvnajk.js";class R{_id;props;get id(){return this._id}constructor(A,B){this._id=B??new k,this.props=A}equals(A){if(A===this)return!0;return A.id.equals(this._id)}}class z extends R{_domainEvents=new Set;get domainEvents(){return Array.from(this._domainEvents)}addDomainEvent(A){this._domainEvents.add(A),j.markAggregateForDispatch(this)}clearEvents(){this._domainEvents.clear()}}export{R as a,z as b};
|
package/package.json
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
class H{value;constructor(x){this.value=x}isRight(){return!1}isLeft(){return!0}}class V{value;constructor(x){this.value=x}isRight(){return!0}isLeft(){return!1}}var B=(x)=>new H(x),F=(x)=>new V(x);class W{handlersMap=new Map;markedAggregates=new Set;markAggregateForDispatch(x){if(!this.findMarkedAggregateByID(x.id))this.markedAggregates.add(x)}dispatchEventsForAggregate(x){let E=this.findMarkedAggregateByID(x);if(E)this.dispatchAggregateEvents(E),E.clearEvents(),this.removeAggregateFromMarkedDispatchList(E)}register(x,E){if(!this.handlersMap.has(E))this.handlersMap.set(E,[]);this.handlersMap.get(E)?.push(x)}clearHandlers(){this.handlersMap.clear()}clearMarkedAggregates(){this.markedAggregates.clear()}dispatchAggregateEvents(x){for(let E of x.domainEvents)this.dispatch(E)}removeAggregateFromMarkedDispatchList(x){this.markedAggregates.delete(x)}findMarkedAggregateByID(x){return[...this.markedAggregates].find((E)=>E.id.equals(x))}dispatch(x){let E=this.handlersMap.get(x.constructor.name)??[];for(let O of E)O(x)}}var G=new W;var{randomUUIDv7:J}=globalThis.Bun;class f{value;constructor(x){this.value=x??J()}toValue(){return this.value}toString(){return this.value}equals(x){return x.toValue()===this.value}}class z{props;constructor(x){this.props=x}equals(x){return JSON.stringify(this.props)===JSON.stringify(x.props)}}class A{currentItems;initial;new;removed;constructor(x){this.currentItems=x??[],this.initial=x??[],this.new=[],this.removed=[]}getItems(){return this.currentItems}getNewItems(){return this.new}getRemovedItems(){return this.removed}exists(x){return this.isCurrentItem(x)}add(x){if(this.isRemovedItem(x))this.removeFromRemoved(x);if(!(this.isNewItem(x)||this.wasAddedInitially(x)))this.new.push(x);if(!this.isCurrentItem(x))this.currentItems.push(x)}remove(x){if(this.removeFromCurrent(x),this.isNewItem(x)){this.removeFromNew(x);return}if(!this.isRemovedItem(x))this.removed.push(x)}update(x){this.new=x.filter((E)=>!this.getItems().some((O)=>this.compareItems(E,O))),this.removed=this.getItems().filter((E)=>!x.some((O)=>this.compareItems(E,O))),this.currentItems=x}isCurrentItem(x){return this.currentItems.some((E)=>this.compareItems(x,E))}isNewItem(x){return this.new.some((E)=>this.compareItems(x,E))}isRemovedItem(x){return this.removed.some((E)=>this.compareItems(x,E))}removeFromNew(x){this.new=this.new.filter((E)=>!this.compareItems(E,x))}removeFromCurrent(x){this.currentItems=this.currentItems.filter((E)=>!this.compareItems(x,E))}removeFromRemoved(x){this.removed=this.removed.filter((E)=>!this.compareItems(x,E))}wasAddedInitially(x){return this.initial.some((E)=>this.compareItems(x,E))}}export{B as c,F as d,G as e,f,z as g,A as h};
|