@hexaijs/core 0.6.0 → 0.8.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 +61 -5
- package/dist/index.d.ts +19 -1
- package/dist/index.js +48 -1
- package/dist/index.js.map +1 -1
- package/package.json +57 -57
package/README.md
CHANGED
|
@@ -273,7 +273,7 @@ async function confirmOrder(
|
|
|
273
273
|
|
|
274
274
|
### UnitOfWork
|
|
275
275
|
|
|
276
|
-
Interface for transaction management.
|
|
276
|
+
Interface for transaction management. The primary API is `scope()` for defining transaction boundaries.
|
|
277
277
|
|
|
278
278
|
```typescript
|
|
279
279
|
import { UnitOfWork, Propagation } from "@hexaijs/core";
|
|
@@ -284,10 +284,64 @@ interface OrderApplicationContext {
|
|
|
284
284
|
getOrderRepository(): OrderRepository;
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
+
// Define a transaction boundary with scope()
|
|
288
|
+
await unitOfWork.scope(async () => {
|
|
289
|
+
const order = Order.create(orderId, customerId);
|
|
290
|
+
await orderRepository.add(order);
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
#### scope() vs wrap()
|
|
295
|
+
|
|
296
|
+
| Method | Signature | Status |
|
|
297
|
+
|--------|-----------|--------|
|
|
298
|
+
| `scope(fn)` | `fn: () => Promise<T>` | **Recommended** |
|
|
299
|
+
| `wrap(fn)` | `fn: (client) => Promise<T>` | **Deprecated** |
|
|
300
|
+
|
|
301
|
+
`scope()` defines a transaction boundary without exposing the database client. Client access is handled separately through infrastructure methods (e.g., `withClient()` in `@hexaijs/postgres`). This separation enables lazy transaction initialization — the actual `BEGIN` is deferred until the first client access.
|
|
302
|
+
|
|
303
|
+
#### Transaction Lifecycle Hooks
|
|
304
|
+
|
|
305
|
+
Register callbacks that execute at specific points in the transaction lifecycle. Hooks must be registered inside an active `scope()`.
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
await unitOfWork.scope(async () => {
|
|
309
|
+
// Runs before COMMIT — if it throws, transaction rolls back instead
|
|
310
|
+
unitOfWork.beforeCommit(async () => {
|
|
311
|
+
await validateBusinessRules();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Runs after successful COMMIT (best-effort)
|
|
315
|
+
unitOfWork.afterCommit(async () => {
|
|
316
|
+
await sendNotification();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Runs after ROLLBACK (best-effort)
|
|
320
|
+
unitOfWork.afterRollback(async () => {
|
|
321
|
+
await cleanupResources();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
await repository.save(order);
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Hook execution semantics:**
|
|
329
|
+
|
|
330
|
+
| Hook | When | On failure |
|
|
331
|
+
|------|------|------------|
|
|
332
|
+
| `beforeCommit` | Before COMMIT, sequentially | Transaction rolls back instead of committing |
|
|
333
|
+
| `afterCommit` | After COMMIT, sequentially | Best-effort: all hooks run, errors collected into `AggregateError` |
|
|
334
|
+
| `afterRollback` | After ROLLBACK, sequentially | Best-effort: all hooks run, errors collected into `AggregateError` |
|
|
335
|
+
|
|
336
|
+
Hooks are scope-local — they are cleared after the transaction completes. Calling `beforeCommit()` / `afterCommit()` / `afterRollback()` outside a `scope()` throws an error.
|
|
337
|
+
|
|
338
|
+
#### Transaction Propagation
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
287
341
|
// Transaction propagation options
|
|
288
|
-
Propagation.NEW //
|
|
289
|
-
Propagation.EXISTING //
|
|
290
|
-
Propagation.NESTED //
|
|
342
|
+
Propagation.NEW // Always start a new transaction
|
|
343
|
+
Propagation.EXISTING // Join existing transaction, or create new if none
|
|
344
|
+
Propagation.NESTED // Create a savepoint within current transaction
|
|
291
345
|
```
|
|
292
346
|
|
|
293
347
|
### EventStore
|
|
@@ -369,7 +423,9 @@ throw new DuplicateObjectError("Order with this ID already exists");
|
|
|
369
423
|
| `Id<T>` | Value object for typed identities |
|
|
370
424
|
| `Identifiable<T>` | Interface for entities with identity |
|
|
371
425
|
| `Repository<T>` | Interface for aggregate persistence |
|
|
372
|
-
| `UnitOfWork` | Interface for transaction management |
|
|
426
|
+
| `UnitOfWork` | Interface for transaction management (`scope()` for boundaries, `wrap()` deprecated) |
|
|
427
|
+
| `TransactionHook` | Type for hook callbacks: `() => void \| Promise<void>` |
|
|
428
|
+
| `TransactionHooks` | Reusable hook registry with commit/rollback lifecycle execution |
|
|
373
429
|
| `Propagation` | Enum for transaction propagation modes |
|
|
374
430
|
| `EventStore` | Interface for event store implementations |
|
|
375
431
|
|
package/dist/index.d.ts
CHANGED
|
@@ -56,12 +56,30 @@ declare enum Propagation {
|
|
|
56
56
|
EXISTING = "existing",
|
|
57
57
|
NESTED = "nested"
|
|
58
58
|
}
|
|
59
|
+
type TransactionHook = () => void | Promise<void>;
|
|
59
60
|
interface BaseUnitOfWorkOptions {
|
|
60
61
|
propagation: Propagation;
|
|
61
62
|
}
|
|
62
63
|
interface UnitOfWork<Client = unknown, Options extends BaseUnitOfWorkOptions = BaseUnitOfWorkOptions> {
|
|
63
64
|
getClient(): Client;
|
|
65
|
+
scope<T>(fn: () => Promise<T>, options?: Partial<Options>): Promise<T>;
|
|
66
|
+
/** @deprecated Use scope() for transaction boundaries and withClient() for client access. */
|
|
64
67
|
wrap<T>(fn: (client: Client) => Promise<T>, options?: Partial<Options>): Promise<T>;
|
|
68
|
+
beforeCommit(hook: TransactionHook): void;
|
|
69
|
+
afterCommit(hook: TransactionHook): void;
|
|
70
|
+
afterRollback(hook: TransactionHook): void;
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
|
|
73
|
+
declare class TransactionHooks {
|
|
74
|
+
private beforeCommitHooks;
|
|
75
|
+
private afterCommitHooks;
|
|
76
|
+
private afterRollbackHooks;
|
|
77
|
+
addBeforeCommit(hook: TransactionHook): void;
|
|
78
|
+
addAfterCommit(hook: TransactionHook): void;
|
|
79
|
+
addAfterRollback(hook: TransactionHook): void;
|
|
80
|
+
executeCommit(commitFn: () => Promise<void>, rollbackFn: () => Promise<void>): Promise<void>;
|
|
81
|
+
executeRollback(rollbackFn: () => Promise<void>, cause?: unknown): Promise<void>;
|
|
82
|
+
private runBestEffort;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { AggregateRoot, type BaseUnitOfWorkOptions, DomainError, DomainEvent, DuplicateObjectError, Id, type IdOf, type Identifiable, InvariantNotSatisfiedError, Message, ObjectNotFoundError, Propagation, type Repository, RepositoryError, type TransactionHook, TransactionHooks, type UnitOfWork, ValidationError };
|
package/dist/index.js
CHANGED
|
@@ -224,6 +224,53 @@ var Propagation = /* @__PURE__ */ ((Propagation2) => {
|
|
|
224
224
|
return Propagation2;
|
|
225
225
|
})(Propagation || {});
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
// src/transaction-hooks.ts
|
|
228
|
+
var TransactionHooks = class {
|
|
229
|
+
beforeCommitHooks = [];
|
|
230
|
+
afterCommitHooks = [];
|
|
231
|
+
afterRollbackHooks = [];
|
|
232
|
+
addBeforeCommit(hook) {
|
|
233
|
+
this.beforeCommitHooks.push(hook);
|
|
234
|
+
}
|
|
235
|
+
addAfterCommit(hook) {
|
|
236
|
+
this.afterCommitHooks.push(hook);
|
|
237
|
+
}
|
|
238
|
+
addAfterRollback(hook) {
|
|
239
|
+
this.afterRollbackHooks.push(hook);
|
|
240
|
+
}
|
|
241
|
+
async executeCommit(commitFn, rollbackFn) {
|
|
242
|
+
try {
|
|
243
|
+
for (const hook of this.beforeCommitHooks) await hook();
|
|
244
|
+
} catch (e) {
|
|
245
|
+
await this.executeRollback(rollbackFn, e);
|
|
246
|
+
throw e;
|
|
247
|
+
}
|
|
248
|
+
await commitFn();
|
|
249
|
+
await this.runBestEffort(this.afterCommitHooks);
|
|
250
|
+
}
|
|
251
|
+
async executeRollback(rollbackFn, cause) {
|
|
252
|
+
await rollbackFn();
|
|
253
|
+
await this.runBestEffort(this.afterRollbackHooks, cause);
|
|
254
|
+
}
|
|
255
|
+
async runBestEffort(hooks, cause) {
|
|
256
|
+
const errors = [];
|
|
257
|
+
for (const hook of hooks) {
|
|
258
|
+
try {
|
|
259
|
+
await hook();
|
|
260
|
+
} catch (e) {
|
|
261
|
+
errors.push(e);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (errors.length > 0) {
|
|
265
|
+
throw cause !== void 0 ? new AggregateError(
|
|
266
|
+
errors,
|
|
267
|
+
"Transaction hook(s) failed",
|
|
268
|
+
{ cause }
|
|
269
|
+
) : new AggregateError(errors);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export { AggregateRoot, DomainError, DomainEvent, DuplicateObjectError, Id, InvariantNotSatisfiedError, Message, ObjectNotFoundError, Propagation, RepositoryError, TransactionHooks, ValidationError };
|
|
228
275
|
//# sourceMappingURL=index.js.map
|
|
229
276
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/domain/aggregate-root.ts","../src/domain/domain-error.ts","../src/message.ts","../src/domain/domain-event.ts","../src/domain/identifiable.ts","../src/domain/repository.ts","../src/unit-of-work.ts"],"names":["uuid","Propagation"],"mappings":";;;AAGO,IAAM,gBAAN,MAEP;AAAA,EAGI,YAA+B,EAAA,EAAO;AAAP,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AAAA,EAAQ;AAAA,EAF7B,SAAwB,EAAC;AAAA,EAI5B,KAAA,GAAW;AACd,IAAA,OAAO,IAAA,CAAK,EAAA;AAAA,EAChB;AAAA,EAEU,MAAM,KAAA,EAA0B;AACtC,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,EAC1B;AAAA,EAEO,iBAAA,GAAmC;AACtC,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,MAAM,CAAA;AAAA,EAC1B;AACJ;;;ACrBO,IAAM,WAAA,GAAN,cAA0B,KAAA,CAAM;AAAC;AAEjC,IAAM,0BAAA,GAAN,cAAyC,WAAA,CAAY;AAAA,EACxD,WAAA,CACoB,IAAA,EAChB,OAAA,GAAkB,EAAA,EACpB;AACE,IAAA,KAAA,CAAM,OAAO,CAAA;AAHG,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAIhB,IAAA,IAAA,CAAK,IAAA,GAAO,4BAAA;AAAA,EAChB;AACJ;AAEO,IAAM,eAAA,GAAN,cAA8B,0BAAA,CAA2B;AAAA,EAC5D,WAAA,CACoB,KAAA,EAChB,IAAA,EACA,OAAA,GAAkB,EAAA,EACpB;AACE,IAAA,KAAA,CAAM,MAAM,OAAO,CAAA;AAJH,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAMhB,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EAChB;AACJ;ACUO,IAAM,UAAN,MAA6B;AAAA,EA2ChC,WAAA,CACuB,SACnB,OAAA,EACF;AAFqB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAGnB,IAAA,IAAA,CAAK,UAAU,MAAA,CAAO,MAAA;AAAA,MACjB,KAAK,WAAA,CAAoB,YAAA,CAAa,OAAA,EAAS,OAAA,IAAW,EAAE;AAAA,KACjE;AAEA,IAAA,IAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AACxC,MAAA,MAAA,CAAO,OAAO,OAAO,CAAA;AAAA,IACzB;AAAA,EACJ;AAAA,EArDU,OAAA;AAAA,EAEV,OAAc,gBAAA,GAA4B;AACtC,IAAA,OAAQ,KAAa,aAAA,IAAiB,MAAA;AAAA,EAC1C;AAAA,EAEA,OAAc,OAAA,GAAkB;AAC5B,IAAA,OAAQ,IAAA,CAAa,QAAQ,IAAA,CAAK,IAAA;AAAA,EACtC;AAAA,EAEA,OAAc,SAAA,GAAgC;AAC1C,IAAA,OAAQ,KAAa,MAAA,IAAU,MAAA;AAAA,EACnC;AAAA,EAEA,OAAiB,cAAc,QAAA,EAAoC;AAC/D,IAAA,OAAO,iBAAA,CAAkB,IAAA,EAAa,GAAG,QAAQ,CAAA;AAAA,EACrD;AAAA,EAEA,OAAc,IAAA,CACV,UAAA,EACA,OAAA,EACO;AACP,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,qBAAA,CAAsB,UAAU,CAAA;AACrD,IAAA,OAAO,IAAI,KAAK,OAAA,EAAS;AAAA,MACrB,SAAS,OAAA,GACH,IAAA,CAAK,sBAAsB,OAAO,CAAA,GAClC,KAAK,UAAA;AAAW,KACzB,CAAA;AAAA,EACL;AAAA,EAEA,OAAiB,sBAAsB,UAAA,EAAsB;AACzD,IAAA,OAAO,UAAA;AAAA,EACX;AAAA,EAEA,OAAiB,sBACb,OAAA,EACc;AACd,IAAA,OAAA,CAAQ,SAAA,GAAY,IAAI,IAAA,CAAK,OAAA,CAAQ,SAAS,CAAA;AAE9C,IAAA,OAAO,OAAA;AAAA,EACX;AAAA,EAeA,OAAiB,aACb,OAAA,EACc;AACd,IAAA,OAAO;AAAA,MACH,GAAG,IAAA,CAAK,UAAA,CAAW,GAAG,MAAA,CAAO,IAAA,CAAK,OAAO,CAAC,CAAA;AAAA,MAC1C,GAAG;AAAA,KACP;AAAA,EACJ;AAAA,EAEO,UAAA,CAAW,OAAyB,KAAA,EAAsB;AAC7D,IAAA,MAAM,UAAA,GAAa,EAAE,GAAG,IAAA,CAAK,SAAS,CAAC,KAAK,GAAG,KAAA,EAAM;AACrD,IAAA,OAAO,IAAA,CAAK,iBAAiB,UAAU,CAAA;AAAA,EAC3C;AAAA,EAEU,KAAA,GAAc;AACpB,IAAA,MAAM,SAAS,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,cAAA,CAAe,IAAI,CAAC,CAAA;AACxD,IAAA,MAAA,CAAO,MAAA,CAAO,QAAQ,IAAI,CAAA;AAC1B,IAAA,OAAO,MAAA;AAAA,EACX;AAAA,EAEU,iBAAiB,OAAA,EAAwC;AAC/D,IAAA,MAAM,MAAA,GAAS,KAAK,KAAA,EAAM;AAC1B,IAAA,MAAA,CAAO,cAAA,CAAe,QAAQ,SAAA,EAAW;AAAA,MACrC,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,OAAO,CAAA;AAAA,MAC5B,QAAA,EAAU,KAAA;AAAA,MACV,YAAA,EAAc;AAAA,KACjB,CAAA;AACD,IAAA,OAAO,MAAA;AAAA,EACX;AAAA,EAEO,UAAsB,KAAA,EAA8B;AACvD,IAAA,OAAO,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,EAC7B;AAAA,EAEO,UAAA,GAA6B;AAChC,IAAA,OAAO,OAAO,MAAA,CAAO,EAAE,GAAG,IAAA,CAAK,SAAS,CAAA;AAAA,EAC5C;AAAA,EAEO,UAAA,GAAsB;AACzB,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EAChB;AAAA,EAEO,YAAA,GAAuB;AAC1B,IAAA,OAAO,KAAK,OAAA,CAAQ,EAAA;AAAA,EACxB;AAAA,EAEO,cAAA,GAAyB;AAC5B,IAAA,OAAO,KAAK,OAAA,CAAQ,IAAA;AAAA,EACxB;AAAA,EAEO,gBAAA,GAAwC;AAC3C,IAAA,OAAO,KAAK,OAAA,CAAQ,aAAA;AAAA,EACxB;AAAA,EAEO,YAAA,GAAqB;AACxB,IAAA,OAAO,KAAK,OAAA,CAAQ,SAAA;AAAA,EACxB;AAAA,EAEO,SAAA,GAAgC;AACnC,IAAA,OAAQ,IAAA,CAAK,YAA6B,SAAA,EAAU;AAAA,EACxD;AAAA,EAEO,MAAA,GAGL;AACE,IAAA,OAAO;AAAA,MACH,OAAA,EAAS,EAAE,GAAG,IAAA,CAAK,OAAA,EAAQ;AAAA,MAC3B,OAAA,EAAS,IAAA,CAAK,gBAAA,CAAiB,IAAA,CAAK,OAAO;AAAA,KAC/C;AAAA,EACJ;AAAA,EAEO,SAAA,GAGL;AACE,IAAA,OAAO,KAAK,KAAA,CAAM,IAAA,CAAK,UAAU,IAAA,CAAK,MAAA,EAAQ,CAAC,CAAA;AAAA,EACnD;AAAA,EAEU,iBAAiB,OAAA,EAA2B;AAClD,IAAA,OAAO,OAAA;AAAA,EACX;AAAA,EAEO,OAA+B,GAAA,EAAyB;AAC3D,IAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAQ,GAAI,KAAK,SAAA,EAAU;AAC5C,IAAA,OAAO,GAAA,CAAI,IAAA,CAAK,OAAA,EAAS,OAAO,CAAA;AAAA,EACpC;AAAA,EAEO,OAAA,GAAwB;AAC3B,IAAA,OAAO,EAAE,IAAI,IAAA,CAAK,YAAA,IAAgB,IAAA,EAAM,IAAA,CAAK,gBAAe,EAAE;AAAA,EAClE;AAAA,EAEO,YAAA,GAAyC;AAC5C,IAAA,OAAO,IAAA,CAAK,UAAwB,WAAW,CAAA;AAAA,EACnD;AAAA,EAEO,cAAA,GAA2C;AAC9C,IAAA,OAAO,IAAA,CAAK,UAAwB,aAAa,CAAA;AAAA,EACrD;AAAA,EAEO,cAAc,KAAA,EAA2B;AAC5C,IAAA,OAAO,IAAA,CAAK,UAAA,CAAW,WAAA,EAAa,KAAK,CAAA;AAAA,EAC7C;AAAA,EAEO,gBAAgB,KAAA,EAA2B;AAC9C,IAAA,OAAO,IAAA,CAAK,UAAA,CAAW,aAAA,EAAe,KAAK,CAAA;AAAA,EAC/C;AACJ;AAYA,SAAS,iBAAA,CACL,QACG,QAAA,EACW;AACd,EAAA,MAAM,UAAmC,EAAC;AAE1C,EAAA,IAAI,CAAC,QAAA,CAAS,QAAA,CAAS,IAAI,CAAA,EAAG;AAC1B,IAAA,OAAA,CAAQ,KAAKA,EAAA,EAAK;AAAA,EACtB;AAEA,EAAA,IAAI,CAAC,QAAA,CAAS,QAAA,CAAS,MAAM,CAAA,EAAG;AAC5B,IAAA,OAAA,CAAQ,IAAA,GAAO,IAAI,OAAA,EAAQ;AAAA,EAC/B;AAEA,EAAA,IAAI,CAAC,QAAA,CAAS,QAAA,CAAS,QAAQ,CAAA,EAAG;AAC9B,IAAA,MAAM,MAAA,GAAS,IAAI,SAAA,EAAU;AAC7B,IAAA,IAAI,WAAW,MAAA,EAAW;AACtB,MAAA,OAAA,CAAQ,MAAA,GAAS,MAAA;AAAA,IACrB;AAAA,EACJ;AAEA,EAAA,IAAI,CAAC,QAAA,CAAS,QAAA,CAAS,eAAe,CAAA,EAAG;AACrC,IAAA,MAAM,aAAA,GAAgB,IAAI,gBAAA,EAAiB;AAC3C,IAAA,IAAI,kBAAkB,MAAA,EAAW;AAC7B,MAAA,OAAA,CAAQ,aAAA,GAAgB,aAAA;AAAA,IAC5B;AAAA,EACJ;AAEA,EAAA,IAAI,CAAC,QAAA,CAAS,QAAA,CAAS,WAAW,CAAA,EAAG;AACjC,IAAA,OAAA,CAAQ,SAAA,uBAAgB,IAAA,EAAK;AAAA,EACjC;AAEA,EAAA,OAAO,OAAA;AACX;;;AC9OO,IAAM,WAAA,GAAN,cAEG,OAAA,CAAW;AAAA,EACjB,OAAgB,SAAA,GAAY;AACxB,IAAA,OAAO,OAAA;AAAA,EACX;AACJ;;;ACRO,IAAM,KAAN,MAAoC;AAAA,EAChC,YAA6B,KAAA,EAAU;AAAV,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAAW;AAAA,EAExC,QAAA,GAAc;AACjB,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EAChB;AAAA,EAEO,OAAO,KAAA,EAAuB;AACjC,IAAA,OACI,IAAA,CAAK,gBAAgB,KAAA,CAAM,WAAA,IAC3B,KAAK,QAAA,EAAS,KAAM,MAAM,QAAA,EAAS;AAAA,EAE3C;AACJ;;;ACLO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EACvC,YAAY,OAAA,EAAiB;AACzB,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EAChB;AACJ;AAEO,IAAM,oBAAA,GAAN,cAAmC,eAAA,CAAgB;AAAA,EACtD,YAAY,OAAA,EAAiB;AACzB,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,sBAAA;AAAA,EAChB;AACJ;AAEO,IAAM,mBAAA,GAAN,cAAkC,eAAA,CAAgB;AAAA,EACrD,YAAY,OAAA,EAAiB;AACzB,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EAChB;AACJ;;;AC3BO,IAAK,WAAA,qBAAAC,YAAAA,KAAL;AACH,EAAAA,aAAA,KAAA,CAAA,GAAM,KAAA;AACN,EAAAA,aAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,aAAA,QAAA,CAAA,GAAS,QAAA;AAHD,EAAA,OAAAA,YAAAA;AAAA,CAAA,EAAA,WAAA,IAAA,EAAA","file":"index.js","sourcesContent":["import { DomainEvent } from \"./domain-event\";\nimport { Id, Identifiable } from \"./identifiable\";\n\nexport class AggregateRoot<T extends Id<string | number>>\n implements Identifiable<T>\n{\n protected events: DomainEvent[] = [];\n\n constructor(protected readonly id: T) {}\n\n public getId(): T {\n return this.id;\n }\n\n protected raise(event: DomainEvent): void {\n this.events.push(event);\n }\n\n public getEventsOccurred(): DomainEvent[] {\n return [...this.events];\n }\n}\n","export class DomainError extends Error {}\n\nexport class InvariantNotSatisfiedError extends DomainError {\n constructor(\n public readonly code: string,\n message: string = \"\"\n ) {\n super(message);\n this.name = \"InvariantNotSatisfiedError\";\n }\n}\n\nexport class ValidationError extends InvariantNotSatisfiedError {\n constructor(\n public readonly field: string,\n code: string,\n message: string = \"\"\n ) {\n super(code, message);\n\n this.name = \"ValidationError\";\n }\n}\n","import { v4 as uuid } from \"uuid\";\n\ntype Version = string | number | undefined;\n\nexport interface MessageTrace {\n id: string;\n type: string;\n}\n\nexport interface MessageHeaders {\n id: string;\n type: string;\n intent?: string;\n schemaVersion?: Version;\n createdAt: Date;\n\n [key: string]: unknown;\n}\n\ntype ExtraHeaderField = Exclude<\n keyof MessageHeaders,\n \"id\" | \"type\" | \"intent\" | \"schemaVersion\" | \"createdAt\"\n>;\n\ntype RawMessageHeaders = Omit<MessageHeaders, \"createdAt\"> & {\n createdAt: string | Date;\n};\n\nexport interface MessageOptions {\n headers?: Record<string, unknown>;\n}\n\nexport class Message<Payload = any> {\n protected headers!: MessageHeaders;\n\n public static getSchemaVersion(): Version {\n return (this as any).schemaVersion ?? undefined;\n }\n\n public static getType(): string {\n return (this as any).type ?? this.name;\n }\n\n public static getIntent(): string | undefined {\n return (this as any).intent ?? undefined;\n }\n\n protected static newHeaders(...excludes: string[]): MessageHeaders {\n return generateHeaderFor(this as any, ...excludes);\n }\n\n public static from(\n rawPayload: Record<string, unknown>,\n headers?: RawMessageHeaders\n ): Message {\n const payload = this.deserializeRawPayload(rawPayload);\n return new this(payload, {\n headers: headers\n ? this.deserializeRawHeaders(headers)\n : this.newHeaders(),\n });\n }\n\n protected static deserializeRawPayload(rawPayload: any): any {\n return rawPayload;\n }\n\n protected static deserializeRawHeaders(\n headers: RawMessageHeaders\n ): MessageHeaders {\n headers.createdAt = new Date(headers.createdAt);\n\n return headers as MessageHeaders;\n }\n\n constructor(\n protected readonly payload: Payload,\n options?: MessageOptions\n ) {\n this.headers = Object.freeze(\n (this.constructor as any).mergeHeaders(options?.headers ?? {})\n );\n\n if (payload && typeof payload === \"object\") {\n Object.freeze(payload);\n }\n }\n\n protected static mergeHeaders(\n headers: Record<string, unknown>\n ): MessageHeaders {\n return {\n ...this.newHeaders(...Object.keys(headers)),\n ...headers,\n };\n }\n\n public withHeader(field: ExtraHeaderField, value: unknown): this {\n const newHeaders = { ...this.headers, [field]: value };\n return this.cloneWithHeaders(newHeaders);\n }\n\n protected clone(): this {\n const cloned = Object.create(Object.getPrototypeOf(this));\n Object.assign(cloned, this);\n return cloned;\n }\n\n protected cloneWithHeaders(headers: Record<string, unknown>): this {\n const cloned = this.clone();\n Object.defineProperty(cloned, \"headers\", {\n value: Object.freeze(headers),\n writable: false,\n configurable: true,\n });\n return cloned;\n }\n\n public getHeader<T = string>(field: string): T | undefined {\n return this.headers[field] as any;\n }\n\n public getHeaders(): MessageHeaders {\n return Object.freeze({ ...this.headers });\n }\n\n public getPayload(): Payload {\n return this.payload;\n }\n\n public getMessageId(): string {\n return this.headers.id;\n }\n\n public getMessageType(): string {\n return this.headers.type;\n }\n\n public getSchemaVersion(): Version | undefined {\n return this.headers.schemaVersion;\n }\n\n public getTimestamp(): Date {\n return this.headers.createdAt;\n }\n\n public getIntent(): string | undefined {\n return (this.constructor as MessageClass).getIntent();\n }\n\n public toJSON(): {\n headers: MessageHeaders;\n payload: unknown;\n } {\n return {\n headers: { ...this.headers },\n payload: this.serializePayload(this.payload),\n };\n }\n\n public serialize(): {\n headers: MessageHeaders;\n payload: Record<string, unknown>;\n } {\n return JSON.parse(JSON.stringify(this.toJSON()));\n }\n\n protected serializePayload(payload: Payload): unknown {\n return payload;\n }\n\n public asType<M extends MessageClass>(cls: M): InstanceType<M> {\n const { headers, payload } = this.serialize();\n return cls.from(payload, headers) as InstanceType<M>;\n }\n\n public asTrace(): MessageTrace {\n return { id: this.getMessageId(), type: this.getMessageType() };\n }\n\n public getCausation(): MessageTrace | undefined {\n return this.getHeader<MessageTrace>(\"causation\");\n }\n\n public getCorrelation(): MessageTrace | undefined {\n return this.getHeader<MessageTrace>(\"correlation\");\n }\n\n public withCausation(trace: MessageTrace): this {\n return this.withHeader(\"causation\", trace);\n }\n\n public withCorrelation(trace: MessageTrace): this {\n return this.withHeader(\"correlation\", trace);\n }\n}\n\nexport type AnyMessage = Message;\n\nexport type MessageClass<T extends Message = Message> = {\n getSchemaVersion(): Version;\n getType(): string;\n getIntent(): string | undefined;\n from: (rawPayload: any, header?: MessageHeaders) => T;\n new (...args: any[]): T;\n};\n\nfunction generateHeaderFor(\n cls: MessageClass,\n ...excludes: string[]\n): MessageHeaders {\n const headers: Partial<MessageHeaders> = {};\n\n if (!excludes.includes(\"id\")) {\n headers.id = uuid();\n }\n\n if (!excludes.includes(\"type\")) {\n headers.type = cls.getType();\n }\n\n if (!excludes.includes(\"intent\")) {\n const intent = cls.getIntent();\n if (intent !== undefined) {\n headers.intent = intent;\n }\n }\n\n if (!excludes.includes(\"schemaVersion\")) {\n const schemaVersion = cls.getSchemaVersion();\n if (schemaVersion !== undefined) {\n headers.schemaVersion = schemaVersion;\n }\n }\n\n if (!excludes.includes(\"createdAt\")) {\n headers.createdAt = new Date();\n }\n\n return headers as MessageHeaders;\n}\n\nexport type PayloadOf<M> = M extends Message<infer P> ? P : never;\n","import { Message } from \"@/message\";\n\nexport class DomainEvent<\n P extends Record<string, any> = Record<string, unknown>,\n> extends Message<P> {\n static override getIntent() {\n return \"event\";\n }\n}\n","export class Id<T extends string | number> {\n public constructor(private readonly value: T) {}\n\n public getValue(): T {\n return this.value;\n }\n\n public equals(other: Id<T>): boolean {\n return (\n this.constructor === other.constructor &&\n this.getValue() === other.getValue()\n );\n }\n}\n\nexport type IdOf<T> = T extends Identifiable<infer Id> ? Id : never;\n\nexport interface Identifiable<T extends Id<string | number> = Id<string>> {\n getId(): T;\n}\n","import { Identifiable, IdOf } from \"./identifiable\";\n\nexport interface Repository<T extends Identifiable<any>> {\n get(id: IdOf<T>): Promise<T>;\n add(entity: T): Promise<void>;\n update(entity: T): Promise<void>;\n}\n\nexport class RepositoryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"RepositoryError\";\n }\n}\n\nexport class DuplicateObjectError extends RepositoryError {\n constructor(message: string) {\n super(message);\n this.name = \"DuplicateObjectError\";\n }\n}\n\nexport class ObjectNotFoundError extends RepositoryError {\n constructor(message: string) {\n super(message);\n this.name = \"ObjectNotFoundError\";\n }\n}\n","export enum Propagation {\n NEW = \"new\",\n EXISTING = \"existing\",\n NESTED = \"nested\",\n}\n\nexport interface BaseUnitOfWorkOptions {\n propagation: Propagation;\n}\n\nexport interface UnitOfWork<\n Client = unknown,\n Options extends BaseUnitOfWorkOptions = BaseUnitOfWorkOptions,\n> {\n getClient(): Client;\n wrap<T>(\n fn: (client: Client) => Promise<T>,\n options?: Partial<Options>\n ): Promise<T>;\n}\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/domain/aggregate-root.ts","../src/domain/domain-error.ts","../src/message.ts","../src/domain/domain-event.ts","../src/domain/identifiable.ts","../src/domain/repository.ts","../src/unit-of-work.ts","../src/transaction-hooks.ts"],"names":["uuid","Propagation"],"mappings":"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","file":"index.js","sourcesContent":["import { DomainEvent } from \"./domain-event\";\nimport { Id, Identifiable } from \"./identifiable\";\n\nexport class AggregateRoot<T extends Id<string | number>>\n implements Identifiable<T>\n{\n protected events: DomainEvent[] = [];\n\n constructor(protected readonly id: T) {}\n\n public getId(): T {\n return this.id;\n }\n\n protected raise(event: DomainEvent): void {\n this.events.push(event);\n }\n\n public getEventsOccurred(): DomainEvent[] {\n return [...this.events];\n }\n}\n","export class DomainError extends Error {}\n\nexport class InvariantNotSatisfiedError extends DomainError {\n constructor(\n public readonly code: string,\n message: string = \"\"\n ) {\n super(message);\n this.name = \"InvariantNotSatisfiedError\";\n }\n}\n\nexport class ValidationError extends InvariantNotSatisfiedError {\n constructor(\n public readonly field: string,\n code: string,\n message: string = \"\"\n ) {\n super(code, message);\n\n this.name = \"ValidationError\";\n }\n}\n","import { v4 as uuid } from \"uuid\";\n\ntype Version = string | number | undefined;\n\nexport interface MessageTrace {\n id: string;\n type: string;\n}\n\nexport interface MessageHeaders {\n id: string;\n type: string;\n intent?: string;\n schemaVersion?: Version;\n createdAt: Date;\n\n [key: string]: unknown;\n}\n\ntype ExtraHeaderField = Exclude<\n keyof MessageHeaders,\n \"id\" | \"type\" | \"intent\" | \"schemaVersion\" | \"createdAt\"\n>;\n\ntype RawMessageHeaders = Omit<MessageHeaders, \"createdAt\"> & {\n createdAt: string | Date;\n};\n\nexport interface MessageOptions {\n headers?: Record<string, unknown>;\n}\n\nexport class Message<Payload = any> {\n protected headers!: MessageHeaders;\n\n public static getSchemaVersion(): Version {\n return (this as any).schemaVersion ?? undefined;\n }\n\n public static getType(): string {\n return (this as any).type ?? this.name;\n }\n\n public static getIntent(): string | undefined {\n return (this as any).intent ?? undefined;\n }\n\n protected static newHeaders(...excludes: string[]): MessageHeaders {\n return generateHeaderFor(this as any, ...excludes);\n }\n\n public static from(\n rawPayload: Record<string, unknown>,\n headers?: RawMessageHeaders\n ): Message {\n const payload = this.deserializeRawPayload(rawPayload);\n return new this(payload, {\n headers: headers\n ? this.deserializeRawHeaders(headers)\n : this.newHeaders(),\n });\n }\n\n protected static deserializeRawPayload(rawPayload: any): any {\n return rawPayload;\n }\n\n protected static deserializeRawHeaders(\n headers: RawMessageHeaders\n ): MessageHeaders {\n headers.createdAt = new Date(headers.createdAt);\n\n return headers as MessageHeaders;\n }\n\n constructor(\n protected readonly payload: Payload,\n options?: MessageOptions\n ) {\n this.headers = Object.freeze(\n (this.constructor as any).mergeHeaders(options?.headers ?? {})\n );\n\n if (payload && typeof payload === \"object\") {\n Object.freeze(payload);\n }\n }\n\n protected static mergeHeaders(\n headers: Record<string, unknown>\n ): MessageHeaders {\n return {\n ...this.newHeaders(...Object.keys(headers)),\n ...headers,\n };\n }\n\n public withHeader(field: ExtraHeaderField, value: unknown): this {\n const newHeaders = { ...this.headers, [field]: value };\n return this.cloneWithHeaders(newHeaders);\n }\n\n protected clone(): this {\n const cloned = Object.create(Object.getPrototypeOf(this));\n Object.assign(cloned, this);\n return cloned;\n }\n\n protected cloneWithHeaders(headers: Record<string, unknown>): this {\n const cloned = this.clone();\n Object.defineProperty(cloned, \"headers\", {\n value: Object.freeze(headers),\n writable: false,\n configurable: true,\n });\n return cloned;\n }\n\n public getHeader<T = string>(field: string): T | undefined {\n return this.headers[field] as any;\n }\n\n public getHeaders(): MessageHeaders {\n return Object.freeze({ ...this.headers });\n }\n\n public getPayload(): Payload {\n return this.payload;\n }\n\n public getMessageId(): string {\n return this.headers.id;\n }\n\n public getMessageType(): string {\n return this.headers.type;\n }\n\n public getSchemaVersion(): Version | undefined {\n return this.headers.schemaVersion;\n }\n\n public getTimestamp(): Date {\n return this.headers.createdAt;\n }\n\n public getIntent(): string | undefined {\n return (this.constructor as MessageClass).getIntent();\n }\n\n public toJSON(): {\n headers: MessageHeaders;\n payload: unknown;\n } {\n return {\n headers: { ...this.headers },\n payload: this.serializePayload(this.payload),\n };\n }\n\n public serialize(): {\n headers: MessageHeaders;\n payload: Record<string, unknown>;\n } {\n return JSON.parse(JSON.stringify(this.toJSON()));\n }\n\n protected serializePayload(payload: Payload): unknown {\n return payload;\n }\n\n public asType<M extends MessageClass>(cls: M): InstanceType<M> {\n const { headers, payload } = this.serialize();\n return cls.from(payload, headers) as InstanceType<M>;\n }\n\n public asTrace(): MessageTrace {\n return { id: this.getMessageId(), type: this.getMessageType() };\n }\n\n public getCausation(): MessageTrace | undefined {\n return this.getHeader<MessageTrace>(\"causation\");\n }\n\n public getCorrelation(): MessageTrace | undefined {\n return this.getHeader<MessageTrace>(\"correlation\");\n }\n\n public withCausation(trace: MessageTrace): this {\n return this.withHeader(\"causation\", trace);\n }\n\n public withCorrelation(trace: MessageTrace): this {\n return this.withHeader(\"correlation\", trace);\n }\n}\n\nexport type AnyMessage = Message;\n\nexport type MessageClass<T extends Message = Message> = {\n getSchemaVersion(): Version;\n getType(): string;\n getIntent(): string | undefined;\n from: (rawPayload: any, header?: MessageHeaders) => T;\n new (...args: any[]): T;\n};\n\nfunction generateHeaderFor(\n cls: MessageClass,\n ...excludes: string[]\n): MessageHeaders {\n const headers: Partial<MessageHeaders> = {};\n\n if (!excludes.includes(\"id\")) {\n headers.id = uuid();\n }\n\n if (!excludes.includes(\"type\")) {\n headers.type = cls.getType();\n }\n\n if (!excludes.includes(\"intent\")) {\n const intent = cls.getIntent();\n if (intent !== undefined) {\n headers.intent = intent;\n }\n }\n\n if (!excludes.includes(\"schemaVersion\")) {\n const schemaVersion = cls.getSchemaVersion();\n if (schemaVersion !== undefined) {\n headers.schemaVersion = schemaVersion;\n }\n }\n\n if (!excludes.includes(\"createdAt\")) {\n headers.createdAt = new Date();\n }\n\n return headers as MessageHeaders;\n}\n\nexport type PayloadOf<M> = M extends Message<infer P> ? P : never;\n","import { Message } from \"@/message\";\n\nexport class DomainEvent<\n P extends Record<string, any> = Record<string, unknown>,\n> extends Message<P> {\n static override getIntent() {\n return \"event\";\n }\n}\n","export class Id<T extends string | number> {\n public constructor(private readonly value: T) {}\n\n public getValue(): T {\n return this.value;\n }\n\n public equals(other: Id<T>): boolean {\n return (\n this.constructor === other.constructor &&\n this.getValue() === other.getValue()\n );\n }\n}\n\nexport type IdOf<T> = T extends Identifiable<infer Id> ? Id : never;\n\nexport interface Identifiable<T extends Id<string | number> = Id<string>> {\n getId(): T;\n}\n","import { Identifiable, IdOf } from \"./identifiable\";\n\nexport interface Repository<T extends Identifiable<any>> {\n get(id: IdOf<T>): Promise<T>;\n add(entity: T): Promise<void>;\n update(entity: T): Promise<void>;\n}\n\nexport class RepositoryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"RepositoryError\";\n }\n}\n\nexport class DuplicateObjectError extends RepositoryError {\n constructor(message: string) {\n super(message);\n this.name = \"DuplicateObjectError\";\n }\n}\n\nexport class ObjectNotFoundError extends RepositoryError {\n constructor(message: string) {\n super(message);\n this.name = \"ObjectNotFoundError\";\n }\n}\n","export enum Propagation {\n NEW = \"new\",\n EXISTING = \"existing\",\n NESTED = \"nested\",\n}\n\nexport type TransactionHook = () => void | Promise<void>;\n\nexport interface BaseUnitOfWorkOptions {\n propagation: Propagation;\n}\n\nexport interface UnitOfWork<\n Client = unknown,\n Options extends BaseUnitOfWorkOptions = BaseUnitOfWorkOptions,\n> {\n getClient(): Client;\n\n scope<T>(\n fn: () => Promise<T>,\n options?: Partial<Options>\n ): Promise<T>;\n\n /** @deprecated Use scope() for transaction boundaries and withClient() for client access. */\n wrap<T>(\n fn: (client: Client) => Promise<T>,\n options?: Partial<Options>\n ): Promise<T>;\n\n beforeCommit(hook: TransactionHook): void;\n afterCommit(hook: TransactionHook): void;\n afterRollback(hook: TransactionHook): void;\n}\n\n","import type { TransactionHook } from \"./unit-of-work\";\n\nexport class TransactionHooks {\n private beforeCommitHooks: TransactionHook[] = [];\n private afterCommitHooks: TransactionHook[] = [];\n private afterRollbackHooks: TransactionHook[] = [];\n\n addBeforeCommit(hook: TransactionHook): void {\n this.beforeCommitHooks.push(hook);\n }\n\n addAfterCommit(hook: TransactionHook): void {\n this.afterCommitHooks.push(hook);\n }\n\n addAfterRollback(hook: TransactionHook): void {\n this.afterRollbackHooks.push(hook);\n }\n\n async executeCommit(\n commitFn: () => Promise<void>,\n rollbackFn: () => Promise<void>\n ): Promise<void> {\n try {\n for (const hook of this.beforeCommitHooks) await hook();\n } catch (e) {\n await this.executeRollback(rollbackFn, e);\n throw e;\n }\n\n await commitFn();\n await this.runBestEffort(this.afterCommitHooks);\n }\n\n async executeRollback(\n rollbackFn: () => Promise<void>,\n cause?: unknown\n ): Promise<void> {\n await rollbackFn();\n await this.runBestEffort(this.afterRollbackHooks, cause);\n }\n\n private async runBestEffort(\n hooks: TransactionHook[],\n cause?: unknown\n ): Promise<void> {\n const errors: unknown[] = [];\n for (const hook of hooks) {\n try {\n await hook();\n } catch (e) {\n errors.push(e);\n }\n }\n if (errors.length > 0) {\n throw cause !== undefined\n ? new AggregateError(\n errors,\n \"Transaction hook(s) failed\",\n { cause }\n )\n : new AggregateError(errors);\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
"name": "@hexaijs/core",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.8.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"description": "Core utilities/types/base classes for hexai projects",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": "Sangwoo Hyun <wkdny.hyun@gmail.com>",
|
|
11
|
+
"contributors": [
|
|
12
|
+
"Seungcheol Baek <victoryiron.baek@gmail.com>",
|
|
13
|
+
"Donghyun Lee <edonghyun@naver.com>"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/workingdanny911/hexai.git",
|
|
18
|
+
"directory": "packages/core"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/workingdanny911/hexai#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/workingdanny911/hexai/issues"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"hexai",
|
|
26
|
+
"hexagonal",
|
|
27
|
+
"clean-architecture",
|
|
28
|
+
"ddd",
|
|
29
|
+
"cqrs",
|
|
30
|
+
"typescript",
|
|
31
|
+
"event-sourcing"
|
|
32
|
+
],
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"import": "./dist/index.js"
|
|
5
41
|
},
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"license": "MIT",
|
|
10
|
-
"author": "Sangwoo Hyun <wkdny.hyun@gmail.com>",
|
|
11
|
-
"contributors": [
|
|
12
|
-
"Seungcheol Baek <victoryiron.baek@gmail.com>",
|
|
13
|
-
"Donghyun Lee <edonghyun@naver.com>"
|
|
14
|
-
],
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/workingdanny911/hexai.git",
|
|
18
|
-
"directory": "packages/core"
|
|
42
|
+
"./test": {
|
|
43
|
+
"types": "./dist/test/index.d.ts",
|
|
44
|
+
"import": "./dist/test/index.js"
|
|
19
45
|
},
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"LICENSE"
|
|
36
|
-
],
|
|
37
|
-
"exports": {
|
|
38
|
-
".": {
|
|
39
|
-
"types": "./dist/index.d.ts",
|
|
40
|
-
"import": "./dist/index.js"
|
|
41
|
-
},
|
|
42
|
-
"./test": {
|
|
43
|
-
"types": "./dist/test/index.d.ts",
|
|
44
|
-
"import": "./dist/test/index.js"
|
|
45
|
-
},
|
|
46
|
-
"./package.json": "./package.json"
|
|
47
|
-
},
|
|
48
|
-
"scripts": {
|
|
49
|
-
"test": "vitest run",
|
|
50
|
-
"build": "tsup"
|
|
51
|
-
},
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"lodash": "^4.17.21",
|
|
54
|
-
"uuid": "^9.0.1"
|
|
55
|
-
},
|
|
56
|
-
"devDependencies": {
|
|
57
|
-
"@hexaijs/tooling": "workspace:*",
|
|
58
|
-
"@types/uuid": "^9.0.8"
|
|
59
|
-
}
|
|
60
|
-
}
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"lodash": "^4.17.21",
|
|
50
|
+
"uuid": "^9.0.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/uuid": "^9.0.8",
|
|
54
|
+
"@hexaijs/tooling": "0.1.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"build": "tsup"
|
|
59
|
+
}
|
|
60
|
+
}
|