archstone 1.2.1 → 1.3.0-rc.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/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +1 -1
- package/dist/domain/application/index.d.ts +2 -2
- package/dist/domain/enterprise/index.d.ts +2 -2
- package/dist/domain/enterprise/index.js +1 -1
- package/dist/domain/index.d.ts +3 -3
- package/dist/domain/index.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/shared/chunk-8hx7pxm3.js +2 -0
- package/dist/shared/{chunk-axgwjkjg.d.ts → chunk-a9db46y5.d.ts} +326 -58
- package/dist/shared/{chunk-txbqwwkc.d.ts → chunk-hhnmv4hq.d.ts} +1 -1
- package/dist/shared/chunk-s2r7zghq.js +2 -0
- package/package.json +2 -1
- package/dist/shared/chunk-jw0ckhsy.js +0 -2
- package/dist/shared/chunk-r63mxet1.js +0 -2
- package/dist/shared/chunk-t21213sm.d.ts +0 -270
package/dist/core/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Either, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "../shared/chunk-
|
|
2
|
-
export { right, left, WatchedList, ValueObject, UniqueEntityId, Optional, Either };
|
|
1
|
+
import { DomainEvent, DomainEvents, Either, EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "../shared/chunk-a9db46y5.js";
|
|
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{
|
|
2
|
+
import{c as a,d as b,e as c,f as d,g as e,h as f}from"../shared/chunk-8hx7pxm3.js";export{b as right,a as left,f as WatchedList,e as ValueObject,d as UniqueEntityId,c as DomainEvents};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "../../shared/chunk-
|
|
2
|
-
import "../../shared/chunk-
|
|
1
|
+
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "../../shared/chunk-hhnmv4hq.js";
|
|
2
|
+
import "../../shared/chunk-a9db46y5.js";
|
|
3
3
|
export { UseCaseError, UseCase, Saveable, Repository, Findable, Deletable, Creatable };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "../../shared/chunk-
|
|
1
|
+
import "../../shared/chunk-v7ff2em7.js";
|
|
2
|
+
import { AggregateRoot, DomainEvent, DomainEvents, Entity, EventHandler } from "../../shared/chunk-a9db46y5.js";
|
|
3
3
|
export { EventHandler, Entity, DomainEvents, DomainEvent, AggregateRoot };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{a,b
|
|
2
|
+
import{a as b,b as c}from"../../shared/chunk-s2r7zghq.js";import{e as a}from"../../shared/chunk-8hx7pxm3.js";export{b as Entity,a as DomainEvents,c as AggregateRoot};
|
package/dist/domain/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../shared/chunk-em5cewpk.js";
|
|
2
|
-
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "../shared/chunk-
|
|
3
|
-
import
|
|
4
|
-
import "../shared/chunk-
|
|
2
|
+
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "../shared/chunk-hhnmv4hq.js";
|
|
3
|
+
import "../shared/chunk-v7ff2em7.js";
|
|
4
|
+
import { AggregateRoot, DomainEvent, DomainEvents, Entity, EventHandler } from "../shared/chunk-a9db46y5.js";
|
|
5
5
|
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,b
|
|
2
|
+
import"../shared/chunk-x296z7h1.js";import"../shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"../shared/chunk-s2r7zghq.js";import{e as a}from"../shared/chunk-8hx7pxm3.js";export{b as Entity,a as DomainEvents,c as AggregateRoot};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./shared/chunk-em5cewpk.js";
|
|
2
|
-
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "./shared/chunk-
|
|
3
|
-
import
|
|
4
|
-
import { Either, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "./shared/chunk-
|
|
2
|
+
import { Creatable, Deletable, Findable, Repository, Saveable, UseCase, UseCaseError } from "./shared/chunk-hhnmv4hq.js";
|
|
3
|
+
import "./shared/chunk-v7ff2em7.js";
|
|
4
|
+
import { AggregateRoot, DomainEvent, DomainEvents, Either, Entity, EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList, left, right } from "./shared/chunk-a9db46y5.js";
|
|
5
5
|
export { right, left, WatchedList, ValueObject, UseCaseError, UseCase, UniqueEntityId, Saveable, Repository, Optional, Findable, EventHandler, 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,b
|
|
2
|
+
import"./shared/chunk-x296z7h1.js";import"./shared/chunk-wsjyhnpq.js";import{a as b,b as c}from"./shared/chunk-s2r7zghq.js";import{c as f,d as m,e as p,f as t,g as x,h as a}from"./shared/chunk-8hx7pxm3.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};
|
|
@@ -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;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};
|
|
@@ -1,4 +1,88 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Represents the left side of an {@link Either} — conventionally used for
|
|
3
|
+
* failure or error values.
|
|
4
|
+
*
|
|
5
|
+
* @template L - The type of the failure value
|
|
6
|
+
* @template R - The type of the success value
|
|
7
|
+
*/
|
|
8
|
+
declare class Left<
|
|
9
|
+
L,
|
|
10
|
+
R
|
|
11
|
+
> {
|
|
12
|
+
readonly value: L;
|
|
13
|
+
constructor(value: L);
|
|
14
|
+
isRight(): this is Right<L, R>;
|
|
15
|
+
isLeft(): this is Left<L, R>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Represents the right side of an {@link Either} — conventionally used for
|
|
19
|
+
* success values.
|
|
20
|
+
*
|
|
21
|
+
* @template L - The type of the failure value
|
|
22
|
+
* @template R - The type of the success value
|
|
23
|
+
*/
|
|
24
|
+
declare class Right<
|
|
25
|
+
L,
|
|
26
|
+
R
|
|
27
|
+
> {
|
|
28
|
+
readonly value: R;
|
|
29
|
+
constructor(value: R);
|
|
30
|
+
isRight(): this is Right<L, R>;
|
|
31
|
+
isLeft(): this is Left<L, R>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A discriminated union that represents a value of one of two possible types.
|
|
35
|
+
*
|
|
36
|
+
* Commonly used as a type-safe alternative to throwing exceptions — the left
|
|
37
|
+
* side carries an error, and the right side carries a success value.
|
|
38
|
+
*
|
|
39
|
+
* Use the {@link left} and {@link right} helper functions to construct values,
|
|
40
|
+
* and `isLeft()` / `isRight()` to narrow the type.
|
|
41
|
+
*
|
|
42
|
+
* @template L - The type of the failure value
|
|
43
|
+
* @template R - The type of the success value
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* type FindUserResult = Either<NotFoundError, User>
|
|
48
|
+
*
|
|
49
|
+
* function findUser(id: string): FindUserResult {
|
|
50
|
+
* const user = db.find(id)
|
|
51
|
+
* if (!user) return left(new NotFoundError("User", id))
|
|
52
|
+
* return right(user)
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* const result = findUser("123")
|
|
56
|
+
*
|
|
57
|
+
* if (result.isLeft()) {
|
|
58
|
+
* console.error(result.value) // NotFoundError
|
|
59
|
+
* } else {
|
|
60
|
+
* console.log(result.value) // User
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
type Either<
|
|
65
|
+
L,
|
|
66
|
+
R
|
|
67
|
+
> = Left<L, R> | Right<L, R>;
|
|
68
|
+
/**
|
|
69
|
+
* Constructs a {@link Left} value, representing a failure.
|
|
70
|
+
*
|
|
71
|
+
* @param value - The error or failure value
|
|
72
|
+
*/
|
|
73
|
+
declare const left: <
|
|
74
|
+
L,
|
|
75
|
+
R
|
|
76
|
+
>(value: L) => Either<L, R>;
|
|
77
|
+
/**
|
|
78
|
+
* Constructs a {@link Right} value, representing a success.
|
|
79
|
+
*
|
|
80
|
+
* @param value - The success value
|
|
81
|
+
*/
|
|
82
|
+
declare const right: <
|
|
83
|
+
L,
|
|
84
|
+
R
|
|
85
|
+
>(value: R) => Either<L, R>;
|
|
2
86
|
/**
|
|
3
87
|
* Base contract for all domain events.
|
|
4
88
|
*
|
|
@@ -30,6 +114,101 @@ interface DomainEvent {
|
|
|
30
114
|
occurredAt: Date;
|
|
31
115
|
}
|
|
32
116
|
/**
|
|
117
|
+
* Base class for all domain entities.
|
|
118
|
+
*
|
|
119
|
+
* An entity is an object defined by its identity rather than its attributes.
|
|
120
|
+
* Two entities are considered equal if they share the same `id`, regardless
|
|
121
|
+
* of their other properties.
|
|
122
|
+
*
|
|
123
|
+
* All entities must implement a static `create` factory method to encapsulate
|
|
124
|
+
* construction logic and keep the constructor protected.
|
|
125
|
+
*
|
|
126
|
+
* @template Props - The shape of the entity's properties
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* interface UserProps {
|
|
131
|
+
* id: UniqueEntityId
|
|
132
|
+
* name: string
|
|
133
|
+
* email: string
|
|
134
|
+
* createdAt: Date
|
|
135
|
+
* }
|
|
136
|
+
*
|
|
137
|
+
* class User extends Entity<UserProps> {
|
|
138
|
+
* get name() { return this.props.name }
|
|
139
|
+
* get email() { return this.props.email }
|
|
140
|
+
*
|
|
141
|
+
* static create(props: Optional<UserProps, "id" | "createdAt">): User {
|
|
142
|
+
* return new User(
|
|
143
|
+
* { ...props, createdAt: props.createdAt ?? new Date() },
|
|
144
|
+
* props.id ?? new UniqueEntityId(),
|
|
145
|
+
* )
|
|
146
|
+
* }
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
declare abstract class Entity<Props> {
|
|
151
|
+
private readonly _id;
|
|
152
|
+
protected props: Props;
|
|
153
|
+
get id(): UniqueEntityId;
|
|
154
|
+
protected constructor(props: Props, id?: UniqueEntityId);
|
|
155
|
+
/**
|
|
156
|
+
* Compares this entity with another by identity.
|
|
157
|
+
*
|
|
158
|
+
* @param entity - The entity to compare against
|
|
159
|
+
* @returns `true` if both entities share the same `id`
|
|
160
|
+
*/
|
|
161
|
+
equals(entity: Entity<unknown>): boolean;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Base class for all aggregate roots in the domain.
|
|
165
|
+
*
|
|
166
|
+
* An aggregate root is the entry point to an aggregate — a cluster of domain
|
|
167
|
+
* objects that are treated as a single unit. It is responsible for enforcing
|
|
168
|
+
* invariants and coordinating domain events within its boundary.
|
|
169
|
+
*
|
|
170
|
+
* Domain events are collected internally and dispatched after the aggregate
|
|
171
|
+
* is persisted, ensuring side effects only occur after a successful transaction.
|
|
172
|
+
*
|
|
173
|
+
* @template Props - The shape of the aggregate's properties
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* class User extends AggregateRoot<UserProps> {
|
|
178
|
+
* static create(props: Optional<UserProps, "id" | "createdAt">): User {
|
|
179
|
+
* const user = new User(
|
|
180
|
+
* { ...props, createdAt: props.createdAt ?? new Date() },
|
|
181
|
+
* props.id ?? new UniqueEntityId(),
|
|
182
|
+
* )
|
|
183
|
+
*
|
|
184
|
+
* user.addDomainEvent(new UserCreatedEvent(user))
|
|
185
|
+
*
|
|
186
|
+
* return user
|
|
187
|
+
* }
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
declare abstract class AggregateRoot<Props> extends Entity<Props> {
|
|
192
|
+
private readonly _domainEvents;
|
|
193
|
+
/**
|
|
194
|
+
* Returns all domain events that have been raised by this aggregate
|
|
195
|
+
* and are pending dispatch.
|
|
196
|
+
*/
|
|
197
|
+
get domainEvents(): DomainEvent[];
|
|
198
|
+
/**
|
|
199
|
+
* Registers a domain event to be dispatched after the aggregate is persisted.
|
|
200
|
+
* Automatically marks this aggregate for dispatch in the {@link DomainEvents} registry.
|
|
201
|
+
*
|
|
202
|
+
* @param domainEvent - The domain event to register
|
|
203
|
+
*/
|
|
204
|
+
protected addDomainEvent(domainEvent: DomainEvent): void;
|
|
205
|
+
/**
|
|
206
|
+
* Clears all pending domain events from this aggregate.
|
|
207
|
+
* Should be called by the infrastructure layer after events are dispatched.
|
|
208
|
+
*/
|
|
209
|
+
clearEvents(): void;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
33
212
|
* Callback function invoked when a domain event is dispatched.
|
|
34
213
|
*/
|
|
35
214
|
type DomainEventCallback = (event: DomainEvent) => void;
|
|
@@ -136,98 +315,187 @@ interface EventHandler {
|
|
|
136
315
|
setupSubscriptions(): void;
|
|
137
316
|
}
|
|
138
317
|
/**
|
|
139
|
-
*
|
|
318
|
+
* Make some property optional on type
|
|
140
319
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* type Post {
|
|
323
|
+
* id: string;
|
|
324
|
+
* name: string;
|
|
325
|
+
* email: string;
|
|
326
|
+
* }
|
|
144
327
|
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
328
|
+
* Optional<Post, 'id' | 'email'>
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
type Optional<
|
|
332
|
+
T,
|
|
333
|
+
K extends keyof T
|
|
334
|
+
> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
335
|
+
/**
|
|
336
|
+
* Represents a unique identifier for a domain entity.
|
|
147
337
|
*
|
|
148
|
-
*
|
|
338
|
+
* Wraps a UUID v7 string, which is time-sortable and ideal for database
|
|
339
|
+
* indexing. A new identifier is generated automatically if no value is provided.
|
|
149
340
|
*
|
|
150
341
|
* @example
|
|
151
342
|
* ```ts
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
343
|
+
* // auto-generated
|
|
344
|
+
* const id = new UniqueEntityId()
|
|
345
|
+
*
|
|
346
|
+
* // from existing value (e.g. reconstructing from database)
|
|
347
|
+
* const id = new UniqueEntityId("0195d810-5b3e-7000-8e3e-1a2b3c4d5e6f")
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
declare class UniqueEntityId {
|
|
351
|
+
private readonly value;
|
|
352
|
+
constructor(value?: string);
|
|
353
|
+
/**
|
|
354
|
+
* Returns the identifier as a primitive string.
|
|
355
|
+
* Useful for serialization and database persistence.
|
|
356
|
+
*/
|
|
357
|
+
toValue(): string;
|
|
358
|
+
/**
|
|
359
|
+
* Returns the string representation of the identifier.
|
|
360
|
+
*/
|
|
361
|
+
toString(): string;
|
|
362
|
+
/**
|
|
363
|
+
* Compares this identifier with another by value equality.
|
|
364
|
+
*
|
|
365
|
+
* @param id - The identifier to compare against
|
|
366
|
+
* @returns `true` if both identifiers have the same value
|
|
367
|
+
*/
|
|
368
|
+
equals(id: UniqueEntityId): boolean;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Base class for all value objects in the domain.
|
|
372
|
+
*
|
|
373
|
+
* A value object is an object defined entirely by its attributes — it has no
|
|
374
|
+
* identity. Two value objects are considered equal if all their properties
|
|
375
|
+
* are deeply equal, regardless of reference.
|
|
376
|
+
*
|
|
377
|
+
* Value objects should be immutable. Avoid mutating `props` directly;
|
|
378
|
+
* instead, create a new instance with the updated values.
|
|
379
|
+
*
|
|
380
|
+
* All value objects must implement a static `create` factory method to
|
|
381
|
+
* encapsulate construction and validation logic, keeping the constructor
|
|
382
|
+
* protected from external instantiation.
|
|
383
|
+
*
|
|
384
|
+
* @template Props - The shape of the value object's properties
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```ts
|
|
388
|
+
* interface EmailProps {
|
|
389
|
+
* value: string
|
|
157
390
|
* }
|
|
158
391
|
*
|
|
159
|
-
* class
|
|
160
|
-
* get
|
|
161
|
-
* get email() { return this.props.email }
|
|
392
|
+
* class Email extends ValueObject<EmailProps> {
|
|
393
|
+
* get value() { return this.props.value }
|
|
162
394
|
*
|
|
163
|
-
* static create(
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
395
|
+
* static create(email: string): Email {
|
|
396
|
+
* if (!email.includes("@")) {
|
|
397
|
+
* throw new ValidationError("Invalid email address.")
|
|
398
|
+
* }
|
|
399
|
+
*
|
|
400
|
+
* return new Email({ value: email })
|
|
168
401
|
* }
|
|
169
402
|
* }
|
|
170
403
|
* ```
|
|
171
404
|
*/
|
|
172
|
-
declare abstract class
|
|
173
|
-
private readonly _id;
|
|
405
|
+
declare abstract class ValueObject<Props> {
|
|
174
406
|
protected props: Props;
|
|
175
|
-
|
|
176
|
-
protected constructor(props: Props, id?: UniqueEntityId);
|
|
407
|
+
protected constructor(props: Props);
|
|
177
408
|
/**
|
|
178
|
-
* Compares this
|
|
409
|
+
* Compares this value object with another by deep equality of their properties.
|
|
179
410
|
*
|
|
180
|
-
* @param
|
|
181
|
-
* @returns `true` if both
|
|
411
|
+
* @param other - The value object to compare against
|
|
412
|
+
* @returns `true` if both value objects have deeply equal properties
|
|
182
413
|
*/
|
|
183
|
-
equals(
|
|
414
|
+
equals(other: ValueObject<Props>): boolean;
|
|
184
415
|
}
|
|
185
416
|
/**
|
|
186
|
-
*
|
|
417
|
+
* Tracks additions and removals of items in a collection, enabling
|
|
418
|
+
* efficient persistence of only what changed.
|
|
187
419
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
420
|
+
* Commonly used inside aggregate roots to manage one-to-many relationships
|
|
421
|
+
* without rewriting the entire collection on every save — only new and
|
|
422
|
+
* removed items are persisted.
|
|
191
423
|
*
|
|
192
|
-
*
|
|
193
|
-
* is persisted, ensuring side effects only occur after a successful transaction.
|
|
424
|
+
* Subclasses must implement {@link compareItems} to define equality between items.
|
|
194
425
|
*
|
|
195
|
-
* @template
|
|
426
|
+
* @template T - The type of items in the list
|
|
196
427
|
*
|
|
197
428
|
* @example
|
|
198
429
|
* ```ts
|
|
199
|
-
* class
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* props.id ?? new UniqueEntityId(),
|
|
204
|
-
* )
|
|
205
|
-
*
|
|
206
|
-
* user.addDomainEvent(new UserCreatedEvent(user))
|
|
430
|
+
* class TagList extends WatchedList<Tag> {
|
|
431
|
+
* compareItems(a: Tag, b: Tag): boolean {
|
|
432
|
+
* return a.id.equals(b.id)
|
|
433
|
+
* }
|
|
207
434
|
*
|
|
208
|
-
*
|
|
435
|
+
* static create(tags: Tag[]): TagList {
|
|
436
|
+
* return new TagList(tags)
|
|
209
437
|
* }
|
|
210
438
|
* }
|
|
439
|
+
*
|
|
440
|
+
* const tags = TagList.create([existingTag])
|
|
441
|
+
*
|
|
442
|
+
* tags.add(newTag) // tracked as new
|
|
443
|
+
* tags.remove(oldTag) // tracked as removed
|
|
444
|
+
*
|
|
445
|
+
* tags.getNewItems() // [newTag]
|
|
446
|
+
* tags.getRemovedItems() // [oldTag]
|
|
211
447
|
* ```
|
|
212
448
|
*/
|
|
213
|
-
declare abstract class
|
|
214
|
-
|
|
449
|
+
declare abstract class WatchedList<T> {
|
|
450
|
+
protected currentItems: T[];
|
|
451
|
+
protected initial: T[];
|
|
452
|
+
protected new: T[];
|
|
453
|
+
protected removed: T[];
|
|
454
|
+
constructor(initialItems?: T[]);
|
|
215
455
|
/**
|
|
216
|
-
* Returns all
|
|
217
|
-
* and are pending dispatch.
|
|
456
|
+
* Returns all current items in the list.
|
|
218
457
|
*/
|
|
219
|
-
|
|
458
|
+
getItems(): T[];
|
|
220
459
|
/**
|
|
221
|
-
*
|
|
222
|
-
|
|
460
|
+
* Returns items that were added since the list was created.
|
|
461
|
+
*/
|
|
462
|
+
getNewItems(): T[];
|
|
463
|
+
/**
|
|
464
|
+
* Returns items that were removed since the list was created.
|
|
465
|
+
*/
|
|
466
|
+
getRemovedItems(): T[];
|
|
467
|
+
/**
|
|
468
|
+
* Returns whether the given item exists in the current list.
|
|
223
469
|
*
|
|
224
|
-
* @param
|
|
470
|
+
* @param item - The item to check
|
|
225
471
|
*/
|
|
226
|
-
|
|
472
|
+
exists(item: T): boolean;
|
|
227
473
|
/**
|
|
228
|
-
*
|
|
229
|
-
*
|
|
474
|
+
* Adds an item to the list, tracking it as new if it wasn't in the initial set.
|
|
475
|
+
* If the item was previously removed, it is restored.
|
|
476
|
+
*
|
|
477
|
+
* @param item - The item to add
|
|
230
478
|
*/
|
|
231
|
-
|
|
479
|
+
add(item: T): void;
|
|
480
|
+
/**
|
|
481
|
+
* Removes an item from the list, tracking it as removed if it was in the initial set.
|
|
482
|
+
*
|
|
483
|
+
* @param item - The item to remove
|
|
484
|
+
*/
|
|
485
|
+
remove(item: T): void;
|
|
486
|
+
/**
|
|
487
|
+
* Replaces the entire list with a new set of items, automatically
|
|
488
|
+
* computing what was added and what was removed.
|
|
489
|
+
*
|
|
490
|
+
* @param items - The new set of items
|
|
491
|
+
*/
|
|
492
|
+
update(items: T[]): void;
|
|
493
|
+
private isCurrentItem;
|
|
494
|
+
private isNewItem;
|
|
495
|
+
private isRemovedItem;
|
|
496
|
+
private removeFromNew;
|
|
497
|
+
private removeFromCurrent;
|
|
498
|
+
private removeFromRemoved;
|
|
499
|
+
private wasAddedInitially;
|
|
232
500
|
}
|
|
233
|
-
export { DomainEvent, DomainEvents, EventHandler,
|
|
501
|
+
export { Either, left, right, DomainEvent, Entity, AggregateRoot, DomainEvents, EventHandler, Optional, UniqueEntityId, ValueObject, WatchedList };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{e as D,f as H}from"./chunk-8hx7pxm3.js";class A{_id;props;get id(){return this._id}constructor(x,f){this._id=f??new H,this.props=x}equals(x){if(x===this)return!0;return x.id.equals(this._id)}}class R extends A{_domainEvents=new Set;get domainEvents(){return Array.from(this._domainEvents)}addDomainEvent(x){this._domainEvents.add(x),D.markAggregateForDispatch(this)}clearEvents(){this._domainEvents.clear()}}export{A as a,R as b};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archstone",
|
|
3
3
|
"description": "TypeScript architecture foundation for backend services based on Domain-Driven Design and Clean Architecture",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0-rc.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"files": [
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"test": "bun test",
|
|
77
77
|
"check": "ultracite check",
|
|
78
78
|
"fix": "ultracite fix",
|
|
79
|
+
"release": "bun publish --access public",
|
|
79
80
|
"prepublishOnly": "bun run build",
|
|
80
81
|
"prepare": "bunx husky"
|
|
81
82
|
},
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{f as j}from"./chunk-r63mxet1.js";class R{handlersMap=new Map;markedAggregates=new Set;markAggregateForDispatch(x){if(!this.findMarkedAggregateByID(x.id))this.markedAggregates.add(x)}dispatchEventsForAggregate(x){let f=this.findMarkedAggregateByID(x);if(f)this.dispatchAggregateEvents(f),f.clearEvents(),this.removeAggregateFromMarkedDispatchList(f)}register(x,f){if(!this.handlersMap.has(f))this.handlersMap.set(f,[]);this.handlersMap.get(f)?.push(x)}clearHandlers(){this.handlersMap.clear()}clearMarkedAggregates(){this.markedAggregates.clear()}dispatchAggregateEvents(x){for(let f of x.domainEvents)this.dispatch(f)}removeAggregateFromMarkedDispatchList(x){this.markedAggregates.delete(x)}findMarkedAggregateByID(x){return[...this.markedAggregates].find((f)=>f.id.equals(x))}dispatch(x){let f=this.handlersMap.get(x.constructor.name)??[];for(let B of f)B(x)}}var H=new R;class A{_id;props;get id(){return this._id}constructor(x,f){this._id=f??new j,this.props=x}equals(x){if(x===this)return!0;return x.id.equals(this._id)}}class z extends A{_domainEvents=new Set;get domainEvents(){return Array.from(this._domainEvents)}addDomainEvent(x){this._domainEvents.add(x),H.markAggregateForDispatch(this)}clearEvents(){this._domainEvents.clear()}}export{H as a,A as b,z as c};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
class V{value;constructor(x){this.value=x}isRight(){return!1}isLeft(){return!0}}class W{value;constructor(x){this.value=x}isRight(){return!0}isLeft(){return!1}}var F=(x)=>new V(x),G=(x)=>new W(x);var{randomUUIDv7:H}=globalThis.Bun;class z{value;constructor(x){this.value=x??H()}toValue(){return this.value}toString(){return this.value}equals(x){return x.toValue()===this.value}}class A{props;constructor(x){this.props=x}equals(x){return JSON.stringify(this.props)===JSON.stringify(x.props)}}class B{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{F as d,G as e,z as f,A as g,B as h};
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Represents the left side of an {@link Either} — conventionally used for
|
|
3
|
-
* failure or error values.
|
|
4
|
-
*
|
|
5
|
-
* @template L - The type of the failure value
|
|
6
|
-
* @template R - The type of the success value
|
|
7
|
-
*/
|
|
8
|
-
declare class Left<
|
|
9
|
-
L,
|
|
10
|
-
R
|
|
11
|
-
> {
|
|
12
|
-
readonly value: L;
|
|
13
|
-
constructor(value: L);
|
|
14
|
-
isRight(): this is Right<L, R>;
|
|
15
|
-
isLeft(): this is Left<L, R>;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Represents the right side of an {@link Either} — conventionally used for
|
|
19
|
-
* success values.
|
|
20
|
-
*
|
|
21
|
-
* @template L - The type of the failure value
|
|
22
|
-
* @template R - The type of the success value
|
|
23
|
-
*/
|
|
24
|
-
declare class Right<
|
|
25
|
-
L,
|
|
26
|
-
R
|
|
27
|
-
> {
|
|
28
|
-
readonly value: R;
|
|
29
|
-
constructor(value: R);
|
|
30
|
-
isRight(): this is Right<L, R>;
|
|
31
|
-
isLeft(): this is Left<L, R>;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* A discriminated union that represents a value of one of two possible types.
|
|
35
|
-
*
|
|
36
|
-
* Commonly used as a type-safe alternative to throwing exceptions — the left
|
|
37
|
-
* side carries an error, and the right side carries a success value.
|
|
38
|
-
*
|
|
39
|
-
* Use the {@link left} and {@link right} helper functions to construct values,
|
|
40
|
-
* and `isLeft()` / `isRight()` to narrow the type.
|
|
41
|
-
*
|
|
42
|
-
* @template L - The type of the failure value
|
|
43
|
-
* @template R - The type of the success value
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```ts
|
|
47
|
-
* type FindUserResult = Either<NotFoundError, User>
|
|
48
|
-
*
|
|
49
|
-
* function findUser(id: string): FindUserResult {
|
|
50
|
-
* const user = db.find(id)
|
|
51
|
-
* if (!user) return left(new NotFoundError("User", id))
|
|
52
|
-
* return right(user)
|
|
53
|
-
* }
|
|
54
|
-
*
|
|
55
|
-
* const result = findUser("123")
|
|
56
|
-
*
|
|
57
|
-
* if (result.isLeft()) {
|
|
58
|
-
* console.error(result.value) // NotFoundError
|
|
59
|
-
* } else {
|
|
60
|
-
* console.log(result.value) // User
|
|
61
|
-
* }
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
type Either<
|
|
65
|
-
L,
|
|
66
|
-
R
|
|
67
|
-
> = Left<L, R> | Right<L, R>;
|
|
68
|
-
/**
|
|
69
|
-
* Constructs a {@link Left} value, representing a failure.
|
|
70
|
-
*
|
|
71
|
-
* @param value - The error or failure value
|
|
72
|
-
*/
|
|
73
|
-
declare const left: <
|
|
74
|
-
L,
|
|
75
|
-
R
|
|
76
|
-
>(value: L) => Either<L, R>;
|
|
77
|
-
/**
|
|
78
|
-
* Constructs a {@link Right} value, representing a success.
|
|
79
|
-
*
|
|
80
|
-
* @param value - The success value
|
|
81
|
-
*/
|
|
82
|
-
declare const right: <
|
|
83
|
-
L,
|
|
84
|
-
R
|
|
85
|
-
>(value: R) => Either<L, R>;
|
|
86
|
-
/**
|
|
87
|
-
* Make some property optional on type
|
|
88
|
-
*
|
|
89
|
-
* @example
|
|
90
|
-
* ```typescript
|
|
91
|
-
* type Post {
|
|
92
|
-
* id: string;
|
|
93
|
-
* name: string;
|
|
94
|
-
* email: string;
|
|
95
|
-
* }
|
|
96
|
-
*
|
|
97
|
-
* Optional<Post, 'id' | 'email'>
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
type Optional<
|
|
101
|
-
T,
|
|
102
|
-
K extends keyof T
|
|
103
|
-
> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
104
|
-
/**
|
|
105
|
-
* Represents a unique identifier for a domain entity.
|
|
106
|
-
*
|
|
107
|
-
* Wraps a UUID v7 string, which is time-sortable and ideal for database
|
|
108
|
-
* indexing. A new identifier is generated automatically if no value is provided.
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* ```ts
|
|
112
|
-
* // auto-generated
|
|
113
|
-
* const id = new UniqueEntityId()
|
|
114
|
-
*
|
|
115
|
-
* // from existing value (e.g. reconstructing from database)
|
|
116
|
-
* const id = new UniqueEntityId("0195d810-5b3e-7000-8e3e-1a2b3c4d5e6f")
|
|
117
|
-
* ```
|
|
118
|
-
*/
|
|
119
|
-
declare class UniqueEntityId {
|
|
120
|
-
private readonly value;
|
|
121
|
-
constructor(value?: string);
|
|
122
|
-
/**
|
|
123
|
-
* Returns the identifier as a primitive string.
|
|
124
|
-
* Useful for serialization and database persistence.
|
|
125
|
-
*/
|
|
126
|
-
toValue(): string;
|
|
127
|
-
/**
|
|
128
|
-
* Returns the string representation of the identifier.
|
|
129
|
-
*/
|
|
130
|
-
toString(): string;
|
|
131
|
-
/**
|
|
132
|
-
* Compares this identifier with another by value equality.
|
|
133
|
-
*
|
|
134
|
-
* @param id - The identifier to compare against
|
|
135
|
-
* @returns `true` if both identifiers have the same value
|
|
136
|
-
*/
|
|
137
|
-
equals(id: UniqueEntityId): boolean;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Base class for all value objects in the domain.
|
|
141
|
-
*
|
|
142
|
-
* A value object is an object defined entirely by its attributes — it has no
|
|
143
|
-
* identity. Two value objects are considered equal if all their properties
|
|
144
|
-
* are deeply equal, regardless of reference.
|
|
145
|
-
*
|
|
146
|
-
* Value objects should be immutable. Avoid mutating `props` directly;
|
|
147
|
-
* instead, create a new instance with the updated values.
|
|
148
|
-
*
|
|
149
|
-
* All value objects must implement a static `create` factory method to
|
|
150
|
-
* encapsulate construction and validation logic, keeping the constructor
|
|
151
|
-
* protected from external instantiation.
|
|
152
|
-
*
|
|
153
|
-
* @template Props - The shape of the value object's properties
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* ```ts
|
|
157
|
-
* interface EmailProps {
|
|
158
|
-
* value: string
|
|
159
|
-
* }
|
|
160
|
-
*
|
|
161
|
-
* class Email extends ValueObject<EmailProps> {
|
|
162
|
-
* get value() { return this.props.value }
|
|
163
|
-
*
|
|
164
|
-
* static create(email: string): Email {
|
|
165
|
-
* if (!email.includes("@")) {
|
|
166
|
-
* throw new ValidationError("Invalid email address.")
|
|
167
|
-
* }
|
|
168
|
-
*
|
|
169
|
-
* return new Email({ value: email })
|
|
170
|
-
* }
|
|
171
|
-
* }
|
|
172
|
-
* ```
|
|
173
|
-
*/
|
|
174
|
-
declare abstract class ValueObject<Props> {
|
|
175
|
-
protected props: Props;
|
|
176
|
-
protected constructor(props: Props);
|
|
177
|
-
/**
|
|
178
|
-
* Compares this value object with another by deep equality of their properties.
|
|
179
|
-
*
|
|
180
|
-
* @param other - The value object to compare against
|
|
181
|
-
* @returns `true` if both value objects have deeply equal properties
|
|
182
|
-
*/
|
|
183
|
-
equals(other: ValueObject<Props>): boolean;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Tracks additions and removals of items in a collection, enabling
|
|
187
|
-
* efficient persistence of only what changed.
|
|
188
|
-
*
|
|
189
|
-
* Commonly used inside aggregate roots to manage one-to-many relationships
|
|
190
|
-
* without rewriting the entire collection on every save — only new and
|
|
191
|
-
* removed items are persisted.
|
|
192
|
-
*
|
|
193
|
-
* Subclasses must implement {@link compareItems} to define equality between items.
|
|
194
|
-
*
|
|
195
|
-
* @template T - The type of items in the list
|
|
196
|
-
*
|
|
197
|
-
* @example
|
|
198
|
-
* ```ts
|
|
199
|
-
* class TagList extends WatchedList<Tag> {
|
|
200
|
-
* compareItems(a: Tag, b: Tag): boolean {
|
|
201
|
-
* return a.id.equals(b.id)
|
|
202
|
-
* }
|
|
203
|
-
*
|
|
204
|
-
* static create(tags: Tag[]): TagList {
|
|
205
|
-
* return new TagList(tags)
|
|
206
|
-
* }
|
|
207
|
-
* }
|
|
208
|
-
*
|
|
209
|
-
* const tags = TagList.create([existingTag])
|
|
210
|
-
*
|
|
211
|
-
* tags.add(newTag) // tracked as new
|
|
212
|
-
* tags.remove(oldTag) // tracked as removed
|
|
213
|
-
*
|
|
214
|
-
* tags.getNewItems() // [newTag]
|
|
215
|
-
* tags.getRemovedItems() // [oldTag]
|
|
216
|
-
* ```
|
|
217
|
-
*/
|
|
218
|
-
declare abstract class WatchedList<T> {
|
|
219
|
-
protected currentItems: T[];
|
|
220
|
-
protected initial: T[];
|
|
221
|
-
protected new: T[];
|
|
222
|
-
protected removed: T[];
|
|
223
|
-
constructor(initialItems?: T[]);
|
|
224
|
-
/**
|
|
225
|
-
* Returns all current items in the list.
|
|
226
|
-
*/
|
|
227
|
-
getItems(): T[];
|
|
228
|
-
/**
|
|
229
|
-
* Returns items that were added since the list was created.
|
|
230
|
-
*/
|
|
231
|
-
getNewItems(): T[];
|
|
232
|
-
/**
|
|
233
|
-
* Returns items that were removed since the list was created.
|
|
234
|
-
*/
|
|
235
|
-
getRemovedItems(): T[];
|
|
236
|
-
/**
|
|
237
|
-
* Returns whether the given item exists in the current list.
|
|
238
|
-
*
|
|
239
|
-
* @param item - The item to check
|
|
240
|
-
*/
|
|
241
|
-
exists(item: T): boolean;
|
|
242
|
-
/**
|
|
243
|
-
* Adds an item to the list, tracking it as new if it wasn't in the initial set.
|
|
244
|
-
* If the item was previously removed, it is restored.
|
|
245
|
-
*
|
|
246
|
-
* @param item - The item to add
|
|
247
|
-
*/
|
|
248
|
-
add(item: T): void;
|
|
249
|
-
/**
|
|
250
|
-
* Removes an item from the list, tracking it as removed if it was in the initial set.
|
|
251
|
-
*
|
|
252
|
-
* @param item - The item to remove
|
|
253
|
-
*/
|
|
254
|
-
remove(item: T): void;
|
|
255
|
-
/**
|
|
256
|
-
* Replaces the entire list with a new set of items, automatically
|
|
257
|
-
* computing what was added and what was removed.
|
|
258
|
-
*
|
|
259
|
-
* @param items - The new set of items
|
|
260
|
-
*/
|
|
261
|
-
update(items: T[]): void;
|
|
262
|
-
private isCurrentItem;
|
|
263
|
-
private isNewItem;
|
|
264
|
-
private isRemovedItem;
|
|
265
|
-
private removeFromNew;
|
|
266
|
-
private removeFromCurrent;
|
|
267
|
-
private removeFromRemoved;
|
|
268
|
-
private wasAddedInitially;
|
|
269
|
-
}
|
|
270
|
-
export { Either, left, right, Optional, UniqueEntityId, ValueObject, WatchedList };
|