@rineex/ddd 2.0.0 → 2.1.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 +360 -4
- package/dist/index.d.ts +360 -4
- 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 +2 -1
package/dist/index.d.mts
CHANGED
|
@@ -197,7 +197,7 @@ interface DomainErrorMetadata {
|
|
|
197
197
|
* }
|
|
198
198
|
* }
|
|
199
199
|
*/
|
|
200
|
-
declare abstract class DomainError extends Error {
|
|
200
|
+
declare abstract class DomainError$1 extends Error {
|
|
201
201
|
/** Stable, machine-readable error code */
|
|
202
202
|
readonly code: string;
|
|
203
203
|
/** Structured, immutable domain metadata */
|
|
@@ -310,11 +310,11 @@ declare abstract class ValueObject<T> {
|
|
|
310
310
|
/**
|
|
311
311
|
* Custom error class for entity validation failures.
|
|
312
312
|
*/
|
|
313
|
-
declare class EntityValidationError extends DomainError {
|
|
313
|
+
declare class EntityValidationError extends DomainError$1 {
|
|
314
314
|
constructor(message: string, cause?: Error);
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
declare class InvalidValueObjectError extends DomainError {
|
|
317
|
+
declare class InvalidValueObjectError extends DomainError$1 {
|
|
318
318
|
constructor(message: string);
|
|
319
319
|
}
|
|
320
320
|
|
|
@@ -521,6 +521,362 @@ declare const HttpStatusMessage: {
|
|
|
521
521
|
};
|
|
522
522
|
type HttpStatusMessage = (typeof HttpStatusMessage)[keyof typeof HttpStatusMessage];
|
|
523
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Strongly-typed domain error used in Result failures.
|
|
526
|
+
* Does NOT extend native Error on purpose — infrastructure maps to transport later.
|
|
527
|
+
*/
|
|
528
|
+
declare abstract class DomainError {
|
|
529
|
+
abstract readonly code: DomainErrorCode;
|
|
530
|
+
readonly message: string;
|
|
531
|
+
readonly metadata: Readonly<Record<string, boolean | number | string>>;
|
|
532
|
+
protected constructor(params: {
|
|
533
|
+
message: string;
|
|
534
|
+
metadata?: Record<string, boolean | number | string>;
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
type DomainErrorCode = 'DOMAIN.INVALID_STATE' | 'DOMAIN.INVALID_VALUE';
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Represents the result of an operation, which can be either a success or a failure.
|
|
541
|
+
*
|
|
542
|
+
* This is a functional programming pattern that helps avoid throwing exceptions
|
|
543
|
+
* and makes error handling explicit in the type system. It's commonly used in
|
|
544
|
+
* Domain-Driven Design (DDD) to represent domain operation outcomes.
|
|
545
|
+
*
|
|
546
|
+
* @template T The type of a successful result.
|
|
547
|
+
* @template E The type of the error in case of failure (defaults to DomainError).
|
|
548
|
+
*
|
|
549
|
+
* @example
|
|
550
|
+
* ```typescript
|
|
551
|
+
* // Creating a successful result
|
|
552
|
+
* const success = Result.ok({ id: 1, name: 'John' });
|
|
553
|
+
* if (success.isSuccess) {
|
|
554
|
+
* const user = success.getValue(); // { id: 1, name: 'John' }
|
|
555
|
+
* }
|
|
556
|
+
*
|
|
557
|
+
* // Creating a failed result
|
|
558
|
+
* const failure = Result.fail(new InvalidUserError('User not found'));
|
|
559
|
+
* if (failure.isFailure) {
|
|
560
|
+
* const error = failure.getError(); // InvalidUserError instance
|
|
561
|
+
* }
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* @example
|
|
565
|
+
* ```typescript
|
|
566
|
+
* // Using in a domain service
|
|
567
|
+
* function createUser(name: string): Result<User, DomainError> {
|
|
568
|
+
* if (!name || name.trim().length === 0) {
|
|
569
|
+
* return Result.fail(new InvalidValueError('Name cannot be empty'));
|
|
570
|
+
* }
|
|
571
|
+
*
|
|
572
|
+
* const user = new User(name);
|
|
573
|
+
* return Result.ok(user);
|
|
574
|
+
* }
|
|
575
|
+
*
|
|
576
|
+
* const result = createUser('John Doe');
|
|
577
|
+
* if (result.isSuccess) {
|
|
578
|
+
* console.log('User created:', result.getValue());
|
|
579
|
+
* } else {
|
|
580
|
+
* console.error('Failed:', result.getError()?.message);
|
|
581
|
+
* }
|
|
582
|
+
* ```
|
|
583
|
+
*
|
|
584
|
+
* @example
|
|
585
|
+
* ```typescript
|
|
586
|
+
* // Chaining operations
|
|
587
|
+
* function validateEmail(email: string): Result<string, DomainError> {
|
|
588
|
+
* if (!email.includes('@')) {
|
|
589
|
+
* return Result.fail(new InvalidValueError('Invalid email format'));
|
|
590
|
+
* }
|
|
591
|
+
* return Result.ok(email);
|
|
592
|
+
* }
|
|
593
|
+
*
|
|
594
|
+
* function createAccount(email: string): Result<Account, DomainError> {
|
|
595
|
+
* const emailResult = validateEmail(email);
|
|
596
|
+
* if (emailResult.isFailure) {
|
|
597
|
+
* return emailResult; // Forward the error
|
|
598
|
+
* }
|
|
599
|
+
*
|
|
600
|
+
* const account = new Account(emailResult.getValue()!);
|
|
601
|
+
* return Result.ok(account);
|
|
602
|
+
* }
|
|
603
|
+
* ```
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* ```typescript
|
|
607
|
+
* // Working with async operations
|
|
608
|
+
* async function fetchUser(id: number): Promise<Result<User, DomainError>> {
|
|
609
|
+
* try {
|
|
610
|
+
* const user = await userRepository.findById(id);
|
|
611
|
+
* if (!user) {
|
|
612
|
+
* return Result.fail(new NotFoundError(`User ${id} not found`));
|
|
613
|
+
* }
|
|
614
|
+
* return Result.ok(user);
|
|
615
|
+
* } catch (error) {
|
|
616
|
+
* return Result.fail(new SystemError('Database connection failed'));
|
|
617
|
+
* }
|
|
618
|
+
* }
|
|
619
|
+
*
|
|
620
|
+
* const result = await fetchUser(123);
|
|
621
|
+
* if (result.isSuccess) {
|
|
622
|
+
* // Handle success
|
|
623
|
+
* } else {
|
|
624
|
+
* // Handle failure
|
|
625
|
+
* }
|
|
626
|
+
* ```
|
|
627
|
+
*/
|
|
628
|
+
declare class Result<T, E> {
|
|
629
|
+
/**
|
|
630
|
+
* Indicates if the result is a failure.
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* ```typescript
|
|
634
|
+
* const result = Result.fail(new Error('Something went wrong'));
|
|
635
|
+
* if (result.isFailure) {
|
|
636
|
+
* // Handle error case
|
|
637
|
+
* console.error(result.getError());
|
|
638
|
+
* }
|
|
639
|
+
* ```
|
|
640
|
+
*/
|
|
641
|
+
readonly isFailure: boolean;
|
|
642
|
+
/**
|
|
643
|
+
* Indicates if the result is a success.
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* ```typescript
|
|
647
|
+
* const result = Result.ok(42);
|
|
648
|
+
* if (result.isSuccess) {
|
|
649
|
+
* // Handle success case
|
|
650
|
+
* const value = result.getValue(); // 42
|
|
651
|
+
* }
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
654
|
+
readonly isSuccess: boolean;
|
|
655
|
+
/**
|
|
656
|
+
* The error, if any.
|
|
657
|
+
* @private
|
|
658
|
+
*/
|
|
659
|
+
private readonly _error?;
|
|
660
|
+
/**
|
|
661
|
+
* The value, if any.
|
|
662
|
+
* @private
|
|
663
|
+
*/
|
|
664
|
+
private readonly _value?;
|
|
665
|
+
/**
|
|
666
|
+
* Private constructor to enforce the use of static methods.
|
|
667
|
+
* @param params.value The value on success.
|
|
668
|
+
* @param params.error The error on failure.
|
|
669
|
+
* @private
|
|
670
|
+
*/
|
|
671
|
+
private constructor();
|
|
672
|
+
/**
|
|
673
|
+
* Creates a failed result.
|
|
674
|
+
*
|
|
675
|
+
* @template T The type of a successful result (never for failure).
|
|
676
|
+
* @template E The type of the error (defaults to DomainError).
|
|
677
|
+
* @param error The error object.
|
|
678
|
+
* @returns {Result<T, E>} A failed Result instance.
|
|
679
|
+
*
|
|
680
|
+
* @example
|
|
681
|
+
* ```typescript
|
|
682
|
+
* // With DomainError
|
|
683
|
+
* class InvalidValueError extends DomainError {
|
|
684
|
+
* public readonly code = 'DOMAIN.INVALID_VALUE' as const;
|
|
685
|
+
* constructor(message: string) {
|
|
686
|
+
* super({ message });
|
|
687
|
+
* }
|
|
688
|
+
* }
|
|
689
|
+
*
|
|
690
|
+
* const result = Result.fail(new InvalidValueError('Value must be positive'));
|
|
691
|
+
* // result.isFailure === true
|
|
692
|
+
* // result.getError() === InvalidValueError instance
|
|
693
|
+
* ```
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
* ```typescript
|
|
697
|
+
* // With custom error type
|
|
698
|
+
* interface ValidationError {
|
|
699
|
+
* field: string;
|
|
700
|
+
* message: string;
|
|
701
|
+
* }
|
|
702
|
+
*
|
|
703
|
+
* const result = Result.fail<never, ValidationError>({
|
|
704
|
+
* field: 'email',
|
|
705
|
+
* message: 'Invalid email format'
|
|
706
|
+
* });
|
|
707
|
+
* ```
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```typescript
|
|
711
|
+
* // In a validation function
|
|
712
|
+
* function validateAge(age: number): Result<number, DomainError> {
|
|
713
|
+
* if (age < 0) {
|
|
714
|
+
* return Result.fail(new InvalidValueError('Age cannot be negative'));
|
|
715
|
+
* }
|
|
716
|
+
* if (age > 150) {
|
|
717
|
+
* return Result.fail(new InvalidValueError('Age seems unrealistic'));
|
|
718
|
+
* }
|
|
719
|
+
* return Result.ok(age);
|
|
720
|
+
* }
|
|
721
|
+
* ```
|
|
722
|
+
*/
|
|
723
|
+
static fail<T = never, E = DomainError>(error: E): Result<T, E>;
|
|
724
|
+
/**
|
|
725
|
+
* Creates a successful result.
|
|
726
|
+
*
|
|
727
|
+
* @template T The type of the successful result.
|
|
728
|
+
* @template E The type of the error (never for success).
|
|
729
|
+
* @param value The success value.
|
|
730
|
+
* @returns {Result<T, E>} A successful Result instance.
|
|
731
|
+
*
|
|
732
|
+
* @example
|
|
733
|
+
* ```typescript
|
|
734
|
+
* // With primitive value
|
|
735
|
+
* const result = Result.ok(42);
|
|
736
|
+
* // result.isSuccess === true
|
|
737
|
+
* // result.getValue() === 42
|
|
738
|
+
* ```
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```typescript
|
|
742
|
+
* // With object
|
|
743
|
+
* const user = { id: 1, name: 'John', email: 'john@example.com' };
|
|
744
|
+
* const result = Result.ok(user);
|
|
745
|
+
* if (result.isSuccess) {
|
|
746
|
+
* const savedUser = result.getValue(); // { id: 1, name: 'John', ... }
|
|
747
|
+
* }
|
|
748
|
+
* ```
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```typescript
|
|
752
|
+
* // With domain entity
|
|
753
|
+
* class User {
|
|
754
|
+
* constructor(public readonly id: number, public readonly name: string) {}
|
|
755
|
+
* }
|
|
756
|
+
*
|
|
757
|
+
* function createUser(name: string): Result<User, DomainError> {
|
|
758
|
+
* const user = new User(Date.now(), name);
|
|
759
|
+
* return Result.ok(user);
|
|
760
|
+
* }
|
|
761
|
+
*
|
|
762
|
+
* const result = createUser('Alice');
|
|
763
|
+
* if (result.isSuccess) {
|
|
764
|
+
* console.log(`Created user: ${result.getValue()?.name}`);
|
|
765
|
+
* }
|
|
766
|
+
* ```
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```typescript
|
|
770
|
+
* // With void/undefined (for operations that don't return a value)
|
|
771
|
+
* function deleteUser(id: number): Result<void, DomainError> {
|
|
772
|
+
* // ... deletion logic ...
|
|
773
|
+
* return Result.ok(undefined);
|
|
774
|
+
* }
|
|
775
|
+
*
|
|
776
|
+
* const result = deleteUser(123);
|
|
777
|
+
* if (result.isSuccess) {
|
|
778
|
+
* console.log('User deleted successfully');
|
|
779
|
+
* }
|
|
780
|
+
* ```
|
|
781
|
+
*/
|
|
782
|
+
static ok<T, E = never>(value: T): Result<T, E>;
|
|
783
|
+
/**
|
|
784
|
+
* Returns the error if present, otherwise undefined.
|
|
785
|
+
*
|
|
786
|
+
* **Note:** Always check `isFailure` before calling this method to ensure
|
|
787
|
+
* the result is actually a failure. This method will return `undefined` for
|
|
788
|
+
* successful results.
|
|
789
|
+
*
|
|
790
|
+
* @returns {E | undefined} The error or undefined if successful.
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```typescript
|
|
794
|
+
* const result = Result.fail(new InvalidValueError('Invalid input'));
|
|
795
|
+
*
|
|
796
|
+
* if (result.isFailure) {
|
|
797
|
+
* const error = result.getError();
|
|
798
|
+
* if (error) {
|
|
799
|
+
* console.error(`Error code: ${error.code}, Message: ${error.message}`);
|
|
800
|
+
* }
|
|
801
|
+
* }
|
|
802
|
+
* ```
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* ```typescript
|
|
806
|
+
* // Safe error handling pattern
|
|
807
|
+
* function handleResult<T>(result: Result<T, DomainError>): void {
|
|
808
|
+
* if (result.isFailure) {
|
|
809
|
+
* const error = result.getError();
|
|
810
|
+
* if (error) {
|
|
811
|
+
* // Log error with metadata
|
|
812
|
+
* console.error({
|
|
813
|
+
* code: error.code,
|
|
814
|
+
* message: error.message,
|
|
815
|
+
* metadata: error.metadata
|
|
816
|
+
* });
|
|
817
|
+
* }
|
|
818
|
+
* }
|
|
819
|
+
* }
|
|
820
|
+
* ```
|
|
821
|
+
*/
|
|
822
|
+
getError(): E | undefined;
|
|
823
|
+
/**
|
|
824
|
+
* Returns the value if present, otherwise undefined.
|
|
825
|
+
*
|
|
826
|
+
* **Note:** Always check `isSuccess` before calling this method to ensure
|
|
827
|
+
* the result is actually a success. This method will return `undefined` for
|
|
828
|
+
* failed results.
|
|
829
|
+
*
|
|
830
|
+
* @returns {T | undefined} The value or undefined if failed.
|
|
831
|
+
*
|
|
832
|
+
* @example
|
|
833
|
+
* ```typescript
|
|
834
|
+
* const result = Result.ok({ id: 1, name: 'John' });
|
|
835
|
+
*
|
|
836
|
+
* if (result.isSuccess) {
|
|
837
|
+
* const user = result.getValue();
|
|
838
|
+
* if (user) {
|
|
839
|
+
* console.log(`User: ${user.name} (ID: ${user.id})`);
|
|
840
|
+
* }
|
|
841
|
+
* }
|
|
842
|
+
* ```
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```typescript
|
|
846
|
+
* // Type-safe value extraction
|
|
847
|
+
* function processUser(result: Result<User, DomainError>): void {
|
|
848
|
+
* if (result.isSuccess) {
|
|
849
|
+
* const user = result.getValue();
|
|
850
|
+
* // TypeScript knows user is User | undefined here
|
|
851
|
+
* if (user) {
|
|
852
|
+
* // Process the user
|
|
853
|
+
* userRepository.save(user);
|
|
854
|
+
* }
|
|
855
|
+
* }
|
|
856
|
+
* }
|
|
857
|
+
* ```
|
|
858
|
+
*
|
|
859
|
+
* @example
|
|
860
|
+
* ```typescript
|
|
861
|
+
* // Using non-null assertion (use with caution)
|
|
862
|
+
* const result = Result.ok(42);
|
|
863
|
+
* if (result.isSuccess) {
|
|
864
|
+
* const value = result.getValue()!; // Safe because we checked isSuccess
|
|
865
|
+
* console.log(value * 2); // 84
|
|
866
|
+
* }
|
|
867
|
+
* ```
|
|
868
|
+
*/
|
|
869
|
+
getValue(): T | undefined;
|
|
870
|
+
/**
|
|
871
|
+
* Type guard for failure.
|
|
872
|
+
*/
|
|
873
|
+
isFailureResult(): this is Result<never, E>;
|
|
874
|
+
/**
|
|
875
|
+
* Type guard for success.
|
|
876
|
+
*/
|
|
877
|
+
isSuccessResult(): this is Result<T, never>;
|
|
878
|
+
}
|
|
879
|
+
|
|
524
880
|
/**
|
|
525
881
|
* Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.
|
|
526
882
|
*
|
|
@@ -582,4 +938,4 @@ type UnwrapValueObject<T> = T extends ValueObject<infer V> ? UnwrapValueObject<V
|
|
|
582
938
|
declare function unwrapValueObject<T>(input: T, seen?: WeakSet<object>): UnwrapValueObject<T>;
|
|
583
939
|
declare function ensureObject<T>(input: UnwrapValueObject<T>): object;
|
|
584
940
|
|
|
585
|
-
export { AggregateId, AggregateRoot, ApplicationError, type ApplicationServicePort, DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityId, type EntityProps, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, PrimitiveValueObject, type Props, UUID, type UnixTimestampMillis, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|
|
941
|
+
export { AggregateId, AggregateRoot, ApplicationError, type ApplicationServicePort, DomainError$1 as DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityId, type EntityProps, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, PrimitiveValueObject, type Props, Result, UUID, type UnixTimestampMillis, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|
package/dist/index.d.ts
CHANGED
|
@@ -197,7 +197,7 @@ interface DomainErrorMetadata {
|
|
|
197
197
|
* }
|
|
198
198
|
* }
|
|
199
199
|
*/
|
|
200
|
-
declare abstract class DomainError extends Error {
|
|
200
|
+
declare abstract class DomainError$1 extends Error {
|
|
201
201
|
/** Stable, machine-readable error code */
|
|
202
202
|
readonly code: string;
|
|
203
203
|
/** Structured, immutable domain metadata */
|
|
@@ -310,11 +310,11 @@ declare abstract class ValueObject<T> {
|
|
|
310
310
|
/**
|
|
311
311
|
* Custom error class for entity validation failures.
|
|
312
312
|
*/
|
|
313
|
-
declare class EntityValidationError extends DomainError {
|
|
313
|
+
declare class EntityValidationError extends DomainError$1 {
|
|
314
314
|
constructor(message: string, cause?: Error);
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
declare class InvalidValueObjectError extends DomainError {
|
|
317
|
+
declare class InvalidValueObjectError extends DomainError$1 {
|
|
318
318
|
constructor(message: string);
|
|
319
319
|
}
|
|
320
320
|
|
|
@@ -521,6 +521,362 @@ declare const HttpStatusMessage: {
|
|
|
521
521
|
};
|
|
522
522
|
type HttpStatusMessage = (typeof HttpStatusMessage)[keyof typeof HttpStatusMessage];
|
|
523
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Strongly-typed domain error used in Result failures.
|
|
526
|
+
* Does NOT extend native Error on purpose — infrastructure maps to transport later.
|
|
527
|
+
*/
|
|
528
|
+
declare abstract class DomainError {
|
|
529
|
+
abstract readonly code: DomainErrorCode;
|
|
530
|
+
readonly message: string;
|
|
531
|
+
readonly metadata: Readonly<Record<string, boolean | number | string>>;
|
|
532
|
+
protected constructor(params: {
|
|
533
|
+
message: string;
|
|
534
|
+
metadata?: Record<string, boolean | number | string>;
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
type DomainErrorCode = 'DOMAIN.INVALID_STATE' | 'DOMAIN.INVALID_VALUE';
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Represents the result of an operation, which can be either a success or a failure.
|
|
541
|
+
*
|
|
542
|
+
* This is a functional programming pattern that helps avoid throwing exceptions
|
|
543
|
+
* and makes error handling explicit in the type system. It's commonly used in
|
|
544
|
+
* Domain-Driven Design (DDD) to represent domain operation outcomes.
|
|
545
|
+
*
|
|
546
|
+
* @template T The type of a successful result.
|
|
547
|
+
* @template E The type of the error in case of failure (defaults to DomainError).
|
|
548
|
+
*
|
|
549
|
+
* @example
|
|
550
|
+
* ```typescript
|
|
551
|
+
* // Creating a successful result
|
|
552
|
+
* const success = Result.ok({ id: 1, name: 'John' });
|
|
553
|
+
* if (success.isSuccess) {
|
|
554
|
+
* const user = success.getValue(); // { id: 1, name: 'John' }
|
|
555
|
+
* }
|
|
556
|
+
*
|
|
557
|
+
* // Creating a failed result
|
|
558
|
+
* const failure = Result.fail(new InvalidUserError('User not found'));
|
|
559
|
+
* if (failure.isFailure) {
|
|
560
|
+
* const error = failure.getError(); // InvalidUserError instance
|
|
561
|
+
* }
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* @example
|
|
565
|
+
* ```typescript
|
|
566
|
+
* // Using in a domain service
|
|
567
|
+
* function createUser(name: string): Result<User, DomainError> {
|
|
568
|
+
* if (!name || name.trim().length === 0) {
|
|
569
|
+
* return Result.fail(new InvalidValueError('Name cannot be empty'));
|
|
570
|
+
* }
|
|
571
|
+
*
|
|
572
|
+
* const user = new User(name);
|
|
573
|
+
* return Result.ok(user);
|
|
574
|
+
* }
|
|
575
|
+
*
|
|
576
|
+
* const result = createUser('John Doe');
|
|
577
|
+
* if (result.isSuccess) {
|
|
578
|
+
* console.log('User created:', result.getValue());
|
|
579
|
+
* } else {
|
|
580
|
+
* console.error('Failed:', result.getError()?.message);
|
|
581
|
+
* }
|
|
582
|
+
* ```
|
|
583
|
+
*
|
|
584
|
+
* @example
|
|
585
|
+
* ```typescript
|
|
586
|
+
* // Chaining operations
|
|
587
|
+
* function validateEmail(email: string): Result<string, DomainError> {
|
|
588
|
+
* if (!email.includes('@')) {
|
|
589
|
+
* return Result.fail(new InvalidValueError('Invalid email format'));
|
|
590
|
+
* }
|
|
591
|
+
* return Result.ok(email);
|
|
592
|
+
* }
|
|
593
|
+
*
|
|
594
|
+
* function createAccount(email: string): Result<Account, DomainError> {
|
|
595
|
+
* const emailResult = validateEmail(email);
|
|
596
|
+
* if (emailResult.isFailure) {
|
|
597
|
+
* return emailResult; // Forward the error
|
|
598
|
+
* }
|
|
599
|
+
*
|
|
600
|
+
* const account = new Account(emailResult.getValue()!);
|
|
601
|
+
* return Result.ok(account);
|
|
602
|
+
* }
|
|
603
|
+
* ```
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* ```typescript
|
|
607
|
+
* // Working with async operations
|
|
608
|
+
* async function fetchUser(id: number): Promise<Result<User, DomainError>> {
|
|
609
|
+
* try {
|
|
610
|
+
* const user = await userRepository.findById(id);
|
|
611
|
+
* if (!user) {
|
|
612
|
+
* return Result.fail(new NotFoundError(`User ${id} not found`));
|
|
613
|
+
* }
|
|
614
|
+
* return Result.ok(user);
|
|
615
|
+
* } catch (error) {
|
|
616
|
+
* return Result.fail(new SystemError('Database connection failed'));
|
|
617
|
+
* }
|
|
618
|
+
* }
|
|
619
|
+
*
|
|
620
|
+
* const result = await fetchUser(123);
|
|
621
|
+
* if (result.isSuccess) {
|
|
622
|
+
* // Handle success
|
|
623
|
+
* } else {
|
|
624
|
+
* // Handle failure
|
|
625
|
+
* }
|
|
626
|
+
* ```
|
|
627
|
+
*/
|
|
628
|
+
declare class Result<T, E> {
|
|
629
|
+
/**
|
|
630
|
+
* Indicates if the result is a failure.
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* ```typescript
|
|
634
|
+
* const result = Result.fail(new Error('Something went wrong'));
|
|
635
|
+
* if (result.isFailure) {
|
|
636
|
+
* // Handle error case
|
|
637
|
+
* console.error(result.getError());
|
|
638
|
+
* }
|
|
639
|
+
* ```
|
|
640
|
+
*/
|
|
641
|
+
readonly isFailure: boolean;
|
|
642
|
+
/**
|
|
643
|
+
* Indicates if the result is a success.
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* ```typescript
|
|
647
|
+
* const result = Result.ok(42);
|
|
648
|
+
* if (result.isSuccess) {
|
|
649
|
+
* // Handle success case
|
|
650
|
+
* const value = result.getValue(); // 42
|
|
651
|
+
* }
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
654
|
+
readonly isSuccess: boolean;
|
|
655
|
+
/**
|
|
656
|
+
* The error, if any.
|
|
657
|
+
* @private
|
|
658
|
+
*/
|
|
659
|
+
private readonly _error?;
|
|
660
|
+
/**
|
|
661
|
+
* The value, if any.
|
|
662
|
+
* @private
|
|
663
|
+
*/
|
|
664
|
+
private readonly _value?;
|
|
665
|
+
/**
|
|
666
|
+
* Private constructor to enforce the use of static methods.
|
|
667
|
+
* @param params.value The value on success.
|
|
668
|
+
* @param params.error The error on failure.
|
|
669
|
+
* @private
|
|
670
|
+
*/
|
|
671
|
+
private constructor();
|
|
672
|
+
/**
|
|
673
|
+
* Creates a failed result.
|
|
674
|
+
*
|
|
675
|
+
* @template T The type of a successful result (never for failure).
|
|
676
|
+
* @template E The type of the error (defaults to DomainError).
|
|
677
|
+
* @param error The error object.
|
|
678
|
+
* @returns {Result<T, E>} A failed Result instance.
|
|
679
|
+
*
|
|
680
|
+
* @example
|
|
681
|
+
* ```typescript
|
|
682
|
+
* // With DomainError
|
|
683
|
+
* class InvalidValueError extends DomainError {
|
|
684
|
+
* public readonly code = 'DOMAIN.INVALID_VALUE' as const;
|
|
685
|
+
* constructor(message: string) {
|
|
686
|
+
* super({ message });
|
|
687
|
+
* }
|
|
688
|
+
* }
|
|
689
|
+
*
|
|
690
|
+
* const result = Result.fail(new InvalidValueError('Value must be positive'));
|
|
691
|
+
* // result.isFailure === true
|
|
692
|
+
* // result.getError() === InvalidValueError instance
|
|
693
|
+
* ```
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
* ```typescript
|
|
697
|
+
* // With custom error type
|
|
698
|
+
* interface ValidationError {
|
|
699
|
+
* field: string;
|
|
700
|
+
* message: string;
|
|
701
|
+
* }
|
|
702
|
+
*
|
|
703
|
+
* const result = Result.fail<never, ValidationError>({
|
|
704
|
+
* field: 'email',
|
|
705
|
+
* message: 'Invalid email format'
|
|
706
|
+
* });
|
|
707
|
+
* ```
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```typescript
|
|
711
|
+
* // In a validation function
|
|
712
|
+
* function validateAge(age: number): Result<number, DomainError> {
|
|
713
|
+
* if (age < 0) {
|
|
714
|
+
* return Result.fail(new InvalidValueError('Age cannot be negative'));
|
|
715
|
+
* }
|
|
716
|
+
* if (age > 150) {
|
|
717
|
+
* return Result.fail(new InvalidValueError('Age seems unrealistic'));
|
|
718
|
+
* }
|
|
719
|
+
* return Result.ok(age);
|
|
720
|
+
* }
|
|
721
|
+
* ```
|
|
722
|
+
*/
|
|
723
|
+
static fail<T = never, E = DomainError>(error: E): Result<T, E>;
|
|
724
|
+
/**
|
|
725
|
+
* Creates a successful result.
|
|
726
|
+
*
|
|
727
|
+
* @template T The type of the successful result.
|
|
728
|
+
* @template E The type of the error (never for success).
|
|
729
|
+
* @param value The success value.
|
|
730
|
+
* @returns {Result<T, E>} A successful Result instance.
|
|
731
|
+
*
|
|
732
|
+
* @example
|
|
733
|
+
* ```typescript
|
|
734
|
+
* // With primitive value
|
|
735
|
+
* const result = Result.ok(42);
|
|
736
|
+
* // result.isSuccess === true
|
|
737
|
+
* // result.getValue() === 42
|
|
738
|
+
* ```
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```typescript
|
|
742
|
+
* // With object
|
|
743
|
+
* const user = { id: 1, name: 'John', email: 'john@example.com' };
|
|
744
|
+
* const result = Result.ok(user);
|
|
745
|
+
* if (result.isSuccess) {
|
|
746
|
+
* const savedUser = result.getValue(); // { id: 1, name: 'John', ... }
|
|
747
|
+
* }
|
|
748
|
+
* ```
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```typescript
|
|
752
|
+
* // With domain entity
|
|
753
|
+
* class User {
|
|
754
|
+
* constructor(public readonly id: number, public readonly name: string) {}
|
|
755
|
+
* }
|
|
756
|
+
*
|
|
757
|
+
* function createUser(name: string): Result<User, DomainError> {
|
|
758
|
+
* const user = new User(Date.now(), name);
|
|
759
|
+
* return Result.ok(user);
|
|
760
|
+
* }
|
|
761
|
+
*
|
|
762
|
+
* const result = createUser('Alice');
|
|
763
|
+
* if (result.isSuccess) {
|
|
764
|
+
* console.log(`Created user: ${result.getValue()?.name}`);
|
|
765
|
+
* }
|
|
766
|
+
* ```
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```typescript
|
|
770
|
+
* // With void/undefined (for operations that don't return a value)
|
|
771
|
+
* function deleteUser(id: number): Result<void, DomainError> {
|
|
772
|
+
* // ... deletion logic ...
|
|
773
|
+
* return Result.ok(undefined);
|
|
774
|
+
* }
|
|
775
|
+
*
|
|
776
|
+
* const result = deleteUser(123);
|
|
777
|
+
* if (result.isSuccess) {
|
|
778
|
+
* console.log('User deleted successfully');
|
|
779
|
+
* }
|
|
780
|
+
* ```
|
|
781
|
+
*/
|
|
782
|
+
static ok<T, E = never>(value: T): Result<T, E>;
|
|
783
|
+
/**
|
|
784
|
+
* Returns the error if present, otherwise undefined.
|
|
785
|
+
*
|
|
786
|
+
* **Note:** Always check `isFailure` before calling this method to ensure
|
|
787
|
+
* the result is actually a failure. This method will return `undefined` for
|
|
788
|
+
* successful results.
|
|
789
|
+
*
|
|
790
|
+
* @returns {E | undefined} The error or undefined if successful.
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```typescript
|
|
794
|
+
* const result = Result.fail(new InvalidValueError('Invalid input'));
|
|
795
|
+
*
|
|
796
|
+
* if (result.isFailure) {
|
|
797
|
+
* const error = result.getError();
|
|
798
|
+
* if (error) {
|
|
799
|
+
* console.error(`Error code: ${error.code}, Message: ${error.message}`);
|
|
800
|
+
* }
|
|
801
|
+
* }
|
|
802
|
+
* ```
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* ```typescript
|
|
806
|
+
* // Safe error handling pattern
|
|
807
|
+
* function handleResult<T>(result: Result<T, DomainError>): void {
|
|
808
|
+
* if (result.isFailure) {
|
|
809
|
+
* const error = result.getError();
|
|
810
|
+
* if (error) {
|
|
811
|
+
* // Log error with metadata
|
|
812
|
+
* console.error({
|
|
813
|
+
* code: error.code,
|
|
814
|
+
* message: error.message,
|
|
815
|
+
* metadata: error.metadata
|
|
816
|
+
* });
|
|
817
|
+
* }
|
|
818
|
+
* }
|
|
819
|
+
* }
|
|
820
|
+
* ```
|
|
821
|
+
*/
|
|
822
|
+
getError(): E | undefined;
|
|
823
|
+
/**
|
|
824
|
+
* Returns the value if present, otherwise undefined.
|
|
825
|
+
*
|
|
826
|
+
* **Note:** Always check `isSuccess` before calling this method to ensure
|
|
827
|
+
* the result is actually a success. This method will return `undefined` for
|
|
828
|
+
* failed results.
|
|
829
|
+
*
|
|
830
|
+
* @returns {T | undefined} The value or undefined if failed.
|
|
831
|
+
*
|
|
832
|
+
* @example
|
|
833
|
+
* ```typescript
|
|
834
|
+
* const result = Result.ok({ id: 1, name: 'John' });
|
|
835
|
+
*
|
|
836
|
+
* if (result.isSuccess) {
|
|
837
|
+
* const user = result.getValue();
|
|
838
|
+
* if (user) {
|
|
839
|
+
* console.log(`User: ${user.name} (ID: ${user.id})`);
|
|
840
|
+
* }
|
|
841
|
+
* }
|
|
842
|
+
* ```
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```typescript
|
|
846
|
+
* // Type-safe value extraction
|
|
847
|
+
* function processUser(result: Result<User, DomainError>): void {
|
|
848
|
+
* if (result.isSuccess) {
|
|
849
|
+
* const user = result.getValue();
|
|
850
|
+
* // TypeScript knows user is User | undefined here
|
|
851
|
+
* if (user) {
|
|
852
|
+
* // Process the user
|
|
853
|
+
* userRepository.save(user);
|
|
854
|
+
* }
|
|
855
|
+
* }
|
|
856
|
+
* }
|
|
857
|
+
* ```
|
|
858
|
+
*
|
|
859
|
+
* @example
|
|
860
|
+
* ```typescript
|
|
861
|
+
* // Using non-null assertion (use with caution)
|
|
862
|
+
* const result = Result.ok(42);
|
|
863
|
+
* if (result.isSuccess) {
|
|
864
|
+
* const value = result.getValue()!; // Safe because we checked isSuccess
|
|
865
|
+
* console.log(value * 2); // 84
|
|
866
|
+
* }
|
|
867
|
+
* ```
|
|
868
|
+
*/
|
|
869
|
+
getValue(): T | undefined;
|
|
870
|
+
/**
|
|
871
|
+
* Type guard for failure.
|
|
872
|
+
*/
|
|
873
|
+
isFailureResult(): this is Result<never, E>;
|
|
874
|
+
/**
|
|
875
|
+
* Type guard for success.
|
|
876
|
+
*/
|
|
877
|
+
isSuccessResult(): this is Result<T, never>;
|
|
878
|
+
}
|
|
879
|
+
|
|
524
880
|
/**
|
|
525
881
|
* Utility to deeply freeze objects to ensure immutability - handles nested objects and arrays.
|
|
526
882
|
*
|
|
@@ -582,4 +938,4 @@ type UnwrapValueObject<T> = T extends ValueObject<infer V> ? UnwrapValueObject<V
|
|
|
582
938
|
declare function unwrapValueObject<T>(input: T, seen?: WeakSet<object>): UnwrapValueObject<T>;
|
|
583
939
|
declare function ensureObject<T>(input: UnwrapValueObject<T>): object;
|
|
584
940
|
|
|
585
|
-
export { AggregateId, AggregateRoot, ApplicationError, type ApplicationServicePort, DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityId, type EntityProps, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, PrimitiveValueObject, type Props, UUID, type UnixTimestampMillis, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|
|
941
|
+
export { AggregateId, AggregateRoot, ApplicationError, type ApplicationServicePort, DomainError$1 as DomainError, type DomainErrorMetadata, DomainEvent, type DomainEventPayload, Entity, type EntityId, type EntityProps, EntityValidationError, HttpStatus, type HttpStatusCode, HttpStatusMessage, InvalidValueObjectError, PrimitiveValueObject, type Props, Result, UUID, type UnixTimestampMillis, type UnwrapValueObject, ValueObject, deepFreeze, ensureObject, unwrapValueObject };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var V=Object.create;var s=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames,D=Object.getOwnPropertySymbols,C=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty,j=Object.prototype.propertyIsEnumerable;var N=(t,e,r)=>e in t?s(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,O=(t,e)=>{for(var r in e||(e={}))_.call(e,r)&&N(t,r,e[r]);if(D)for(var r of D(e))j.call(e,r)&&N(t,r,e[r]);return t};var F=(t,e)=>{for(var r in e)s(t,r,{get:e[r],enumerable:!0})},h=(t,e,r,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of M(e))!_.call(t,n)&&n!==r&&s(t,n,{get:()=>e[n],enumerable:!(a=L(e,n))||a.enumerable});return t};var v=(t,e,r)=>(r=t!=null?V(C(t)):{},h(e||!t||!t.__esModule?s(r,"default",{value:t,enumerable:!0}):r,t)),H=t=>h(s({},"__esModule",{value:!0}),t);var Y={};F(Y,{AggregateId:()=>b,AggregateRoot:()=>y,ApplicationError:()=>A,DomainError:()=>o,DomainEvent:()=>g,Entity:()=>d,EntityValidationError:()=>I,HttpStatus:()=>G,HttpStatusMessage:()=>R,InvalidValueObjectError:()=>E,PrimitiveValueObject:()=>l,Result:()=>m,UUID:()=>p,ValueObject:()=>u,deepFreeze:()=>c,ensureObject:()=>q,unwrapValueObject:()=>i});module.exports=H(Y);var d=class{constructor(e){var r;this.id=e.id,this.createdAt=(r=e.createdAt)!=null?r:new Date}equals(e){return e==null?!1:this===e?!0:this.id.equals(e.id)}};var y=class extends d{constructor(){super(...arguments);this._domainEvents=[]}get domainEvents(){return[...this._domainEvents]}addEvent(r){this._domainEvents.push(r)}pullDomainEvents(){let r=[...this._domainEvents];return this._domainEvents.length=0,r}};var k=t=>t instanceof Error?{cause:{message:t.message,stack:t.stack,name:t.name}}:t?{cause:t}:{},o=class extends Error{constructor(e,r,a={}){super(e,O({},k(a.cause))),Object.setPrototypeOf(this,new.target.prototype),this.name=new.target.name,this.code=r,this.metadata=Object.freeze(O({},a))}};var l=class{constructor(e){this.validate(e),this.value=e}equals(e){return e==null||Object.getPrototypeOf(this)!==Object.getPrototypeOf(e)?!1:this.value===e.value}getValue(){return this.value}toJSON(){return this.value}toString(){return String(this.value)}};var S=v(require("fast-deep-equal/es6"));function c(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=>c(r,e));else for(let r in t)Object.hasOwn(t,r)&&c(t[r],e);return Object.freeze(t)}var u=class t{get value(){return this.props}constructor(e){this.validate(e),this.props=c(e)}static is(e){return e instanceof t}equals(e){return e==null||Object.getPrototypeOf(this)!==Object.getPrototypeOf(e)?!1:(0,S.default)(this.props,e.props)}toJSON(){return this.props}toString(){return JSON.stringify(this.props)}};var I=class extends o{constructor(e,r){super(e,"ENTITY_VALIDATION_ERROR",{cause:r})}};var E=class extends o{constructor(e){super(e,"INVALID_VALUE_OBJECT")}};var P=require("crypto"),g=class{constructor(e){var r;this.id=(r=e.id)!=null?r:(0,P.randomUUID)(),this.aggregateId=e.aggregateId,this.schemaVersion=e.schemaVersion,this.occurredAt=e.occurredAt,this.payload=Object.freeze(e.payload)}toPrimitives(){return{aggregateId:this.aggregateId.toString(),schemaVersion:this.schemaVersion,occurredAt:this.occurredAt,eventName:this.eventName,payload:this.payload,id:this.id}}};var U=require("uuid"),x=v(require("zod"));var T=class T extends l{constructor(e){super(e),this.validate(e)}static fromString(e){return new this(e)}static generate(){return new this((0,U.v4)())}validate(e){let r=T.schema.safeParse(e);if(!r.success)throw new E(`Invalid UUID: ${r.error.message}`)}};T.schema=x.default.uuid();var p=T;var b=class extends p{};var G=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}),R={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 m=class t{constructor(e){let r=e.error!==void 0;this.isFailure=r,this.isSuccess=!r,this._value=e.value,this._error=e.error,Object.freeze(this)}static fail(e){return new t({error:e})}static ok(e){return new t({value:e})}getError(){return this._error}getValue(){return this._value}isFailureResult(){return this.isFailure}isSuccessResult(){return this.isSuccess}};var A=class extends Error{constructor({code:e=R[500],isOperational:r=!1,status:a=500,metadata:n,message:f,cause:w}){super(f),this.name=new.target.name,this.code=e,this.status=a,this.isOperational=r,this.metadata=n,this.cause=w,Error.captureStackTrace(this,new.target)}};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(n=>i(n,e));return e.delete(t),a}if(t instanceof u){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((n,f)=>{a.set(f,i(n,e))}),e.delete(t),a}if(t instanceof Set){let a=new Set(Array.from(t.values()).map(n=>i(n,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 q(t){return t==null?{}:typeof t=="object"?t:{value:t}}0&&(module.exports={AggregateId,AggregateRoot,ApplicationError,DomainError,DomainEvent,Entity,EntityValidationError,HttpStatus,HttpStatusMessage,InvalidValueObjectError,PrimitiveValueObject,Result,UUID,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/domain/entities/entity.ts","../src/domain/aggregates/aggregate-root.ts","../src/domain/base/domain.error.ts","../src/domain/base/primitive-vo.ts","../src/domain/base/vo.ts","../src/utils/deep-freeze.util.ts","../src/domain/errors/entity-validation.error.ts","../src/domain/errors/invalid-vo.error.ts","../src/domain/events/domain.event.ts","../src/domain/value-objects/id.vo.ts","../src/domain/value-objects/aggregate-id.vo.ts","../src/gateway/constants/http-code.ts","../src/utils/errors/application.error.ts","../src/utils/unwrap-vo.util.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/types';\nexport * from './domain/value-objects/aggregate-id.vo';\nexport * from './domain/value-objects/id.vo';\nexport * from './gateway/constants/http-code';\nexport * from './utils';\n","import { EntityId } from '../types';\n\n/**\n * Configuration for the base Entity constructor.\n * Forces a single-object argument pattern to avoid positional argument errors.\n * @template ID - A type satisfying the EntityId interface.\n */\nexport interface EntityProps<ID extends EntityId, Props> {\n /** The unique identity of the entity */\n readonly id: ID;\n /** Optional creation timestamp; defaults to 'now' if not provided */\n readonly createdAt?: Date;\n\n readonly props: Props;\n}\n\n/**\n * Abstract Base Entity for Domain-Driven Design (DDD).\n * This class provides the standard contract for entity equality and identity.\n * It intentionally avoids \"magic\" property bags to ensure V8 engine optimization\n * and better IDE intellisense.\n * @template ID - The specific Identity Value Object type.\n */\nexport abstract class Entity<ID extends EntityId, Props> {\n /** The timestamp when this entity was first instantiated/created */\n public readonly createdAt: Date;\n /** The immutable unique identifier for this entity */\n public readonly id: ID;\n\n /**\n * Protected constructor to be called by subclasses.\n * @param props - Initial identity and metadata.\n */\n protected constructor(props: EntityProps<ID, Props>) {\n this.id = props.id;\n this.createdAt = props.createdAt ?? new Date();\n }\n\n /**\n * Compares entities by identity.\n * In DDD, two entities are considered equal if their IDs match,\n * regardless of their other properties.\n * @param other - The entity to compare against.\n * @returns True if IDs are equal.\n */\n public equals(other?: Entity<ID, Props>): boolean {\n if (other == null) return false;\n if (this === other) return true;\n return this.id.equals(other.id);\n }\n\n /**\n * Converts the Entity into a plain Javascript object.\n * Subclasses must implement this to explicitly control serialization,\n * @returns A plain object representation of the entity.\n */\n public abstract toObject(): Record<string, unknown>;\n\n /**\n * Validates the current state of the entity against domain invariants.\n * This method should be called after construction and any mutation.\n * @throws {Error} Should throw a specific DomainError if validation fails.\n */\n public abstract validate(): void;\n}\n","import { Entity, EntityProps } from '../entities/entity';\nimport { DomainEvent } from '../events';\nimport { EntityId } from '../types';\n\n/**\n * Interface for AggregateRoot to ensure type safety and extensibility.\n */\nexport interface Props<P> extends EntityProps<EntityId, P> {\n readonly domainEvents: readonly DomainEvent[];\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<ID extends EntityId, P> extends Entity<\n ID,\n P\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._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","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 { EntityId } from '../types';\n\ntype Primitive = boolean | number | string;\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 Primitive,\n> implements EntityId {\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 * 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> | null | undefined): boolean {\n if (other === undefined || other === null) 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 * Returns the primitive value.\n * Prefer explicit access over implicit coercion.\n */\n public getValue(): T {\n return this.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 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 * 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 * 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 * Validates the value object props\n * @throws InvalidValueObjectError if validation fails\n */\n protected abstract validate(props: T): void;\n}\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","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 { 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 { randomUUID } from 'node:crypto';\n\nimport { EntityId } from '../types';\n\ntype Primitive = boolean | number | string | null;\n\ntype Serializable =\n | Primitive\n | Serializable[]\n | { [key: string]: Serializable };\n\nexport type DomainEventPayload = Record<string, Serializable>;\n\nexport type UnixTimestampMillis = number;\n\ntype DomainEventProps<AggregateId extends EntityId, Payload> = {\n id?: string;\n aggregateId: AggregateId;\n schemaVersion: number;\n occurredAt: UnixTimestampMillis;\n payload: Payload;\n};\n\n// Abstract base class for domain events\nexport abstract class DomainEvent<\n AggregateId extends EntityId = EntityId,\n T extends DomainEventPayload = DomainEventPayload,\n> {\n public readonly aggregateId: AggregateId;\n\n public abstract readonly eventName: string;\n public readonly id: string;\n public readonly occurredAt: number;\n public readonly payload: Readonly<T>;\n public readonly schemaVersion: number;\n\n protected constructor(props: DomainEventProps<AggregateId, T>) {\n this.id = props.id ?? randomUUID();\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(): Readonly<{\n id: string;\n eventName: string;\n aggregateId: string;\n schemaVersion: number;\n occurredAt: UnixTimestampMillis;\n payload: T;\n }> {\n return {\n aggregateId: this.aggregateId.toString(),\n schemaVersion: this.schemaVersion,\n occurredAt: this.occurredAt,\n eventName: this.eventName,\n payload: this.payload,\n id: this.id,\n };\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 * 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 /**\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 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 { 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","/**\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 { 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"],"mappings":"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","names":["index_exports","__export","AggregateId","AggregateRoot","ApplicationError","DomainError","DomainEvent","Entity","EntityValidationError","HttpStatus","HttpStatusMessage","InvalidValueObjectError","PrimitiveValueObject","UUID","ValueObject","deepFreeze","ensureObject","unwrapValueObject","__toCommonJS","Entity","props","_a","other","AggregateRoot","Entity","domainEvent","events","getCauseInfo","cause","DomainError","message","code","metadata","__spreadValues","PrimitiveValueObject","value","other","import_es6","deepFreeze","obj","seen","item","key","ValueObject","_ValueObject","props","deepFreeze","vo","other","deepEqual","EntityValidationError","DomainError","message","cause","InvalidValueObjectError","DomainError","message","import_node_crypto","DomainEvent","props","_a","import_uuid","import_zod","_UUID","PrimitiveValueObject","value","result","InvalidValueObjectError","z","UUID","AggregateId","UUID","HttpStatus","HttpStatusMessage","ApplicationError","code","HttpStatusMessage","isOperational","status","metadata","message","cause","unwrapValueObject","input","seen","result","item","ValueObject","value","key","v","ensureObject"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/domain/entities/entity.ts","../src/domain/aggregates/aggregate-root.ts","../src/domain/base/domain.error.ts","../src/domain/base/primitive-vo.ts","../src/domain/base/vo.ts","../src/utils/deep-freeze.util.ts","../src/domain/errors/entity-validation.error.ts","../src/domain/errors/invalid-vo.error.ts","../src/domain/events/domain.event.ts","../src/domain/value-objects/id.vo.ts","../src/domain/value-objects/aggregate-id.vo.ts","../src/gateway/constants/http-code.ts","../src/shared/domain/result.ts","../src/utils/errors/application.error.ts","../src/utils/unwrap-vo.util.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/types';\nexport * from './domain/value-objects/aggregate-id.vo';\nexport * from './domain/value-objects/id.vo';\nexport * from './gateway/constants/http-code';\nexport { Result } from './shared/domain/result';\nexport * from './utils';\n","import { EntityId } from '../types';\n\n/**\n * Configuration for the base Entity constructor.\n * Forces a single-object argument pattern to avoid positional argument errors.\n * @template ID - A type satisfying the EntityId interface.\n */\nexport interface EntityProps<ID extends EntityId, Props> {\n /** The unique identity of the entity */\n readonly id: ID;\n /** Optional creation timestamp; defaults to 'now' if not provided */\n readonly createdAt?: Date;\n\n readonly props: Props;\n}\n\n/**\n * Abstract Base Entity for Domain-Driven Design (DDD).\n * This class provides the standard contract for entity equality and identity.\n * It intentionally avoids \"magic\" property bags to ensure V8 engine optimization\n * and better IDE intellisense.\n * @template ID - The specific Identity Value Object type.\n */\nexport abstract class Entity<ID extends EntityId, Props> {\n /** The timestamp when this entity was first instantiated/created */\n public readonly createdAt: Date;\n /** The immutable unique identifier for this entity */\n public readonly id: ID;\n\n /**\n * Protected constructor to be called by subclasses.\n * @param props - Initial identity and metadata.\n */\n protected constructor(props: EntityProps<ID, Props>) {\n this.id = props.id;\n this.createdAt = props.createdAt ?? new Date();\n }\n\n /**\n * Compares entities by identity.\n * In DDD, two entities are considered equal if their IDs match,\n * regardless of their other properties.\n * @param other - The entity to compare against.\n * @returns True if IDs are equal.\n */\n public equals(other?: Entity<ID, Props>): boolean {\n if (other == null) return false;\n if (this === other) return true;\n return this.id.equals(other.id);\n }\n\n /**\n * Converts the Entity into a plain Javascript object.\n * Subclasses must implement this to explicitly control serialization,\n * @returns A plain object representation of the entity.\n */\n public abstract toObject(): Record<string, unknown>;\n\n /**\n * Validates the current state of the entity against domain invariants.\n * This method should be called after construction and any mutation.\n * @throws {Error} Should throw a specific DomainError if validation fails.\n */\n public abstract validate(): void;\n}\n","import { Entity, EntityProps } from '../entities/entity';\nimport { DomainEvent } from '../events';\nimport { EntityId } from '../types';\n\n/**\n * Interface for AggregateRoot to ensure type safety and extensibility.\n */\nexport interface Props<P> extends EntityProps<EntityId, P> {\n readonly domainEvents: readonly DomainEvent[];\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<ID extends EntityId, P> extends Entity<\n ID,\n P\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._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","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 { EntityId } from '../types';\n\ntype Primitive = boolean | number | string;\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 Primitive,\n> implements EntityId {\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 * 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> | null | undefined): boolean {\n if (other === undefined || other === null) 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 * Returns the primitive value.\n * Prefer explicit access over implicit coercion.\n */\n public getValue(): T {\n return this.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 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 * 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 * 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 * Validates the value object props\n * @throws InvalidValueObjectError if validation fails\n */\n protected abstract validate(props: T): void;\n}\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","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 { 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 { randomUUID } from 'node:crypto';\n\nimport { EntityId } from '../types';\n\ntype Primitive = boolean | number | string | null;\n\ntype Serializable =\n | Primitive\n | Serializable[]\n | { [key: string]: Serializable };\n\nexport type DomainEventPayload = Record<string, Serializable>;\n\nexport type UnixTimestampMillis = number;\n\ntype DomainEventProps<AggregateId extends EntityId, Payload> = {\n id?: string;\n aggregateId: AggregateId;\n schemaVersion: number;\n occurredAt: UnixTimestampMillis;\n payload: Payload;\n};\n\n// Abstract base class for domain events\nexport abstract class DomainEvent<\n AggregateId extends EntityId = EntityId,\n T extends DomainEventPayload = DomainEventPayload,\n> {\n public readonly aggregateId: AggregateId;\n\n public abstract readonly eventName: string;\n public readonly id: string;\n public readonly occurredAt: number;\n public readonly payload: Readonly<T>;\n public readonly schemaVersion: number;\n\n protected constructor(props: DomainEventProps<AggregateId, T>) {\n this.id = props.id ?? randomUUID();\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(): Readonly<{\n id: string;\n eventName: string;\n aggregateId: string;\n schemaVersion: number;\n occurredAt: UnixTimestampMillis;\n payload: T;\n }> {\n return {\n aggregateId: this.aggregateId.toString(),\n schemaVersion: this.schemaVersion,\n occurredAt: this.occurredAt,\n eventName: this.eventName,\n payload: this.payload,\n id: this.id,\n };\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 * 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 /**\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 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 { 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","/**\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 { DomainError } from './domain.error';\n\n/**\n * Represents the result of an operation, which can be either a success or a failure.\n *\n * This is a functional programming pattern that helps avoid throwing exceptions\n * and makes error handling explicit in the type system. It's commonly used in\n * Domain-Driven Design (DDD) to represent domain operation outcomes.\n *\n * @template T The type of a successful result.\n * @template E The type of the error in case of failure (defaults to DomainError).\n *\n * @example\n * ```typescript\n * // Creating a successful result\n * const success = Result.ok({ id: 1, name: 'John' });\n * if (success.isSuccess) {\n * const user = success.getValue(); // { id: 1, name: 'John' }\n * }\n *\n * // Creating a failed result\n * const failure = Result.fail(new InvalidUserError('User not found'));\n * if (failure.isFailure) {\n * const error = failure.getError(); // InvalidUserError instance\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Using in a domain service\n * function createUser(name: string): Result<User, DomainError> {\n * if (!name || name.trim().length === 0) {\n * return Result.fail(new InvalidValueError('Name cannot be empty'));\n * }\n *\n * const user = new User(name);\n * return Result.ok(user);\n * }\n *\n * const result = createUser('John Doe');\n * if (result.isSuccess) {\n * console.log('User created:', result.getValue());\n * } else {\n * console.error('Failed:', result.getError()?.message);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Chaining operations\n * function validateEmail(email: string): Result<string, DomainError> {\n * if (!email.includes('@')) {\n * return Result.fail(new InvalidValueError('Invalid email format'));\n * }\n * return Result.ok(email);\n * }\n *\n * function createAccount(email: string): Result<Account, DomainError> {\n * const emailResult = validateEmail(email);\n * if (emailResult.isFailure) {\n * return emailResult; // Forward the error\n * }\n *\n * const account = new Account(emailResult.getValue()!);\n * return Result.ok(account);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Working with async operations\n * async function fetchUser(id: number): Promise<Result<User, DomainError>> {\n * try {\n * const user = await userRepository.findById(id);\n * if (!user) {\n * return Result.fail(new NotFoundError(`User ${id} not found`));\n * }\n * return Result.ok(user);\n * } catch (error) {\n * return Result.fail(new SystemError('Database connection failed'));\n * }\n * }\n *\n * const result = await fetchUser(123);\n * if (result.isSuccess) {\n * // Handle success\n * } else {\n * // Handle failure\n * }\n * ```\n */\nexport class Result<T, E> {\n /**\n * Indicates if the result is a failure.\n *\n * @example\n * ```typescript\n * const result = Result.fail(new Error('Something went wrong'));\n * if (result.isFailure) {\n * // Handle error case\n * console.error(result.getError());\n * }\n * ```\n */\n public readonly isFailure: boolean;\n\n /**\n * Indicates if the result is a success.\n *\n * @example\n * ```typescript\n * const result = Result.ok(42);\n * if (result.isSuccess) {\n * // Handle success case\n * const value = result.getValue(); // 42\n * }\n * ```\n */\n public readonly isSuccess: boolean;\n\n /**\n * The error, if any.\n * @private\n */\n private readonly _error?: E;\n\n /**\n * The value, if any.\n * @private\n */\n private readonly _value?: T;\n\n /**\n * Private constructor to enforce the use of static methods.\n * @param params.value The value on success.\n * @param params.error The error on failure.\n * @private\n */\n private constructor(params: { value?: T; error?: E }) {\n const hasError = params.error !== undefined;\n this.isFailure = hasError;\n this.isSuccess = !hasError;\n\n this._value = params.value;\n this._error = params.error;\n Object.freeze(this);\n }\n\n /**\n * Creates a failed result.\n *\n * @template T The type of a successful result (never for failure).\n * @template E The type of the error (defaults to DomainError).\n * @param error The error object.\n * @returns {Result<T, E>} A failed Result instance.\n *\n * @example\n * ```typescript\n * // With DomainError\n * class InvalidValueError extends DomainError {\n * public readonly code = 'DOMAIN.INVALID_VALUE' as const;\n * constructor(message: string) {\n * super({ message });\n * }\n * }\n *\n * const result = Result.fail(new InvalidValueError('Value must be positive'));\n * // result.isFailure === true\n * // result.getError() === InvalidValueError instance\n * ```\n *\n * @example\n * ```typescript\n * // With custom error type\n * interface ValidationError {\n * field: string;\n * message: string;\n * }\n *\n * const result = Result.fail<never, ValidationError>({\n * field: 'email',\n * message: 'Invalid email format'\n * });\n * ```\n *\n * @example\n * ```typescript\n * // In a validation function\n * function validateAge(age: number): Result<number, DomainError> {\n * if (age < 0) {\n * return Result.fail(new InvalidValueError('Age cannot be negative'));\n * }\n * if (age > 150) {\n * return Result.fail(new InvalidValueError('Age seems unrealistic'));\n * }\n * return Result.ok(age);\n * }\n * ```\n */\n public static fail<T = never, E = DomainError>(error: E): Result<T, E> {\n return new Result({ error });\n }\n\n /**\n * Creates a successful result.\n *\n * @template T The type of the successful result.\n * @template E The type of the error (never for success).\n * @param value The success value.\n * @returns {Result<T, E>} A successful Result instance.\n *\n * @example\n * ```typescript\n * // With primitive value\n * const result = Result.ok(42);\n * // result.isSuccess === true\n * // result.getValue() === 42\n * ```\n *\n * @example\n * ```typescript\n * // With object\n * const user = { id: 1, name: 'John', email: 'john@example.com' };\n * const result = Result.ok(user);\n * if (result.isSuccess) {\n * const savedUser = result.getValue(); // { id: 1, name: 'John', ... }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With domain entity\n * class User {\n * constructor(public readonly id: number, public readonly name: string) {}\n * }\n *\n * function createUser(name: string): Result<User, DomainError> {\n * const user = new User(Date.now(), name);\n * return Result.ok(user);\n * }\n *\n * const result = createUser('Alice');\n * if (result.isSuccess) {\n * console.log(`Created user: ${result.getValue()?.name}`);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With void/undefined (for operations that don't return a value)\n * function deleteUser(id: number): Result<void, DomainError> {\n * // ... deletion logic ...\n * return Result.ok(undefined);\n * }\n *\n * const result = deleteUser(123);\n * if (result.isSuccess) {\n * console.log('User deleted successfully');\n * }\n * ```\n */\n public static ok<T, E = never>(value: T): Result<T, E> {\n return new Result({ value });\n }\n\n /**\n * Returns the error if present, otherwise undefined.\n *\n * **Note:** Always check `isFailure` before calling this method to ensure\n * the result is actually a failure. This method will return `undefined` for\n * successful results.\n *\n * @returns {E | undefined} The error or undefined if successful.\n *\n * @example\n * ```typescript\n * const result = Result.fail(new InvalidValueError('Invalid input'));\n *\n * if (result.isFailure) {\n * const error = result.getError();\n * if (error) {\n * console.error(`Error code: ${error.code}, Message: ${error.message}`);\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Safe error handling pattern\n * function handleResult<T>(result: Result<T, DomainError>): void {\n * if (result.isFailure) {\n * const error = result.getError();\n * if (error) {\n * // Log error with metadata\n * console.error({\n * code: error.code,\n * message: error.message,\n * metadata: error.metadata\n * });\n * }\n * }\n * }\n * ```\n */\n public getError(): E | undefined {\n return this._error;\n }\n\n /**\n * Returns the value if present, otherwise undefined.\n *\n * **Note:** Always check `isSuccess` before calling this method to ensure\n * the result is actually a success. This method will return `undefined` for\n * failed results.\n *\n * @returns {T | undefined} The value or undefined if failed.\n *\n * @example\n * ```typescript\n * const result = Result.ok({ id: 1, name: 'John' });\n *\n * if (result.isSuccess) {\n * const user = result.getValue();\n * if (user) {\n * console.log(`User: ${user.name} (ID: ${user.id})`);\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Type-safe value extraction\n * function processUser(result: Result<User, DomainError>): void {\n * if (result.isSuccess) {\n * const user = result.getValue();\n * // TypeScript knows user is User | undefined here\n * if (user) {\n * // Process the user\n * userRepository.save(user);\n * }\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Using non-null assertion (use with caution)\n * const result = Result.ok(42);\n * if (result.isSuccess) {\n * const value = result.getValue()!; // Safe because we checked isSuccess\n * console.log(value * 2); // 84\n * }\n * ```\n */\n public getValue(): T | undefined {\n return this._value;\n }\n\n /**\n * Type guard for failure.\n */\n public isFailureResult(): this is Result<never, E> {\n return this.isFailure;\n }\n\n /**\n * Type guard for success.\n */\n public isSuccessResult(): this is Result<T, never> {\n return this.isSuccess;\n }\n}\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 { 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"],"mappings":"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","names":["index_exports","__export","AggregateId","AggregateRoot","ApplicationError","DomainError","DomainEvent","Entity","EntityValidationError","HttpStatus","HttpStatusMessage","InvalidValueObjectError","PrimitiveValueObject","Result","UUID","ValueObject","deepFreeze","ensureObject","unwrapValueObject","__toCommonJS","Entity","props","_a","other","AggregateRoot","Entity","domainEvent","events","getCauseInfo","cause","DomainError","message","code","metadata","__spreadValues","PrimitiveValueObject","value","other","import_es6","deepFreeze","obj","seen","item","key","ValueObject","_ValueObject","props","deepFreeze","vo","other","deepEqual","EntityValidationError","DomainError","message","cause","InvalidValueObjectError","DomainError","message","import_node_crypto","DomainEvent","props","_a","import_uuid","import_zod","_UUID","PrimitiveValueObject","value","result","InvalidValueObjectError","z","UUID","AggregateId","UUID","HttpStatus","HttpStatusMessage","Result","_Result","params","hasError","error","value","ApplicationError","code","HttpStatusMessage","isOperational","status","metadata","message","cause","unwrapValueObject","input","seen","result","item","ValueObject","value","key","v","ensureObject"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var _=Object.defineProperty;var O=Object.getOwnPropertySymbols;var h=Object.prototype.hasOwnProperty,v=Object.prototype.propertyIsEnumerable;var y=(t,e,r)=>e in t?_(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,m=(t,e)=>{for(var r in e||(e={}))h.call(e,r)&&y(t,r,e[r]);if(O)for(var r of O(e))v.call(e,r)&&y(t,r,e[r]);return t};var s=class{constructor(e){var r;this.id=e.id,this.createdAt=(r=e.createdAt)!=null?r:new Date}equals(e){return e==null?!1:this===e?!0:this.id.equals(e.id)}};var I=class extends s{constructor(){super(...arguments);this._domainEvents=[]}get domainEvents(){return[...this._domainEvents]}addEvent(r){this._domainEvents.push(r)}pullDomainEvents(){let r=[...this._domainEvents];return this._domainEvents.length=0,r}};var S=t=>t instanceof Error?{cause:{message:t.message,stack:t.stack,name:t.name}}:t?{cause:t}:{},o=class extends Error{constructor(e,r,a={}){super(e,m({},S(a.cause))),Object.setPrototypeOf(this,new.target.prototype),this.name=new.target.name,this.code=r,this.metadata=Object.freeze(m({},a))}};var d=class{constructor(e){this.validate(e),this.value=e}equals(e){return e==null||Object.getPrototypeOf(this)!==Object.getPrototypeOf(e)?!1:this.value===e.value}getValue(){return this.value}toJSON(){return this.value}toString(){return String(this.value)}};import P from"fast-deep-equal/es6";function l(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=>l(r,e));else for(let r in t)Object.hasOwn(t,r)&&l(t[r],e);return Object.freeze(t)}var c=class t{get value(){return this.props}constructor(e){this.validate(e),this.props=l(e)}static is(e){return e instanceof t}equals(e){return e==null||Object.getPrototypeOf(this)!==Object.getPrototypeOf(e)?!1:P(this.props,e.props)}toJSON(){return this.props}toString(){return JSON.stringify(this.props)}};var g=class extends o{constructor(e,r){super(e,"ENTITY_VALIDATION_ERROR",{cause:r})}};var u=class extends o{constructor(e){super(e,"INVALID_VALUE_OBJECT")}};import{randomUUID as U}from"crypto";var b=class{constructor(e){var r;this.id=(r=e.id)!=null?r:U(),this.aggregateId=e.aggregateId,this.schemaVersion=e.schemaVersion,this.occurredAt=e.occurredAt,this.payload=Object.freeze(e.payload)}toPrimitives(){return{aggregateId:this.aggregateId.toString(),schemaVersion:this.schemaVersion,occurredAt:this.occurredAt,eventName:this.eventName,payload:this.payload,id:this.id}}};import{v4 as x}from"uuid";import w from"zod";var p=class p extends d{constructor(e){super(e),this.validate(e)}static fromString(e){return new this(e)}static generate(){return new this(x())}validate(e){let r=p.schema.safeParse(e);if(!r.success)throw new u(`Invalid UUID: ${r.error.message}`)}};p.schema=w.uuid();var E=p;var R=class extends E{};var oe=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}),A={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 f=class t{constructor(e){let r=e.error!==void 0;this.isFailure=r,this.isSuccess=!r,this._value=e.value,this._error=e.error,Object.freeze(this)}static fail(e){return new t({error:e})}static ok(e){return new t({value:e})}getError(){return this._error}getValue(){return this._value}isFailureResult(){return this.isFailure}isSuccessResult(){return this.isSuccess}};var D=class extends Error{constructor({code:e=A[500],isOperational:r=!1,status:a=500,metadata:n,message:T,cause:N}){super(T),this.name=new.target.name,this.code=e,this.status=a,this.isOperational=r,this.metadata=n,this.cause=N,Error.captureStackTrace(this,new.target)}};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(n=>i(n,e));return e.delete(t),a}if(t instanceof c){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((n,T)=>{a.set(T,i(n,e))}),e.delete(t),a}if(t instanceof Set){let a=new Set(Array.from(t.values()).map(n=>i(n,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 Ee(t){return t==null?{}:typeof t=="object"?t:{value:t}}export{R as AggregateId,I as AggregateRoot,D as ApplicationError,o as DomainError,b as DomainEvent,s as Entity,g as EntityValidationError,oe as HttpStatus,A as HttpStatusMessage,u as InvalidValueObjectError,d as PrimitiveValueObject,f as Result,E as UUID,c as ValueObject,l as deepFreeze,Ee as ensureObject,i as unwrapValueObject};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/domain/entities/entity.ts","../src/domain/aggregates/aggregate-root.ts","../src/domain/base/domain.error.ts","../src/domain/base/primitive-vo.ts","../src/domain/base/vo.ts","../src/utils/deep-freeze.util.ts","../src/domain/errors/entity-validation.error.ts","../src/domain/errors/invalid-vo.error.ts","../src/domain/events/domain.event.ts","../src/domain/value-objects/id.vo.ts","../src/domain/value-objects/aggregate-id.vo.ts","../src/gateway/constants/http-code.ts","../src/utils/errors/application.error.ts","../src/utils/unwrap-vo.util.ts"],"sourcesContent":["import { EntityId } from '../types';\n\n/**\n * Configuration for the base Entity constructor.\n * Forces a single-object argument pattern to avoid positional argument errors.\n * @template ID - A type satisfying the EntityId interface.\n */\nexport interface EntityProps<ID extends EntityId, Props> {\n /** The unique identity of the entity */\n readonly id: ID;\n /** Optional creation timestamp; defaults to 'now' if not provided */\n readonly createdAt?: Date;\n\n readonly props: Props;\n}\n\n/**\n * Abstract Base Entity for Domain-Driven Design (DDD).\n * This class provides the standard contract for entity equality and identity.\n * It intentionally avoids \"magic\" property bags to ensure V8 engine optimization\n * and better IDE intellisense.\n * @template ID - The specific Identity Value Object type.\n */\nexport abstract class Entity<ID extends EntityId, Props> {\n /** The timestamp when this entity was first instantiated/created */\n public readonly createdAt: Date;\n /** The immutable unique identifier for this entity */\n public readonly id: ID;\n\n /**\n * Protected constructor to be called by subclasses.\n * @param props - Initial identity and metadata.\n */\n protected constructor(props: EntityProps<ID, Props>) {\n this.id = props.id;\n this.createdAt = props.createdAt ?? new Date();\n }\n\n /**\n * Compares entities by identity.\n * In DDD, two entities are considered equal if their IDs match,\n * regardless of their other properties.\n * @param other - The entity to compare against.\n * @returns True if IDs are equal.\n */\n public equals(other?: Entity<ID, Props>): boolean {\n if (other == null) return false;\n if (this === other) return true;\n return this.id.equals(other.id);\n }\n\n /**\n * Converts the Entity into a plain Javascript object.\n * Subclasses must implement this to explicitly control serialization,\n * @returns A plain object representation of the entity.\n */\n public abstract toObject(): Record<string, unknown>;\n\n /**\n * Validates the current state of the entity against domain invariants.\n * This method should be called after construction and any mutation.\n * @throws {Error} Should throw a specific DomainError if validation fails.\n */\n public abstract validate(): void;\n}\n","import { Entity, EntityProps } from '../entities/entity';\nimport { DomainEvent } from '../events';\nimport { EntityId } from '../types';\n\n/**\n * Interface for AggregateRoot to ensure type safety and extensibility.\n */\nexport interface Props<P> extends EntityProps<EntityId, P> {\n readonly domainEvents: readonly DomainEvent[];\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<ID extends EntityId, P> extends Entity<\n ID,\n P\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._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","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 { EntityId } from '../types';\n\ntype Primitive = boolean | number | string;\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 Primitive,\n> implements EntityId {\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 * 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> | null | undefined): boolean {\n if (other === undefined || other === null) 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 * Returns the primitive value.\n * Prefer explicit access over implicit coercion.\n */\n public getValue(): T {\n return this.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 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 * 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 * 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 * Validates the value object props\n * @throws InvalidValueObjectError if validation fails\n */\n protected abstract validate(props: T): void;\n}\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","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 { 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 { randomUUID } from 'node:crypto';\n\nimport { EntityId } from '../types';\n\ntype Primitive = boolean | number | string | null;\n\ntype Serializable =\n | Primitive\n | Serializable[]\n | { [key: string]: Serializable };\n\nexport type DomainEventPayload = Record<string, Serializable>;\n\nexport type UnixTimestampMillis = number;\n\ntype DomainEventProps<AggregateId extends EntityId, Payload> = {\n id?: string;\n aggregateId: AggregateId;\n schemaVersion: number;\n occurredAt: UnixTimestampMillis;\n payload: Payload;\n};\n\n// Abstract base class for domain events\nexport abstract class DomainEvent<\n AggregateId extends EntityId = EntityId,\n T extends DomainEventPayload = DomainEventPayload,\n> {\n public readonly aggregateId: AggregateId;\n\n public abstract readonly eventName: string;\n public readonly id: string;\n public readonly occurredAt: number;\n public readonly payload: Readonly<T>;\n public readonly schemaVersion: number;\n\n protected constructor(props: DomainEventProps<AggregateId, T>) {\n this.id = props.id ?? randomUUID();\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(): Readonly<{\n id: string;\n eventName: string;\n aggregateId: string;\n schemaVersion: number;\n occurredAt: UnixTimestampMillis;\n payload: T;\n }> {\n return {\n aggregateId: this.aggregateId.toString(),\n schemaVersion: this.schemaVersion,\n occurredAt: this.occurredAt,\n eventName: this.eventName,\n payload: this.payload,\n id: this.id,\n };\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 * 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 /**\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 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 { 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","/**\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 { 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"],"mappings":"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","names":["Entity","props","_a","other","AggregateRoot","Entity","domainEvent","events","getCauseInfo","cause","DomainError","message","code","metadata","__spreadValues","PrimitiveValueObject","value","other","deepEqual","deepFreeze","obj","seen","item","key","ValueObject","_ValueObject","props","deepFreeze","vo","other","deepEqual","EntityValidationError","DomainError","message","cause","InvalidValueObjectError","DomainError","message","randomUUID","DomainEvent","props","_a","v4","z","_UUID","PrimitiveValueObject","value","v4","result","InvalidValueObjectError","z","UUID","AggregateId","UUID","HttpStatus","HttpStatusMessage","ApplicationError","code","HttpStatusMessage","isOperational","status","metadata","message","cause","unwrapValueObject","input","seen","result","item","ValueObject","value","key","v","ensureObject"]}
|
|
1
|
+
{"version":3,"sources":["../src/domain/entities/entity.ts","../src/domain/aggregates/aggregate-root.ts","../src/domain/base/domain.error.ts","../src/domain/base/primitive-vo.ts","../src/domain/base/vo.ts","../src/utils/deep-freeze.util.ts","../src/domain/errors/entity-validation.error.ts","../src/domain/errors/invalid-vo.error.ts","../src/domain/events/domain.event.ts","../src/domain/value-objects/id.vo.ts","../src/domain/value-objects/aggregate-id.vo.ts","../src/gateway/constants/http-code.ts","../src/shared/domain/result.ts","../src/utils/errors/application.error.ts","../src/utils/unwrap-vo.util.ts"],"sourcesContent":["import { EntityId } from '../types';\n\n/**\n * Configuration for the base Entity constructor.\n * Forces a single-object argument pattern to avoid positional argument errors.\n * @template ID - A type satisfying the EntityId interface.\n */\nexport interface EntityProps<ID extends EntityId, Props> {\n /** The unique identity of the entity */\n readonly id: ID;\n /** Optional creation timestamp; defaults to 'now' if not provided */\n readonly createdAt?: Date;\n\n readonly props: Props;\n}\n\n/**\n * Abstract Base Entity for Domain-Driven Design (DDD).\n * This class provides the standard contract for entity equality and identity.\n * It intentionally avoids \"magic\" property bags to ensure V8 engine optimization\n * and better IDE intellisense.\n * @template ID - The specific Identity Value Object type.\n */\nexport abstract class Entity<ID extends EntityId, Props> {\n /** The timestamp when this entity was first instantiated/created */\n public readonly createdAt: Date;\n /** The immutable unique identifier for this entity */\n public readonly id: ID;\n\n /**\n * Protected constructor to be called by subclasses.\n * @param props - Initial identity and metadata.\n */\n protected constructor(props: EntityProps<ID, Props>) {\n this.id = props.id;\n this.createdAt = props.createdAt ?? new Date();\n }\n\n /**\n * Compares entities by identity.\n * In DDD, two entities are considered equal if their IDs match,\n * regardless of their other properties.\n * @param other - The entity to compare against.\n * @returns True if IDs are equal.\n */\n public equals(other?: Entity<ID, Props>): boolean {\n if (other == null) return false;\n if (this === other) return true;\n return this.id.equals(other.id);\n }\n\n /**\n * Converts the Entity into a plain Javascript object.\n * Subclasses must implement this to explicitly control serialization,\n * @returns A plain object representation of the entity.\n */\n public abstract toObject(): Record<string, unknown>;\n\n /**\n * Validates the current state of the entity against domain invariants.\n * This method should be called after construction and any mutation.\n * @throws {Error} Should throw a specific DomainError if validation fails.\n */\n public abstract validate(): void;\n}\n","import { Entity, EntityProps } from '../entities/entity';\nimport { DomainEvent } from '../events';\nimport { EntityId } from '../types';\n\n/**\n * Interface for AggregateRoot to ensure type safety and extensibility.\n */\nexport interface Props<P> extends EntityProps<EntityId, P> {\n readonly domainEvents: readonly DomainEvent[];\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<ID extends EntityId, P> extends Entity<\n ID,\n P\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._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","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 { EntityId } from '../types';\n\ntype Primitive = boolean | number | string;\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 Primitive,\n> implements EntityId {\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 * 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> | null | undefined): boolean {\n if (other === undefined || other === null) 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 * Returns the primitive value.\n * Prefer explicit access over implicit coercion.\n */\n public getValue(): T {\n return this.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 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 * 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 * 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 * Validates the value object props\n * @throws InvalidValueObjectError if validation fails\n */\n protected abstract validate(props: T): void;\n}\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","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 { 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 { randomUUID } from 'node:crypto';\n\nimport { EntityId } from '../types';\n\ntype Primitive = boolean | number | string | null;\n\ntype Serializable =\n | Primitive\n | Serializable[]\n | { [key: string]: Serializable };\n\nexport type DomainEventPayload = Record<string, Serializable>;\n\nexport type UnixTimestampMillis = number;\n\ntype DomainEventProps<AggregateId extends EntityId, Payload> = {\n id?: string;\n aggregateId: AggregateId;\n schemaVersion: number;\n occurredAt: UnixTimestampMillis;\n payload: Payload;\n};\n\n// Abstract base class for domain events\nexport abstract class DomainEvent<\n AggregateId extends EntityId = EntityId,\n T extends DomainEventPayload = DomainEventPayload,\n> {\n public readonly aggregateId: AggregateId;\n\n public abstract readonly eventName: string;\n public readonly id: string;\n public readonly occurredAt: number;\n public readonly payload: Readonly<T>;\n public readonly schemaVersion: number;\n\n protected constructor(props: DomainEventProps<AggregateId, T>) {\n this.id = props.id ?? randomUUID();\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(): Readonly<{\n id: string;\n eventName: string;\n aggregateId: string;\n schemaVersion: number;\n occurredAt: UnixTimestampMillis;\n payload: T;\n }> {\n return {\n aggregateId: this.aggregateId.toString(),\n schemaVersion: this.schemaVersion,\n occurredAt: this.occurredAt,\n eventName: this.eventName,\n payload: this.payload,\n id: this.id,\n };\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 * 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 /**\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 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 { 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","/**\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 { DomainError } from './domain.error';\n\n/**\n * Represents the result of an operation, which can be either a success or a failure.\n *\n * This is a functional programming pattern that helps avoid throwing exceptions\n * and makes error handling explicit in the type system. It's commonly used in\n * Domain-Driven Design (DDD) to represent domain operation outcomes.\n *\n * @template T The type of a successful result.\n * @template E The type of the error in case of failure (defaults to DomainError).\n *\n * @example\n * ```typescript\n * // Creating a successful result\n * const success = Result.ok({ id: 1, name: 'John' });\n * if (success.isSuccess) {\n * const user = success.getValue(); // { id: 1, name: 'John' }\n * }\n *\n * // Creating a failed result\n * const failure = Result.fail(new InvalidUserError('User not found'));\n * if (failure.isFailure) {\n * const error = failure.getError(); // InvalidUserError instance\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Using in a domain service\n * function createUser(name: string): Result<User, DomainError> {\n * if (!name || name.trim().length === 0) {\n * return Result.fail(new InvalidValueError('Name cannot be empty'));\n * }\n *\n * const user = new User(name);\n * return Result.ok(user);\n * }\n *\n * const result = createUser('John Doe');\n * if (result.isSuccess) {\n * console.log('User created:', result.getValue());\n * } else {\n * console.error('Failed:', result.getError()?.message);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Chaining operations\n * function validateEmail(email: string): Result<string, DomainError> {\n * if (!email.includes('@')) {\n * return Result.fail(new InvalidValueError('Invalid email format'));\n * }\n * return Result.ok(email);\n * }\n *\n * function createAccount(email: string): Result<Account, DomainError> {\n * const emailResult = validateEmail(email);\n * if (emailResult.isFailure) {\n * return emailResult; // Forward the error\n * }\n *\n * const account = new Account(emailResult.getValue()!);\n * return Result.ok(account);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Working with async operations\n * async function fetchUser(id: number): Promise<Result<User, DomainError>> {\n * try {\n * const user = await userRepository.findById(id);\n * if (!user) {\n * return Result.fail(new NotFoundError(`User ${id} not found`));\n * }\n * return Result.ok(user);\n * } catch (error) {\n * return Result.fail(new SystemError('Database connection failed'));\n * }\n * }\n *\n * const result = await fetchUser(123);\n * if (result.isSuccess) {\n * // Handle success\n * } else {\n * // Handle failure\n * }\n * ```\n */\nexport class Result<T, E> {\n /**\n * Indicates if the result is a failure.\n *\n * @example\n * ```typescript\n * const result = Result.fail(new Error('Something went wrong'));\n * if (result.isFailure) {\n * // Handle error case\n * console.error(result.getError());\n * }\n * ```\n */\n public readonly isFailure: boolean;\n\n /**\n * Indicates if the result is a success.\n *\n * @example\n * ```typescript\n * const result = Result.ok(42);\n * if (result.isSuccess) {\n * // Handle success case\n * const value = result.getValue(); // 42\n * }\n * ```\n */\n public readonly isSuccess: boolean;\n\n /**\n * The error, if any.\n * @private\n */\n private readonly _error?: E;\n\n /**\n * The value, if any.\n * @private\n */\n private readonly _value?: T;\n\n /**\n * Private constructor to enforce the use of static methods.\n * @param params.value The value on success.\n * @param params.error The error on failure.\n * @private\n */\n private constructor(params: { value?: T; error?: E }) {\n const hasError = params.error !== undefined;\n this.isFailure = hasError;\n this.isSuccess = !hasError;\n\n this._value = params.value;\n this._error = params.error;\n Object.freeze(this);\n }\n\n /**\n * Creates a failed result.\n *\n * @template T The type of a successful result (never for failure).\n * @template E The type of the error (defaults to DomainError).\n * @param error The error object.\n * @returns {Result<T, E>} A failed Result instance.\n *\n * @example\n * ```typescript\n * // With DomainError\n * class InvalidValueError extends DomainError {\n * public readonly code = 'DOMAIN.INVALID_VALUE' as const;\n * constructor(message: string) {\n * super({ message });\n * }\n * }\n *\n * const result = Result.fail(new InvalidValueError('Value must be positive'));\n * // result.isFailure === true\n * // result.getError() === InvalidValueError instance\n * ```\n *\n * @example\n * ```typescript\n * // With custom error type\n * interface ValidationError {\n * field: string;\n * message: string;\n * }\n *\n * const result = Result.fail<never, ValidationError>({\n * field: 'email',\n * message: 'Invalid email format'\n * });\n * ```\n *\n * @example\n * ```typescript\n * // In a validation function\n * function validateAge(age: number): Result<number, DomainError> {\n * if (age < 0) {\n * return Result.fail(new InvalidValueError('Age cannot be negative'));\n * }\n * if (age > 150) {\n * return Result.fail(new InvalidValueError('Age seems unrealistic'));\n * }\n * return Result.ok(age);\n * }\n * ```\n */\n public static fail<T = never, E = DomainError>(error: E): Result<T, E> {\n return new Result({ error });\n }\n\n /**\n * Creates a successful result.\n *\n * @template T The type of the successful result.\n * @template E The type of the error (never for success).\n * @param value The success value.\n * @returns {Result<T, E>} A successful Result instance.\n *\n * @example\n * ```typescript\n * // With primitive value\n * const result = Result.ok(42);\n * // result.isSuccess === true\n * // result.getValue() === 42\n * ```\n *\n * @example\n * ```typescript\n * // With object\n * const user = { id: 1, name: 'John', email: 'john@example.com' };\n * const result = Result.ok(user);\n * if (result.isSuccess) {\n * const savedUser = result.getValue(); // { id: 1, name: 'John', ... }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With domain entity\n * class User {\n * constructor(public readonly id: number, public readonly name: string) {}\n * }\n *\n * function createUser(name: string): Result<User, DomainError> {\n * const user = new User(Date.now(), name);\n * return Result.ok(user);\n * }\n *\n * const result = createUser('Alice');\n * if (result.isSuccess) {\n * console.log(`Created user: ${result.getValue()?.name}`);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With void/undefined (for operations that don't return a value)\n * function deleteUser(id: number): Result<void, DomainError> {\n * // ... deletion logic ...\n * return Result.ok(undefined);\n * }\n *\n * const result = deleteUser(123);\n * if (result.isSuccess) {\n * console.log('User deleted successfully');\n * }\n * ```\n */\n public static ok<T, E = never>(value: T): Result<T, E> {\n return new Result({ value });\n }\n\n /**\n * Returns the error if present, otherwise undefined.\n *\n * **Note:** Always check `isFailure` before calling this method to ensure\n * the result is actually a failure. This method will return `undefined` for\n * successful results.\n *\n * @returns {E | undefined} The error or undefined if successful.\n *\n * @example\n * ```typescript\n * const result = Result.fail(new InvalidValueError('Invalid input'));\n *\n * if (result.isFailure) {\n * const error = result.getError();\n * if (error) {\n * console.error(`Error code: ${error.code}, Message: ${error.message}`);\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Safe error handling pattern\n * function handleResult<T>(result: Result<T, DomainError>): void {\n * if (result.isFailure) {\n * const error = result.getError();\n * if (error) {\n * // Log error with metadata\n * console.error({\n * code: error.code,\n * message: error.message,\n * metadata: error.metadata\n * });\n * }\n * }\n * }\n * ```\n */\n public getError(): E | undefined {\n return this._error;\n }\n\n /**\n * Returns the value if present, otherwise undefined.\n *\n * **Note:** Always check `isSuccess` before calling this method to ensure\n * the result is actually a success. This method will return `undefined` for\n * failed results.\n *\n * @returns {T | undefined} The value or undefined if failed.\n *\n * @example\n * ```typescript\n * const result = Result.ok({ id: 1, name: 'John' });\n *\n * if (result.isSuccess) {\n * const user = result.getValue();\n * if (user) {\n * console.log(`User: ${user.name} (ID: ${user.id})`);\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Type-safe value extraction\n * function processUser(result: Result<User, DomainError>): void {\n * if (result.isSuccess) {\n * const user = result.getValue();\n * // TypeScript knows user is User | undefined here\n * if (user) {\n * // Process the user\n * userRepository.save(user);\n * }\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Using non-null assertion (use with caution)\n * const result = Result.ok(42);\n * if (result.isSuccess) {\n * const value = result.getValue()!; // Safe because we checked isSuccess\n * console.log(value * 2); // 84\n * }\n * ```\n */\n public getValue(): T | undefined {\n return this._value;\n }\n\n /**\n * Type guard for failure.\n */\n public isFailureResult(): this is Result<never, E> {\n return this.isFailure;\n }\n\n /**\n * Type guard for success.\n */\n public isSuccessResult(): this is Result<T, never> {\n return this.isSuccess;\n }\n}\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 { 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"],"mappings":"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","names":["Entity","props","_a","other","AggregateRoot","Entity","domainEvent","events","getCauseInfo","cause","DomainError","message","code","metadata","__spreadValues","PrimitiveValueObject","value","other","deepEqual","deepFreeze","obj","seen","item","key","ValueObject","_ValueObject","props","deepFreeze","vo","other","deepEqual","EntityValidationError","DomainError","message","cause","InvalidValueObjectError","DomainError","message","randomUUID","DomainEvent","props","_a","v4","z","_UUID","PrimitiveValueObject","value","v4","result","InvalidValueObjectError","z","UUID","AggregateId","UUID","HttpStatus","HttpStatusMessage","Result","_Result","params","hasError","error","value","ApplicationError","code","HttpStatusMessage","isOperational","status","metadata","message","cause","unwrapValueObject","input","seen","result","item","ValueObject","value","key","v","ensureObject"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rineex/ddd",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Domain Driven Design package for Rineex core modules",
|
|
5
5
|
"author": "Rineex Team",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"url": "https://github.com/rineex/core.git",
|
|
44
44
|
"directory": "packages/ddd"
|
|
45
45
|
},
|
|
46
|
+
"homepage": "https://github.com/rineex/core/tree/main/packages/ddd",
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"fast-deep-equal": "3.1.3",
|
|
48
49
|
"isbot": "5.1.32",
|