drimion 0.1.12 → 0.2.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/README.md +136 -44
- package/dist/cli/index.js +1 -1
- package/dist/cli/templates/repository.ts.hbs +3 -1
- package/dist/cli/templates/use-case.ts.hbs +1 -8
- package/dist/kernel/core/entity.ts +1 -1
- package/dist/kernel/core/index.ts +2 -0
- package/dist/kernel/core/specification.ts +180 -0
- package/dist/kernel/core/value-object.ts +1 -1
- package/dist/kernel/events/event-utils.ts +1 -1
- package/dist/kernel/events/index.ts +1 -3
- package/dist/kernel/events/server-event-manager.ts +1 -0
- package/dist/kernel/helpers/auto-mapper.ts +1 -1
- package/dist/kernel/helpers/getters-setters.ts +5 -5
- package/dist/kernel/helpers/index.ts +1 -7
- package/dist/kernel/index.ts +66 -64
- package/dist/kernel/{helpers → libs}/domain-classes.ts +3 -3
- package/dist/kernel/libs/index.ts +7 -1
- package/dist/kernel/libs/result.ts +1 -1
- package/dist/kernel/libs/utils.ts +6 -1
- package/dist/kernel/libs/validator.ts +13 -0
- package/dist/kernel/types/command.types.ts +1 -3
- package/dist/kernel/types/index.ts +4 -24
- package/dist/kernel/types/specification.types.ts +45 -0
- package/dist/kernel/types/utils.types.ts +2 -15
- package/package.json +1 -1
- /package/dist/kernel/{events → core}/domain-event.ts +0 -0
- /package/dist/kernel/{libs → helpers}/iterator.ts +0 -0
- /package/dist/kernel/{helpers → libs}/domain-error.ts +0 -0
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ A developer-first CLI tool for bringing Domain-Driven Design (DDD) primitives in
|
|
|
28
28
|
- [Entity](#entity)
|
|
29
29
|
- [Aggregate](#aggregate)
|
|
30
30
|
- [Repository](#repository)
|
|
31
|
+
- [Specification](#specification)
|
|
31
32
|
- [Use Cases](#use-cases)
|
|
32
33
|
- [API Reference](#api-reference)
|
|
33
34
|
- [Core API](#core-api)
|
|
@@ -83,7 +84,7 @@ Instead, it:
|
|
|
83
84
|
Domain-Driven Design organizes code into layers of responsibility. This library provides the building blocks for the **Domain Layer** and defines the contracts for the **Application** and **Infrastructure** layers.
|
|
84
85
|
|
|
85
86
|
```
|
|
86
|
-
|
|
87
|
+
┌──────────────────────────────────────────────────┐
|
|
87
88
|
│ Application Layer │
|
|
88
89
|
│ Use Cases · Commands · Queries · Event Handlers │
|
|
89
90
|
├──────────────────────────────────────────────────┤
|
|
@@ -111,7 +112,6 @@ Key DDD principles this library embraces:
|
|
|
111
112
|
| `ValueObject` | ❌ By value | ❌ Immutable | ❌ | Domain concept defined entirely by its properties |
|
|
112
113
|
| `Entity` | ✅ By `id` | ✅ | ❌ | Domain object with stable identity and lifecycle |
|
|
113
114
|
| `Aggregate` | ✅ By `id` | ✅ | ✅ | Consistency boundary; single entry point for mutations |
|
|
114
|
-
| `ID` | — | — | — | Typed unique identifier (UUID or custom) |
|
|
115
115
|
|
|
116
116
|
---
|
|
117
117
|
|
|
@@ -132,7 +132,7 @@ yarn dlx drimion init
|
|
|
132
132
|
pnpm dlx drimion init
|
|
133
133
|
|
|
134
134
|
# bun
|
|
135
|
-
|
|
135
|
+
bun x drimion init
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
Running `init -y` will skip interactive prompts and install defaults and:
|
|
@@ -336,14 +336,53 @@ await bus.publishAll(order.pullEvents()); // then publish
|
|
|
336
336
|
A **Repository** is the persistence contract for an aggregate. The interface is defined in the **domain layer** and implemented in the **infrastructure layer** — keeping your domain code free of storage concerns.
|
|
337
337
|
|
|
338
338
|
```typescript
|
|
339
|
-
|
|
340
|
-
|
|
339
|
+
// defined within the domain layer as port
|
|
340
|
+
interface IUserRepository extends BaseRepository<User> {
|
|
341
|
+
findById(id: UID): Promise<User | null>
|
|
342
|
+
save(user: User): Promise<void>
|
|
343
|
+
delete(id: UID): Promise<void>
|
|
344
|
+
exists(id: UID): Promise<boolean>
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// defined within the infra layer as adapter
|
|
348
|
+
class UserRepository extends IUserRepository {
|
|
349
|
+
async findById(id: string): Promise<User> {
|
|
350
|
+
/* ... */
|
|
351
|
+
}
|
|
352
|
+
async save(user: User): Promise<void> {
|
|
353
|
+
/* ... */
|
|
354
|
+
}
|
|
355
|
+
async delete(id: string): Promise<void> {
|
|
356
|
+
/* ... */
|
|
357
|
+
}
|
|
358
|
+
async exists(id: string): Promise<boolean> {
|
|
359
|
+
/* ... */
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
class DrizzleORMUserRepository extends IUserRepository {
|
|
364
|
+
async findById(id: string): Promise<User> {
|
|
365
|
+
/* ... */
|
|
366
|
+
}
|
|
367
|
+
async save(user: User): Promise<void> {
|
|
368
|
+
/* ... */
|
|
369
|
+
}
|
|
370
|
+
async delete(id: string): Promise<void> {
|
|
341
371
|
/* ... */
|
|
342
372
|
}
|
|
343
|
-
async
|
|
373
|
+
async exists(id: string): Promise<boolean> {
|
|
374
|
+
/* ... */
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
class PrismaORMUserRepository extends IUserRepository {
|
|
379
|
+
async findById(id: string): Promise<User> {
|
|
344
380
|
/* ... */
|
|
345
381
|
}
|
|
346
|
-
async
|
|
382
|
+
async save(user: User): Promise<void> {
|
|
383
|
+
/* ... */
|
|
384
|
+
}
|
|
385
|
+
async delete(id: string): Promise<void> {
|
|
347
386
|
/* ... */
|
|
348
387
|
}
|
|
349
388
|
async exists(id: string): Promise<boolean> {
|
|
@@ -352,6 +391,59 @@ class UserRepository extends BaseRepository<User> {
|
|
|
352
391
|
}
|
|
353
392
|
```
|
|
354
393
|
|
|
394
|
+
### Specification
|
|
395
|
+
|
|
396
|
+
A **Specification** encapsulates a single, named business rule as a pure predicate. Instead of scattering `if` conditions across use cases and domain methods, you give each rule a name that lives in your ubiquitous language — and compose rules together without subclassing.
|
|
397
|
+
|
|
398
|
+
Three scenarios where specifications belong:
|
|
399
|
+
|
|
400
|
+
1. **Validation** — guard Aggregate domain methods against invalid state transitions.
|
|
401
|
+
2. **In-memory selection** — filter already-loaded collections by business criteria.
|
|
402
|
+
3. **Construction criteria** — express what a valid candidate must look like before building something.
|
|
403
|
+
|
|
404
|
+
> ⚠️ Specifications are **not** intended to drive persistence queries. Repository methods should accept explicit parameters and delegate to the ORM/query builder directly — translating a spec into SQL couples domain rules to infrastructure.
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
// Define — implement `satisfiedBy`, not `isSatisfiedBy`
|
|
408
|
+
class MinimumOrderAmountSpec extends BaseSpecification<Order> {
|
|
409
|
+
constructor(private readonly minimum: number) { super(); }
|
|
410
|
+
|
|
411
|
+
protected satisfiedBy(order: Order): boolean {
|
|
412
|
+
return order.get('total') >= this.minimum;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
class IsPaidSpec extends BaseSpecification<Order> {
|
|
417
|
+
protected satisfiedBy(order: Order): boolean {
|
|
418
|
+
return order.get('isPaid') === true;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
class IsFraudSpec extends BaseSpecification<Order> {
|
|
423
|
+
protected satisfiedBy(order: Order): boolean {
|
|
424
|
+
return order.get('isFraud') === true;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Compose — .and() / .or() / .not()
|
|
429
|
+
const eligibleForShipping = new MinimumOrderAmountSpec(100)
|
|
430
|
+
.and(new IsPaidSpec())
|
|
431
|
+
.and(new IsFraudSpec().not());
|
|
432
|
+
|
|
433
|
+
// Guard inside an Aggregate domain method
|
|
434
|
+
ship(): void {
|
|
435
|
+
const spec = new MinimumOrderAmountSpec(100).and(new IsPaidSpec());
|
|
436
|
+
if (!spec.isSatisfiedBy(this)) {
|
|
437
|
+
throw new DomainError('Order is not eligible for shipping', { context: 'Order' });
|
|
438
|
+
}
|
|
439
|
+
this.change('status', 'shipped');
|
|
440
|
+
this.emit({ type: 'order:shipped' });
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// In-memory selection
|
|
444
|
+
const eligible = orders.filter(o => eligibleForShipping.isSatisfiedBy(o));
|
|
445
|
+
```
|
|
446
|
+
|
|
355
447
|
### Use Cases
|
|
356
448
|
|
|
357
449
|
Use cases are the entry points to your application logic. This library provides two interfaces to model them: `ICommand` for operations that mutate state, and `IQuery` for read-only operations.
|
|
@@ -498,6 +590,26 @@ abstract class BaseRepository<T extends Entity, ID = UID> {
|
|
|
498
590
|
}
|
|
499
591
|
```
|
|
500
592
|
|
|
593
|
+
#### Specification
|
|
594
|
+
|
|
595
|
+
```typescript
|
|
596
|
+
// Extend BaseSpecification<T> and implement satisfiedBy()
|
|
597
|
+
class MySpec extends BaseSpecification<MyType> {
|
|
598
|
+
protected satisfiedBy(candidate: MyType): boolean { /* rule */ }
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Evaluate
|
|
602
|
+
spec.isSatisfiedBy(candidate) // → boolean
|
|
603
|
+
|
|
604
|
+
// Compose — each returns a new ISpecification<T>
|
|
605
|
+
spec.and(other) // both must be satisfied
|
|
606
|
+
spec.or(other) // either must be satisfied
|
|
607
|
+
spec.not() // negates this spec
|
|
608
|
+
|
|
609
|
+
// Chain freely
|
|
610
|
+
new ASpec().and(new BSpec()).or(new CSpec().not()).and(new DSpec())
|
|
611
|
+
```
|
|
612
|
+
|
|
501
613
|
#### Adapters
|
|
502
614
|
|
|
503
615
|
```typescript
|
|
@@ -592,44 +704,12 @@ Thrown automatically by:
|
|
|
592
704
|
- `init()` — `isValidProps()` returns `false`
|
|
593
705
|
- Event managers — event name missing `context:EventName` format
|
|
594
706
|
|
|
595
|
-
#### Iterator
|
|
596
|
-
|
|
597
|
-
Bi-directional sequential traversal over a collection.
|
|
598
|
-
|
|
599
|
-
```typescript
|
|
600
|
-
// Create
|
|
601
|
-
Iterator.create({ initialData, restartOnFinish?, returnCurrentOnReversion? })
|
|
602
|
-
|
|
603
|
-
// Navigate
|
|
604
|
-
iter.next() // move forward, return item
|
|
605
|
-
iter.prev() // move backward, return item
|
|
606
|
-
iter.hasNext() // boolean
|
|
607
|
-
iter.hasPrev() // boolean
|
|
608
|
-
iter.first() // peek first item (no cursor move)
|
|
609
|
-
iter.last() // peek last item (no cursor move)
|
|
610
|
-
iter.toFirst() // reset cursor to before first item
|
|
611
|
-
iter.toLast() // reset cursor to after last item
|
|
612
|
-
|
|
613
|
-
// Mutate
|
|
614
|
-
iter.add(item) // alias for addToEnd
|
|
615
|
-
iter.addToEnd(item)
|
|
616
|
-
iter.addToStart(item) // resets cursor
|
|
617
|
-
iter.removeFirst()
|
|
618
|
-
iter.removeLast()
|
|
619
|
-
iter.removeItem(item) // by JSON equality, adjusts cursor
|
|
620
|
-
iter.clear()
|
|
621
|
-
|
|
622
|
-
// Export
|
|
623
|
-
iter.toArray() // copy as plain array
|
|
624
|
-
iter.clone() // new Iterator with same items and config
|
|
625
|
-
iter.total() // item count
|
|
626
|
-
iter.isEmpty() // boolean
|
|
627
|
-
```
|
|
628
|
-
|
|
629
707
|
#### Validators & Utils
|
|
630
708
|
|
|
631
709
|
Built-in `validator` and `util` instances inherited by all domain classes, accessible as both instance and static members.
|
|
632
710
|
|
|
711
|
+
|
|
712
|
+
|
|
633
713
|
**Type guards:**
|
|
634
714
|
|
|
635
715
|
```typescript
|
|
@@ -642,6 +722,18 @@ this.validator.isID(v) this.validator.isEntity(v)
|
|
|
642
722
|
this.validator.isAggregate(v) this.validator.isValueObject(v)
|
|
643
723
|
```
|
|
644
724
|
|
|
725
|
+
> You can also import the instance directly from the kernel using the lowercase name exports
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
// import the instance instead of using it inside class through `this` context
|
|
729
|
+
import { validator, utils } from "{your-import-alias}"
|
|
730
|
+
|
|
731
|
+
function myFunctionOutsideOfClass() {
|
|
732
|
+
validator.string("Some string")
|
|
733
|
+
utils.string("some string")
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
645
737
|
**String checks:** `hasLengthBetweenOrEqual(min, max)` · `hasLengthGreaterThan(n)` · `hasLengthLessOrEqualTo(n)` · `hasLengthEqualTo(n)` · `isEmpty()` · `hasSpecialChar()` · `hasOnlyNumbers()` · `hasOnlyLetters()` · `match(regex)` · `isEqual(str)` · `includes(str)`
|
|
646
738
|
|
|
647
739
|
**Number checks:** `isPositive()` · `isNegative()` · `isGreaterThan(n)` · `isGreaterOrEqualTo(n)` · `isLessThan(n)` · `isLessOrEqualTo(n)` · `isBetween(min, max)` · `isBetweenOrEqual(min, max)` · `isEqualTo(n)` · `isInteger()` · `isSafeInteger()` · `isEven()`
|
|
@@ -663,8 +755,8 @@ Aggregate (emits) ──→ pullEvents() ──→ [ Your Transport ]
|
|
|
663
755
|
│
|
|
664
756
|
┌───────────────────┼──────────────────────┐
|
|
665
757
|
▼ ▼ ▼
|
|
666
|
-
EventBus Redis / Kafka
|
|
667
|
-
|
|
758
|
+
EventBus Redis / Kafka Custom IEventBus
|
|
759
|
+
(in-process) (distributed) (anything)
|
|
668
760
|
```
|
|
669
761
|
|
|
670
762
|
#### Domain Event
|
|
@@ -952,4 +1044,4 @@ remove it. So you can still remove the config file or library directory manually
|
|
|
952
1044
|
|
|
953
1045
|
## License
|
|
954
1046
|
|
|
955
|
-
Licensed under the MIT License.
|
|
1047
|
+
Licensed under the MIT License.
|
package/dist/cli/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { BaseRepository } from "@pokepulse/kernel";
|
|
2
2
|
// import { {{className}} } from "./path-to-your-{{lowerName}}";
|
|
3
3
|
|
|
4
|
-
export interface I{{className}}Repository extends BaseRepository<{{className}}> {
|
|
4
|
+
export interface I{{className}}Repository extends BaseRepository<{{className}}> {
|
|
5
|
+
// add your custom method
|
|
6
|
+
}
|
|
@@ -9,16 +9,9 @@ export type {{className}}Output = {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export type {{className}}Metadata = {
|
|
12
|
-
// define metadata - use never if no
|
|
12
|
+
// define metadata - use never if no metadata needed
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
* @note
|
|
17
|
-
* In order to avoid this TS error:
|
|
18
|
-
* `A class can only implement an object type or intersection of object types with statically known members`.
|
|
19
|
-
*
|
|
20
|
-
* Decide wether you want to use `IQuery` or `ICommand` then change the `IUseCase` usage into one of them.
|
|
21
|
-
*/
|
|
22
15
|
export class {{className}} implements IUseCase<{{className}}Input, {{className}}Output> {
|
|
23
16
|
public constructor() {}
|
|
24
17
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
/** biome-ignore-all lint/complexity/noThisInStatic: Required for polymorphic `this` in base factory (DDD pattern). */
|
|
3
3
|
|
|
4
4
|
import { AutoMapper } from "../helpers/auto-mapper";
|
|
5
|
-
import { DomainError } from "../helpers/domain-error";
|
|
6
5
|
import { GettersAndSetters } from "../helpers/getters-setters";
|
|
6
|
+
import { DomainError } from "../libs/domain-error";
|
|
7
7
|
import { Result } from "../libs/result";
|
|
8
8
|
import type { Adapter, IAdapter } from "../types/adapter.types";
|
|
9
9
|
import type {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { Aggregate } from "./aggregate";
|
|
2
|
+
export { BaseDomainEvent } from "./domain-event";
|
|
2
3
|
export { Entity } from "./entity";
|
|
3
4
|
export { ID, Id } from "./id";
|
|
4
5
|
export { BaseRepository } from "./repository";
|
|
6
|
+
export { BaseSpecification } from "./specification";
|
|
5
7
|
export { ValueObject } from "./value-object";
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { ISpecification } from "../types/specification.types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description
|
|
5
|
+
* Abstract base class for all domain specifications.
|
|
6
|
+
*
|
|
7
|
+
* A `Specification` is a pure predicate — it answers one question: does this
|
|
8
|
+
* candidate satisfy the rule? Nothing more. Error messaging and failure context
|
|
9
|
+
* are concerns of the caller (domain method, application layer) via `DomainError`.
|
|
10
|
+
*
|
|
11
|
+
* Specifications are:
|
|
12
|
+
* - **Named** — each subclass carries a meaningful ubiquitous-language name.
|
|
13
|
+
* - **Composable** — combine via `.and()`, `.or()`, `.not()` without subclassing.
|
|
14
|
+
* - **Pure** — no side effects, no I/O, no error state.
|
|
15
|
+
*
|
|
16
|
+
* **Three use cases (Evans):**
|
|
17
|
+
* 1. **Validation** — guard Aggregate domain methods against invalid state transitions.
|
|
18
|
+
* 2. **In-memory selection** — filter already-loaded collections.
|
|
19
|
+
* 3. **Construction criteria** — express what a valid object must look like.
|
|
20
|
+
*
|
|
21
|
+
* Specifications are **not** intended to drive persistence queries. Repository
|
|
22
|
+
* methods should use explicit parameters and delegate to the ORM/query builder
|
|
23
|
+
* directly — translating specs to SQL couples domain rules to infrastructure.
|
|
24
|
+
*
|
|
25
|
+
* **Implementing a specification**
|
|
26
|
+
*
|
|
27
|
+
* Extend `BaseSpecification<T>` and implement the `protected satisfiedBy()` method.
|
|
28
|
+
* Never override `isSatisfiedBy` — the base class owns that method.
|
|
29
|
+
*
|
|
30
|
+
* @template T The type of the candidate being evaluated.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // 1. Define
|
|
35
|
+
* class MinimumOrderAmountSpec extends BaseSpecification<Order> {
|
|
36
|
+
* constructor(private readonly minimum: number) { super(); }
|
|
37
|
+
*
|
|
38
|
+
* protected satisfiedBy(order: Order): boolean {
|
|
39
|
+
* return order.get('total') >= this.minimum;
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* // 2. Compose
|
|
44
|
+
* const eligible = new MinimumOrderAmountSpec(100)
|
|
45
|
+
* .and(new IsPaidSpec())
|
|
46
|
+
* .and(new IsFraudSpec().not());
|
|
47
|
+
*
|
|
48
|
+
* // 3. Guard inside an Aggregate domain method
|
|
49
|
+
* ship(): void {
|
|
50
|
+
* if (!new MinimumOrderAmountSpec(100).and(new IsPaidSpec()).isSatisfiedBy(this)) {
|
|
51
|
+
* throw new DomainError('Order is not eligible for shipping', { context: 'Order' });
|
|
52
|
+
* }
|
|
53
|
+
* this.change('status', 'shipped');
|
|
54
|
+
* this.emit({ type: 'order:shipped' });
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* // 4. In-memory selection
|
|
58
|
+
* const eligibleOrders = orders.filter(o => eligible.isSatisfiedBy(o));
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export abstract class BaseSpecification<T> implements ISpecification<T> {
|
|
62
|
+
/**
|
|
63
|
+
* @description
|
|
64
|
+
* Marker used by `Validator.isSpecification()` to identify this instance
|
|
65
|
+
* without an `instanceof` check (avoids circular imports).
|
|
66
|
+
* @internal
|
|
67
|
+
*/
|
|
68
|
+
protected readonly __kind = "Specification" as const;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @description
|
|
72
|
+
* Implement the business rule here.
|
|
73
|
+
*
|
|
74
|
+
* Called internally by `isSatisfiedBy`. Do **not** call this method directly.
|
|
75
|
+
*
|
|
76
|
+
* @param candidate The domain object to evaluate.
|
|
77
|
+
* @returns `true` if the candidate satisfies the rule; `false` otherwise.
|
|
78
|
+
*/
|
|
79
|
+
protected abstract satisfiedBy(candidate: T): boolean;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @description
|
|
83
|
+
* Evaluates whether the candidate satisfies this specification.
|
|
84
|
+
*
|
|
85
|
+
* Delegates to `satisfiedBy()` — do not override this method in subclasses.
|
|
86
|
+
*
|
|
87
|
+
* @param candidate The domain object to evaluate.
|
|
88
|
+
* @returns `true` if satisfied; `false` otherwise.
|
|
89
|
+
*/
|
|
90
|
+
public isSatisfiedBy(candidate: T): boolean {
|
|
91
|
+
return this.satisfiedBy(candidate);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @description
|
|
96
|
+
* Returns a new specification satisfied only when **both** this and `other`
|
|
97
|
+
* are satisfied (logical AND).
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const spec = new MinimumAmountSpec(100).and(new IsPaidSpec());
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
public and(other: ISpecification<T>): ISpecification<T> {
|
|
105
|
+
return new AndSpecification(this, other as BaseSpecification<T>);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @description
|
|
110
|
+
* Returns a new specification satisfied when **either** this or `other`
|
|
111
|
+
* is satisfied (logical OR).
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const spec = new IsVipSpec().or(new HasCouponSpec());
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
public or(other: ISpecification<T>): ISpecification<T> {
|
|
119
|
+
return new OrSpecification(this, other as BaseSpecification<T>);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @description
|
|
124
|
+
* Returns a new specification satisfied when this specification is **not**
|
|
125
|
+
* satisfied (logical NOT).
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* const spec = new IsFraudSpec().not();
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
public not(): ISpecification<T> {
|
|
133
|
+
return new NotSpecification(this);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ─── Composite Implementations ───────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
/** @internal */
|
|
140
|
+
class AndSpecification<T> extends BaseSpecification<T> {
|
|
141
|
+
constructor(
|
|
142
|
+
private readonly left: BaseSpecification<T>,
|
|
143
|
+
private readonly right: BaseSpecification<T>,
|
|
144
|
+
) {
|
|
145
|
+
super();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
protected satisfiedBy(candidate: T): boolean {
|
|
149
|
+
return (
|
|
150
|
+
this.left.isSatisfiedBy(candidate) && this.right.isSatisfiedBy(candidate)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @internal */
|
|
156
|
+
class OrSpecification<T> extends BaseSpecification<T> {
|
|
157
|
+
constructor(
|
|
158
|
+
private readonly left: BaseSpecification<T>,
|
|
159
|
+
private readonly right: BaseSpecification<T>,
|
|
160
|
+
) {
|
|
161
|
+
super();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
protected satisfiedBy(candidate: T): boolean {
|
|
165
|
+
return (
|
|
166
|
+
this.left.isSatisfiedBy(candidate) || this.right.isSatisfiedBy(candidate)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** @internal */
|
|
172
|
+
class NotSpecification<T> extends BaseSpecification<T> {
|
|
173
|
+
constructor(private readonly wrapped: BaseSpecification<T>) {
|
|
174
|
+
super();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
protected satisfiedBy(candidate: T): boolean {
|
|
178
|
+
return !this.wrapped.isSatisfiedBy(candidate);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
/** biome-ignore-all lint/complexity/noThisInStatic: Required for polymorphic `this` in base factory (DDD pattern). */
|
|
3
3
|
|
|
4
4
|
import { AutoMapper } from "../helpers/auto-mapper";
|
|
5
|
-
import { DomainError } from "../helpers/domain-error";
|
|
6
5
|
import { GettersAndSetters } from "../helpers/getters-setters";
|
|
6
|
+
import { DomainError } from "../libs/domain-error";
|
|
7
7
|
import { Result } from "../libs/result";
|
|
8
8
|
import type { Adapter, IAdapter } from "../types/adapter.types";
|
|
9
9
|
import type { IResult } from "../types/result.types";
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
export { BrowserEventManager
|
|
2
|
-
export { BaseDomainEvent } from "./domain-event";
|
|
1
|
+
export { BrowserEventManager } from "./browser-event-manager";
|
|
3
2
|
export { EventBus } from "./event-bus";
|
|
4
3
|
export { EventContext } from "./event-context";
|
|
5
4
|
export { BaseEventManager } from "./event-manager";
|
|
6
|
-
export { ValidateEventName } from "./event-utils";
|
|
7
5
|
export { ServerEventManager } from "./server-event-manager";
|
|
@@ -5,7 +5,7 @@ import type { AnyObject } from "../types/utils.types";
|
|
|
5
5
|
* @description
|
|
6
6
|
* Defines the shape of data used for mapping an entity's properties.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
interface EntityAutoMapperPayload {
|
|
9
9
|
id: string;
|
|
10
10
|
createdAt: Date;
|
|
11
11
|
updatedAt: Date;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { ID } from "../core/id";
|
|
2
|
+
import { DomainError } from "../libs/domain-error";
|
|
2
3
|
import utils, { type Utils } from "../libs/utils";
|
|
3
4
|
import validator, { type Validator } from "../libs/validator";
|
|
4
5
|
import type { UID } from "../types/uid.types";
|
|
5
6
|
import type { AnyObject } from "../types/utils.types";
|
|
6
|
-
import { DomainError } from "./domain-error";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @description
|
|
10
10
|
* Discriminator indicating which domain primitive this instance belongs to.
|
|
11
11
|
* Used internally to apply different mutation behaviors for `ValueObject` vs `Entity`.
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
type ParentKind = "ValueObject" | "Entity";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @description
|
|
@@ -20,7 +20,7 @@ export type ParentKind = "ValueObject" | "Entity";
|
|
|
20
20
|
* Disabling getters or setters can be useful when enforcing strict encapsulation
|
|
21
21
|
* or building read-only / write-only domain primitives.
|
|
22
22
|
*/
|
|
23
|
-
|
|
23
|
+
interface GettersSettersConfig {
|
|
24
24
|
/**
|
|
25
25
|
* @description
|
|
26
26
|
* When `true`, calling `get()` on this instance will throw a `DomainError`.
|
|
@@ -245,10 +245,10 @@ export abstract class GettersAndSetters<Props> {
|
|
|
245
245
|
* const raw = stringVo.get('value'); // 'hello'
|
|
246
246
|
* ```
|
|
247
247
|
*/
|
|
248
|
-
public get<Key extends keyof Props>(key: Key):
|
|
248
|
+
public get<Key extends keyof Props>(key: Key): Props[Key];
|
|
249
249
|
public get(
|
|
250
250
|
key: "value",
|
|
251
|
-
):
|
|
251
|
+
): "value" extends keyof Props ? Props["value"] : Props;
|
|
252
252
|
/** biome-ignore lint/suspicious/noExplicitAny: TS cannot correlate overload branches with runtime narrowing. */
|
|
253
253
|
public get(key: any): any {
|
|
254
254
|
if (this.config.disableGetters) {
|
|
@@ -1,7 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export { AutoMapper } from "./auto-mapper";
|
|
3
|
-
export type { CreateManyResult } from "./domain-classes";
|
|
4
|
-
export { DomainClasses } from "./domain-classes";
|
|
5
|
-
export { DomainError } from "./domain-error";
|
|
6
|
-
export type { GettersSettersConfig, ParentKind } from "./getters-setters";
|
|
7
|
-
export { GettersAndSetters } from "./getters-setters";
|
|
1
|
+
export { Iterator } from "./iterator";
|
package/dist/kernel/index.ts
CHANGED
|
@@ -1,77 +1,79 @@
|
|
|
1
1
|
// ── Core domain primitives ─────────────────────────────────────────────────
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
export {
|
|
3
|
+
Aggregate,
|
|
4
|
+
BaseDomainEvent,
|
|
5
|
+
BaseRepository,
|
|
6
|
+
BaseSpecification,
|
|
7
|
+
Entity,
|
|
8
|
+
ID,
|
|
9
|
+
Id,
|
|
10
|
+
ValueObject,
|
|
11
|
+
} from "./core";
|
|
12
|
+
|
|
7
13
|
// ── Event system ───────────────────────────────────────────────────────────
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export {
|
|
15
|
+
BaseEventManager,
|
|
16
|
+
BrowserEventManager,
|
|
17
|
+
EventBus,
|
|
18
|
+
EventContext,
|
|
19
|
+
ServerEventManager,
|
|
20
|
+
} from "./events";
|
|
21
|
+
|
|
16
22
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
17
|
-
export
|
|
18
|
-
export { AutoMapper } from "./helpers/auto-mapper";
|
|
19
|
-
export type { CreateManyResult } from "./helpers/domain-classes";
|
|
20
|
-
export { DomainClasses } from "./helpers/domain-classes";
|
|
21
|
-
export { DomainError } from "./helpers/domain-error";
|
|
22
|
-
export type {
|
|
23
|
-
GettersSettersConfig,
|
|
24
|
-
ParentKind,
|
|
25
|
-
} from "./helpers/getters-setters";
|
|
26
|
-
export { GettersAndSetters } from "./helpers/getters-setters";
|
|
23
|
+
export { Iterator } from "./helpers";
|
|
27
24
|
|
|
28
25
|
// ── Libs ───────────────────────────────────────────────────────────────────
|
|
29
|
-
export {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
export {
|
|
27
|
+
DomainClasses,
|
|
28
|
+
DomainError,
|
|
29
|
+
Result,
|
|
30
|
+
Utils,
|
|
31
|
+
UUID,
|
|
32
|
+
utils,
|
|
33
|
+
Validator,
|
|
34
|
+
validator,
|
|
35
|
+
} from "./libs";
|
|
33
36
|
|
|
34
37
|
// ── Types ──────────────────────────────────────────────────────────────────
|
|
35
|
-
export type { Adapter, IAdapter } from "./types/adapter.types";
|
|
36
|
-
export type { ICommand, IQuery, IUseCase } from "./types/command.types";
|
|
37
|
-
export type {
|
|
38
|
-
AggregateConstructor,
|
|
39
|
-
EntityConstructor,
|
|
40
|
-
EntityProps,
|
|
41
|
-
IEntityProps,
|
|
42
|
-
IEntitySettings,
|
|
43
|
-
} from "./types/entity.types";
|
|
44
38
|
export type {
|
|
39
|
+
Adapter,
|
|
40
|
+
AnyObject,
|
|
45
41
|
DomainEvent,
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
EventSubscriber,
|
|
43
|
+
IAdapter,
|
|
44
|
+
ICommand,
|
|
48
45
|
IEventBus,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
IResult,
|
|
53
|
-
IResultExecuteFn,
|
|
54
|
-
IResultHook,
|
|
55
|
-
IResultObject,
|
|
56
|
-
IResultOption,
|
|
57
|
-
} from "./types/result.types";
|
|
58
|
-
export type { UID } from "./types/uid.types";
|
|
59
|
-
export type {
|
|
60
|
-
AnyObject,
|
|
61
|
-
BuiltIns,
|
|
62
|
-
CalcOpt,
|
|
63
|
-
Primitive,
|
|
46
|
+
IQuery,
|
|
47
|
+
ISpecification,
|
|
48
|
+
IUseCase,
|
|
64
49
|
ReadonlyDeep,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
IValueObjectSettings,
|
|
68
|
-
ValueObjectConstructor,
|
|
69
|
-
} from "./types/value-object.types";
|
|
50
|
+
UID,
|
|
51
|
+
} from "./types";
|
|
70
52
|
|
|
71
53
|
// ── Utils ──────────────────────────────────────────────────────────────────
|
|
72
|
-
export {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
54
|
+
export {
|
|
55
|
+
DecrementTime,
|
|
56
|
+
DeepFreeze,
|
|
57
|
+
Divide,
|
|
58
|
+
EnsureNumber,
|
|
59
|
+
Float,
|
|
60
|
+
IncrementTime,
|
|
61
|
+
IsNaN,
|
|
62
|
+
Multiply,
|
|
63
|
+
ONE_DAY,
|
|
64
|
+
ONE_HOUR,
|
|
65
|
+
ONE_MINUTE,
|
|
66
|
+
ONE_MONTH,
|
|
67
|
+
ONE_WEEK,
|
|
68
|
+
ONE_YEAR,
|
|
69
|
+
RemoveChars,
|
|
70
|
+
RemoveSpaces,
|
|
71
|
+
ReplaceChars,
|
|
72
|
+
StableStringify,
|
|
73
|
+
Stringify,
|
|
74
|
+
Subtract,
|
|
75
|
+
Sum,
|
|
76
|
+
ToDecimal,
|
|
77
|
+
ToLong,
|
|
78
|
+
ToPrecision,
|
|
79
|
+
} from "./utils";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Iterator } from "../
|
|
2
|
-
import { Result } from "../libs/result";
|
|
1
|
+
import { Iterator } from "../helpers/iterator";
|
|
3
2
|
import type { IIterator } from "../types/iterator.types";
|
|
4
3
|
import type { IResult } from "../types/result.types";
|
|
4
|
+
import { Result } from "./result";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @description
|
|
@@ -10,7 +10,7 @@ import type { IResult } from "../types/result.types";
|
|
|
10
10
|
* Contains both an iterator over each individual creation result and a combined
|
|
11
11
|
* result that reflects the overall success or failure of the batch operation.
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
interface CreateManyResult {
|
|
14
14
|
/**
|
|
15
15
|
* @description
|
|
16
16
|
* An iterator over each individual `Result` produced during the batch creation.
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export { UUID } from "./crypto";
|
|
2
|
-
export {
|
|
2
|
+
export { DomainClasses } from "./domain-classes";
|
|
3
|
+
export { DomainError } from "./domain-error";
|
|
3
4
|
export { Result } from "./result";
|
|
4
5
|
export { Utils } from "./utils";
|
|
5
6
|
export { Validator } from "./validator";
|
|
7
|
+
|
|
8
|
+
import utils from "./utils";
|
|
9
|
+
import validator from "./validator";
|
|
10
|
+
|
|
11
|
+
export { utils, validator };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Iterator } from "../helpers/iterator";
|
|
1
2
|
import type { ICommand } from "../types/command.types";
|
|
2
3
|
import type { IIterator } from "../types/iterator.types";
|
|
3
4
|
import type {
|
|
@@ -8,7 +9,6 @@ import type {
|
|
|
8
9
|
IResultOption,
|
|
9
10
|
} from "../types/result.types";
|
|
10
11
|
import type { AnyObject } from "../types/utils.types";
|
|
11
|
-
import { Iterator } from "./iterator";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @description
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import type { CalcOpt } from "../types/utils.types";
|
|
2
1
|
import { DecrementTime, IncrementTime } from "../utils/date.utils";
|
|
3
2
|
import { Divide, Multiply, Subtract, Sum } from "../utils/number.utils";
|
|
4
3
|
import { RemoveChars, RemoveSpaces, ReplaceChars } from "../utils/string.utils";
|
|
5
4
|
import validator, { type Validator } from "./validator";
|
|
6
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @description
|
|
8
|
+
* Options for calculations, such as specifying precision.
|
|
9
|
+
*/
|
|
10
|
+
type CalcOpt = { fractionDigits: number };
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* @description
|
|
9
14
|
* Utility class providing various helper methods for date, number, and string manipulations.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Aggregate, Entity, ID, ValueObject } from "../core";
|
|
2
|
+
import type { BaseSpecification } from "../core/specification";
|
|
2
3
|
import type { AnyObject } from "../types/utils.types";
|
|
3
4
|
import { Stringify } from "../utils/string.utils";
|
|
4
5
|
|
|
@@ -170,6 +171,18 @@ export class Validator {
|
|
|
170
171
|
);
|
|
171
172
|
}
|
|
172
173
|
|
|
174
|
+
/**
|
|
175
|
+
* @description
|
|
176
|
+
* Checks if the provided value is a `BaseSpecification` instance.
|
|
177
|
+
*
|
|
178
|
+
* @param props The value to check.
|
|
179
|
+
* @returns True if the value is a Specification, false otherwise.
|
|
180
|
+
*/
|
|
181
|
+
public isSpecification(props: unknown): props is BaseSpecification<never> {
|
|
182
|
+
if (props === null || typeof props !== "object") return false;
|
|
183
|
+
return (props as AnyObject).__kind === "Specification";
|
|
184
|
+
}
|
|
185
|
+
|
|
173
186
|
/**
|
|
174
187
|
* @description
|
|
175
188
|
* Checks if the provided value is a symbol.
|
|
@@ -32,6 +32,4 @@ export interface IQuery<Input = void, Output = void> {
|
|
|
32
32
|
* @template Input The input type.
|
|
33
33
|
* @template Output The output type.
|
|
34
34
|
*/
|
|
35
|
-
export type IUseCase<Input = void, Output = void> =
|
|
36
|
-
| ICommand<Input, Output>
|
|
37
|
-
| IQuery<Input, Output>;
|
|
35
|
+
export type IUseCase<Input = void, Output = void> = ICommand<Input, Output>;
|
|
@@ -1,26 +1,6 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
Adapter,
|
|
3
|
-
IAdapter,
|
|
4
|
-
} from "./adapter.types";
|
|
5
|
-
|
|
1
|
+
export type { Adapter, IAdapter } from "./adapter.types";
|
|
6
2
|
export type { ICommand, IQuery, IUseCase } from "./command.types";
|
|
7
|
-
export type {
|
|
8
|
-
export type {
|
|
9
|
-
export type {
|
|
10
|
-
IResult,
|
|
11
|
-
IResultExecuteFn,
|
|
12
|
-
IResultHook,
|
|
13
|
-
IResultObject,
|
|
14
|
-
IResultOption,
|
|
15
|
-
} from "./result.types";
|
|
16
|
-
|
|
3
|
+
export type { DomainEvent, EventSubscriber, IEventBus } from "./event.types";
|
|
4
|
+
export type { ISpecification } from "./specification.types";
|
|
17
5
|
export type { UID } from "./uid.types";
|
|
18
|
-
export type {
|
|
19
|
-
AnyObject,
|
|
20
|
-
BuiltIns,
|
|
21
|
-
CalcOpt,
|
|
22
|
-
Primitive,
|
|
23
|
-
ReadonlyDeep,
|
|
24
|
-
} from "./utils.types";
|
|
25
|
-
|
|
26
|
-
export type { IValueObjectSettings } from "./value-object.types";
|
|
6
|
+
export type { AnyObject, ReadonlyDeep } from "./utils.types";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description
|
|
3
|
+
* Core contract for the Specification pattern.
|
|
4
|
+
*
|
|
5
|
+
* A specification encapsulates a single named business rule that can be evaluated
|
|
6
|
+
* against a candidate object. The rule is reusable, composable, and lives in the
|
|
7
|
+
* domain layer — not scattered across use cases or services.
|
|
8
|
+
*
|
|
9
|
+
* @template T The type of the candidate being evaluated.
|
|
10
|
+
*/
|
|
11
|
+
export interface ISpecification<T> {
|
|
12
|
+
/**
|
|
13
|
+
* @description
|
|
14
|
+
* Evaluates whether the candidate satisfies this specification.
|
|
15
|
+
*
|
|
16
|
+
* @param candidate The domain object to evaluate.
|
|
17
|
+
* @returns `true` if satisfied; `false` otherwise.
|
|
18
|
+
*/
|
|
19
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @description
|
|
23
|
+
* Returns a new specification that is satisfied only when **both** this and
|
|
24
|
+
* the provided specification are satisfied (logical AND).
|
|
25
|
+
*
|
|
26
|
+
* @param other The specification to AND with.
|
|
27
|
+
*/
|
|
28
|
+
and(other: ISpecification<T>): ISpecification<T>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @description
|
|
32
|
+
* Returns a new specification that is satisfied when **either** this or the
|
|
33
|
+
* provided specification is satisfied (logical OR).
|
|
34
|
+
*
|
|
35
|
+
* @param other The specification to OR with.
|
|
36
|
+
*/
|
|
37
|
+
or(other: ISpecification<T>): ISpecification<T>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description
|
|
41
|
+
* Returns a new specification that is satisfied when this specification is
|
|
42
|
+
* **not** satisfied (logical NOT).
|
|
43
|
+
*/
|
|
44
|
+
not(): ISpecification<T>;
|
|
45
|
+
}
|
|
@@ -8,13 +8,7 @@ export type AnyObject = Record<string, unknown>;
|
|
|
8
8
|
* @description
|
|
9
9
|
* Built-in types that are excluded from recursive transformations.
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @description
|
|
15
|
-
* Options for calculations, such as specifying precision.
|
|
16
|
-
*/
|
|
17
|
-
export type CalcOpt = { fractionDigits: number };
|
|
11
|
+
type BuiltIns = Primitive | Date | RegExp;
|
|
18
12
|
|
|
19
13
|
/**
|
|
20
14
|
* @description
|
|
@@ -41,14 +35,7 @@ type HasMultipleCallSignatures<
|
|
|
41
35
|
* @description
|
|
42
36
|
* Primitive types that are not recursively transformed.
|
|
43
37
|
*/
|
|
44
|
-
|
|
45
|
-
| null
|
|
46
|
-
| undefined
|
|
47
|
-
| string
|
|
48
|
-
| number
|
|
49
|
-
| boolean
|
|
50
|
-
| symbol
|
|
51
|
-
| bigint;
|
|
38
|
+
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
|
|
52
39
|
|
|
53
40
|
/**
|
|
54
41
|
* @description
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|