@rineex/ddd 1.2.0 → 1.4.0
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/index.d.mts +119 -52
- package/dist/index.d.ts +119 -52
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -27,78 +27,115 @@ interface ApplicationServicePort<I, O> {
|
|
|
27
27
|
execute: (args: I) => Promise<O>;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Base class for primitive-based Value Objects.
|
|
32
|
+
*
|
|
33
|
+
* This class is intended for Value Objects that are represented by
|
|
34
|
+
* a single primitive value (string, number, or boolean).
|
|
35
|
+
*
|
|
36
|
+
* Characteristics:
|
|
37
|
+
* - Immutable by construction
|
|
38
|
+
* - Cheap equality comparison
|
|
39
|
+
* - No deep cloning or freezing
|
|
40
|
+
* - Safe for serialization and logging
|
|
41
|
+
*
|
|
42
|
+
* Examples:
|
|
43
|
+
* - AggregateId
|
|
44
|
+
* - EmailAddress
|
|
45
|
+
* - Username
|
|
46
|
+
* - Slug
|
|
47
|
+
*/
|
|
48
|
+
declare abstract class PrimitiveValueObject<T extends boolean | number | string> {
|
|
34
49
|
/**
|
|
35
|
-
*
|
|
50
|
+
* The underlying primitive value.
|
|
51
|
+
* Guaranteed to be valid after construction.
|
|
36
52
|
*/
|
|
37
|
-
|
|
53
|
+
protected readonly value: T;
|
|
38
54
|
/**
|
|
39
|
-
*
|
|
55
|
+
* Constructs a new PrimitiveValueObject.
|
|
56
|
+
*
|
|
57
|
+
* @param value - The primitive value to wrap
|
|
58
|
+
* @throws Error if validation fails
|
|
40
59
|
*/
|
|
41
|
-
|
|
60
|
+
protected constructor(value: T);
|
|
42
61
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
62
|
+
* Returns the primitive value.
|
|
63
|
+
* Prefer explicit access over implicit coercion.
|
|
64
|
+
*/
|
|
65
|
+
getValue(): T;
|
|
66
|
+
/**
|
|
67
|
+
* Compares two Value Objects for equality.
|
|
45
68
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
69
|
+
* Equality rules:
|
|
70
|
+
* - Same concrete class
|
|
71
|
+
* - Same primitive value (===)
|
|
72
|
+
*
|
|
73
|
+
* @param other - Another Value Object
|
|
48
74
|
*/
|
|
49
|
-
|
|
75
|
+
equals(other?: PrimitiveValueObject<T>): boolean;
|
|
50
76
|
/**
|
|
51
|
-
*
|
|
77
|
+
* JSON serialization hook.
|
|
78
|
+
* Produces the raw primitive value.
|
|
52
79
|
*/
|
|
53
|
-
|
|
80
|
+
toJSON(): T;
|
|
54
81
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
82
|
+
* String representation.
|
|
83
|
+
* Useful for logging and debugging.
|
|
57
84
|
*/
|
|
58
|
-
|
|
85
|
+
toString(): string;
|
|
86
|
+
/**
|
|
87
|
+
* Domain invariant validation.
|
|
88
|
+
* Must throw if the value is invalid.
|
|
89
|
+
*
|
|
90
|
+
* @param value - The value to validate
|
|
91
|
+
*/
|
|
92
|
+
protected abstract validate(value: T): void;
|
|
59
93
|
}
|
|
60
94
|
|
|
61
95
|
/**
|
|
62
|
-
*
|
|
96
|
+
* Represents a UUID (Universally Unique Identifier) value object.
|
|
97
|
+
*
|
|
98
|
+
* This class extends PrimitiveValueObject to provide type-safe UUID handling
|
|
99
|
+
* with validation using Zod schema. UUIDs are immutable and can be generated
|
|
100
|
+
* randomly or created from string values.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* // Generate a new random UUID
|
|
104
|
+
* const id = UUID.generate();
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* // Create UUID from an existing string
|
|
108
|
+
* const id = UUID.fromString('550e8400-e29b-41d4-a716-446655440000');
|
|
109
|
+
*
|
|
110
|
+
* @throws {InvalidValueObjectError} When the provided string is not a valid UUID format
|
|
63
111
|
*/
|
|
64
|
-
declare class
|
|
65
|
-
/**
|
|
66
|
-
* The schema for the AggregateId
|
|
67
|
-
*/
|
|
112
|
+
declare class UUID extends PrimitiveValueObject<string> {
|
|
68
113
|
private static readonly schema;
|
|
114
|
+
constructor(value: string);
|
|
69
115
|
/**
|
|
70
|
-
*
|
|
71
|
-
* @returns The UUID of the AggregateId
|
|
72
|
-
*/
|
|
73
|
-
get uuid(): string;
|
|
74
|
-
/**
|
|
75
|
-
* Create a new AggregateId
|
|
76
|
-
* @param value The value to create the AggregateId from
|
|
77
|
-
* @returns The new AggregateId
|
|
78
|
-
*/
|
|
79
|
-
static create(value: string): AggregateId;
|
|
80
|
-
/**
|
|
81
|
-
* Create a new AggregateId with a random UUID v4
|
|
116
|
+
* Generates a new AggregateId.
|
|
82
117
|
*/
|
|
83
|
-
static generate():
|
|
118
|
+
static generate<T extends typeof UUID>(this: T): InstanceType<T>;
|
|
84
119
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Convert the AggregateId to a string
|
|
91
|
-
* @returns The string representation of the AggregateId
|
|
92
|
-
*/
|
|
93
|
-
toString(): string;
|
|
94
|
-
/**
|
|
95
|
-
* Validate the AggregateId
|
|
96
|
-
* @param value The value to validate
|
|
97
|
-
* @throws InvalidValueObjectException if the value is invalid
|
|
120
|
+
* Creates an UUID from an external string.
|
|
121
|
+
* Use only for untrusted input.
|
|
122
|
+
*
|
|
123
|
+
* @param value - UUID string
|
|
98
124
|
*/
|
|
125
|
+
static fromString(value: string): UUID;
|
|
99
126
|
protected validate(value: string): void;
|
|
100
127
|
}
|
|
101
128
|
|
|
129
|
+
/**
|
|
130
|
+
* AggregateId represents a strongly-typed aggregate identifier.
|
|
131
|
+
*
|
|
132
|
+
* - Backed by UUID v4
|
|
133
|
+
* - Immutable
|
|
134
|
+
* - Comparable only to AggregateId
|
|
135
|
+
*/
|
|
136
|
+
declare class AggregateId extends UUID {
|
|
137
|
+
}
|
|
138
|
+
|
|
102
139
|
/**
|
|
103
140
|
* Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.
|
|
104
141
|
*
|
|
@@ -313,6 +350,37 @@ declare abstract class ApplicationError extends Error {
|
|
|
313
350
|
constructor({ code, isOperational, status, metadata, message, cause, }: ErrorParams);
|
|
314
351
|
}
|
|
315
352
|
|
|
353
|
+
declare abstract class ValueObject<T> {
|
|
354
|
+
get value(): T;
|
|
355
|
+
protected readonly props: Readonly<T>;
|
|
356
|
+
protected constructor(props: T);
|
|
357
|
+
/**
|
|
358
|
+
* Standard for clean API integration and logging.
|
|
359
|
+
*/
|
|
360
|
+
toJSON(): T;
|
|
361
|
+
/**
|
|
362
|
+
* Useful for debugging and string-based indexing.
|
|
363
|
+
*/
|
|
364
|
+
toString(): string;
|
|
365
|
+
/**
|
|
366
|
+
* Type guard to check if an unknown object is an instance of ValueObject.
|
|
367
|
+
* This is useful for runtime type checking.
|
|
368
|
+
*
|
|
369
|
+
* @param vo The object to check.
|
|
370
|
+
* @returns True if the object is a ValueObject instance, false otherwise.
|
|
371
|
+
*/
|
|
372
|
+
static is(vo: unknown): vo is ValueObject<unknown>;
|
|
373
|
+
/**
|
|
374
|
+
* Deep equality comparison of ValueObjects
|
|
375
|
+
*/
|
|
376
|
+
equals(other?: ValueObject<T>): boolean;
|
|
377
|
+
/**
|
|
378
|
+
* Validates the value object props
|
|
379
|
+
* @throws InvalidValueObjectError if validation fails
|
|
380
|
+
*/
|
|
381
|
+
protected abstract validate(props: T): void;
|
|
382
|
+
}
|
|
383
|
+
|
|
316
384
|
type UnwrapValueObject<T> = T extends ValueObject<infer V> ? UnwrapValueObject<V> : T extends (infer U)[] ? UnwrapValueObject<U>[] : T extends Map<infer K, infer V> ? Map<K, UnwrapValueObject<V>> : T extends Set<infer V> ? Set<UnwrapValueObject<V>> : T extends Date ? string : T extends object ? {
|
|
317
385
|
[K in keyof T]: UnwrapValueObject<T[K]>;
|
|
318
386
|
} : T;
|
|
@@ -362,7 +430,7 @@ declare abstract class Entity<EntityProps> {
|
|
|
362
430
|
get id(): AggregateId;
|
|
363
431
|
get metadata(): Readonly<{
|
|
364
432
|
createdAt: string;
|
|
365
|
-
id:
|
|
433
|
+
id: AggregateId;
|
|
366
434
|
}>;
|
|
367
435
|
/**
|
|
368
436
|
* Returns an immutable shallow copy of the entity's properties.
|
|
@@ -404,7 +472,6 @@ declare abstract class Entity<EntityProps> {
|
|
|
404
472
|
*
|
|
405
473
|
* @returns True if the entity is transient (not persisted), otherwise false.
|
|
406
474
|
*/
|
|
407
|
-
isPersisted(): boolean;
|
|
408
475
|
toJSON(): Record<string, unknown>;
|
|
409
476
|
toObject(): Readonly<UnwrapValueObject<EntityProps> & {
|
|
410
477
|
createdAt: string;
|
|
@@ -560,4 +627,4 @@ declare class InvalidValueObjectError extends DomainError {
|
|
|
560
627
|
constructor(message: string);
|
|
561
628
|
}
|
|
562
629
|
|
|
563
|
-
export { AggregateId, AggregateRoot, type AggregateRootInterface, ApplicationError, type ApplicationServicePort, type BaseEntityProps, type CreateEntityProps, DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityBaseInterface, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|
|
630
|
+
export { AggregateId, AggregateRoot, type AggregateRootInterface, ApplicationError, type ApplicationServicePort, type BaseEntityProps, type CreateEntityProps, DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityBaseInterface, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, PrimitiveValueObject, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|
package/dist/index.d.ts
CHANGED
|
@@ -27,78 +27,115 @@ interface ApplicationServicePort<I, O> {
|
|
|
27
27
|
execute: (args: I) => Promise<O>;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Base class for primitive-based Value Objects.
|
|
32
|
+
*
|
|
33
|
+
* This class is intended for Value Objects that are represented by
|
|
34
|
+
* a single primitive value (string, number, or boolean).
|
|
35
|
+
*
|
|
36
|
+
* Characteristics:
|
|
37
|
+
* - Immutable by construction
|
|
38
|
+
* - Cheap equality comparison
|
|
39
|
+
* - No deep cloning or freezing
|
|
40
|
+
* - Safe for serialization and logging
|
|
41
|
+
*
|
|
42
|
+
* Examples:
|
|
43
|
+
* - AggregateId
|
|
44
|
+
* - EmailAddress
|
|
45
|
+
* - Username
|
|
46
|
+
* - Slug
|
|
47
|
+
*/
|
|
48
|
+
declare abstract class PrimitiveValueObject<T extends boolean | number | string> {
|
|
34
49
|
/**
|
|
35
|
-
*
|
|
50
|
+
* The underlying primitive value.
|
|
51
|
+
* Guaranteed to be valid after construction.
|
|
36
52
|
*/
|
|
37
|
-
|
|
53
|
+
protected readonly value: T;
|
|
38
54
|
/**
|
|
39
|
-
*
|
|
55
|
+
* Constructs a new PrimitiveValueObject.
|
|
56
|
+
*
|
|
57
|
+
* @param value - The primitive value to wrap
|
|
58
|
+
* @throws Error if validation fails
|
|
40
59
|
*/
|
|
41
|
-
|
|
60
|
+
protected constructor(value: T);
|
|
42
61
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
62
|
+
* Returns the primitive value.
|
|
63
|
+
* Prefer explicit access over implicit coercion.
|
|
64
|
+
*/
|
|
65
|
+
getValue(): T;
|
|
66
|
+
/**
|
|
67
|
+
* Compares two Value Objects for equality.
|
|
45
68
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
69
|
+
* Equality rules:
|
|
70
|
+
* - Same concrete class
|
|
71
|
+
* - Same primitive value (===)
|
|
72
|
+
*
|
|
73
|
+
* @param other - Another Value Object
|
|
48
74
|
*/
|
|
49
|
-
|
|
75
|
+
equals(other?: PrimitiveValueObject<T>): boolean;
|
|
50
76
|
/**
|
|
51
|
-
*
|
|
77
|
+
* JSON serialization hook.
|
|
78
|
+
* Produces the raw primitive value.
|
|
52
79
|
*/
|
|
53
|
-
|
|
80
|
+
toJSON(): T;
|
|
54
81
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
82
|
+
* String representation.
|
|
83
|
+
* Useful for logging and debugging.
|
|
57
84
|
*/
|
|
58
|
-
|
|
85
|
+
toString(): string;
|
|
86
|
+
/**
|
|
87
|
+
* Domain invariant validation.
|
|
88
|
+
* Must throw if the value is invalid.
|
|
89
|
+
*
|
|
90
|
+
* @param value - The value to validate
|
|
91
|
+
*/
|
|
92
|
+
protected abstract validate(value: T): void;
|
|
59
93
|
}
|
|
60
94
|
|
|
61
95
|
/**
|
|
62
|
-
*
|
|
96
|
+
* Represents a UUID (Universally Unique Identifier) value object.
|
|
97
|
+
*
|
|
98
|
+
* This class extends PrimitiveValueObject to provide type-safe UUID handling
|
|
99
|
+
* with validation using Zod schema. UUIDs are immutable and can be generated
|
|
100
|
+
* randomly or created from string values.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* // Generate a new random UUID
|
|
104
|
+
* const id = UUID.generate();
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* // Create UUID from an existing string
|
|
108
|
+
* const id = UUID.fromString('550e8400-e29b-41d4-a716-446655440000');
|
|
109
|
+
*
|
|
110
|
+
* @throws {InvalidValueObjectError} When the provided string is not a valid UUID format
|
|
63
111
|
*/
|
|
64
|
-
declare class
|
|
65
|
-
/**
|
|
66
|
-
* The schema for the AggregateId
|
|
67
|
-
*/
|
|
112
|
+
declare class UUID extends PrimitiveValueObject<string> {
|
|
68
113
|
private static readonly schema;
|
|
114
|
+
constructor(value: string);
|
|
69
115
|
/**
|
|
70
|
-
*
|
|
71
|
-
* @returns The UUID of the AggregateId
|
|
72
|
-
*/
|
|
73
|
-
get uuid(): string;
|
|
74
|
-
/**
|
|
75
|
-
* Create a new AggregateId
|
|
76
|
-
* @param value The value to create the AggregateId from
|
|
77
|
-
* @returns The new AggregateId
|
|
78
|
-
*/
|
|
79
|
-
static create(value: string): AggregateId;
|
|
80
|
-
/**
|
|
81
|
-
* Create a new AggregateId with a random UUID v4
|
|
116
|
+
* Generates a new AggregateId.
|
|
82
117
|
*/
|
|
83
|
-
static generate():
|
|
118
|
+
static generate<T extends typeof UUID>(this: T): InstanceType<T>;
|
|
84
119
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Convert the AggregateId to a string
|
|
91
|
-
* @returns The string representation of the AggregateId
|
|
92
|
-
*/
|
|
93
|
-
toString(): string;
|
|
94
|
-
/**
|
|
95
|
-
* Validate the AggregateId
|
|
96
|
-
* @param value The value to validate
|
|
97
|
-
* @throws InvalidValueObjectException if the value is invalid
|
|
120
|
+
* Creates an UUID from an external string.
|
|
121
|
+
* Use only for untrusted input.
|
|
122
|
+
*
|
|
123
|
+
* @param value - UUID string
|
|
98
124
|
*/
|
|
125
|
+
static fromString(value: string): UUID;
|
|
99
126
|
protected validate(value: string): void;
|
|
100
127
|
}
|
|
101
128
|
|
|
129
|
+
/**
|
|
130
|
+
* AggregateId represents a strongly-typed aggregate identifier.
|
|
131
|
+
*
|
|
132
|
+
* - Backed by UUID v4
|
|
133
|
+
* - Immutable
|
|
134
|
+
* - Comparable only to AggregateId
|
|
135
|
+
*/
|
|
136
|
+
declare class AggregateId extends UUID {
|
|
137
|
+
}
|
|
138
|
+
|
|
102
139
|
/**
|
|
103
140
|
* Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.
|
|
104
141
|
*
|
|
@@ -313,6 +350,37 @@ declare abstract class ApplicationError extends Error {
|
|
|
313
350
|
constructor({ code, isOperational, status, metadata, message, cause, }: ErrorParams);
|
|
314
351
|
}
|
|
315
352
|
|
|
353
|
+
declare abstract class ValueObject<T> {
|
|
354
|
+
get value(): T;
|
|
355
|
+
protected readonly props: Readonly<T>;
|
|
356
|
+
protected constructor(props: T);
|
|
357
|
+
/**
|
|
358
|
+
* Standard for clean API integration and logging.
|
|
359
|
+
*/
|
|
360
|
+
toJSON(): T;
|
|
361
|
+
/**
|
|
362
|
+
* Useful for debugging and string-based indexing.
|
|
363
|
+
*/
|
|
364
|
+
toString(): string;
|
|
365
|
+
/**
|
|
366
|
+
* Type guard to check if an unknown object is an instance of ValueObject.
|
|
367
|
+
* This is useful for runtime type checking.
|
|
368
|
+
*
|
|
369
|
+
* @param vo The object to check.
|
|
370
|
+
* @returns True if the object is a ValueObject instance, false otherwise.
|
|
371
|
+
*/
|
|
372
|
+
static is(vo: unknown): vo is ValueObject<unknown>;
|
|
373
|
+
/**
|
|
374
|
+
* Deep equality comparison of ValueObjects
|
|
375
|
+
*/
|
|
376
|
+
equals(other?: ValueObject<T>): boolean;
|
|
377
|
+
/**
|
|
378
|
+
* Validates the value object props
|
|
379
|
+
* @throws InvalidValueObjectError if validation fails
|
|
380
|
+
*/
|
|
381
|
+
protected abstract validate(props: T): void;
|
|
382
|
+
}
|
|
383
|
+
|
|
316
384
|
type UnwrapValueObject<T> = T extends ValueObject<infer V> ? UnwrapValueObject<V> : T extends (infer U)[] ? UnwrapValueObject<U>[] : T extends Map<infer K, infer V> ? Map<K, UnwrapValueObject<V>> : T extends Set<infer V> ? Set<UnwrapValueObject<V>> : T extends Date ? string : T extends object ? {
|
|
317
385
|
[K in keyof T]: UnwrapValueObject<T[K]>;
|
|
318
386
|
} : T;
|
|
@@ -362,7 +430,7 @@ declare abstract class Entity<EntityProps> {
|
|
|
362
430
|
get id(): AggregateId;
|
|
363
431
|
get metadata(): Readonly<{
|
|
364
432
|
createdAt: string;
|
|
365
|
-
id:
|
|
433
|
+
id: AggregateId;
|
|
366
434
|
}>;
|
|
367
435
|
/**
|
|
368
436
|
* Returns an immutable shallow copy of the entity's properties.
|
|
@@ -404,7 +472,6 @@ declare abstract class Entity<EntityProps> {
|
|
|
404
472
|
*
|
|
405
473
|
* @returns True if the entity is transient (not persisted), otherwise false.
|
|
406
474
|
*/
|
|
407
|
-
isPersisted(): boolean;
|
|
408
475
|
toJSON(): Record<string, unknown>;
|
|
409
476
|
toObject(): Readonly<UnwrapValueObject<EntityProps> & {
|
|
410
477
|
createdAt: string;
|
|
@@ -560,4 +627,4 @@ declare class InvalidValueObjectError extends DomainError {
|
|
|
560
627
|
constructor(message: string);
|
|
561
628
|
}
|
|
562
629
|
|
|
563
|
-
export { AggregateId, AggregateRoot, type AggregateRootInterface, ApplicationError, type ApplicationServicePort, type BaseEntityProps, type CreateEntityProps, DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityBaseInterface, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|
|
630
|
+
export { AggregateId, AggregateRoot, type AggregateRootInterface, ApplicationError, type ApplicationServicePort, type BaseEntityProps, type CreateEntityProps, DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityBaseInterface, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, PrimitiveValueObject, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var q=Object.create;var p=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames,U=Object.getOwnPropertySymbols,G=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty,Y=Object.prototype.propertyIsEnumerable;var V=t=>{throw TypeError(t)};var w=(t,e,r)=>e in t?p(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,d=(t,e)=>{for(var r in e||(e={}))x.call(e,r)&&w(t,r,e[r]);if(U)for(var r of U(e))Y.call(e,r)&&w(t,r,e[r]);return t};var Q=(t,e)=>{for(var r in e)p(t,r,{get:e[r],enumerable:!0})},C=(t,e,r,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of B(e))!x.call(t,n)&&n!==r&&p(t,n,{get:()=>e[n],enumerable:!(a=z(e,n))||a.enumerable});return t};var L=(t,e,r)=>(r=t!=null?q(G(t)):{},C(e||!t||!t.__esModule?p(r,"default",{value:t,enumerable:!0}):r,t)),K=t=>C(p({},"__esModule",{value:!0}),t);var M=(t,e,r)=>e.has(t)||V("Cannot "+r);var o=(t,e,r)=>(M(t,e,"read from private field"),r?r.call(t):e.get(t)),b=(t,e,r)=>e.has(t)?V("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r),R=(t,e,r,a)=>(M(t,e,"write to private field"),a?a.call(t,r):e.set(t,r),r);var X={};Q(X,{AggregateId:()=>g,AggregateRoot:()=>P,ApplicationError:()=>h,DomainError:()=>c,DomainEvent:()=>v,Entity:()=>y,EntityValidationError:()=>O,HttpStatus:()=>W,HttpStatusMessage:()=>D,InvalidValueObjectError:()=>f,PrimitiveValueObject:()=>m,ValueObject:()=>T,deepFreeze:()=>E,ensureObject:()=>S,unwrapValueObject:()=>s});module.exports=K(X);function E(t,e=new WeakSet){if(t==null||typeof t!="object"&&!Array.isArray(t)||Object.isFrozen(t)||e.has(t))return t;if(e.add(t),Array.isArray(t))t.forEach(r=>E(r,e));else for(let r in t)Object.hasOwn(t,r)&&E(t[r],e);return Object.freeze(t)}var W=Object.freeze({REQUEST_HEADER_FIELDS_TOO_LARGE:431,NETWORK_AUTHENTICATION_REQUIRED:511,NON_AUTHORITATIVE_INFORMATION:203,PROXY_AUTHENTICATION_REQUIRED:407,UNAVAILABLE_FOR_LEGAL_REASONS:451,HTTP_VERSION_NOT_SUPPORTED:505,BANDWIDTH_LIMIT_EXCEEDED:509,VARIANT_ALSO_NEGOTIATES:506,UNSUPPORTED_MEDIA_TYPE:415,RANGE_NOT_SATISFIABLE:416,PRECONDITION_REQUIRED:428,INTERNAL_SERVER_ERROR:500,UNPROCESSABLE_ENTITY:422,INSUFFICIENT_STORAGE:507,SWITCHING_PROTOCOLS:101,PRECONDITION_FAILED:412,MISDIRECTED_REQUEST:421,SERVICE_UNAVAILABLE:503,TEMPORARY_REDIRECT:307,PERMANENT_REDIRECT:308,METHOD_NOT_ALLOWED:405,EXPECTATION_FAILED:417,MOVED_PERMANENTLY:301,PAYLOAD_TOO_LARGE:413,FAILED_DEPENDENCY:424,TOO_MANY_REQUESTS:429,ALREADY_REPORTED:208,MULTIPLE_CHOICES:300,PAYMENT_REQUIRED:402,UPGRADE_REQUIRED:426,PARTIAL_CONTENT:206,REQUEST_TIMEOUT:408,LENGTH_REQUIRED:411,NOT_IMPLEMENTED:501,GATEWAY_TIMEOUT:504,NOT_ACCEPTABLE:406,RESET_CONTENT:205,LOOP_DETECTED:508,MULTI_STATUS:207,NOT_MODIFIED:304,UNAUTHORIZED:401,URI_TOO_LONG:414,NOT_EXTENDED:510,EARLY_HINTS:103,BAD_REQUEST:400,IM_A_TEAPOT:418,BAD_GATEWAY:502,PROCESSING:102,NO_CONTENT:204,SEE_OTHER:303,USE_PROXY:305,FORBIDDEN:403,NOT_FOUND:404,TOO_EARLY:425,CONTINUE:100,ACCEPTED:202,CONFLICT:409,CREATED:201,IM_USED:226,LOCKED:423,FOUND:302,GONE:410,OK:200}),D={431:"Request Header Fields Too Large",511:"Network Authentication Required",203:"Non-Authoritative Information",407:"Proxy Authentication Required",451:"Unavailable For Legal Reasons",505:"HTTP Version Not Supported",509:"Bandwidth Limit Exceeded",506:"Variant Also Negotiates",415:"Unsupported Media Type",416:"Range Not Satisfiable",428:"Precondition Required",500:"Internal Server Error",422:"Unprocessable Entity",507:"Insufficient Storage",101:"Switching Protocols",412:"Precondition Failed",421:"Misdirected Request",503:"Service Unavailable",307:"Temporary Redirect",308:"Permanent Redirect",405:"Method Not Allowed",417:"Expectation Failed",301:"Moved Permanently",413:"Payload Too Large",424:"Failed Dependency",429:"Too Many Requests",208:"Already Reported",300:"Multiple Choices",402:"Payment Required",426:"Upgrade Required",206:"Partial Content",408:"Request Timeout",411:"Length Required",501:"Not Implemented",504:"Gateway Timeout",406:"Not Acceptable",205:"Reset Content",508:"Loop Detected",207:"Multi-Status",304:"Not Modified",401:"Unauthorized",414:"URI Too Long",418:"I'm a Teapot",510:"Not Extended",103:"Early Hints",400:"Bad Request",502:"Bad Gateway",102:"Processing",204:"No Content",303:"See Other",305:"Use Proxy",403:"Forbidden",404:"Not Found",425:"Too Early",100:"Continue",202:"Accepted",226:"I'm Used",409:"Conflict",201:"Created",423:"Locked",302:"Found",410:"Gone",200:"OK"};var h=class extends Error{constructor({code:e=D[500],isOperational:r=!1,status:a=500,metadata:n,message:N,cause:F}){super(N),this.name=new.target.name,this.code=e,this.status=a,this.isOperational=r,this.metadata=n,this.cause=F,Error.captureStackTrace(this,new.target)}};var j=L(require("fast-deep-equal/es6"));var T=class t{get value(){return this.props}constructor(e){this.validate(e),this.props=E(e)}toJSON(){return this.props}toString(){return JSON.stringify(this.props)}static is(e){return e instanceof t}equals(e){return e==null||Object.getPrototypeOf(this)!==Object.getPrototypeOf(e)?!1:(0,j.default)(this.props,e.props)}};function s(t,e=new WeakSet){if(t==null||typeof t!="object")return t;if(e.has(t))throw new Error("Circular reference detected in ValueObject unwrap");if(e.add(t),Array.isArray(t)){let a=t.map(n=>s(n,e));return e.delete(t),a}if(t instanceof T){let a=s(t.value,e);return e.delete(t),a}if(t instanceof Date)return e.delete(t),t.toISOString();if(t instanceof Map){let a=new Map;return t.forEach((n,N)=>{a.set(N,s(n,e))}),e.delete(t),a}if(t instanceof Set){let a=new Set(Array.from(t.values()).map(n=>s(n,e)));return e.delete(t),a}let r={};for(let a in t)Object.hasOwn(t,a)&&(r[a]=s(t[a],e));return e.delete(t),r}function S(t){return t==null?{}:typeof t=="object"?t:{value:t}}var J=t=>t instanceof Error?{cause:{message:t.message,stack:t.stack,name:t.name}}:t?{cause:t}:{},c=class extends Error{constructor(e,r,a={}){super(e,d({},J(a.cause))),Object.setPrototypeOf(this,new.target.prototype),this.name=new.target.name,this.code=r,this.metadata=Object.freeze(d({},a))}};var O=class extends c{constructor(e,r){super(e,"ENTITY_VALIDATION_ERROR",{cause:r})}};var H=require("uuid"),k=L(require("zod"));var f=class extends c{constructor(e){super(e,"INVALID_VALUE_OBJECT")}};var m=class{constructor(e){this.validate(e),this.value=e}getValue(){return this.value}equals(e){return e===void 0||Object.getPrototypeOf(this)!==Object.getPrototypeOf(e)?!1:this.value===e.value}toJSON(){return this.value}toString(){return String(this.value)}};var A=class A extends m{constructor(e){super(e),this.validate(e)}static generate(){return new this((0,H.v4)())}static fromString(e){return new this(e)}validate(e){let r=A.schema.safeParse(e);if(!r.success)throw new f(`Invalid UUID: ${r.error.message}`)}};A.schema=k.default.uuid();var I=A;var g=class extends I{};var l,i,u,_=class _{constructor({createdAt:e,props:r,id:a}){b(this,l);b(this,i);b(this,u);if(!a)throw new O("Entity ID cannot be empty");R(this,i,a!=null?a:g.generate()),R(this,u,Object.freeze(r)),R(this,l,e?new Date(e):new Date)}get createdAt(){return new Date(o(this,l))}get id(){return o(this,i)}get metadata(){return Object.freeze({createdAt:o(this,l).toISOString(),id:o(this,i)})}get props(){return o(this,u)}static isEntity(e){return typeof e=="object"&&e!==null&&"id"in e&&typeof e.equals=="function"}equals(e){return!e||!_.isEntity(e)?!1:o(this,i).equals(o(e,i))}getPropsCopy(){return Object.freeze(o(this,u))}toJSON(){return this.toObject()}toObject(){let e=s(this.getPropsCopy()),r=S(e);return Object.freeze(d(d({},this.metadata),r))}};l=new WeakMap,i=new WeakMap,u=new WeakMap;var y=_;var P=class extends y{constructor(){super(...arguments);this._domainEvents=[]}get domainEvents(){return[...this._domainEvents]}addEvent(r){this.validate(),this._domainEvents.push(r)}pullDomainEvents(){let r=[...this._domainEvents];return this._domainEvents.length=0,r}};var v=class{constructor(e){this.id=e.id,this.aggregateId=e.aggregateId,this.schemaVersion=e.schemaVersion,this.occurredAt=e.occurredAt,this.payload=Object.freeze(e.payload)}toPrimitives(){return{schemaVersion:this.schemaVersion,aggregateId:this.aggregateId,occurredAt:this.occurredAt,eventName:this.eventName,payload:this.payload,id:this.id}}};0&&(module.exports={AggregateId,AggregateRoot,ApplicationError,DomainError,DomainEvent,Entity,EntityValidationError,HttpStatus,HttpStatusMessage,InvalidValueObjectError,PrimitiveValueObject,ValueObject,deepFreeze,ensureObject,unwrapValueObject});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/utils/deep-freeze.util.ts","../src/gateway/constants/http-code.ts","../src/utils/errors/application.error.ts","../src/domain/base/vo.ts","../src/utils/unwrap-vo.util.ts","../src/domain/base/domain.error.ts","../src/domain/errors/entity-validation.error.ts","../src/domain/value-objects/aggregate-id.vo.ts","../src/domain/errors/invalid-vo.error.ts","../src/domain/entities/entity.ts","../src/domain/aggregates/aggregate-root.ts","../src/domain/events/domain.event.ts"],"sourcesContent":["export * from './application';\nexport * from './domain/aggregates';\nexport * from './domain/base/domain.error';\nexport * from './domain/base/vo';\nexport * from './domain/entities';\nexport * from './domain/errors/entity-validation.error';\nexport * from './domain/errors/invalid-vo.error';\nexport * from './domain/events';\nexport * from './domain/value-objects/aggregate-id.vo';\nexport * from './gateway/constants/http-code';\nexport * from './utils';\n","/**\n * Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.\n *\n * @param obj - The object to be deeply frozen.\n * @param seen - A WeakSet to track already processed objects (for circular references).\n * @returns A deeply frozen version of the input object.\n */\nexport function deepFreeze<T>(\n obj: T,\n seen = new WeakSet<object>(),\n): Readonly<T> {\n // Handle null, undefined, or non-object types\n if (obj == null || (typeof obj !== 'object' && !Array.isArray(obj))) {\n return obj;\n }\n\n // Skip if already frozen\n if (Object.isFrozen(obj)) {\n return obj as Readonly<T>;\n }\n\n // Handle circular references\n if (seen.has(obj as object)) {\n return obj as Readonly<T>;\n }\n\n seen.add(obj as object);\n\n // Handle arrays explicitly\n if (Array.isArray(obj)) {\n obj.forEach(item => deepFreeze(item, seen));\n } else {\n // Handle plain objects\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n deepFreeze((obj as Record<string, unknown>)[key], seen);\n }\n }\n }\n\n return Object.freeze(obj) as Readonly<T>;\n}\n","/**\n * HTTP status code catalog.\n *\n * This object provides a typed, immutable map of standard HTTP status names\n * to their numeric codes. Designed for server-side frameworks such as Fastify.\n *\n * - All identifiers use clear, canonical semantic names.\n * - Values are numeric status codes.\n * - Frozen to prevent runtime mutation.\n * - Exporting `HttpStatus` ensures type-safe usage across the codebase.\n * Usage:\n * ```ts\n * import { HttpStatus } from 'path-to-this-file';\n *\n * function handleRequest() {\n * return {\n * statusCode: HttpStatus.OK,\n * body: 'Success',\n * };\n * }\n * ```\n */\nexport const HttpStatus = Object.freeze({\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n NON_AUTHORITATIVE_INFORMATION: 203,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n\n UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n BANDWIDTH_LIMIT_EXCEEDED: 509,\n VARIANT_ALSO_NEGOTIATES: 506,\n UNSUPPORTED_MEDIA_TYPE: 415,\n RANGE_NOT_SATISFIABLE: 416,\n PRECONDITION_REQUIRED: 428,\n INTERNAL_SERVER_ERROR: 500,\n UNPROCESSABLE_ENTITY: 422,\n INSUFFICIENT_STORAGE: 507,\n\n SWITCHING_PROTOCOLS: 101,\n PRECONDITION_FAILED: 412,\n MISDIRECTED_REQUEST: 421,\n SERVICE_UNAVAILABLE: 503,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n METHOD_NOT_ALLOWED: 405,\n EXPECTATION_FAILED: 417,\n\n MOVED_PERMANENTLY: 301,\n PAYLOAD_TOO_LARGE: 413,\n FAILED_DEPENDENCY: 424,\n TOO_MANY_REQUESTS: 429,\n ALREADY_REPORTED: 208,\n MULTIPLE_CHOICES: 300,\n PAYMENT_REQUIRED: 402,\n UPGRADE_REQUIRED: 426,\n PARTIAL_CONTENT: 206,\n REQUEST_TIMEOUT: 408,\n LENGTH_REQUIRED: 411,\n NOT_IMPLEMENTED: 501,\n GATEWAY_TIMEOUT: 504,\n NOT_ACCEPTABLE: 406,\n RESET_CONTENT: 205,\n LOOP_DETECTED: 508,\n MULTI_STATUS: 207,\n NOT_MODIFIED: 304,\n UNAUTHORIZED: 401,\n URI_TOO_LONG: 414,\n NOT_EXTENDED: 510,\n EARLY_HINTS: 103,\n BAD_REQUEST: 400,\n IM_A_TEAPOT: 418,\n BAD_GATEWAY: 502,\n PROCESSING: 102,\n NO_CONTENT: 204,\n SEE_OTHER: 303,\n USE_PROXY: 305,\n\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n TOO_EARLY: 425,\n CONTINUE: 100,\n ACCEPTED: 202,\n CONFLICT: 409,\n CREATED: 201,\n IM_USED: 226,\n LOCKED: 423,\n FOUND: 302,\n GONE: 410,\n OK: 200,\n} as const);\n\n/**\n * HTTP status messages mapped by numeric code.\n * Use for sending descriptive text in responses or logging.\n */\nexport const HttpStatusMessage = {\n 431: 'Request Header Fields Too Large',\n 511: 'Network Authentication Required',\n 203: 'Non-Authoritative Information',\n 407: 'Proxy Authentication Required',\n 451: 'Unavailable For Legal Reasons',\n 505: 'HTTP Version Not Supported',\n 509: 'Bandwidth Limit Exceeded',\n 506: 'Variant Also Negotiates',\n 415: 'Unsupported Media Type',\n 416: 'Range Not Satisfiable',\n 428: 'Precondition Required',\n 500: 'Internal Server Error',\n 422: 'Unprocessable Entity',\n 507: 'Insufficient Storage',\n 101: 'Switching Protocols',\n 412: 'Precondition Failed',\n 421: 'Misdirected Request',\n 503: 'Service Unavailable',\n 307: 'Temporary Redirect',\n 308: 'Permanent Redirect',\n 405: 'Method Not Allowed',\n 417: 'Expectation Failed',\n 301: 'Moved Permanently',\n 413: 'Payload Too Large',\n 424: 'Failed Dependency',\n 429: 'Too Many Requests',\n 208: 'Already Reported',\n 300: 'Multiple Choices',\n 402: 'Payment Required',\n 426: 'Upgrade Required',\n 206: 'Partial Content',\n 408: 'Request Timeout',\n 411: 'Length Required',\n 501: 'Not Implemented',\n 504: 'Gateway Timeout',\n 406: 'Not Acceptable',\n 205: 'Reset Content',\n 508: 'Loop Detected',\n 207: 'Multi-Status',\n 304: 'Not Modified',\n 401: 'Unauthorized',\n 414: 'URI Too Long',\n 418: \"I'm a Teapot\",\n 510: 'Not Extended',\n 103: 'Early Hints',\n 400: 'Bad Request',\n 502: 'Bad Gateway',\n 102: 'Processing',\n 204: 'No Content',\n 303: 'See Other',\n 305: 'Use Proxy',\n 403: 'Forbidden',\n 404: 'Not Found',\n 425: 'Too Early',\n 100: 'Continue',\n 202: 'Accepted',\n 226: \"I'm Used\",\n 409: 'Conflict',\n 201: 'Created',\n 423: 'Locked',\n 302: 'Found',\n 410: 'Gone',\n 200: 'OK',\n} as const;\n\nexport type HttpStatusCode = keyof typeof HttpStatusMessage;\n\nexport type HttpStatusMessage =\n (typeof HttpStatusMessage)[keyof typeof HttpStatusMessage];\n","import {\n HttpStatusCode,\n HttpStatusMessage,\n} from '@/gateway/constants/http-code';\n\ninterface ErrorParams {\n message: string;\n code: HttpStatusMessage;\n status?: HttpStatusCode;\n metadata?: Record<string, unknown> | undefined;\n isOperational?: boolean;\n cause?: Error | undefined;\n}\n\n/**\n * Abstract base class for application-level errors with structured error handling.\n *\n * Extends the native Error class to provide machine-readable error codes, HTTP status codes,\n * operational error classification, and optional metadata for better error tracking and client communication.\n *\n * @abstract\n * @extends {Error}\n *\n * @example\n * ```typescript\n * class UserNotFoundError extends ApplicationError {\n * constructor(userId: string) {\n * super({\n * code: HttpStatusMessage['404'],\n * status: 404,\n * isOperational: true,\n * message: `User with id ${userId} not found`,\n * metadata: { userId }\n * });\n * }\n * }\n * ```\n */\nexport abstract class ApplicationError extends Error {\n /** Optional cause (linked error) */\n public readonly cause?: Error | undefined;\n /** Machine-readable error code (e.g. `OTP_LOCKED`, `USER_NOT_FOUND`) */\n public readonly code: HttpStatusMessage;\n /** Operational vs programmer error flag */\n public readonly isOperational: boolean;\n /** Optional structured metadata for debugging or clients */\n public readonly metadata?: Record<string, unknown> | undefined;\n /** HTTP status code intended for response layer */\n public readonly status: HttpStatusCode;\n\n constructor({\n code = HttpStatusMessage['500'],\n isOperational = false,\n status = 500,\n metadata,\n message,\n cause,\n }: ErrorParams) {\n super(message);\n\n this.name = new.target.name;\n this.code = code;\n this.status = status;\n this.isOperational = isOperational;\n this.metadata = metadata;\n this.cause = cause;\n\n Error.captureStackTrace(this, new.target);\n }\n}\n","import deepEqual from 'fast-deep-equal/es6';\n\nimport { deepFreeze } from '@/utils/deep-freeze.util';\n\nexport abstract class ValueObject<T> {\n get value(): T {\n return this.props;\n }\n\n protected readonly props: Readonly<T>;\n\n protected constructor(props: T) {\n this.validate(props);\n this.props = deepFreeze(props);\n }\n\n /**\n * Standard for clean API integration and logging.\n */\n public toJSON(): T {\n return this.props;\n }\n\n /**\n * Useful for debugging and string-based indexing.\n */\n public toString(): string {\n return JSON.stringify(this.props);\n }\n\n /**\n * Type guard to check if an unknown object is an instance of ValueObject.\n * This is useful for runtime type checking.\n *\n * @param vo The object to check.\n * @returns True if the object is a ValueObject instance, false otherwise.\n */\n public static is(vo: unknown): vo is ValueObject<unknown> {\n return vo instanceof ValueObject;\n }\n\n /**\n * Deep equality comparison of ValueObjects\n */\n public equals(other?: ValueObject<T>): boolean {\n if (other === null || other === undefined) return false;\n\n // Check if they share the same constructor (Type check)\n if (Object.getPrototypeOf(this) !== Object.getPrototypeOf(other)) {\n return false;\n }\n\n return deepEqual(this.props, other.props);\n }\n\n /**\n * Validates the value object props\n * @throws InvalidValueObjectError if validation fails\n */\n protected abstract validate(props: T): void;\n}\n","import { ValueObject } from '@/domain/base/vo';\n\nexport type UnwrapValueObject<T> =\n T extends ValueObject<infer V>\n ? UnwrapValueObject<V>\n : T extends (infer U)[]\n ? UnwrapValueObject<U>[]\n : T extends Map<infer K, infer V>\n ? Map<K, UnwrapValueObject<V>>\n : T extends Set<infer V>\n ? Set<UnwrapValueObject<V>>\n : T extends Date\n ? string\n : T extends object\n ? { [K in keyof T]: UnwrapValueObject<T[K]> }\n : T;\n\nexport function unwrapValueObject<T>(\n input: T,\n seen = new WeakSet(),\n): UnwrapValueObject<T> {\n if (input === null || input === undefined) {\n return input as UnwrapValueObject<T>;\n }\n\n if (typeof input !== 'object') {\n return input as UnwrapValueObject<T>;\n }\n\n if (seen.has(input)) {\n // Prevent circular reference infinite recursion, just return input or throw\n throw new Error('Circular reference detected in ValueObject unwrap');\n }\n\n seen.add(input);\n\n if (Array.isArray(input)) {\n const result = input.map(item => unwrapValueObject(item, seen));\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof ValueObject) {\n const result = unwrapValueObject(input.value, seen);\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof Date) {\n seen.delete(input);\n return input.toISOString() as UnwrapValueObject<T>;\n }\n\n if (input instanceof Map) {\n const result = new Map();\n input.forEach((value, key) => {\n result.set(key, unwrapValueObject(value, seen));\n });\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof Set) {\n const result = new Set(\n Array.from(input.values()).map(v => unwrapValueObject(v, seen)),\n );\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n // generic object\n const result: Record<string, unknown> = {};\n\n for (const key in input) {\n if (Object.hasOwn(input, key)) {\n result[key] = unwrapValueObject((input as any)[key], seen);\n }\n }\n\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n}\n\nexport function ensureObject<T>(input: UnwrapValueObject<T>): object {\n if (input === null || input === undefined) {\n return {};\n }\n if (typeof input === 'object') {\n return input;\n }\n\n // for primitives, wrap inside object with default key (or throw)\n return { value: input };\n}\n","export interface DomainErrorMetadata {\n cause?: { name: string; message: string; stack?: string };\n [key: string]: unknown;\n}\n\nconst getCauseInfo = (cause: Error | undefined) => {\n // 1. Handle Error objects specifically\n if (cause instanceof Error) {\n return {\n cause: {\n message: cause.message,\n stack: cause.stack,\n name: cause.name,\n },\n };\n }\n\n // 2. Handle other existing values\n if (cause) {\n return { cause };\n }\n\n // 3. Default to empty\n return {};\n};\n\n/**\n * Base class for all Domain Errors in the application.\n *\n * This class ensures:\n * 1. Serializable and structured for logs or API responses.\n * 2. Identifiable via stable error codes (not just class names).\n * 3. Contextual with optional structured metadata.\n * 4. Supports error chaining (cause) and stack trace preservation.\n *\n * @example\n * export class InsufficientFundsError extends DomainError {\n * constructor(accountId: string, currentBalance: number) {\n * super(\n * `Account ${accountId} has insufficient funds.`,\n * 'INSUFFICIENT_FUNDS',\n * { accountId, currentBalance }\n * );\n * }\n * }\n */\nexport abstract class DomainError extends Error {\n /** Stable, machine-readable error code */\n public readonly code: string;\n /** Structured, immutable domain metadata */\n public readonly metadata: Readonly<DomainErrorMetadata>;\n\n /**\n * @param message - Human-readable error message\n * @param code - Stable error code\n * @param metadata - Domain-specific structured data; optional `cause` can be included\n */\n protected constructor(\n message: string,\n code: string,\n metadata: DomainErrorMetadata = {},\n ) {\n super(message, { ...getCauseInfo(metadata.cause) });\n\n // Restore prototype chain for proper `instanceof` checks\n Object.setPrototypeOf(this, new.target.prototype);\n\n this.name = new.target.name;\n this.code = code;\n this.metadata = Object.freeze({ ...metadata });\n }\n}\n","import { DomainError } from '../base/domain.error';\n\n/**\n * Custom error class for entity validation failures.\n */\nexport class EntityValidationError extends DomainError {\n constructor(message: string, cause?: Error) {\n super(message, 'ENTITY_VALIDATION_ERROR', { cause });\n }\n}\n","import { v4 } from 'uuid';\nimport z from 'zod';\n\nimport { InvalidValueObjectError } from '../errors/invalid-vo.error';\nimport { ValueObject } from '../base/vo';\n\n/**\n * AggregateId is a ValueObject that represents a unique identifier for an aggregate.\n */\nexport class AggregateId extends ValueObject<string> {\n /**\n * The schema for the AggregateId\n */\n private static readonly schema = z.uuid();\n\n /**\n * Get the UUID of the AggregateId\n * @returns The UUID of the AggregateId\n */\n public get uuid(): string {\n return this.value;\n }\n\n /**\n * Create a new AggregateId\n * @param value The value to create the AggregateId from\n * @returns The new AggregateId\n */\n public static create(value: string): AggregateId {\n return new AggregateId(value);\n }\n\n /**\n * Create a new AggregateId with a random UUID v4\n */\n public static generate(): AggregateId {\n return new AggregateId(v4());\n }\n\n /**\n * Check if the AggregateId is empty\n * @returns True if the AggregateId is empty, false otherwise\n */\n public isEmpty(): boolean {\n return !this.value;\n }\n\n /**\n * Convert the AggregateId to a string\n * @returns The string representation of the AggregateId\n */\n public toString(): string {\n return this.value;\n }\n\n /**\n * Validate the AggregateId\n * @param value The value to validate\n * @throws InvalidValueObjectException if the value is invalid\n */\n protected validate(value: string): void {\n const result = AggregateId.schema.safeParse(value);\n\n if (!result.success) {\n throw new InvalidValueObjectError(\n `Invalid AggregateId: ${result.error.message}`,\n );\n }\n }\n}\n","import { DomainError } from '../base/domain.error';\n\nexport class InvalidValueObjectError extends DomainError {\n constructor(message: string) {\n super(message, 'INVALID_VALUE_OBJECT');\n }\n}\n","import { ensureObject, UnwrapValueObject, unwrapValueObject } from '@/utils';\n\nimport { EntityValidationError } from '../errors/entity-validation.error';\nimport { AggregateId } from '../value-objects/aggregate-id.vo';\n\nexport interface EntityBaseInterface {\n id: AggregateId;\n equals: (entity: unknown) => boolean;\n}\n\n/**\n * Base properties common to all entities, including ID and timestamps.\n */\nexport interface BaseEntityProps {\n /** Unique identifier for the entity */\n id?: AggregateId;\n /** Date when the entity was created */\n createdAt: Date;\n}\n\n/**\n * Interface for constructing an entity with optional timestamps.\n * @template Props - The specific props type for the entity\n */\nexport type CreateEntityProps<T> = BaseEntityProps & {\n props: T;\n};\n\n/**\n * Abstract base class for domain entities in a Domain-Driven Design (DDD) context.\n * Provides common functionality for entity identification, equality comparison,\n * immutability, and validation. Entities extending this class must implement\n * the `validate` method to enforce domain invariants.\n * @template EntityProps - The specific props type for the entity\n */\nexport abstract class Entity<EntityProps> {\n /**\n * Returns the creation timestamp.\n * A new Date instance is returned to preserve immutability.\n *\n * @returns {Date} The creation date of the entity.\n */\n get createdAt(): Date {\n return new Date(this.#createdAt);\n }\n\n /**\n * Gets the entity's unique identifier.\n * @returns The entity's ID\n */\n get id(): AggregateId {\n return this.#id;\n }\n\n public get metadata() {\n return Object.freeze({\n createdAt: this.#createdAt.toISOString(),\n id: this.#id.uuid,\n });\n }\n\n /**\n * Returns an immutable shallow copy of the entity's properties.\n *\n * @returns {Readonly<EntityProps>} The entity domain properties.\n */\n get props(): Readonly<EntityProps> {\n return this.#props;\n }\n\n /** Private creation timestamp */\n #createdAt: Date;\n /** Private unique identifier for the entity */\n #id: AggregateId;\n /** Private entity-specific properties */\n #props: Readonly<EntityProps>;\n\n /**\n * Constructs an entity with the provided properties and timestamps.\n * Ensures immutability by cloning props and validates the entity.\n * @param params - Entity creation parameters\n * @throws EntityValidationError if the ID is empty or validation fails\n */\n protected constructor({\n createdAt,\n props,\n id,\n }: CreateEntityProps<EntityProps>) {\n if (!id) {\n throw new EntityValidationError('Entity ID cannot be empty');\n }\n\n this.#id = id ?? AggregateId.generate();\n this.#props = Object.freeze(props);\n const now = new Date();\n this.#createdAt = createdAt ? new Date(createdAt) : now;\n }\n\n /**\n * Checks if the provided value is an instance of Entity.\n * @param entity - The value to check\n * @returns True if the value is an Entity instance\n */\n static isEntity(entity: unknown): entity is EntityBaseInterface {\n return (\n typeof entity === 'object' &&\n entity !== null &&\n 'id' in entity &&\n typeof (entity as any).equals === 'function'\n );\n }\n\n /**\n * Compares this entity with another to determine if they are the same.\n * Equality is based on the entity ID.\n * @param other - The entity to compare with\n * @returns True if the entities have the same ID\n */\n public equals(other?: Entity<EntityProps>): boolean {\n if (!other || !Entity.isEntity(other)) {\n return false;\n }\n\n return this.#id.equals(other.#id);\n }\n\n /**\n * Returns a frozen copy of the entity's properties, including base properties.\n * Ensures immutability by returning a new object.\n * @returns A frozen copy of the entity's properties\n */\n public getPropsCopy(): Readonly<EntityProps> {\n return Object.freeze(this.#props);\n }\n\n /**\n * Determines if the entity is transient, i.e., it has not been persisted yet.\n * By convention, an entity is considered transient if it lacks a valid identifier.\n * This can be useful when performing logic that depends on persistence state,\n * such as conditional inserts or validations that only apply to new entities.\n *\n * @returns True if the entity is transient (not persisted), otherwise false.\n */\n public isPersisted(): boolean {\n return this.#id.isEmpty();\n }\n\n public toJSON(): Record<string, unknown> {\n return this.toObject();\n }\n\n public toObject(): Readonly<\n UnwrapValueObject<EntityProps> & { createdAt: string; id: string }\n > {\n const props = unwrapValueObject(this.getPropsCopy());\n const safeProps = ensureObject(props);\n return Object.freeze({\n ...this.metadata,\n ...safeProps,\n }) as Readonly<\n UnwrapValueObject<EntityProps> & { createdAt: string; id: string }\n >;\n }\n\n /**\n * Validates the entity's state to enforce domain invariants.\n * Must be implemented by subclasses to define specific validation rules.\n * @throws EntityValidationError if validation fails\n */\n public abstract validate(): void;\n}\n","import { AggregateId } from '../value-objects/aggregate-id.vo';\nimport { Entity } from '../entities/entity';\nimport { DomainEvent } from '../events';\n\n/**\n * Interface for AggregateRoot to ensure type safety and extensibility.\n */\nexport interface AggregateRootInterface {\n readonly id: AggregateId;\n readonly domainEvents: readonly DomainEvent[];\n /**\n * Validates the aggregate's invariants.\n * @throws {EntityValidationError} If validation fails.\n */\n validate: () => void;\n /**\n * Adds a domain event to the aggregate.\n * @param event The domain event to add.\n */\n addEvent: (event: DomainEvent) => void;\n\n /**\n * Retrieves and clears all domain events recorded by this aggregate.\n *\n * Domain events represent facts that occurred as a result of state changes\n * within the aggregate. This method transfers ownership of those events\n * to the application layer for further processing (e.g. publishing).\n *\n * Calling this method has the side effect of clearing the aggregate's\n * internal event collection to prevent duplicate handling.\n *\n * This method is intended to be invoked by application services\n * after the aggregate has been successfully persisted.\n *\n * @returns A read-only list of domain events raised by this aggregate.\n */\n pullDomainEvents: () => readonly DomainEvent[];\n}\n\n/**\n * Base class for aggregate roots in DDD, encapsulating domain events and validation.\n * @template EntityProps The type of the entity's properties.\n */\nexport abstract class AggregateRoot<EntityProps>\n extends Entity<EntityProps>\n implements AggregateRootInterface\n{\n /**\n * Gets a read-only copy of the domain events.\n */\n get domainEvents(): readonly DomainEvent[] {\n return [...this._domainEvents];\n }\n\n /**\n * Internal list of domain events.\n */\n private readonly _domainEvents: DomainEvent[] = [];\n\n /**\n * Adds a domain event to the aggregate after validating invariants.\n * @param domainEvent The domain event to add.\n * @throws {EntityValidationError} If invariants are not met.\n */\n addEvent(domainEvent: DomainEvent): void {\n this.validate(); // Ensure invariants before adding events\n this._domainEvents.push(domainEvent);\n }\n\n public pullDomainEvents(): readonly DomainEvent[] {\n const events = [...this._domainEvents];\n this._domainEvents.length = 0;\n return events;\n }\n\n /**\n * Validates the entity's invariants.\n * @throws {EntityValidationError} If validation fails.\n */\n abstract validate(): void;\n}\n","type Primitive = boolean | number | string | null;\n\ntype Serializable =\n | Primitive\n | Serializable[]\n | { [key: string]: Serializable };\n\nexport type DomainEventPayload = Record<string, Serializable>;\n\ntype DomainEventProps<T> = {\n id: string;\n aggregateId: string;\n schemaVersion: number;\n occurredAt: number;\n payload: T;\n};\n\n// Abstract base class for domain events\nexport abstract class DomainEvent<\n T extends DomainEventPayload = DomainEventPayload,\n> {\n public abstract readonly eventName: string;\n\n public readonly id: string;\n public readonly aggregateId: string;\n public readonly schemaVersion: number;\n public readonly occurredAt: number;\n public readonly payload: Readonly<T>;\n\n protected constructor(props: DomainEventProps<T>) {\n this.id = props.id;\n this.aggregateId = props.aggregateId;\n this.schemaVersion = props.schemaVersion;\n this.occurredAt = props.occurredAt;\n this.payload = Object.freeze(props.payload);\n }\n\n public toPrimitives() {\n return {\n schemaVersion: this.schemaVersion,\n aggregateId: this.aggregateId,\n occurredAt: this.occurredAt,\n eventName: this.eventName,\n payload: this.payload,\n id: this.id,\n };\n }\n}\n"],"mappings":"uoCAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,iBAAAE,EAAA,kBAAAC,EAAA,qBAAAC,EAAA,gBAAAC,EAAA,gBAAAC,EAAA,WAAAC,EAAA,0BAAAC,EAAA,eAAAC,EAAA,sBAAAC,EAAA,4BAAAC,EAAA,gBAAAC,EAAA,eAAAC,EAAA,iBAAAC,EAAA,sBAAAC,IAAA,eAAAC,EAAAhB,GCOO,SAASiB,EACdC,EACAC,EAAO,IAAI,QACE,CAYb,GAVID,GAAO,MAAS,OAAOA,GAAQ,UAAY,CAAC,MAAM,QAAQA,CAAG,GAK7D,OAAO,SAASA,CAAG,GAKnBC,EAAK,IAAID,CAAa,EACxB,OAAOA,EAMT,GAHAC,EAAK,IAAID,CAAa,EAGlB,MAAM,QAAQA,CAAG,EACnBA,EAAI,QAAQE,GAAQH,EAAWG,EAAMD,CAAI,CAAC,MAG1C,SAAWE,KAAOH,EACZ,OAAO,OAAOA,EAAKG,CAAG,GACxBJ,EAAYC,EAAgCG,CAAG,EAAGF,CAAI,EAK5D,OAAO,OAAO,OAAOD,CAAG,CAC1B,CCnBO,IAAMI,EAAa,OAAO,OAAO,CACtC,gCAAiC,IACjC,gCAAiC,IACjC,8BAA+B,IAC/B,8BAA+B,IAE/B,8BAA+B,IAC/B,2BAA4B,IAC5B,yBAA0B,IAC1B,wBAAyB,IACzB,uBAAwB,IACxB,sBAAuB,IACvB,sBAAuB,IACvB,sBAAuB,IACvB,qBAAsB,IACtB,qBAAsB,IAEtB,oBAAqB,IACrB,oBAAqB,IACrB,oBAAqB,IACrB,oBAAqB,IACrB,mBAAoB,IACpB,mBAAoB,IACpB,mBAAoB,IACpB,mBAAoB,IAEpB,kBAAmB,IACnB,kBAAmB,IACnB,kBAAmB,IACnB,kBAAmB,IACnB,iBAAkB,IAClB,iBAAkB,IAClB,iBAAkB,IAClB,iBAAkB,IAClB,gBAAiB,IACjB,gBAAiB,IACjB,gBAAiB,IACjB,gBAAiB,IACjB,gBAAiB,IACjB,eAAgB,IAChB,cAAe,IACf,cAAe,IACf,aAAc,IACd,aAAc,IACd,aAAc,IACd,aAAc,IACd,aAAc,IACd,YAAa,IACb,YAAa,IACb,YAAa,IACb,YAAa,IACb,WAAY,IACZ,WAAY,IACZ,UAAW,IACX,UAAW,IAEX,UAAW,IACX,UAAW,IACX,UAAW,IACX,SAAU,IACV,SAAU,IACV,SAAU,IACV,QAAS,IACT,QAAS,IACT,OAAQ,IACR,MAAO,IACP,KAAM,IACN,GAAI,GACN,CAAU,EAMGC,EAAoB,CAC/B,IAAK,kCACL,IAAK,kCACL,IAAK,gCACL,IAAK,gCACL,IAAK,gCACL,IAAK,6BACL,IAAK,2BACL,IAAK,0BACL,IAAK,yBACL,IAAK,wBACL,IAAK,wBACL,IAAK,wBACL,IAAK,uBACL,IAAK,uBACL,IAAK,sBACL,IAAK,sBACL,IAAK,sBACL,IAAK,sBACL,IAAK,qBACL,IAAK,qBACL,IAAK,qBACL,IAAK,qBACL,IAAK,oBACL,IAAK,oBACL,IAAK,oBACL,IAAK,oBACL,IAAK,mBACL,IAAK,mBACL,IAAK,mBACL,IAAK,mBACL,IAAK,kBACL,IAAK,kBACL,IAAK,kBACL,IAAK,kBACL,IAAK,kBACL,IAAK,iBACL,IAAK,gBACL,IAAK,gBACL,IAAK,eACL,IAAK,eACL,IAAK,eACL,IAAK,eACL,IAAK,eACL,IAAK,eACL,IAAK,cACL,IAAK,cACL,IAAK,cACL,IAAK,aACL,IAAK,aACL,IAAK,YACL,IAAK,YACL,IAAK,YACL,IAAK,YACL,IAAK,YACL,IAAK,WACL,IAAK,WACL,IAAK,WACL,IAAK,WACL,IAAK,UACL,IAAK,SACL,IAAK,QACL,IAAK,OACL,IAAK,IACP,EC1HO,IAAeC,EAAf,cAAwC,KAAM,CAYnD,YAAY,CACV,KAAAC,EAAOC,EAAkB,GAAK,EAC9B,cAAAC,EAAgB,GAChB,OAAAC,EAAS,IACT,SAAAC,EACA,QAAAC,EACA,MAAAC,CACF,EAAgB,CACd,MAAMD,CAAO,EAEb,KAAK,KAAO,WAAW,KACvB,KAAK,KAAOL,EACZ,KAAK,OAASG,EACd,KAAK,cAAgBD,EACrB,KAAK,SAAWE,EAChB,KAAK,MAAQE,EAEb,MAAM,kBAAkB,KAAM,UAAU,CAC1C,CACF,ECrEA,IAAAC,EAAsB,kCAIf,IAAeC,EAAf,MAAeC,CAAe,CACnC,IAAI,OAAW,CACb,OAAO,KAAK,KACd,CAIU,YAAYC,EAAU,CAC9B,KAAK,SAASA,CAAK,EACnB,KAAK,MAAQC,EAAWD,CAAK,CAC/B,CAKO,QAAY,CACjB,OAAO,KAAK,KACd,CAKO,UAAmB,CACxB,OAAO,KAAK,UAAU,KAAK,KAAK,CAClC,CASA,OAAc,GAAGE,EAAyC,CACxD,OAAOA,aAAcH,CACvB,CAKO,OAAOI,EAAiC,CAI7C,OAHIA,GAAU,MAGV,OAAO,eAAe,IAAI,IAAM,OAAO,eAAeA,CAAK,EACtD,MAGF,EAAAC,SAAU,KAAK,MAAOD,EAAM,KAAK,CAC1C,CAOF,EC3CO,SAASE,EACdC,EACAC,EAAO,IAAI,QACW,CAKtB,GAJID,GAAU,MAIV,OAAOA,GAAU,SACnB,OAAOA,EAGT,GAAIC,EAAK,IAAID,CAAK,EAEhB,MAAM,IAAI,MAAM,mDAAmD,EAKrE,GAFAC,EAAK,IAAID,CAAK,EAEV,MAAM,QAAQA,CAAK,EAAG,CACxB,IAAME,EAASF,EAAM,IAAIG,GAAQJ,EAAkBI,EAAMF,CAAI,CAAC,EAC9D,OAAAA,EAAK,OAAOD,CAAK,EACVE,CACT,CAEA,GAAIF,aAAiBI,EAAa,CAChC,IAAMF,EAASH,EAAkBC,EAAM,MAAOC,CAAI,EAClD,OAAAA,EAAK,OAAOD,CAAK,EACVE,CACT,CAEA,GAAIF,aAAiB,KACnB,OAAAC,EAAK,OAAOD,CAAK,EACVA,EAAM,YAAY,EAG3B,GAAIA,aAAiB,IAAK,CACxB,IAAME,EAAS,IAAI,IACnB,OAAAF,EAAM,QAAQ,CAACK,EAAOC,IAAQ,CAC5BJ,EAAO,IAAII,EAAKP,EAAkBM,EAAOJ,CAAI,CAAC,CAChD,CAAC,EACDA,EAAK,OAAOD,CAAK,EACVE,CACT,CAEA,GAAIF,aAAiB,IAAK,CACxB,IAAME,EAAS,IAAI,IACjB,MAAM,KAAKF,EAAM,OAAO,CAAC,EAAE,IAAIO,GAAKR,EAAkBQ,EAAGN,CAAI,CAAC,CAChE,EACA,OAAAA,EAAK,OAAOD,CAAK,EACVE,CACT,CAGA,IAAMA,EAAkC,CAAC,EAEzC,QAAWI,KAAON,EACZ,OAAO,OAAOA,EAAOM,CAAG,IAC1BJ,EAAOI,CAAG,EAAIP,EAAmBC,EAAcM,CAAG,EAAGL,CAAI,GAI7D,OAAAA,EAAK,OAAOD,CAAK,EACVE,CACT,CAEO,SAASM,EAAgBR,EAAqC,CACnE,OAAIA,GAAU,KACL,CAAC,EAEN,OAAOA,GAAU,SACZA,EAIF,CAAE,MAAOA,CAAM,CACxB,CCxFA,IAAMS,EAAgBC,GAEhBA,aAAiB,MACZ,CACL,MAAO,CACL,QAASA,EAAM,QACf,MAAOA,EAAM,MACb,KAAMA,EAAM,IACd,CACF,EAIEA,EACK,CAAE,MAAAA,CAAM,EAIV,CAAC,EAuBYC,EAAf,cAAmC,KAAM,CAWpC,YACRC,EACAC,EACAC,EAAgC,CAAC,EACjC,CACA,MAAMF,EAASG,EAAA,GAAKN,EAAaK,EAAS,KAAK,EAAG,EAGlD,OAAO,eAAe,KAAM,WAAW,SAAS,EAEhD,KAAK,KAAO,WAAW,KACvB,KAAK,KAAOD,EACZ,KAAK,SAAW,OAAO,OAAOE,EAAA,GAAKD,EAAU,CAC/C,CACF,EClEO,IAAME,EAAN,cAAoCC,CAAY,CACrD,YAAYC,EAAiBC,EAAe,CAC1C,MAAMD,EAAS,0BAA2B,CAAE,MAAAC,CAAM,CAAC,CACrD,CACF,ECTA,IAAAC,EAAmB,gBACnBC,EAAc,kBCCP,IAAMC,EAAN,cAAsCC,CAAY,CACvD,YAAYC,EAAiB,CAC3B,MAAMA,EAAS,sBAAsB,CACvC,CACF,EDGO,IAAMC,EAAN,MAAMA,UAAoBC,CAAoB,CAUnD,IAAW,MAAe,CACxB,OAAO,KAAK,KACd,CAOA,OAAc,OAAOC,EAA4B,CAC/C,OAAO,IAAIF,EAAYE,CAAK,CAC9B,CAKA,OAAc,UAAwB,CACpC,OAAO,IAAIF,KAAY,MAAG,CAAC,CAC7B,CAMO,SAAmB,CACxB,MAAO,CAAC,KAAK,KACf,CAMO,UAAmB,CACxB,OAAO,KAAK,KACd,CAOU,SAASE,EAAqB,CACtC,IAAMC,EAASH,EAAY,OAAO,UAAUE,CAAK,EAEjD,GAAI,CAACC,EAAO,QACV,MAAM,IAAIC,EACR,wBAAwBD,EAAO,MAAM,OAAO,EAC9C,CAEJ,CACF,EA5DaH,EAIa,OAAS,EAAAK,QAAE,KAAK,EAJnC,IAAMC,EAANN,EETP,IAAAO,EAAAC,EAAAC,EAmCsBC,EAAf,MAAeA,CAAoB,CAgD9B,YAAY,CACpB,UAAAC,EACA,MAAAC,EACA,GAAAC,CACF,EAAmC,CAhBnCC,EAAA,KAAAP,GAEAO,EAAA,KAAAN,GAEAM,EAAA,KAAAL,GAaE,GAAI,CAACI,EACH,MAAM,IAAIE,EAAsB,2BAA2B,EAG7DC,EAAA,KAAKR,EAAMK,GAAA,KAAAA,EAAMI,EAAY,SAAS,GACtCD,EAAA,KAAKP,EAAS,OAAO,OAAOG,CAAK,GAEjCI,EAAA,KAAKT,EAAaI,EAAY,IAAI,KAAKA,CAAS,EADpC,IAAI,KAElB,CAtDA,IAAI,WAAkB,CACpB,OAAO,IAAI,KAAKO,EAAA,KAAKX,EAAU,CACjC,CAMA,IAAI,IAAkB,CACpB,OAAOW,EAAA,KAAKV,EACd,CAEA,IAAW,UAAW,CACpB,OAAO,OAAO,OAAO,CACnB,UAAWU,EAAA,KAAKX,GAAW,YAAY,EACvC,GAAIW,EAAA,KAAKV,GAAI,IACf,CAAC,CACH,CAOA,IAAI,OAA+B,CACjC,OAAOU,EAAA,KAAKT,EACd,CAmCA,OAAO,SAASU,EAAgD,CAC9D,OACE,OAAOA,GAAW,UAClBA,IAAW,MACX,OAAQA,GACR,OAAQA,EAAe,QAAW,UAEtC,CAQO,OAAOC,EAAsC,CAClD,MAAI,CAACA,GAAS,CAACV,EAAO,SAASU,CAAK,EAC3B,GAGFF,EAAA,KAAKV,GAAI,OAAOU,EAAAE,EAAMZ,EAAG,CAClC,CAOO,cAAsC,CAC3C,OAAO,OAAO,OAAOU,EAAA,KAAKT,EAAM,CAClC,CAUO,aAAuB,CAC5B,OAAOS,EAAA,KAAKV,GAAI,QAAQ,CAC1B,CAEO,QAAkC,CACvC,OAAO,KAAK,SAAS,CACvB,CAEO,UAEL,CACA,IAAMI,EAAQS,EAAkB,KAAK,aAAa,CAAC,EAC7CC,EAAYC,EAAaX,CAAK,EACpC,OAAO,OAAO,OAAOY,IAAA,GAChB,KAAK,UACLF,EACJ,CAGH,CAQF,EAnGEf,EAAA,YAEAC,EAAA,YAEAC,EAAA,YAxCK,IAAegB,EAAff,ECQA,IAAegB,EAAf,cACGC,CAEV,CAHO,kCAcL,KAAiB,cAA+B,CAAC,EAPjD,IAAI,cAAuC,CACzC,MAAO,CAAC,GAAG,KAAK,aAAa,CAC/B,CAYA,SAASC,EAAgC,CACvC,KAAK,SAAS,EACd,KAAK,cAAc,KAAKA,CAAW,CACrC,CAEO,kBAA2C,CAChD,IAAMC,EAAS,CAAC,GAAG,KAAK,aAAa,EACrC,YAAK,cAAc,OAAS,EACrBA,CACT,CAOF,EC9DO,IAAeC,EAAf,KAEL,CASU,YAAYC,EAA4B,CAChD,KAAK,GAAKA,EAAM,GAChB,KAAK,YAAcA,EAAM,YACzB,KAAK,cAAgBA,EAAM,cAC3B,KAAK,WAAaA,EAAM,WACxB,KAAK,QAAU,OAAO,OAAOA,EAAM,OAAO,CAC5C,CAEO,cAAe,CACpB,MAAO,CACL,cAAe,KAAK,cACpB,YAAa,KAAK,YAClB,WAAY,KAAK,WACjB,UAAW,KAAK,UAChB,QAAS,KAAK,QACd,GAAI,KAAK,EACX,CACF,CACF","names":["index_exports","__export","AggregateId","AggregateRoot","ApplicationError","DomainError","DomainEvent","Entity","EntityValidationError","HttpStatus","HttpStatusMessage","InvalidValueObjectError","ValueObject","deepFreeze","ensureObject","unwrapValueObject","__toCommonJS","deepFreeze","obj","seen","item","key","HttpStatus","HttpStatusMessage","ApplicationError","code","HttpStatusMessage","isOperational","status","metadata","message","cause","import_es6","ValueObject","_ValueObject","props","deepFreeze","vo","other","deepEqual","unwrapValueObject","input","seen","result","item","ValueObject","value","key","v","ensureObject","getCauseInfo","cause","DomainError","message","code","metadata","__spreadValues","EntityValidationError","DomainError","message","cause","import_uuid","import_zod","InvalidValueObjectError","DomainError","message","_AggregateId","ValueObject","value","result","InvalidValueObjectError","z","AggregateId","_createdAt","_id","_props","_Entity","createdAt","props","id","__privateAdd","EntityValidationError","__privateSet","AggregateId","__privateGet","entity","other","unwrapValueObject","safeProps","ensureObject","__spreadValues","Entity","AggregateRoot","Entity","domainEvent","events","DomainEvent","props"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils/deep-freeze.util.ts","../src/gateway/constants/http-code.ts","../src/utils/errors/application.error.ts","../src/domain/base/vo.ts","../src/utils/unwrap-vo.util.ts","../src/domain/base/domain.error.ts","../src/domain/errors/entity-validation.error.ts","../src/domain/value-objects/id.vo.ts","../src/domain/errors/invalid-vo.error.ts","../src/domain/base/primitive-vo.ts","../src/domain/value-objects/aggregate-id.vo.ts","../src/domain/entities/entity.ts","../src/domain/aggregates/aggregate-root.ts","../src/domain/events/domain.event.ts"],"sourcesContent":["export * from './application';\nexport * from './domain/aggregates';\nexport * from './domain/base/domain.error';\nexport * from './domain/base/primitive-vo';\nexport * from './domain/base/vo';\nexport * from './domain/entities';\nexport * from './domain/errors/entity-validation.error';\nexport * from './domain/errors/invalid-vo.error';\nexport * from './domain/events';\nexport * from './domain/value-objects/aggregate-id.vo';\nexport * from './gateway/constants/http-code';\nexport * from './utils';\n","/**\n * Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.\n *\n * @param obj - The object to be deeply frozen.\n * @param seen - A WeakSet to track already processed objects (for circular references).\n * @returns A deeply frozen version of the input object.\n */\nexport function deepFreeze<T>(\n obj: T,\n seen = new WeakSet<object>(),\n): Readonly<T> {\n // Handle null, undefined, or non-object types\n if (obj == null || (typeof obj !== 'object' && !Array.isArray(obj))) {\n return obj;\n }\n\n // Skip if already frozen\n if (Object.isFrozen(obj)) {\n return obj as Readonly<T>;\n }\n\n // Handle circular references\n if (seen.has(obj as object)) {\n return obj as Readonly<T>;\n }\n\n seen.add(obj as object);\n\n // Handle arrays explicitly\n if (Array.isArray(obj)) {\n obj.forEach(item => deepFreeze(item, seen));\n } else {\n // Handle plain objects\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n deepFreeze((obj as Record<string, unknown>)[key], seen);\n }\n }\n }\n\n return Object.freeze(obj) as Readonly<T>;\n}\n","/**\n * HTTP status code catalog.\n *\n * This object provides a typed, immutable map of standard HTTP status names\n * to their numeric codes. Designed for server-side frameworks such as Fastify.\n *\n * - All identifiers use clear, canonical semantic names.\n * - Values are numeric status codes.\n * - Frozen to prevent runtime mutation.\n * - Exporting `HttpStatus` ensures type-safe usage across the codebase.\n * Usage:\n * ```ts\n * import { HttpStatus } from 'path-to-this-file';\n *\n * function handleRequest() {\n * return {\n * statusCode: HttpStatus.OK,\n * body: 'Success',\n * };\n * }\n * ```\n */\nexport const HttpStatus = Object.freeze({\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n NON_AUTHORITATIVE_INFORMATION: 203,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n\n UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n BANDWIDTH_LIMIT_EXCEEDED: 509,\n VARIANT_ALSO_NEGOTIATES: 506,\n UNSUPPORTED_MEDIA_TYPE: 415,\n RANGE_NOT_SATISFIABLE: 416,\n PRECONDITION_REQUIRED: 428,\n INTERNAL_SERVER_ERROR: 500,\n UNPROCESSABLE_ENTITY: 422,\n INSUFFICIENT_STORAGE: 507,\n\n SWITCHING_PROTOCOLS: 101,\n PRECONDITION_FAILED: 412,\n MISDIRECTED_REQUEST: 421,\n SERVICE_UNAVAILABLE: 503,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n METHOD_NOT_ALLOWED: 405,\n EXPECTATION_FAILED: 417,\n\n MOVED_PERMANENTLY: 301,\n PAYLOAD_TOO_LARGE: 413,\n FAILED_DEPENDENCY: 424,\n TOO_MANY_REQUESTS: 429,\n ALREADY_REPORTED: 208,\n MULTIPLE_CHOICES: 300,\n PAYMENT_REQUIRED: 402,\n UPGRADE_REQUIRED: 426,\n PARTIAL_CONTENT: 206,\n REQUEST_TIMEOUT: 408,\n LENGTH_REQUIRED: 411,\n NOT_IMPLEMENTED: 501,\n GATEWAY_TIMEOUT: 504,\n NOT_ACCEPTABLE: 406,\n RESET_CONTENT: 205,\n LOOP_DETECTED: 508,\n MULTI_STATUS: 207,\n NOT_MODIFIED: 304,\n UNAUTHORIZED: 401,\n URI_TOO_LONG: 414,\n NOT_EXTENDED: 510,\n EARLY_HINTS: 103,\n BAD_REQUEST: 400,\n IM_A_TEAPOT: 418,\n BAD_GATEWAY: 502,\n PROCESSING: 102,\n NO_CONTENT: 204,\n SEE_OTHER: 303,\n USE_PROXY: 305,\n\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n TOO_EARLY: 425,\n CONTINUE: 100,\n ACCEPTED: 202,\n CONFLICT: 409,\n CREATED: 201,\n IM_USED: 226,\n LOCKED: 423,\n FOUND: 302,\n GONE: 410,\n OK: 200,\n} as const);\n\n/**\n * HTTP status messages mapped by numeric code.\n * Use for sending descriptive text in responses or logging.\n */\nexport const HttpStatusMessage = {\n 431: 'Request Header Fields Too Large',\n 511: 'Network Authentication Required',\n 203: 'Non-Authoritative Information',\n 407: 'Proxy Authentication Required',\n 451: 'Unavailable For Legal Reasons',\n 505: 'HTTP Version Not Supported',\n 509: 'Bandwidth Limit Exceeded',\n 506: 'Variant Also Negotiates',\n 415: 'Unsupported Media Type',\n 416: 'Range Not Satisfiable',\n 428: 'Precondition Required',\n 500: 'Internal Server Error',\n 422: 'Unprocessable Entity',\n 507: 'Insufficient Storage',\n 101: 'Switching Protocols',\n 412: 'Precondition Failed',\n 421: 'Misdirected Request',\n 503: 'Service Unavailable',\n 307: 'Temporary Redirect',\n 308: 'Permanent Redirect',\n 405: 'Method Not Allowed',\n 417: 'Expectation Failed',\n 301: 'Moved Permanently',\n 413: 'Payload Too Large',\n 424: 'Failed Dependency',\n 429: 'Too Many Requests',\n 208: 'Already Reported',\n 300: 'Multiple Choices',\n 402: 'Payment Required',\n 426: 'Upgrade Required',\n 206: 'Partial Content',\n 408: 'Request Timeout',\n 411: 'Length Required',\n 501: 'Not Implemented',\n 504: 'Gateway Timeout',\n 406: 'Not Acceptable',\n 205: 'Reset Content',\n 508: 'Loop Detected',\n 207: 'Multi-Status',\n 304: 'Not Modified',\n 401: 'Unauthorized',\n 414: 'URI Too Long',\n 418: \"I'm a Teapot\",\n 510: 'Not Extended',\n 103: 'Early Hints',\n 400: 'Bad Request',\n 502: 'Bad Gateway',\n 102: 'Processing',\n 204: 'No Content',\n 303: 'See Other',\n 305: 'Use Proxy',\n 403: 'Forbidden',\n 404: 'Not Found',\n 425: 'Too Early',\n 100: 'Continue',\n 202: 'Accepted',\n 226: \"I'm Used\",\n 409: 'Conflict',\n 201: 'Created',\n 423: 'Locked',\n 302: 'Found',\n 410: 'Gone',\n 200: 'OK',\n} as const;\n\nexport type HttpStatusCode = keyof typeof HttpStatusMessage;\n\nexport type HttpStatusMessage =\n (typeof HttpStatusMessage)[keyof typeof HttpStatusMessage];\n","import {\n HttpStatusCode,\n HttpStatusMessage,\n} from '@/gateway/constants/http-code';\n\ninterface ErrorParams {\n message: string;\n code: HttpStatusMessage;\n status?: HttpStatusCode;\n metadata?: Record<string, unknown> | undefined;\n isOperational?: boolean;\n cause?: Error | undefined;\n}\n\n/**\n * Abstract base class for application-level errors with structured error handling.\n *\n * Extends the native Error class to provide machine-readable error codes, HTTP status codes,\n * operational error classification, and optional metadata for better error tracking and client communication.\n *\n * @abstract\n * @extends {Error}\n *\n * @example\n * ```typescript\n * class UserNotFoundError extends ApplicationError {\n * constructor(userId: string) {\n * super({\n * code: HttpStatusMessage['404'],\n * status: 404,\n * isOperational: true,\n * message: `User with id ${userId} not found`,\n * metadata: { userId }\n * });\n * }\n * }\n * ```\n */\nexport abstract class ApplicationError extends Error {\n /** Optional cause (linked error) */\n public readonly cause?: Error | undefined;\n /** Machine-readable error code (e.g. `OTP_LOCKED`, `USER_NOT_FOUND`) */\n public readonly code: HttpStatusMessage;\n /** Operational vs programmer error flag */\n public readonly isOperational: boolean;\n /** Optional structured metadata for debugging or clients */\n public readonly metadata?: Record<string, unknown> | undefined;\n /** HTTP status code intended for response layer */\n public readonly status: HttpStatusCode;\n\n constructor({\n code = HttpStatusMessage['500'],\n isOperational = false,\n status = 500,\n metadata,\n message,\n cause,\n }: ErrorParams) {\n super(message);\n\n this.name = new.target.name;\n this.code = code;\n this.status = status;\n this.isOperational = isOperational;\n this.metadata = metadata;\n this.cause = cause;\n\n Error.captureStackTrace(this, new.target);\n }\n}\n","import deepEqual from 'fast-deep-equal/es6';\n\nimport { deepFreeze } from '@/utils/deep-freeze.util';\n\nexport abstract class ValueObject<T> {\n get value(): T {\n return this.props;\n }\n\n protected readonly props: Readonly<T>;\n\n protected constructor(props: T) {\n this.validate(props);\n this.props = deepFreeze(props);\n }\n\n /**\n * Standard for clean API integration and logging.\n */\n public toJSON(): T {\n return this.props;\n }\n\n /**\n * Useful for debugging and string-based indexing.\n */\n public toString(): string {\n return JSON.stringify(this.props);\n }\n\n /**\n * Type guard to check if an unknown object is an instance of ValueObject.\n * This is useful for runtime type checking.\n *\n * @param vo The object to check.\n * @returns True if the object is a ValueObject instance, false otherwise.\n */\n public static is(vo: unknown): vo is ValueObject<unknown> {\n return vo instanceof ValueObject;\n }\n\n /**\n * Deep equality comparison of ValueObjects\n */\n public equals(other?: ValueObject<T>): boolean {\n if (other === null || other === undefined) return false;\n\n // Check if they share the same constructor (Type check)\n if (Object.getPrototypeOf(this) !== Object.getPrototypeOf(other)) {\n return false;\n }\n\n return deepEqual(this.props, other.props);\n }\n\n /**\n * Validates the value object props\n * @throws InvalidValueObjectError if validation fails\n */\n protected abstract validate(props: T): void;\n}\n","import { ValueObject } from '@/domain/base/vo';\n\nexport type UnwrapValueObject<T> =\n T extends ValueObject<infer V>\n ? UnwrapValueObject<V>\n : T extends (infer U)[]\n ? UnwrapValueObject<U>[]\n : T extends Map<infer K, infer V>\n ? Map<K, UnwrapValueObject<V>>\n : T extends Set<infer V>\n ? Set<UnwrapValueObject<V>>\n : T extends Date\n ? string\n : T extends object\n ? { [K in keyof T]: UnwrapValueObject<T[K]> }\n : T;\n\nexport function unwrapValueObject<T>(\n input: T,\n seen = new WeakSet(),\n): UnwrapValueObject<T> {\n if (input === null || input === undefined) {\n return input as UnwrapValueObject<T>;\n }\n\n if (typeof input !== 'object') {\n return input as UnwrapValueObject<T>;\n }\n\n if (seen.has(input)) {\n // Prevent circular reference infinite recursion, just return input or throw\n throw new Error('Circular reference detected in ValueObject unwrap');\n }\n\n seen.add(input);\n\n if (Array.isArray(input)) {\n const result = input.map(item => unwrapValueObject(item, seen));\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof ValueObject) {\n const result = unwrapValueObject(input.value, seen);\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof Date) {\n seen.delete(input);\n return input.toISOString() as UnwrapValueObject<T>;\n }\n\n if (input instanceof Map) {\n const result = new Map();\n input.forEach((value, key) => {\n result.set(key, unwrapValueObject(value, seen));\n });\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof Set) {\n const result = new Set(\n Array.from(input.values()).map(v => unwrapValueObject(v, seen)),\n );\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n // generic object\n const result: Record<string, unknown> = {};\n\n for (const key in input) {\n if (Object.hasOwn(input, key)) {\n result[key] = unwrapValueObject((input as any)[key], seen);\n }\n }\n\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n}\n\nexport function ensureObject<T>(input: UnwrapValueObject<T>): object {\n if (input === null || input === undefined) {\n return {};\n }\n if (typeof input === 'object') {\n return input;\n }\n\n // for primitives, wrap inside object with default key (or throw)\n return { value: input };\n}\n","export interface DomainErrorMetadata {\n cause?: { name: string; message: string; stack?: string };\n [key: string]: unknown;\n}\n\nconst getCauseInfo = (cause: Error | undefined) => {\n // 1. Handle Error objects specifically\n if (cause instanceof Error) {\n return {\n cause: {\n message: cause.message,\n stack: cause.stack,\n name: cause.name,\n },\n };\n }\n\n // 2. Handle other existing values\n if (cause) {\n return { cause };\n }\n\n // 3. Default to empty\n return {};\n};\n\n/**\n * Base class for all Domain Errors in the application.\n *\n * This class ensures:\n * 1. Serializable and structured for logs or API responses.\n * 2. Identifiable via stable error codes (not just class names).\n * 3. Contextual with optional structured metadata.\n * 4. Supports error chaining (cause) and stack trace preservation.\n *\n * @example\n * export class InsufficientFundsError extends DomainError {\n * constructor(accountId: string, currentBalance: number) {\n * super(\n * `Account ${accountId} has insufficient funds.`,\n * 'INSUFFICIENT_FUNDS',\n * { accountId, currentBalance }\n * );\n * }\n * }\n */\nexport abstract class DomainError extends Error {\n /** Stable, machine-readable error code */\n public readonly code: string;\n /** Structured, immutable domain metadata */\n public readonly metadata: Readonly<DomainErrorMetadata>;\n\n /**\n * @param message - Human-readable error message\n * @param code - Stable error code\n * @param metadata - Domain-specific structured data; optional `cause` can be included\n */\n protected constructor(\n message: string,\n code: string,\n metadata: DomainErrorMetadata = {},\n ) {\n super(message, { ...getCauseInfo(metadata.cause) });\n\n // Restore prototype chain for proper `instanceof` checks\n Object.setPrototypeOf(this, new.target.prototype);\n\n this.name = new.target.name;\n this.code = code;\n this.metadata = Object.freeze({ ...metadata });\n }\n}\n","import { DomainError } from '../base/domain.error';\n\n/**\n * Custom error class for entity validation failures.\n */\nexport class EntityValidationError extends DomainError {\n constructor(message: string, cause?: Error) {\n super(message, 'ENTITY_VALIDATION_ERROR', { cause });\n }\n}\n","import { v4 } from 'uuid';\nimport z from 'zod';\n\nimport { InvalidValueObjectError } from '../errors/invalid-vo.error';\nimport { PrimitiveValueObject } from '../base/primitive-vo';\n\n/**\n * Represents a UUID (Universally Unique Identifier) value object.\n *\n * This class extends PrimitiveValueObject to provide type-safe UUID handling\n * with validation using Zod schema. UUIDs are immutable and can be generated\n * randomly or created from string values.\n *\n * @example\n * // Generate a new random UUID\n * const id = UUID.generate();\n *\n * @example\n * // Create UUID from an existing string\n * const id = UUID.fromString('550e8400-e29b-41d4-a716-446655440000');\n *\n * @throws {InvalidValueObjectError} When the provided string is not a valid UUID format\n */\nexport class UUID extends PrimitiveValueObject<string> {\n private static readonly schema = z.uuid();\n\n public constructor(value: string) {\n super(value);\n this.validate(value);\n }\n\n /**\n * Generates a new AggregateId.\n */\n public static generate<T extends typeof UUID>(this: T): InstanceType<T> {\n return new this(v4()) as InstanceType<T>;\n }\n\n /**\n * Creates an UUID from an external string.\n * Use only for untrusted input.\n *\n * @param value - UUID string\n */\n public static fromString(value: string): UUID {\n return new this(value);\n }\n\n protected validate(value: string): void {\n const result = UUID.schema.safeParse(value);\n\n if (!result.success) {\n throw new InvalidValueObjectError(\n `Invalid UUID: ${result.error.message}`,\n );\n }\n }\n}\n","import { DomainError } from '../base/domain.error';\n\nexport class InvalidValueObjectError extends DomainError {\n constructor(message: string) {\n super(message, 'INVALID_VALUE_OBJECT');\n }\n}\n","/**\n * Base class for primitive-based Value Objects.\n *\n * This class is intended for Value Objects that are represented by\n * a single primitive value (string, number, or boolean).\n *\n * Characteristics:\n * - Immutable by construction\n * - Cheap equality comparison\n * - No deep cloning or freezing\n * - Safe for serialization and logging\n *\n * Examples:\n * - AggregateId\n * - EmailAddress\n * - Username\n * - Slug\n */\nexport abstract class PrimitiveValueObject<\n T extends boolean | number | string,\n> {\n /**\n * The underlying primitive value.\n * Guaranteed to be valid after construction.\n */\n protected readonly value: T;\n\n /**\n * Constructs a new PrimitiveValueObject.\n *\n * @param value - The primitive value to wrap\n * @throws Error if validation fails\n */\n protected constructor(value: T) {\n this.validate(value);\n this.value = value;\n }\n\n /**\n * Returns the primitive value.\n * Prefer explicit access over implicit coercion.\n */\n public getValue(): T {\n return this.value;\n }\n\n /**\n * Compares two Value Objects for equality.\n *\n * Equality rules:\n * - Same concrete class\n * - Same primitive value (===)\n *\n * @param other - Another Value Object\n */\n public equals(other?: PrimitiveValueObject<T>): boolean {\n if (other === undefined) return false;\n\n if (Object.getPrototypeOf(this) !== Object.getPrototypeOf(other)) {\n return false;\n }\n\n return this.value === other.value;\n }\n\n /**\n * JSON serialization hook.\n * Produces the raw primitive value.\n */\n public toJSON(): T {\n return this.value;\n }\n\n /**\n * String representation.\n * Useful for logging and debugging.\n */\n public toString(): string {\n return String(this.value);\n }\n\n /**\n * Domain invariant validation.\n * Must throw if the value is invalid.\n *\n * @param value - The value to validate\n */\n protected abstract validate(value: T): void;\n}\n","import { UUID } from './id.vo';\n\n/**\n * AggregateId represents a strongly-typed aggregate identifier.\n *\n * - Backed by UUID v4\n * - Immutable\n * - Comparable only to AggregateId\n */\nexport class AggregateId extends UUID {}\n","import { ensureObject, UnwrapValueObject, unwrapValueObject } from '@/utils';\n\nimport { EntityValidationError } from '../errors/entity-validation.error';\nimport { AggregateId } from '../value-objects/aggregate-id.vo';\n\nexport interface EntityBaseInterface {\n id: AggregateId;\n equals: (entity: unknown) => boolean;\n}\n\n/**\n * Base properties common to all entities, including ID and timestamps.\n */\nexport interface BaseEntityProps {\n /** Unique identifier for the entity */\n id?: AggregateId;\n /** Date when the entity was created */\n createdAt: Date;\n}\n\n/**\n * Interface for constructing an entity with optional timestamps.\n * @template Props - The specific props type for the entity\n */\nexport type CreateEntityProps<T> = BaseEntityProps & {\n props: T;\n};\n\n/**\n * Abstract base class for domain entities in a Domain-Driven Design (DDD) context.\n * Provides common functionality for entity identification, equality comparison,\n * immutability, and validation. Entities extending this class must implement\n * the `validate` method to enforce domain invariants.\n * @template EntityProps - The specific props type for the entity\n */\nexport abstract class Entity<EntityProps> {\n /**\n * Returns the creation timestamp.\n * A new Date instance is returned to preserve immutability.\n *\n * @returns {Date} The creation date of the entity.\n */\n get createdAt(): Date {\n return new Date(this.#createdAt);\n }\n\n /**\n * Gets the entity's unique identifier.\n * @returns The entity's ID\n */\n get id(): AggregateId {\n return this.#id;\n }\n\n public get metadata() {\n return Object.freeze({\n createdAt: this.#createdAt.toISOString(),\n id: this.#id,\n });\n }\n\n /**\n * Returns an immutable shallow copy of the entity's properties.\n *\n * @returns {Readonly<EntityProps>} The entity domain properties.\n */\n get props(): Readonly<EntityProps> {\n return this.#props;\n }\n\n /** Private creation timestamp */\n #createdAt: Date;\n /** Private unique identifier for the entity */\n #id: AggregateId;\n /** Private entity-specific properties */\n #props: Readonly<EntityProps>;\n\n /**\n * Constructs an entity with the provided properties and timestamps.\n * Ensures immutability by cloning props and validates the entity.\n * @param params - Entity creation parameters\n * @throws EntityValidationError if the ID is empty or validation fails\n */\n protected constructor({\n createdAt,\n props,\n id,\n }: CreateEntityProps<EntityProps>) {\n if (!id) {\n throw new EntityValidationError('Entity ID cannot be empty');\n }\n\n this.#id = id ?? AggregateId.generate();\n this.#props = Object.freeze(props);\n const now = new Date();\n this.#createdAt = createdAt ? new Date(createdAt) : now;\n }\n\n /**\n * Checks if the provided value is an instance of Entity.\n * @param entity - The value to check\n * @returns True if the value is an Entity instance\n */\n static isEntity(entity: unknown): entity is EntityBaseInterface {\n return (\n typeof entity === 'object' &&\n entity !== null &&\n 'id' in entity &&\n typeof (entity as any).equals === 'function'\n );\n }\n\n /**\n * Compares this entity with another to determine if they are the same.\n * Equality is based on the entity ID.\n * @param other - The entity to compare with\n * @returns True if the entities have the same ID\n */\n public equals(other?: Entity<EntityProps>): boolean {\n if (!other || !Entity.isEntity(other)) {\n return false;\n }\n\n return this.#id.equals(other.#id);\n }\n\n /**\n * Returns a frozen copy of the entity's properties, including base properties.\n * Ensures immutability by returning a new object.\n * @returns A frozen copy of the entity's properties\n */\n public getPropsCopy(): Readonly<EntityProps> {\n return Object.freeze(this.#props);\n }\n\n /**\n * Determines if the entity is transient, i.e., it has not been persisted yet.\n * By convention, an entity is considered transient if it lacks a valid identifier.\n * This can be useful when performing logic that depends on persistence state,\n * such as conditional inserts or validations that only apply to new entities.\n *\n * @returns True if the entity is transient (not persisted), otherwise false.\n */\n // public isPersisted(): boolean {\n // return this.#id.isEmpty();\n // }\n\n public toJSON(): Record<string, unknown> {\n return this.toObject();\n }\n\n public toObject(): Readonly<\n UnwrapValueObject<EntityProps> & { createdAt: string; id: string }\n > {\n const props = unwrapValueObject(this.getPropsCopy());\n const safeProps = ensureObject(props);\n return Object.freeze({\n ...this.metadata,\n ...safeProps,\n }) as Readonly<\n UnwrapValueObject<EntityProps> & { createdAt: string; id: string }\n >;\n }\n\n /**\n * Validates the entity's state to enforce domain invariants.\n * Must be implemented by subclasses to define specific validation rules.\n * @throws EntityValidationError if validation fails\n */\n public abstract validate(): void;\n}\n","import { AggregateId } from '../value-objects/aggregate-id.vo';\nimport { Entity } from '../entities/entity';\nimport { DomainEvent } from '../events';\n\n/**\n * Interface for AggregateRoot to ensure type safety and extensibility.\n */\nexport interface AggregateRootInterface {\n readonly id: AggregateId;\n readonly domainEvents: readonly DomainEvent[];\n /**\n * Validates the aggregate's invariants.\n * @throws {EntityValidationError} If validation fails.\n */\n validate: () => void;\n /**\n * Adds a domain event to the aggregate.\n * @param event The domain event to add.\n */\n addEvent: (event: DomainEvent) => void;\n\n /**\n * Retrieves and clears all domain events recorded by this aggregate.\n *\n * Domain events represent facts that occurred as a result of state changes\n * within the aggregate. This method transfers ownership of those events\n * to the application layer for further processing (e.g. publishing).\n *\n * Calling this method has the side effect of clearing the aggregate's\n * internal event collection to prevent duplicate handling.\n *\n * This method is intended to be invoked by application services\n * after the aggregate has been successfully persisted.\n *\n * @returns A read-only list of domain events raised by this aggregate.\n */\n pullDomainEvents: () => readonly DomainEvent[];\n}\n\n/**\n * Base class for aggregate roots in DDD, encapsulating domain events and validation.\n * @template EntityProps The type of the entity's properties.\n */\nexport abstract class AggregateRoot<EntityProps>\n extends Entity<EntityProps>\n implements AggregateRootInterface\n{\n /**\n * Gets a read-only copy of the domain events.\n */\n get domainEvents(): readonly DomainEvent[] {\n return [...this._domainEvents];\n }\n\n /**\n * Internal list of domain events.\n */\n private readonly _domainEvents: DomainEvent[] = [];\n\n /**\n * Adds a domain event to the aggregate after validating invariants.\n * @param domainEvent The domain event to add.\n * @throws {EntityValidationError} If invariants are not met.\n */\n addEvent(domainEvent: DomainEvent): void {\n this.validate(); // Ensure invariants before adding events\n this._domainEvents.push(domainEvent);\n }\n\n public pullDomainEvents(): readonly DomainEvent[] {\n const events = [...this._domainEvents];\n this._domainEvents.length = 0;\n return events;\n }\n\n /**\n * Validates the entity's invariants.\n * @throws {EntityValidationError} If validation fails.\n */\n abstract validate(): void;\n}\n","type Primitive = boolean | number | string | null;\n\ntype Serializable =\n | Primitive\n | Serializable[]\n | { [key: string]: Serializable };\n\nexport type DomainEventPayload = Record<string, Serializable>;\n\ntype DomainEventProps<T> = {\n id: string;\n aggregateId: string;\n schemaVersion: number;\n occurredAt: number;\n payload: T;\n};\n\n// Abstract base class for domain events\nexport abstract class DomainEvent<\n T extends DomainEventPayload = DomainEventPayload,\n> {\n public abstract readonly eventName: string;\n\n public readonly id: string;\n public readonly aggregateId: string;\n public readonly schemaVersion: number;\n public readonly occurredAt: number;\n public readonly payload: Readonly<T>;\n\n protected constructor(props: DomainEventProps<T>) {\n this.id = props.id;\n this.aggregateId = props.aggregateId;\n this.schemaVersion = props.schemaVersion;\n this.occurredAt = props.occurredAt;\n this.payload = Object.freeze(props.payload);\n }\n\n public toPrimitives() {\n return {\n schemaVersion: this.schemaVersion,\n aggregateId: this.aggregateId,\n occurredAt: this.occurredAt,\n eventName: this.eventName,\n payload: this.payload,\n id: this.id,\n };\n }\n}\n"],"mappings":"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","names":["index_exports","__export","AggregateId","AggregateRoot","ApplicationError","DomainError","DomainEvent","Entity","EntityValidationError","HttpStatus","HttpStatusMessage","InvalidValueObjectError","PrimitiveValueObject","ValueObject","deepFreeze","ensureObject","unwrapValueObject","__toCommonJS","deepFreeze","obj","seen","item","key","HttpStatus","HttpStatusMessage","ApplicationError","code","HttpStatusMessage","isOperational","status","metadata","message","cause","import_es6","ValueObject","_ValueObject","props","deepFreeze","vo","other","deepEqual","unwrapValueObject","input","seen","result","item","ValueObject","value","key","v","ensureObject","getCauseInfo","cause","DomainError","message","code","metadata","__spreadValues","EntityValidationError","DomainError","message","cause","import_uuid","import_zod","InvalidValueObjectError","DomainError","message","PrimitiveValueObject","value","other","_UUID","PrimitiveValueObject","value","result","InvalidValueObjectError","z","UUID","AggregateId","UUID","_createdAt","_id","_props","_Entity","createdAt","props","id","__privateAdd","EntityValidationError","__privateSet","AggregateId","__privateGet","entity","other","unwrapValueObject","safeProps","ensureObject","__spreadValues","Entity","AggregateRoot","Entity","domainEvent","events","DomainEvent","props"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var C=Object.defineProperty;var D=Object.getOwnPropertySymbols;var L=Object.prototype.hasOwnProperty,M=Object.prototype.propertyIsEnumerable;var S=t=>{throw TypeError(t)};var h=(t,e,r)=>e in t?C(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,c=(t,e)=>{for(var r in e||(e={}))L.call(e,r)&&h(t,r,e[r]);if(D)for(var r of D(e))M.call(e,r)&&h(t,r,e[r]);return t};var _=(t,e,r)=>e.has(t)||S("Cannot "+r);var n=(t,e,r)=>(_(t,e,"read from private field"),r?r.call(t):e.get(t)),p=(t,e,r)=>e.has(t)?S("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r),E=(t,e,r,a)=>(_(t,e,"write to private field"),a?a.call(t,r):e.set(t,r),r);function T(t,e=new WeakSet){if(t==null||typeof t!="object"&&!Array.isArray(t)||Object.isFrozen(t)||e.has(t))return t;if(e.add(t),Array.isArray(t))t.forEach(r=>T(r,e));else for(let r in t)Object.hasOwn(t,r)&&T(t[r],e);return Object.freeze(t)}var B=Object.freeze({REQUEST_HEADER_FIELDS_TOO_LARGE:431,NETWORK_AUTHENTICATION_REQUIRED:511,NON_AUTHORITATIVE_INFORMATION:203,PROXY_AUTHENTICATION_REQUIRED:407,UNAVAILABLE_FOR_LEGAL_REASONS:451,HTTP_VERSION_NOT_SUPPORTED:505,BANDWIDTH_LIMIT_EXCEEDED:509,VARIANT_ALSO_NEGOTIATES:506,UNSUPPORTED_MEDIA_TYPE:415,RANGE_NOT_SATISFIABLE:416,PRECONDITION_REQUIRED:428,INTERNAL_SERVER_ERROR:500,UNPROCESSABLE_ENTITY:422,INSUFFICIENT_STORAGE:507,SWITCHING_PROTOCOLS:101,PRECONDITION_FAILED:412,MISDIRECTED_REQUEST:421,SERVICE_UNAVAILABLE:503,TEMPORARY_REDIRECT:307,PERMANENT_REDIRECT:308,METHOD_NOT_ALLOWED:405,EXPECTATION_FAILED:417,MOVED_PERMANENTLY:301,PAYLOAD_TOO_LARGE:413,FAILED_DEPENDENCY:424,TOO_MANY_REQUESTS:429,ALREADY_REPORTED:208,MULTIPLE_CHOICES:300,PAYMENT_REQUIRED:402,UPGRADE_REQUIRED:426,PARTIAL_CONTENT:206,REQUEST_TIMEOUT:408,LENGTH_REQUIRED:411,NOT_IMPLEMENTED:501,GATEWAY_TIMEOUT:504,NOT_ACCEPTABLE:406,RESET_CONTENT:205,LOOP_DETECTED:508,MULTI_STATUS:207,NOT_MODIFIED:304,UNAUTHORIZED:401,URI_TOO_LONG:414,NOT_EXTENDED:510,EARLY_HINTS:103,BAD_REQUEST:400,IM_A_TEAPOT:418,BAD_GATEWAY:502,PROCESSING:102,NO_CONTENT:204,SEE_OTHER:303,USE_PROXY:305,FORBIDDEN:403,NOT_FOUND:404,TOO_EARLY:425,CONTINUE:100,ACCEPTED:202,CONFLICT:409,CREATED:201,IM_USED:226,LOCKED:423,FOUND:302,GONE:410,OK:200}),P={431:"Request Header Fields Too Large",511:"Network Authentication Required",203:"Non-Authoritative Information",407:"Proxy Authentication Required",451:"Unavailable For Legal Reasons",505:"HTTP Version Not Supported",509:"Bandwidth Limit Exceeded",506:"Variant Also Negotiates",415:"Unsupported Media Type",416:"Range Not Satisfiable",428:"Precondition Required",500:"Internal Server Error",422:"Unprocessable Entity",507:"Insufficient Storage",101:"Switching Protocols",412:"Precondition Failed",421:"Misdirected Request",503:"Service Unavailable",307:"Temporary Redirect",308:"Permanent Redirect",405:"Method Not Allowed",417:"Expectation Failed",301:"Moved Permanently",413:"Payload Too Large",424:"Failed Dependency",429:"Too Many Requests",208:"Already Reported",300:"Multiple Choices",402:"Payment Required",426:"Upgrade Required",206:"Partial Content",408:"Request Timeout",411:"Length Required",501:"Not Implemented",504:"Gateway Timeout",406:"Not Acceptable",205:"Reset Content",508:"Loop Detected",207:"Multi-Status",304:"Not Modified",401:"Unauthorized",414:"URI Too Long",418:"I'm a Teapot",510:"Not Extended",103:"Early Hints",400:"Bad Request",502:"Bad Gateway",102:"Processing",204:"No Content",303:"See Other",305:"Use Proxy",403:"Forbidden",404:"Not Found",425:"Too Early",100:"Continue",202:"Accepted",226:"I'm Used",409:"Conflict",201:"Created",423:"Locked",302:"Found",410:"Gone",200:"OK"};var v=class extends Error{constructor({code:e=P[500],isOperational:r=!1,status:a=500,metadata:o,message:A,cause:V}){super(A),this.name=new.target.name,this.code=e,this.status=a,this.isOperational=r,this.metadata=o,this.cause=V,Error.captureStackTrace(this,new.target)}};import j from"fast-deep-equal/es6";var O=class t{get value(){return this.props}constructor(e){this.validate(e),this.props=T(e)}toJSON(){return this.props}toString(){return JSON.stringify(this.props)}static is(e){return e instanceof t}equals(e){return e==null||Object.getPrototypeOf(this)!==Object.getPrototypeOf(e)?!1:j(this.props,e.props)}};function i(t,e=new WeakSet){if(t==null||typeof t!="object")return t;if(e.has(t))throw new Error("Circular reference detected in ValueObject unwrap");if(e.add(t),Array.isArray(t)){let a=t.map(o=>i(o,e));return e.delete(t),a}if(t instanceof O){let a=i(t.value,e);return e.delete(t),a}if(t instanceof Date)return e.delete(t),t.toISOString();if(t instanceof Map){let a=new Map;return t.forEach((o,A)=>{a.set(A,i(o,e))}),e.delete(t),a}if(t instanceof Set){let a=new Set(Array.from(t.values()).map(o=>i(o,e)));return e.delete(t),a}let r={};for(let a in t)Object.hasOwn(t,a)&&(r[a]=i(t[a],e));return e.delete(t),r}function U(t){return t==null?{}:typeof t=="object"?t:{value:t}}var H=t=>t instanceof Error?{cause:{message:t.message,stack:t.stack,name:t.name}}:t?{cause:t}:{},d=class extends Error{constructor(e,r,a={}){super(e,c({},H(a.cause))),Object.setPrototypeOf(this,new.target.prototype),this.name=new.target.name,this.code=r,this.metadata=Object.freeze(c({},a))}};var f=class extends d{constructor(e,r){super(e,"ENTITY_VALIDATION_ERROR",{cause:r})}};import{v4 as k}from"uuid";import F from"zod";var m=class extends d{constructor(e){super(e,"INVALID_VALUE_OBJECT")}};var g=class{constructor(e){this.validate(e),this.value=e}getValue(){return this.value}equals(e){return e===void 0||Object.getPrototypeOf(this)!==Object.getPrototypeOf(e)?!1:this.value===e.value}toJSON(){return this.value}toString(){return String(this.value)}};var b=class b extends g{constructor(e){super(e),this.validate(e)}static generate(){return new this(k())}static fromString(e){return new this(e)}validate(e){let r=b.schema.safeParse(e);if(!r.success)throw new m(`Invalid UUID: ${r.error.message}`)}};b.schema=F.uuid();var y=b;var R=class extends y{};var l,s,u,N=class N{constructor({createdAt:e,props:r,id:a}){p(this,l);p(this,s);p(this,u);if(!a)throw new f("Entity ID cannot be empty");E(this,s,a!=null?a:R.generate()),E(this,u,Object.freeze(r)),E(this,l,e?new Date(e):new Date)}get createdAt(){return new Date(n(this,l))}get id(){return n(this,s)}get metadata(){return Object.freeze({createdAt:n(this,l).toISOString(),id:n(this,s)})}get props(){return n(this,u)}static isEntity(e){return typeof e=="object"&&e!==null&&"id"in e&&typeof e.equals=="function"}equals(e){return!e||!N.isEntity(e)?!1:n(this,s).equals(n(e,s))}getPropsCopy(){return Object.freeze(n(this,u))}toJSON(){return this.toObject()}toObject(){let e=i(this.getPropsCopy()),r=U(e);return Object.freeze(c(c({},this.metadata),r))}};l=new WeakMap,s=new WeakMap,u=new WeakMap;var I=N;var w=class extends I{constructor(){super(...arguments);this._domainEvents=[]}get domainEvents(){return[...this._domainEvents]}addEvent(r){this.validate(),this._domainEvents.push(r)}pullDomainEvents(){let r=[...this._domainEvents];return this._domainEvents.length=0,r}};var x=class{constructor(e){this.id=e.id,this.aggregateId=e.aggregateId,this.schemaVersion=e.schemaVersion,this.occurredAt=e.occurredAt,this.payload=Object.freeze(e.payload)}toPrimitives(){return{schemaVersion:this.schemaVersion,aggregateId:this.aggregateId,occurredAt:this.occurredAt,eventName:this.eventName,payload:this.payload,id:this.id}}};export{R as AggregateId,w as AggregateRoot,v as ApplicationError,d as DomainError,x as DomainEvent,I as Entity,f as EntityValidationError,B as HttpStatus,P as HttpStatusMessage,m as InvalidValueObjectError,g as PrimitiveValueObject,O as ValueObject,T as deepFreeze,U as ensureObject,i as unwrapValueObject};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/deep-freeze.util.ts","../src/gateway/constants/http-code.ts","../src/utils/errors/application.error.ts","../src/domain/base/vo.ts","../src/utils/unwrap-vo.util.ts","../src/domain/base/domain.error.ts","../src/domain/errors/entity-validation.error.ts","../src/domain/value-objects/aggregate-id.vo.ts","../src/domain/errors/invalid-vo.error.ts","../src/domain/entities/entity.ts","../src/domain/aggregates/aggregate-root.ts","../src/domain/events/domain.event.ts"],"sourcesContent":["/**\n * Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.\n *\n * @param obj - The object to be deeply frozen.\n * @param seen - A WeakSet to track already processed objects (for circular references).\n * @returns A deeply frozen version of the input object.\n */\nexport function deepFreeze<T>(\n obj: T,\n seen = new WeakSet<object>(),\n): Readonly<T> {\n // Handle null, undefined, or non-object types\n if (obj == null || (typeof obj !== 'object' && !Array.isArray(obj))) {\n return obj;\n }\n\n // Skip if already frozen\n if (Object.isFrozen(obj)) {\n return obj as Readonly<T>;\n }\n\n // Handle circular references\n if (seen.has(obj as object)) {\n return obj as Readonly<T>;\n }\n\n seen.add(obj as object);\n\n // Handle arrays explicitly\n if (Array.isArray(obj)) {\n obj.forEach(item => deepFreeze(item, seen));\n } else {\n // Handle plain objects\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n deepFreeze((obj as Record<string, unknown>)[key], seen);\n }\n }\n }\n\n return Object.freeze(obj) as Readonly<T>;\n}\n","/**\n * HTTP status code catalog.\n *\n * This object provides a typed, immutable map of standard HTTP status names\n * to their numeric codes. Designed for server-side frameworks such as Fastify.\n *\n * - All identifiers use clear, canonical semantic names.\n * - Values are numeric status codes.\n * - Frozen to prevent runtime mutation.\n * - Exporting `HttpStatus` ensures type-safe usage across the codebase.\n * Usage:\n * ```ts\n * import { HttpStatus } from 'path-to-this-file';\n *\n * function handleRequest() {\n * return {\n * statusCode: HttpStatus.OK,\n * body: 'Success',\n * };\n * }\n * ```\n */\nexport const HttpStatus = Object.freeze({\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n NON_AUTHORITATIVE_INFORMATION: 203,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n\n UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n BANDWIDTH_LIMIT_EXCEEDED: 509,\n VARIANT_ALSO_NEGOTIATES: 506,\n UNSUPPORTED_MEDIA_TYPE: 415,\n RANGE_NOT_SATISFIABLE: 416,\n PRECONDITION_REQUIRED: 428,\n INTERNAL_SERVER_ERROR: 500,\n UNPROCESSABLE_ENTITY: 422,\n INSUFFICIENT_STORAGE: 507,\n\n SWITCHING_PROTOCOLS: 101,\n PRECONDITION_FAILED: 412,\n MISDIRECTED_REQUEST: 421,\n SERVICE_UNAVAILABLE: 503,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n METHOD_NOT_ALLOWED: 405,\n EXPECTATION_FAILED: 417,\n\n MOVED_PERMANENTLY: 301,\n PAYLOAD_TOO_LARGE: 413,\n FAILED_DEPENDENCY: 424,\n TOO_MANY_REQUESTS: 429,\n ALREADY_REPORTED: 208,\n MULTIPLE_CHOICES: 300,\n PAYMENT_REQUIRED: 402,\n UPGRADE_REQUIRED: 426,\n PARTIAL_CONTENT: 206,\n REQUEST_TIMEOUT: 408,\n LENGTH_REQUIRED: 411,\n NOT_IMPLEMENTED: 501,\n GATEWAY_TIMEOUT: 504,\n NOT_ACCEPTABLE: 406,\n RESET_CONTENT: 205,\n LOOP_DETECTED: 508,\n MULTI_STATUS: 207,\n NOT_MODIFIED: 304,\n UNAUTHORIZED: 401,\n URI_TOO_LONG: 414,\n NOT_EXTENDED: 510,\n EARLY_HINTS: 103,\n BAD_REQUEST: 400,\n IM_A_TEAPOT: 418,\n BAD_GATEWAY: 502,\n PROCESSING: 102,\n NO_CONTENT: 204,\n SEE_OTHER: 303,\n USE_PROXY: 305,\n\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n TOO_EARLY: 425,\n CONTINUE: 100,\n ACCEPTED: 202,\n CONFLICT: 409,\n CREATED: 201,\n IM_USED: 226,\n LOCKED: 423,\n FOUND: 302,\n GONE: 410,\n OK: 200,\n} as const);\n\n/**\n * HTTP status messages mapped by numeric code.\n * Use for sending descriptive text in responses or logging.\n */\nexport const HttpStatusMessage = {\n 431: 'Request Header Fields Too Large',\n 511: 'Network Authentication Required',\n 203: 'Non-Authoritative Information',\n 407: 'Proxy Authentication Required',\n 451: 'Unavailable For Legal Reasons',\n 505: 'HTTP Version Not Supported',\n 509: 'Bandwidth Limit Exceeded',\n 506: 'Variant Also Negotiates',\n 415: 'Unsupported Media Type',\n 416: 'Range Not Satisfiable',\n 428: 'Precondition Required',\n 500: 'Internal Server Error',\n 422: 'Unprocessable Entity',\n 507: 'Insufficient Storage',\n 101: 'Switching Protocols',\n 412: 'Precondition Failed',\n 421: 'Misdirected Request',\n 503: 'Service Unavailable',\n 307: 'Temporary Redirect',\n 308: 'Permanent Redirect',\n 405: 'Method Not Allowed',\n 417: 'Expectation Failed',\n 301: 'Moved Permanently',\n 413: 'Payload Too Large',\n 424: 'Failed Dependency',\n 429: 'Too Many Requests',\n 208: 'Already Reported',\n 300: 'Multiple Choices',\n 402: 'Payment Required',\n 426: 'Upgrade Required',\n 206: 'Partial Content',\n 408: 'Request Timeout',\n 411: 'Length Required',\n 501: 'Not Implemented',\n 504: 'Gateway Timeout',\n 406: 'Not Acceptable',\n 205: 'Reset Content',\n 508: 'Loop Detected',\n 207: 'Multi-Status',\n 304: 'Not Modified',\n 401: 'Unauthorized',\n 414: 'URI Too Long',\n 418: \"I'm a Teapot\",\n 510: 'Not Extended',\n 103: 'Early Hints',\n 400: 'Bad Request',\n 502: 'Bad Gateway',\n 102: 'Processing',\n 204: 'No Content',\n 303: 'See Other',\n 305: 'Use Proxy',\n 403: 'Forbidden',\n 404: 'Not Found',\n 425: 'Too Early',\n 100: 'Continue',\n 202: 'Accepted',\n 226: \"I'm Used\",\n 409: 'Conflict',\n 201: 'Created',\n 423: 'Locked',\n 302: 'Found',\n 410: 'Gone',\n 200: 'OK',\n} as const;\n\nexport type HttpStatusCode = keyof typeof HttpStatusMessage;\n\nexport type HttpStatusMessage =\n (typeof HttpStatusMessage)[keyof typeof HttpStatusMessage];\n","import {\n HttpStatusCode,\n HttpStatusMessage,\n} from '@/gateway/constants/http-code';\n\ninterface ErrorParams {\n message: string;\n code: HttpStatusMessage;\n status?: HttpStatusCode;\n metadata?: Record<string, unknown> | undefined;\n isOperational?: boolean;\n cause?: Error | undefined;\n}\n\n/**\n * Abstract base class for application-level errors with structured error handling.\n *\n * Extends the native Error class to provide machine-readable error codes, HTTP status codes,\n * operational error classification, and optional metadata for better error tracking and client communication.\n *\n * @abstract\n * @extends {Error}\n *\n * @example\n * ```typescript\n * class UserNotFoundError extends ApplicationError {\n * constructor(userId: string) {\n * super({\n * code: HttpStatusMessage['404'],\n * status: 404,\n * isOperational: true,\n * message: `User with id ${userId} not found`,\n * metadata: { userId }\n * });\n * }\n * }\n * ```\n */\nexport abstract class ApplicationError extends Error {\n /** Optional cause (linked error) */\n public readonly cause?: Error | undefined;\n /** Machine-readable error code (e.g. `OTP_LOCKED`, `USER_NOT_FOUND`) */\n public readonly code: HttpStatusMessage;\n /** Operational vs programmer error flag */\n public readonly isOperational: boolean;\n /** Optional structured metadata for debugging or clients */\n public readonly metadata?: Record<string, unknown> | undefined;\n /** HTTP status code intended for response layer */\n public readonly status: HttpStatusCode;\n\n constructor({\n code = HttpStatusMessage['500'],\n isOperational = false,\n status = 500,\n metadata,\n message,\n cause,\n }: ErrorParams) {\n super(message);\n\n this.name = new.target.name;\n this.code = code;\n this.status = status;\n this.isOperational = isOperational;\n this.metadata = metadata;\n this.cause = cause;\n\n Error.captureStackTrace(this, new.target);\n }\n}\n","import deepEqual from 'fast-deep-equal/es6';\n\nimport { deepFreeze } from '@/utils/deep-freeze.util';\n\nexport abstract class ValueObject<T> {\n get value(): T {\n return this.props;\n }\n\n protected readonly props: Readonly<T>;\n\n protected constructor(props: T) {\n this.validate(props);\n this.props = deepFreeze(props);\n }\n\n /**\n * Standard for clean API integration and logging.\n */\n public toJSON(): T {\n return this.props;\n }\n\n /**\n * Useful for debugging and string-based indexing.\n */\n public toString(): string {\n return JSON.stringify(this.props);\n }\n\n /**\n * Type guard to check if an unknown object is an instance of ValueObject.\n * This is useful for runtime type checking.\n *\n * @param vo The object to check.\n * @returns True if the object is a ValueObject instance, false otherwise.\n */\n public static is(vo: unknown): vo is ValueObject<unknown> {\n return vo instanceof ValueObject;\n }\n\n /**\n * Deep equality comparison of ValueObjects\n */\n public equals(other?: ValueObject<T>): boolean {\n if (other === null || other === undefined) return false;\n\n // Check if they share the same constructor (Type check)\n if (Object.getPrototypeOf(this) !== Object.getPrototypeOf(other)) {\n return false;\n }\n\n return deepEqual(this.props, other.props);\n }\n\n /**\n * Validates the value object props\n * @throws InvalidValueObjectError if validation fails\n */\n protected abstract validate(props: T): void;\n}\n","import { ValueObject } from '@/domain/base/vo';\n\nexport type UnwrapValueObject<T> =\n T extends ValueObject<infer V>\n ? UnwrapValueObject<V>\n : T extends (infer U)[]\n ? UnwrapValueObject<U>[]\n : T extends Map<infer K, infer V>\n ? Map<K, UnwrapValueObject<V>>\n : T extends Set<infer V>\n ? Set<UnwrapValueObject<V>>\n : T extends Date\n ? string\n : T extends object\n ? { [K in keyof T]: UnwrapValueObject<T[K]> }\n : T;\n\nexport function unwrapValueObject<T>(\n input: T,\n seen = new WeakSet(),\n): UnwrapValueObject<T> {\n if (input === null || input === undefined) {\n return input as UnwrapValueObject<T>;\n }\n\n if (typeof input !== 'object') {\n return input as UnwrapValueObject<T>;\n }\n\n if (seen.has(input)) {\n // Prevent circular reference infinite recursion, just return input or throw\n throw new Error('Circular reference detected in ValueObject unwrap');\n }\n\n seen.add(input);\n\n if (Array.isArray(input)) {\n const result = input.map(item => unwrapValueObject(item, seen));\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof ValueObject) {\n const result = unwrapValueObject(input.value, seen);\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof Date) {\n seen.delete(input);\n return input.toISOString() as UnwrapValueObject<T>;\n }\n\n if (input instanceof Map) {\n const result = new Map();\n input.forEach((value, key) => {\n result.set(key, unwrapValueObject(value, seen));\n });\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof Set) {\n const result = new Set(\n Array.from(input.values()).map(v => unwrapValueObject(v, seen)),\n );\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n // generic object\n const result: Record<string, unknown> = {};\n\n for (const key in input) {\n if (Object.hasOwn(input, key)) {\n result[key] = unwrapValueObject((input as any)[key], seen);\n }\n }\n\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n}\n\nexport function ensureObject<T>(input: UnwrapValueObject<T>): object {\n if (input === null || input === undefined) {\n return {};\n }\n if (typeof input === 'object') {\n return input;\n }\n\n // for primitives, wrap inside object with default key (or throw)\n return { value: input };\n}\n","export interface DomainErrorMetadata {\n cause?: { name: string; message: string; stack?: string };\n [key: string]: unknown;\n}\n\nconst getCauseInfo = (cause: Error | undefined) => {\n // 1. Handle Error objects specifically\n if (cause instanceof Error) {\n return {\n cause: {\n message: cause.message,\n stack: cause.stack,\n name: cause.name,\n },\n };\n }\n\n // 2. Handle other existing values\n if (cause) {\n return { cause };\n }\n\n // 3. Default to empty\n return {};\n};\n\n/**\n * Base class for all Domain Errors in the application.\n *\n * This class ensures:\n * 1. Serializable and structured for logs or API responses.\n * 2. Identifiable via stable error codes (not just class names).\n * 3. Contextual with optional structured metadata.\n * 4. Supports error chaining (cause) and stack trace preservation.\n *\n * @example\n * export class InsufficientFundsError extends DomainError {\n * constructor(accountId: string, currentBalance: number) {\n * super(\n * `Account ${accountId} has insufficient funds.`,\n * 'INSUFFICIENT_FUNDS',\n * { accountId, currentBalance }\n * );\n * }\n * }\n */\nexport abstract class DomainError extends Error {\n /** Stable, machine-readable error code */\n public readonly code: string;\n /** Structured, immutable domain metadata */\n public readonly metadata: Readonly<DomainErrorMetadata>;\n\n /**\n * @param message - Human-readable error message\n * @param code - Stable error code\n * @param metadata - Domain-specific structured data; optional `cause` can be included\n */\n protected constructor(\n message: string,\n code: string,\n metadata: DomainErrorMetadata = {},\n ) {\n super(message, { ...getCauseInfo(metadata.cause) });\n\n // Restore prototype chain for proper `instanceof` checks\n Object.setPrototypeOf(this, new.target.prototype);\n\n this.name = new.target.name;\n this.code = code;\n this.metadata = Object.freeze({ ...metadata });\n }\n}\n","import { DomainError } from '../base/domain.error';\n\n/**\n * Custom error class for entity validation failures.\n */\nexport class EntityValidationError extends DomainError {\n constructor(message: string, cause?: Error) {\n super(message, 'ENTITY_VALIDATION_ERROR', { cause });\n }\n}\n","import { v4 } from 'uuid';\nimport z from 'zod';\n\nimport { InvalidValueObjectError } from '../errors/invalid-vo.error';\nimport { ValueObject } from '../base/vo';\n\n/**\n * AggregateId is a ValueObject that represents a unique identifier for an aggregate.\n */\nexport class AggregateId extends ValueObject<string> {\n /**\n * The schema for the AggregateId\n */\n private static readonly schema = z.uuid();\n\n /**\n * Get the UUID of the AggregateId\n * @returns The UUID of the AggregateId\n */\n public get uuid(): string {\n return this.value;\n }\n\n /**\n * Create a new AggregateId\n * @param value The value to create the AggregateId from\n * @returns The new AggregateId\n */\n public static create(value: string): AggregateId {\n return new AggregateId(value);\n }\n\n /**\n * Create a new AggregateId with a random UUID v4\n */\n public static generate(): AggregateId {\n return new AggregateId(v4());\n }\n\n /**\n * Check if the AggregateId is empty\n * @returns True if the AggregateId is empty, false otherwise\n */\n public isEmpty(): boolean {\n return !this.value;\n }\n\n /**\n * Convert the AggregateId to a string\n * @returns The string representation of the AggregateId\n */\n public toString(): string {\n return this.value;\n }\n\n /**\n * Validate the AggregateId\n * @param value The value to validate\n * @throws InvalidValueObjectException if the value is invalid\n */\n protected validate(value: string): void {\n const result = AggregateId.schema.safeParse(value);\n\n if (!result.success) {\n throw new InvalidValueObjectError(\n `Invalid AggregateId: ${result.error.message}`,\n );\n }\n }\n}\n","import { DomainError } from '../base/domain.error';\n\nexport class InvalidValueObjectError extends DomainError {\n constructor(message: string) {\n super(message, 'INVALID_VALUE_OBJECT');\n }\n}\n","import { ensureObject, UnwrapValueObject, unwrapValueObject } from '@/utils';\n\nimport { EntityValidationError } from '../errors/entity-validation.error';\nimport { AggregateId } from '../value-objects/aggregate-id.vo';\n\nexport interface EntityBaseInterface {\n id: AggregateId;\n equals: (entity: unknown) => boolean;\n}\n\n/**\n * Base properties common to all entities, including ID and timestamps.\n */\nexport interface BaseEntityProps {\n /** Unique identifier for the entity */\n id?: AggregateId;\n /** Date when the entity was created */\n createdAt: Date;\n}\n\n/**\n * Interface for constructing an entity with optional timestamps.\n * @template Props - The specific props type for the entity\n */\nexport type CreateEntityProps<T> = BaseEntityProps & {\n props: T;\n};\n\n/**\n * Abstract base class for domain entities in a Domain-Driven Design (DDD) context.\n * Provides common functionality for entity identification, equality comparison,\n * immutability, and validation. Entities extending this class must implement\n * the `validate` method to enforce domain invariants.\n * @template EntityProps - The specific props type for the entity\n */\nexport abstract class Entity<EntityProps> {\n /**\n * Returns the creation timestamp.\n * A new Date instance is returned to preserve immutability.\n *\n * @returns {Date} The creation date of the entity.\n */\n get createdAt(): Date {\n return new Date(this.#createdAt);\n }\n\n /**\n * Gets the entity's unique identifier.\n * @returns The entity's ID\n */\n get id(): AggregateId {\n return this.#id;\n }\n\n public get metadata() {\n return Object.freeze({\n createdAt: this.#createdAt.toISOString(),\n id: this.#id.uuid,\n });\n }\n\n /**\n * Returns an immutable shallow copy of the entity's properties.\n *\n * @returns {Readonly<EntityProps>} The entity domain properties.\n */\n get props(): Readonly<EntityProps> {\n return this.#props;\n }\n\n /** Private creation timestamp */\n #createdAt: Date;\n /** Private unique identifier for the entity */\n #id: AggregateId;\n /** Private entity-specific properties */\n #props: Readonly<EntityProps>;\n\n /**\n * Constructs an entity with the provided properties and timestamps.\n * Ensures immutability by cloning props and validates the entity.\n * @param params - Entity creation parameters\n * @throws EntityValidationError if the ID is empty or validation fails\n */\n protected constructor({\n createdAt,\n props,\n id,\n }: CreateEntityProps<EntityProps>) {\n if (!id) {\n throw new EntityValidationError('Entity ID cannot be empty');\n }\n\n this.#id = id ?? AggregateId.generate();\n this.#props = Object.freeze(props);\n const now = new Date();\n this.#createdAt = createdAt ? new Date(createdAt) : now;\n }\n\n /**\n * Checks if the provided value is an instance of Entity.\n * @param entity - The value to check\n * @returns True if the value is an Entity instance\n */\n static isEntity(entity: unknown): entity is EntityBaseInterface {\n return (\n typeof entity === 'object' &&\n entity !== null &&\n 'id' in entity &&\n typeof (entity as any).equals === 'function'\n );\n }\n\n /**\n * Compares this entity with another to determine if they are the same.\n * Equality is based on the entity ID.\n * @param other - The entity to compare with\n * @returns True if the entities have the same ID\n */\n public equals(other?: Entity<EntityProps>): boolean {\n if (!other || !Entity.isEntity(other)) {\n return false;\n }\n\n return this.#id.equals(other.#id);\n }\n\n /**\n * Returns a frozen copy of the entity's properties, including base properties.\n * Ensures immutability by returning a new object.\n * @returns A frozen copy of the entity's properties\n */\n public getPropsCopy(): Readonly<EntityProps> {\n return Object.freeze(this.#props);\n }\n\n /**\n * Determines if the entity is transient, i.e., it has not been persisted yet.\n * By convention, an entity is considered transient if it lacks a valid identifier.\n * This can be useful when performing logic that depends on persistence state,\n * such as conditional inserts or validations that only apply to new entities.\n *\n * @returns True if the entity is transient (not persisted), otherwise false.\n */\n public isPersisted(): boolean {\n return this.#id.isEmpty();\n }\n\n public toJSON(): Record<string, unknown> {\n return this.toObject();\n }\n\n public toObject(): Readonly<\n UnwrapValueObject<EntityProps> & { createdAt: string; id: string }\n > {\n const props = unwrapValueObject(this.getPropsCopy());\n const safeProps = ensureObject(props);\n return Object.freeze({\n ...this.metadata,\n ...safeProps,\n }) as Readonly<\n UnwrapValueObject<EntityProps> & { createdAt: string; id: string }\n >;\n }\n\n /**\n * Validates the entity's state to enforce domain invariants.\n * Must be implemented by subclasses to define specific validation rules.\n * @throws EntityValidationError if validation fails\n */\n public abstract validate(): void;\n}\n","import { AggregateId } from '../value-objects/aggregate-id.vo';\nimport { Entity } from '../entities/entity';\nimport { DomainEvent } from '../events';\n\n/**\n * Interface for AggregateRoot to ensure type safety and extensibility.\n */\nexport interface AggregateRootInterface {\n readonly id: AggregateId;\n readonly domainEvents: readonly DomainEvent[];\n /**\n * Validates the aggregate's invariants.\n * @throws {EntityValidationError} If validation fails.\n */\n validate: () => void;\n /**\n * Adds a domain event to the aggregate.\n * @param event The domain event to add.\n */\n addEvent: (event: DomainEvent) => void;\n\n /**\n * Retrieves and clears all domain events recorded by this aggregate.\n *\n * Domain events represent facts that occurred as a result of state changes\n * within the aggregate. This method transfers ownership of those events\n * to the application layer for further processing (e.g. publishing).\n *\n * Calling this method has the side effect of clearing the aggregate's\n * internal event collection to prevent duplicate handling.\n *\n * This method is intended to be invoked by application services\n * after the aggregate has been successfully persisted.\n *\n * @returns A read-only list of domain events raised by this aggregate.\n */\n pullDomainEvents: () => readonly DomainEvent[];\n}\n\n/**\n * Base class for aggregate roots in DDD, encapsulating domain events and validation.\n * @template EntityProps The type of the entity's properties.\n */\nexport abstract class AggregateRoot<EntityProps>\n extends Entity<EntityProps>\n implements AggregateRootInterface\n{\n /**\n * Gets a read-only copy of the domain events.\n */\n get domainEvents(): readonly DomainEvent[] {\n return [...this._domainEvents];\n }\n\n /**\n * Internal list of domain events.\n */\n private readonly _domainEvents: DomainEvent[] = [];\n\n /**\n * Adds a domain event to the aggregate after validating invariants.\n * @param domainEvent The domain event to add.\n * @throws {EntityValidationError} If invariants are not met.\n */\n addEvent(domainEvent: DomainEvent): void {\n this.validate(); // Ensure invariants before adding events\n this._domainEvents.push(domainEvent);\n }\n\n public pullDomainEvents(): readonly DomainEvent[] {\n const events = [...this._domainEvents];\n this._domainEvents.length = 0;\n return events;\n }\n\n /**\n * Validates the entity's invariants.\n * @throws {EntityValidationError} If validation fails.\n */\n abstract validate(): void;\n}\n","type Primitive = boolean | number | string | null;\n\ntype Serializable =\n | Primitive\n | Serializable[]\n | { [key: string]: Serializable };\n\nexport type DomainEventPayload = Record<string, Serializable>;\n\ntype DomainEventProps<T> = {\n id: string;\n aggregateId: string;\n schemaVersion: number;\n occurredAt: number;\n payload: T;\n};\n\n// Abstract base class for domain events\nexport abstract class DomainEvent<\n T extends DomainEventPayload = DomainEventPayload,\n> {\n public abstract readonly eventName: string;\n\n public readonly id: string;\n public readonly aggregateId: string;\n public readonly schemaVersion: number;\n public readonly occurredAt: number;\n public readonly payload: Readonly<T>;\n\n protected constructor(props: DomainEventProps<T>) {\n this.id = props.id;\n this.aggregateId = props.aggregateId;\n this.schemaVersion = props.schemaVersion;\n this.occurredAt = props.occurredAt;\n this.payload = Object.freeze(props.payload);\n }\n\n public toPrimitives() {\n return {\n schemaVersion: this.schemaVersion,\n aggregateId: this.aggregateId,\n occurredAt: this.occurredAt,\n eventName: this.eventName,\n payload: this.payload,\n id: this.id,\n };\n }\n}\n"],"mappings":"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","names":["deepFreeze","obj","seen","item","key","HttpStatus","HttpStatusMessage","ApplicationError","code","HttpStatusMessage","isOperational","status","metadata","message","cause","deepEqual","ValueObject","_ValueObject","props","deepFreeze","vo","other","deepEqual","unwrapValueObject","input","seen","result","item","ValueObject","value","key","v","ensureObject","getCauseInfo","cause","DomainError","message","code","metadata","__spreadValues","EntityValidationError","DomainError","message","cause","v4","z","InvalidValueObjectError","DomainError","message","_AggregateId","ValueObject","value","v4","result","InvalidValueObjectError","z","AggregateId","_createdAt","_id","_props","_Entity","createdAt","props","id","__privateAdd","EntityValidationError","__privateSet","AggregateId","__privateGet","entity","other","unwrapValueObject","safeProps","ensureObject","__spreadValues","Entity","AggregateRoot","Entity","domainEvent","events","DomainEvent","props"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/deep-freeze.util.ts","../src/gateway/constants/http-code.ts","../src/utils/errors/application.error.ts","../src/domain/base/vo.ts","../src/utils/unwrap-vo.util.ts","../src/domain/base/domain.error.ts","../src/domain/errors/entity-validation.error.ts","../src/domain/value-objects/id.vo.ts","../src/domain/errors/invalid-vo.error.ts","../src/domain/base/primitive-vo.ts","../src/domain/value-objects/aggregate-id.vo.ts","../src/domain/entities/entity.ts","../src/domain/aggregates/aggregate-root.ts","../src/domain/events/domain.event.ts"],"sourcesContent":["/**\n * Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.\n *\n * @param obj - The object to be deeply frozen.\n * @param seen - A WeakSet to track already processed objects (for circular references).\n * @returns A deeply frozen version of the input object.\n */\nexport function deepFreeze<T>(\n obj: T,\n seen = new WeakSet<object>(),\n): Readonly<T> {\n // Handle null, undefined, or non-object types\n if (obj == null || (typeof obj !== 'object' && !Array.isArray(obj))) {\n return obj;\n }\n\n // Skip if already frozen\n if (Object.isFrozen(obj)) {\n return obj as Readonly<T>;\n }\n\n // Handle circular references\n if (seen.has(obj as object)) {\n return obj as Readonly<T>;\n }\n\n seen.add(obj as object);\n\n // Handle arrays explicitly\n if (Array.isArray(obj)) {\n obj.forEach(item => deepFreeze(item, seen));\n } else {\n // Handle plain objects\n for (const key in obj) {\n if (Object.hasOwn(obj, key)) {\n deepFreeze((obj as Record<string, unknown>)[key], seen);\n }\n }\n }\n\n return Object.freeze(obj) as Readonly<T>;\n}\n","/**\n * HTTP status code catalog.\n *\n * This object provides a typed, immutable map of standard HTTP status names\n * to their numeric codes. Designed for server-side frameworks such as Fastify.\n *\n * - All identifiers use clear, canonical semantic names.\n * - Values are numeric status codes.\n * - Frozen to prevent runtime mutation.\n * - Exporting `HttpStatus` ensures type-safe usage across the codebase.\n * Usage:\n * ```ts\n * import { HttpStatus } from 'path-to-this-file';\n *\n * function handleRequest() {\n * return {\n * statusCode: HttpStatus.OK,\n * body: 'Success',\n * };\n * }\n * ```\n */\nexport const HttpStatus = Object.freeze({\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n NON_AUTHORITATIVE_INFORMATION: 203,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n\n UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n BANDWIDTH_LIMIT_EXCEEDED: 509,\n VARIANT_ALSO_NEGOTIATES: 506,\n UNSUPPORTED_MEDIA_TYPE: 415,\n RANGE_NOT_SATISFIABLE: 416,\n PRECONDITION_REQUIRED: 428,\n INTERNAL_SERVER_ERROR: 500,\n UNPROCESSABLE_ENTITY: 422,\n INSUFFICIENT_STORAGE: 507,\n\n SWITCHING_PROTOCOLS: 101,\n PRECONDITION_FAILED: 412,\n MISDIRECTED_REQUEST: 421,\n SERVICE_UNAVAILABLE: 503,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n METHOD_NOT_ALLOWED: 405,\n EXPECTATION_FAILED: 417,\n\n MOVED_PERMANENTLY: 301,\n PAYLOAD_TOO_LARGE: 413,\n FAILED_DEPENDENCY: 424,\n TOO_MANY_REQUESTS: 429,\n ALREADY_REPORTED: 208,\n MULTIPLE_CHOICES: 300,\n PAYMENT_REQUIRED: 402,\n UPGRADE_REQUIRED: 426,\n PARTIAL_CONTENT: 206,\n REQUEST_TIMEOUT: 408,\n LENGTH_REQUIRED: 411,\n NOT_IMPLEMENTED: 501,\n GATEWAY_TIMEOUT: 504,\n NOT_ACCEPTABLE: 406,\n RESET_CONTENT: 205,\n LOOP_DETECTED: 508,\n MULTI_STATUS: 207,\n NOT_MODIFIED: 304,\n UNAUTHORIZED: 401,\n URI_TOO_LONG: 414,\n NOT_EXTENDED: 510,\n EARLY_HINTS: 103,\n BAD_REQUEST: 400,\n IM_A_TEAPOT: 418,\n BAD_GATEWAY: 502,\n PROCESSING: 102,\n NO_CONTENT: 204,\n SEE_OTHER: 303,\n USE_PROXY: 305,\n\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n TOO_EARLY: 425,\n CONTINUE: 100,\n ACCEPTED: 202,\n CONFLICT: 409,\n CREATED: 201,\n IM_USED: 226,\n LOCKED: 423,\n FOUND: 302,\n GONE: 410,\n OK: 200,\n} as const);\n\n/**\n * HTTP status messages mapped by numeric code.\n * Use for sending descriptive text in responses or logging.\n */\nexport const HttpStatusMessage = {\n 431: 'Request Header Fields Too Large',\n 511: 'Network Authentication Required',\n 203: 'Non-Authoritative Information',\n 407: 'Proxy Authentication Required',\n 451: 'Unavailable For Legal Reasons',\n 505: 'HTTP Version Not Supported',\n 509: 'Bandwidth Limit Exceeded',\n 506: 'Variant Also Negotiates',\n 415: 'Unsupported Media Type',\n 416: 'Range Not Satisfiable',\n 428: 'Precondition Required',\n 500: 'Internal Server Error',\n 422: 'Unprocessable Entity',\n 507: 'Insufficient Storage',\n 101: 'Switching Protocols',\n 412: 'Precondition Failed',\n 421: 'Misdirected Request',\n 503: 'Service Unavailable',\n 307: 'Temporary Redirect',\n 308: 'Permanent Redirect',\n 405: 'Method Not Allowed',\n 417: 'Expectation Failed',\n 301: 'Moved Permanently',\n 413: 'Payload Too Large',\n 424: 'Failed Dependency',\n 429: 'Too Many Requests',\n 208: 'Already Reported',\n 300: 'Multiple Choices',\n 402: 'Payment Required',\n 426: 'Upgrade Required',\n 206: 'Partial Content',\n 408: 'Request Timeout',\n 411: 'Length Required',\n 501: 'Not Implemented',\n 504: 'Gateway Timeout',\n 406: 'Not Acceptable',\n 205: 'Reset Content',\n 508: 'Loop Detected',\n 207: 'Multi-Status',\n 304: 'Not Modified',\n 401: 'Unauthorized',\n 414: 'URI Too Long',\n 418: \"I'm a Teapot\",\n 510: 'Not Extended',\n 103: 'Early Hints',\n 400: 'Bad Request',\n 502: 'Bad Gateway',\n 102: 'Processing',\n 204: 'No Content',\n 303: 'See Other',\n 305: 'Use Proxy',\n 403: 'Forbidden',\n 404: 'Not Found',\n 425: 'Too Early',\n 100: 'Continue',\n 202: 'Accepted',\n 226: \"I'm Used\",\n 409: 'Conflict',\n 201: 'Created',\n 423: 'Locked',\n 302: 'Found',\n 410: 'Gone',\n 200: 'OK',\n} as const;\n\nexport type HttpStatusCode = keyof typeof HttpStatusMessage;\n\nexport type HttpStatusMessage =\n (typeof HttpStatusMessage)[keyof typeof HttpStatusMessage];\n","import {\n HttpStatusCode,\n HttpStatusMessage,\n} from '@/gateway/constants/http-code';\n\ninterface ErrorParams {\n message: string;\n code: HttpStatusMessage;\n status?: HttpStatusCode;\n metadata?: Record<string, unknown> | undefined;\n isOperational?: boolean;\n cause?: Error | undefined;\n}\n\n/**\n * Abstract base class for application-level errors with structured error handling.\n *\n * Extends the native Error class to provide machine-readable error codes, HTTP status codes,\n * operational error classification, and optional metadata for better error tracking and client communication.\n *\n * @abstract\n * @extends {Error}\n *\n * @example\n * ```typescript\n * class UserNotFoundError extends ApplicationError {\n * constructor(userId: string) {\n * super({\n * code: HttpStatusMessage['404'],\n * status: 404,\n * isOperational: true,\n * message: `User with id ${userId} not found`,\n * metadata: { userId }\n * });\n * }\n * }\n * ```\n */\nexport abstract class ApplicationError extends Error {\n /** Optional cause (linked error) */\n public readonly cause?: Error | undefined;\n /** Machine-readable error code (e.g. `OTP_LOCKED`, `USER_NOT_FOUND`) */\n public readonly code: HttpStatusMessage;\n /** Operational vs programmer error flag */\n public readonly isOperational: boolean;\n /** Optional structured metadata for debugging or clients */\n public readonly metadata?: Record<string, unknown> | undefined;\n /** HTTP status code intended for response layer */\n public readonly status: HttpStatusCode;\n\n constructor({\n code = HttpStatusMessage['500'],\n isOperational = false,\n status = 500,\n metadata,\n message,\n cause,\n }: ErrorParams) {\n super(message);\n\n this.name = new.target.name;\n this.code = code;\n this.status = status;\n this.isOperational = isOperational;\n this.metadata = metadata;\n this.cause = cause;\n\n Error.captureStackTrace(this, new.target);\n }\n}\n","import deepEqual from 'fast-deep-equal/es6';\n\nimport { deepFreeze } from '@/utils/deep-freeze.util';\n\nexport abstract class ValueObject<T> {\n get value(): T {\n return this.props;\n }\n\n protected readonly props: Readonly<T>;\n\n protected constructor(props: T) {\n this.validate(props);\n this.props = deepFreeze(props);\n }\n\n /**\n * Standard for clean API integration and logging.\n */\n public toJSON(): T {\n return this.props;\n }\n\n /**\n * Useful for debugging and string-based indexing.\n */\n public toString(): string {\n return JSON.stringify(this.props);\n }\n\n /**\n * Type guard to check if an unknown object is an instance of ValueObject.\n * This is useful for runtime type checking.\n *\n * @param vo The object to check.\n * @returns True if the object is a ValueObject instance, false otherwise.\n */\n public static is(vo: unknown): vo is ValueObject<unknown> {\n return vo instanceof ValueObject;\n }\n\n /**\n * Deep equality comparison of ValueObjects\n */\n public equals(other?: ValueObject<T>): boolean {\n if (other === null || other === undefined) return false;\n\n // Check if they share the same constructor (Type check)\n if (Object.getPrototypeOf(this) !== Object.getPrototypeOf(other)) {\n return false;\n }\n\n return deepEqual(this.props, other.props);\n }\n\n /**\n * Validates the value object props\n * @throws InvalidValueObjectError if validation fails\n */\n protected abstract validate(props: T): void;\n}\n","import { ValueObject } from '@/domain/base/vo';\n\nexport type UnwrapValueObject<T> =\n T extends ValueObject<infer V>\n ? UnwrapValueObject<V>\n : T extends (infer U)[]\n ? UnwrapValueObject<U>[]\n : T extends Map<infer K, infer V>\n ? Map<K, UnwrapValueObject<V>>\n : T extends Set<infer V>\n ? Set<UnwrapValueObject<V>>\n : T extends Date\n ? string\n : T extends object\n ? { [K in keyof T]: UnwrapValueObject<T[K]> }\n : T;\n\nexport function unwrapValueObject<T>(\n input: T,\n seen = new WeakSet(),\n): UnwrapValueObject<T> {\n if (input === null || input === undefined) {\n return input as UnwrapValueObject<T>;\n }\n\n if (typeof input !== 'object') {\n return input as UnwrapValueObject<T>;\n }\n\n if (seen.has(input)) {\n // Prevent circular reference infinite recursion, just return input or throw\n throw new Error('Circular reference detected in ValueObject unwrap');\n }\n\n seen.add(input);\n\n if (Array.isArray(input)) {\n const result = input.map(item => unwrapValueObject(item, seen));\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof ValueObject) {\n const result = unwrapValueObject(input.value, seen);\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof Date) {\n seen.delete(input);\n return input.toISOString() as UnwrapValueObject<T>;\n }\n\n if (input instanceof Map) {\n const result = new Map();\n input.forEach((value, key) => {\n result.set(key, unwrapValueObject(value, seen));\n });\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n if (input instanceof Set) {\n const result = new Set(\n Array.from(input.values()).map(v => unwrapValueObject(v, seen)),\n );\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n }\n\n // generic object\n const result: Record<string, unknown> = {};\n\n for (const key in input) {\n if (Object.hasOwn(input, key)) {\n result[key] = unwrapValueObject((input as any)[key], seen);\n }\n }\n\n seen.delete(input);\n return result as UnwrapValueObject<T>;\n}\n\nexport function ensureObject<T>(input: UnwrapValueObject<T>): object {\n if (input === null || input === undefined) {\n return {};\n }\n if (typeof input === 'object') {\n return input;\n }\n\n // for primitives, wrap inside object with default key (or throw)\n return { value: input };\n}\n","export interface DomainErrorMetadata {\n cause?: { name: string; message: string; stack?: string };\n [key: string]: unknown;\n}\n\nconst getCauseInfo = (cause: Error | undefined) => {\n // 1. Handle Error objects specifically\n if (cause instanceof Error) {\n return {\n cause: {\n message: cause.message,\n stack: cause.stack,\n name: cause.name,\n },\n };\n }\n\n // 2. Handle other existing values\n if (cause) {\n return { cause };\n }\n\n // 3. Default to empty\n return {};\n};\n\n/**\n * Base class for all Domain Errors in the application.\n *\n * This class ensures:\n * 1. Serializable and structured for logs or API responses.\n * 2. Identifiable via stable error codes (not just class names).\n * 3. Contextual with optional structured metadata.\n * 4. Supports error chaining (cause) and stack trace preservation.\n *\n * @example\n * export class InsufficientFundsError extends DomainError {\n * constructor(accountId: string, currentBalance: number) {\n * super(\n * `Account ${accountId} has insufficient funds.`,\n * 'INSUFFICIENT_FUNDS',\n * { accountId, currentBalance }\n * );\n * }\n * }\n */\nexport abstract class DomainError extends Error {\n /** Stable, machine-readable error code */\n public readonly code: string;\n /** Structured, immutable domain metadata */\n public readonly metadata: Readonly<DomainErrorMetadata>;\n\n /**\n * @param message - Human-readable error message\n * @param code - Stable error code\n * @param metadata - Domain-specific structured data; optional `cause` can be included\n */\n protected constructor(\n message: string,\n code: string,\n metadata: DomainErrorMetadata = {},\n ) {\n super(message, { ...getCauseInfo(metadata.cause) });\n\n // Restore prototype chain for proper `instanceof` checks\n Object.setPrototypeOf(this, new.target.prototype);\n\n this.name = new.target.name;\n this.code = code;\n this.metadata = Object.freeze({ ...metadata });\n }\n}\n","import { DomainError } from '../base/domain.error';\n\n/**\n * Custom error class for entity validation failures.\n */\nexport class EntityValidationError extends DomainError {\n constructor(message: string, cause?: Error) {\n super(message, 'ENTITY_VALIDATION_ERROR', { cause });\n }\n}\n","import { v4 } from 'uuid';\nimport z from 'zod';\n\nimport { InvalidValueObjectError } from '../errors/invalid-vo.error';\nimport { PrimitiveValueObject } from '../base/primitive-vo';\n\n/**\n * Represents a UUID (Universally Unique Identifier) value object.\n *\n * This class extends PrimitiveValueObject to provide type-safe UUID handling\n * with validation using Zod schema. UUIDs are immutable and can be generated\n * randomly or created from string values.\n *\n * @example\n * // Generate a new random UUID\n * const id = UUID.generate();\n *\n * @example\n * // Create UUID from an existing string\n * const id = UUID.fromString('550e8400-e29b-41d4-a716-446655440000');\n *\n * @throws {InvalidValueObjectError} When the provided string is not a valid UUID format\n */\nexport class UUID extends PrimitiveValueObject<string> {\n private static readonly schema = z.uuid();\n\n public constructor(value: string) {\n super(value);\n this.validate(value);\n }\n\n /**\n * Generates a new AggregateId.\n */\n public static generate<T extends typeof UUID>(this: T): InstanceType<T> {\n return new this(v4()) as InstanceType<T>;\n }\n\n /**\n * Creates an UUID from an external string.\n * Use only for untrusted input.\n *\n * @param value - UUID string\n */\n public static fromString(value: string): UUID {\n return new this(value);\n }\n\n protected validate(value: string): void {\n const result = UUID.schema.safeParse(value);\n\n if (!result.success) {\n throw new InvalidValueObjectError(\n `Invalid UUID: ${result.error.message}`,\n );\n }\n }\n}\n","import { DomainError } from '../base/domain.error';\n\nexport class InvalidValueObjectError extends DomainError {\n constructor(message: string) {\n super(message, 'INVALID_VALUE_OBJECT');\n }\n}\n","/**\n * Base class for primitive-based Value Objects.\n *\n * This class is intended for Value Objects that are represented by\n * a single primitive value (string, number, or boolean).\n *\n * Characteristics:\n * - Immutable by construction\n * - Cheap equality comparison\n * - No deep cloning or freezing\n * - Safe for serialization and logging\n *\n * Examples:\n * - AggregateId\n * - EmailAddress\n * - Username\n * - Slug\n */\nexport abstract class PrimitiveValueObject<\n T extends boolean | number | string,\n> {\n /**\n * The underlying primitive value.\n * Guaranteed to be valid after construction.\n */\n protected readonly value: T;\n\n /**\n * Constructs a new PrimitiveValueObject.\n *\n * @param value - The primitive value to wrap\n * @throws Error if validation fails\n */\n protected constructor(value: T) {\n this.validate(value);\n this.value = value;\n }\n\n /**\n * Returns the primitive value.\n * Prefer explicit access over implicit coercion.\n */\n public getValue(): T {\n return this.value;\n }\n\n /**\n * Compares two Value Objects for equality.\n *\n * Equality rules:\n * - Same concrete class\n * - Same primitive value (===)\n *\n * @param other - Another Value Object\n */\n public equals(other?: PrimitiveValueObject<T>): boolean {\n if (other === undefined) return false;\n\n if (Object.getPrototypeOf(this) !== Object.getPrototypeOf(other)) {\n return false;\n }\n\n return this.value === other.value;\n }\n\n /**\n * JSON serialization hook.\n * Produces the raw primitive value.\n */\n public toJSON(): T {\n return this.value;\n }\n\n /**\n * String representation.\n * Useful for logging and debugging.\n */\n public toString(): string {\n return String(this.value);\n }\n\n /**\n * Domain invariant validation.\n * Must throw if the value is invalid.\n *\n * @param value - The value to validate\n */\n protected abstract validate(value: T): void;\n}\n","import { UUID } from './id.vo';\n\n/**\n * AggregateId represents a strongly-typed aggregate identifier.\n *\n * - Backed by UUID v4\n * - Immutable\n * - Comparable only to AggregateId\n */\nexport class AggregateId extends UUID {}\n","import { ensureObject, UnwrapValueObject, unwrapValueObject } from '@/utils';\n\nimport { EntityValidationError } from '../errors/entity-validation.error';\nimport { AggregateId } from '../value-objects/aggregate-id.vo';\n\nexport interface EntityBaseInterface {\n id: AggregateId;\n equals: (entity: unknown) => boolean;\n}\n\n/**\n * Base properties common to all entities, including ID and timestamps.\n */\nexport interface BaseEntityProps {\n /** Unique identifier for the entity */\n id?: AggregateId;\n /** Date when the entity was created */\n createdAt: Date;\n}\n\n/**\n * Interface for constructing an entity with optional timestamps.\n * @template Props - The specific props type for the entity\n */\nexport type CreateEntityProps<T> = BaseEntityProps & {\n props: T;\n};\n\n/**\n * Abstract base class for domain entities in a Domain-Driven Design (DDD) context.\n * Provides common functionality for entity identification, equality comparison,\n * immutability, and validation. Entities extending this class must implement\n * the `validate` method to enforce domain invariants.\n * @template EntityProps - The specific props type for the entity\n */\nexport abstract class Entity<EntityProps> {\n /**\n * Returns the creation timestamp.\n * A new Date instance is returned to preserve immutability.\n *\n * @returns {Date} The creation date of the entity.\n */\n get createdAt(): Date {\n return new Date(this.#createdAt);\n }\n\n /**\n * Gets the entity's unique identifier.\n * @returns The entity's ID\n */\n get id(): AggregateId {\n return this.#id;\n }\n\n public get metadata() {\n return Object.freeze({\n createdAt: this.#createdAt.toISOString(),\n id: this.#id,\n });\n }\n\n /**\n * Returns an immutable shallow copy of the entity's properties.\n *\n * @returns {Readonly<EntityProps>} The entity domain properties.\n */\n get props(): Readonly<EntityProps> {\n return this.#props;\n }\n\n /** Private creation timestamp */\n #createdAt: Date;\n /** Private unique identifier for the entity */\n #id: AggregateId;\n /** Private entity-specific properties */\n #props: Readonly<EntityProps>;\n\n /**\n * Constructs an entity with the provided properties and timestamps.\n * Ensures immutability by cloning props and validates the entity.\n * @param params - Entity creation parameters\n * @throws EntityValidationError if the ID is empty or validation fails\n */\n protected constructor({\n createdAt,\n props,\n id,\n }: CreateEntityProps<EntityProps>) {\n if (!id) {\n throw new EntityValidationError('Entity ID cannot be empty');\n }\n\n this.#id = id ?? AggregateId.generate();\n this.#props = Object.freeze(props);\n const now = new Date();\n this.#createdAt = createdAt ? new Date(createdAt) : now;\n }\n\n /**\n * Checks if the provided value is an instance of Entity.\n * @param entity - The value to check\n * @returns True if the value is an Entity instance\n */\n static isEntity(entity: unknown): entity is EntityBaseInterface {\n return (\n typeof entity === 'object' &&\n entity !== null &&\n 'id' in entity &&\n typeof (entity as any).equals === 'function'\n );\n }\n\n /**\n * Compares this entity with another to determine if they are the same.\n * Equality is based on the entity ID.\n * @param other - The entity to compare with\n * @returns True if the entities have the same ID\n */\n public equals(other?: Entity<EntityProps>): boolean {\n if (!other || !Entity.isEntity(other)) {\n return false;\n }\n\n return this.#id.equals(other.#id);\n }\n\n /**\n * Returns a frozen copy of the entity's properties, including base properties.\n * Ensures immutability by returning a new object.\n * @returns A frozen copy of the entity's properties\n */\n public getPropsCopy(): Readonly<EntityProps> {\n return Object.freeze(this.#props);\n }\n\n /**\n * Determines if the entity is transient, i.e., it has not been persisted yet.\n * By convention, an entity is considered transient if it lacks a valid identifier.\n * This can be useful when performing logic that depends on persistence state,\n * such as conditional inserts or validations that only apply to new entities.\n *\n * @returns True if the entity is transient (not persisted), otherwise false.\n */\n // public isPersisted(): boolean {\n // return this.#id.isEmpty();\n // }\n\n public toJSON(): Record<string, unknown> {\n return this.toObject();\n }\n\n public toObject(): Readonly<\n UnwrapValueObject<EntityProps> & { createdAt: string; id: string }\n > {\n const props = unwrapValueObject(this.getPropsCopy());\n const safeProps = ensureObject(props);\n return Object.freeze({\n ...this.metadata,\n ...safeProps,\n }) as Readonly<\n UnwrapValueObject<EntityProps> & { createdAt: string; id: string }\n >;\n }\n\n /**\n * Validates the entity's state to enforce domain invariants.\n * Must be implemented by subclasses to define specific validation rules.\n * @throws EntityValidationError if validation fails\n */\n public abstract validate(): void;\n}\n","import { AggregateId } from '../value-objects/aggregate-id.vo';\nimport { Entity } from '../entities/entity';\nimport { DomainEvent } from '../events';\n\n/**\n * Interface for AggregateRoot to ensure type safety and extensibility.\n */\nexport interface AggregateRootInterface {\n readonly id: AggregateId;\n readonly domainEvents: readonly DomainEvent[];\n /**\n * Validates the aggregate's invariants.\n * @throws {EntityValidationError} If validation fails.\n */\n validate: () => void;\n /**\n * Adds a domain event to the aggregate.\n * @param event The domain event to add.\n */\n addEvent: (event: DomainEvent) => void;\n\n /**\n * Retrieves and clears all domain events recorded by this aggregate.\n *\n * Domain events represent facts that occurred as a result of state changes\n * within the aggregate. This method transfers ownership of those events\n * to the application layer for further processing (e.g. publishing).\n *\n * Calling this method has the side effect of clearing the aggregate's\n * internal event collection to prevent duplicate handling.\n *\n * This method is intended to be invoked by application services\n * after the aggregate has been successfully persisted.\n *\n * @returns A read-only list of domain events raised by this aggregate.\n */\n pullDomainEvents: () => readonly DomainEvent[];\n}\n\n/**\n * Base class for aggregate roots in DDD, encapsulating domain events and validation.\n * @template EntityProps The type of the entity's properties.\n */\nexport abstract class AggregateRoot<EntityProps>\n extends Entity<EntityProps>\n implements AggregateRootInterface\n{\n /**\n * Gets a read-only copy of the domain events.\n */\n get domainEvents(): readonly DomainEvent[] {\n return [...this._domainEvents];\n }\n\n /**\n * Internal list of domain events.\n */\n private readonly _domainEvents: DomainEvent[] = [];\n\n /**\n * Adds a domain event to the aggregate after validating invariants.\n * @param domainEvent The domain event to add.\n * @throws {EntityValidationError} If invariants are not met.\n */\n addEvent(domainEvent: DomainEvent): void {\n this.validate(); // Ensure invariants before adding events\n this._domainEvents.push(domainEvent);\n }\n\n public pullDomainEvents(): readonly DomainEvent[] {\n const events = [...this._domainEvents];\n this._domainEvents.length = 0;\n return events;\n }\n\n /**\n * Validates the entity's invariants.\n * @throws {EntityValidationError} If validation fails.\n */\n abstract validate(): void;\n}\n","type Primitive = boolean | number | string | null;\n\ntype Serializable =\n | Primitive\n | Serializable[]\n | { [key: string]: Serializable };\n\nexport type DomainEventPayload = Record<string, Serializable>;\n\ntype DomainEventProps<T> = {\n id: string;\n aggregateId: string;\n schemaVersion: number;\n occurredAt: number;\n payload: T;\n};\n\n// Abstract base class for domain events\nexport abstract class DomainEvent<\n T extends DomainEventPayload = DomainEventPayload,\n> {\n public abstract readonly eventName: string;\n\n public readonly id: string;\n public readonly aggregateId: string;\n public readonly schemaVersion: number;\n public readonly occurredAt: number;\n public readonly payload: Readonly<T>;\n\n protected constructor(props: DomainEventProps<T>) {\n this.id = props.id;\n this.aggregateId = props.aggregateId;\n this.schemaVersion = props.schemaVersion;\n this.occurredAt = props.occurredAt;\n this.payload = Object.freeze(props.payload);\n }\n\n public toPrimitives() {\n return {\n schemaVersion: this.schemaVersion,\n aggregateId: this.aggregateId,\n occurredAt: this.occurredAt,\n eventName: this.eventName,\n payload: this.payload,\n id: this.id,\n };\n }\n}\n"],"mappings":"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","names":["deepFreeze","obj","seen","item","key","HttpStatus","HttpStatusMessage","ApplicationError","code","HttpStatusMessage","isOperational","status","metadata","message","cause","deepEqual","ValueObject","_ValueObject","props","deepFreeze","vo","other","deepEqual","unwrapValueObject","input","seen","result","item","ValueObject","value","key","v","ensureObject","getCauseInfo","cause","DomainError","message","code","metadata","__spreadValues","EntityValidationError","DomainError","message","cause","v4","z","InvalidValueObjectError","DomainError","message","PrimitiveValueObject","value","other","_UUID","PrimitiveValueObject","value","v4","result","InvalidValueObjectError","z","UUID","AggregateId","UUID","_createdAt","_id","_props","_Entity","createdAt","props","id","__privateAdd","EntityValidationError","__privateSet","AggregateId","__privateGet","entity","other","unwrapValueObject","safeProps","ensureObject","__spreadValues","Entity","AggregateRoot","Entity","domainEvent","events","DomainEvent","props"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rineex/ddd",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Domain Driven Design package for Rineex core modules",
|
|
5
5
|
"author": "Rineex Team",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"vite-tsconfig-paths": "6.0.2",
|
|
25
25
|
"vitest": "4.0.16",
|
|
26
26
|
"zod": "4.2.1",
|
|
27
|
-
"@rineex/
|
|
28
|
-
"@rineex/
|
|
27
|
+
"@rineex/typescript-config": "0.0.0",
|
|
28
|
+
"@rineex/eslint-config": "0.0.0"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [
|
|
31
31
|
"rineex",
|