@hexaijs/core 0.5.1 → 0.7.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 +55 -8
- package/dist/{event-store-DQXtakLF.d.ts → event-store-UbTJSE4H.d.ts} +10 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +15 -0
- package/dist/test/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ interface MessageOptions {
|
|
|
63
63
|
// Pass custom headers
|
|
64
64
|
const command = new CreateOrderCommand(
|
|
65
65
|
{ customerId: "customer-123", items: [] },
|
|
66
|
-
{ headers: {
|
|
66
|
+
{ headers: { correlation: { id: "corr-abc", type: "HttpRequest" } } }
|
|
67
67
|
);
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -121,14 +121,36 @@ Use `withHeader()` to create a new message instance with an additional header:
|
|
|
121
121
|
|
|
122
122
|
```typescript
|
|
123
123
|
const command = new CreateOrderCommand({ customerId: "c-123", items: [] })
|
|
124
|
-
.
|
|
124
|
+
.withCorrelation({ id: "corr-abc", type: "HttpRequest" })
|
|
125
125
|
.withHeader("source", "api-gateway");
|
|
126
126
|
|
|
127
|
-
command.
|
|
127
|
+
command.getCorrelation(); // { id: "corr-abc", type: "HttpRequest" }
|
|
128
128
|
```
|
|
129
129
|
|
|
130
130
|
`withHeader()` returns a new immutable instance - the original message is not modified.
|
|
131
131
|
|
|
132
|
+
#### Message Tracing
|
|
133
|
+
|
|
134
|
+
Messages support built-in correlation and causation tracing for distributed message flows:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { Message, MessageTrace } from "@hexaijs/core";
|
|
138
|
+
|
|
139
|
+
// Get message identity as trace
|
|
140
|
+
const trace: MessageTrace = command.asTrace();
|
|
141
|
+
// { id: "msg-uuid", type: "order.create-order" }
|
|
142
|
+
|
|
143
|
+
// Set correlation (root message in chain)
|
|
144
|
+
const correlated = command.withCorrelation({ id: "req-123", type: "HttpRequest" });
|
|
145
|
+
correlated.getCorrelation(); // { id: "req-123", type: "HttpRequest" }
|
|
146
|
+
|
|
147
|
+
// Set causation (direct parent message)
|
|
148
|
+
const caused = event.withCausation(command.asTrace());
|
|
149
|
+
caused.getCausation(); // { id: "cmd-uuid", type: "order.create-order" }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
When events are published through `ApplicationContext.publish()`, causation and correlation headers are set automatically.
|
|
153
|
+
|
|
132
154
|
### DomainEvent
|
|
133
155
|
|
|
134
156
|
`DomainEvent` extends `Message` for events that represent something that happened in your domain.
|
|
@@ -251,7 +273,7 @@ async function confirmOrder(
|
|
|
251
273
|
|
|
252
274
|
### UnitOfWork
|
|
253
275
|
|
|
254
|
-
Interface for transaction management.
|
|
276
|
+
Interface for transaction management. The primary API is `scope()` for defining transaction boundaries.
|
|
255
277
|
|
|
256
278
|
```typescript
|
|
257
279
|
import { UnitOfWork, Propagation } from "@hexaijs/core";
|
|
@@ -262,10 +284,29 @@ interface OrderApplicationContext {
|
|
|
262
284
|
getOrderRepository(): OrderRepository;
|
|
263
285
|
}
|
|
264
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 Propagation
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
265
306
|
// Transaction propagation options
|
|
266
|
-
Propagation.NEW //
|
|
267
|
-
Propagation.EXISTING //
|
|
268
|
-
Propagation.NESTED //
|
|
307
|
+
Propagation.NEW // Always start a new transaction
|
|
308
|
+
Propagation.EXISTING // Join existing transaction, or create new if none
|
|
309
|
+
Propagation.NESTED // Create a savepoint within current transaction
|
|
269
310
|
```
|
|
270
311
|
|
|
271
312
|
### EventStore
|
|
@@ -336,12 +377,18 @@ throw new DuplicateObjectError("Order with this ID already exists");
|
|
|
336
377
|
|--------|-------------|
|
|
337
378
|
| `Message<P>` | Base message class with headers and typed payload |
|
|
338
379
|
| `MessageOptions` | Options for Message constructor (`{ headers? }`) |
|
|
380
|
+
| `MessageTrace` | Interface for message identity (`{ id, type }`) used in tracing |
|
|
381
|
+
| `Message.asTrace()` | Returns this message's identity as `MessageTrace` |
|
|
382
|
+
| `Message.getCausation()` | Gets the direct parent message trace |
|
|
383
|
+
| `Message.getCorrelation()` | Gets the root message trace in the chain |
|
|
384
|
+
| `Message.withCausation(trace)` | Sets causation, returns new instance |
|
|
385
|
+
| `Message.withCorrelation(trace)` | Sets correlation, returns new instance |
|
|
339
386
|
| `DomainEvent<P>` | Message subclass for domain events |
|
|
340
387
|
| `AggregateRoot<T>` | Base class for aggregates with event collection |
|
|
341
388
|
| `Id<T>` | Value object for typed identities |
|
|
342
389
|
| `Identifiable<T>` | Interface for entities with identity |
|
|
343
390
|
| `Repository<T>` | Interface for aggregate persistence |
|
|
344
|
-
| `UnitOfWork` | Interface for transaction management |
|
|
391
|
+
| `UnitOfWork` | Interface for transaction management (`scope()` for boundaries, `wrap()` deprecated) |
|
|
345
392
|
| `Propagation` | Enum for transaction propagation modes |
|
|
346
393
|
| `EventStore` | Interface for event store implementations |
|
|
347
394
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
type Version = string | number | undefined;
|
|
2
|
+
interface MessageTrace {
|
|
3
|
+
id: string;
|
|
4
|
+
type: string;
|
|
5
|
+
}
|
|
2
6
|
interface MessageHeaders {
|
|
3
7
|
id: string;
|
|
4
8
|
type: string;
|
|
@@ -47,6 +51,11 @@ declare class Message<Payload = any> {
|
|
|
47
51
|
};
|
|
48
52
|
protected serializePayload(payload: Payload): unknown;
|
|
49
53
|
asType<M extends MessageClass>(cls: M): InstanceType<M>;
|
|
54
|
+
asTrace(): MessageTrace;
|
|
55
|
+
getCausation(): MessageTrace | undefined;
|
|
56
|
+
getCorrelation(): MessageTrace | undefined;
|
|
57
|
+
withCausation(trace: MessageTrace): this;
|
|
58
|
+
withCorrelation(trace: MessageTrace): this;
|
|
50
59
|
}
|
|
51
60
|
type AnyMessage = Message;
|
|
52
61
|
type MessageClass<T extends Message = Message> = {
|
|
@@ -70,4 +79,4 @@ interface EventStore {
|
|
|
70
79
|
fetch(afterPosition: number, limit?: number): Promise<EventStoreFetchResult>;
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
export { type AnyMessage as A, type EventStore as E, Message as M, type PayloadOf as P, type StoredEvent as S, type EventStoreFetchResult as a, type MessageClass as b, type MessageHeaders as c, type MessageOptions as d };
|
|
82
|
+
export { type AnyMessage as A, type EventStore as E, Message as M, type PayloadOf as P, type StoredEvent as S, type EventStoreFetchResult as a, type MessageClass as b, type MessageHeaders as c, type MessageOptions as d, type MessageTrace as e };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as Message } from './event-store-
|
|
2
|
-
export { A as AnyMessage, E as EventStore, a as EventStoreFetchResult, b as MessageClass, c as MessageHeaders, d as MessageOptions, P as PayloadOf, S as StoredEvent } from './event-store-
|
|
1
|
+
import { M as Message } from './event-store-UbTJSE4H.js';
|
|
2
|
+
export { A as AnyMessage, E as EventStore, a as EventStoreFetchResult, b as MessageClass, c as MessageHeaders, d as MessageOptions, e as MessageTrace, P as PayloadOf, S as StoredEvent } from './event-store-UbTJSE4H.js';
|
|
3
3
|
|
|
4
4
|
declare class DomainEvent<P extends Record<string, any> = Record<string, unknown>> extends Message<P> {
|
|
5
5
|
static getIntent(): string;
|
|
@@ -61,6 +61,8 @@ interface BaseUnitOfWorkOptions {
|
|
|
61
61
|
}
|
|
62
62
|
interface UnitOfWork<Client = unknown, Options extends BaseUnitOfWorkOptions = BaseUnitOfWorkOptions> {
|
|
63
63
|
getClient(): Client;
|
|
64
|
+
scope<T>(fn: () => Promise<T>, options?: Partial<Options>): Promise<T>;
|
|
65
|
+
/** @deprecated Use scope() for transaction boundaries and withClient() for client access. */
|
|
64
66
|
wrap<T>(fn: (client: Client) => Promise<T>, options?: Partial<Options>): Promise<T>;
|
|
65
67
|
}
|
|
66
68
|
|
package/dist/index.js
CHANGED
|
@@ -134,6 +134,21 @@ var Message = class {
|
|
|
134
134
|
const { headers, payload } = this.serialize();
|
|
135
135
|
return cls.from(payload, headers);
|
|
136
136
|
}
|
|
137
|
+
asTrace() {
|
|
138
|
+
return { id: this.getMessageId(), type: this.getMessageType() };
|
|
139
|
+
}
|
|
140
|
+
getCausation() {
|
|
141
|
+
return this.getHeader("causation");
|
|
142
|
+
}
|
|
143
|
+
getCorrelation() {
|
|
144
|
+
return this.getHeader("correlation");
|
|
145
|
+
}
|
|
146
|
+
withCausation(trace) {
|
|
147
|
+
return this.withHeader("causation", trace);
|
|
148
|
+
}
|
|
149
|
+
withCorrelation(trace) {
|
|
150
|
+
return this.withHeader("correlation", trace);
|
|
151
|
+
}
|
|
137
152
|
};
|
|
138
153
|
function generateHeaderFor(cls, ...excludes) {
|
|
139
154
|
const headers = {};
|
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":"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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 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\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"],"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 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\n"]}
|
package/dist/test/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as Message, b as MessageClass, c as MessageHeaders, E as EventStore, S as StoredEvent, a as EventStoreFetchResult } from '../event-store-
|
|
1
|
+
import { M as Message, b as MessageClass, c as MessageHeaders, E as EventStore, S as StoredEvent, a as EventStoreFetchResult } from '../event-store-UbTJSE4H.js';
|
|
2
2
|
|
|
3
3
|
declare function expectMessagesToBeFullyEqual(messages: Message[], expectedMessages: Message[]): void;
|
|
4
4
|
declare function expectMessagesToContain(messages: Message[], expectedMessages: Message[]): void;
|
package/dist/test/index.js
CHANGED
|
@@ -238,6 +238,21 @@ var Message = class {
|
|
|
238
238
|
const { headers, payload } = this.serialize();
|
|
239
239
|
return cls.from(payload, headers);
|
|
240
240
|
}
|
|
241
|
+
asTrace() {
|
|
242
|
+
return { id: this.getMessageId(), type: this.getMessageType() };
|
|
243
|
+
}
|
|
244
|
+
getCausation() {
|
|
245
|
+
return this.getHeader("causation");
|
|
246
|
+
}
|
|
247
|
+
getCorrelation() {
|
|
248
|
+
return this.getHeader("correlation");
|
|
249
|
+
}
|
|
250
|
+
withCausation(trace) {
|
|
251
|
+
return this.withHeader("causation", trace);
|
|
252
|
+
}
|
|
253
|
+
withCorrelation(trace) {
|
|
254
|
+
return this.withHeader("correlation", trace);
|
|
255
|
+
}
|
|
241
256
|
};
|
|
242
257
|
function generateHeaderFor(cls, ...excludes) {
|
|
243
258
|
const headers = {};
|
package/dist/test/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/test/matchers.ts","../../src/test/partial-match.ts","../../src/test/utils.ts","../../src/test/expect.ts","../../src/message.ts","../../src/test/dummy-message.ts","../../src/test/in-memory-event-store.ts"],"names":["_","uuid"],"mappings":"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","file":"index.js","sourcesContent":["import _ from \"lodash\";\n\nimport { Message, MessageClass } from \"@/message\";\nimport { partialMatch } from \"./utils\";\nimport { expect } from \"./expect\";\n\nexport function expectMessagesToBeFullyEqual(\n messages: Message[],\n expectedMessages: Message[]\n): void {\n const actualSummary = formatMessagesSummary(messages);\n const expectedSummary = formatMessagesSummary(expectedMessages);\n\n expect(actualSummary, \"Messages should match\").toEqual(expectedSummary);\n\n for (let i = 0; i < Math.max(messages.length, expectedMessages.length); i++) {\n const actual = messages[i]?.serialize();\n const expected = expectedMessages[i]?.serialize();\n\n expect(actual, `message[${i}]`).toEqual(expected);\n }\n}\n\nexport function expectMessagesToContain(\n messages: Message[],\n expectedMessages: Message[]\n): void {\n for (const message of expectedMessages) {\n expectMessageToMatch(\n messages,\n message.getMessageType(),\n message.getPayload()\n );\n }\n}\n\nexport function expectMessageToMatch(\n messages: Message[],\n messageType: string | MessageClass<any>,\n payload: Record<string, unknown> = {}\n): void {\n const resolvedMessageType =\n typeof messageType === \"string\" ? messageType : messageType.getType();\n const sameTypeMessages = messages.filter(\n (msg) => msg.getMessageType() === resolvedMessageType\n );\n\n if (sameTypeMessages.length === 0) {\n const availableTypes = [...new Set(messages.map((m) => m.getMessageType()))];\n expect.fail(\n `Message not found: \"${resolvedMessageType}\"\\n\\n` +\n `Available message types:\\n` +\n (availableTypes.length === 0\n ? \" (none)\"\n : availableTypes.map((t) => ` - ${t}`).join(\"\\n\"))\n );\n }\n\n const found = sameTypeMessages.find((msg) =>\n partialMatch(msg.getPayload(), payload)\n );\n\n if (!found) {\n const closestMatch = findClosestMatch(sameTypeMessages, payload);\n\n expect(\n closestMatch.payload,\n `Found ${sameTypeMessages.length} message(s) of type \"${resolvedMessageType}\", ` +\n `but payload did not match.\\n` +\n `Showing closest match (${closestMatch.matchedKeys}/${closestMatch.totalKeys} keys matched):`\n ).toMatchObject(payload);\n }\n}\n\nfunction formatMessagesSummary(messages: Message[]): Array<{ type: string; payload: unknown }> {\n return messages.map((msg) => ({\n type: msg.getMessageType(),\n payload: msg.getPayload(),\n }));\n}\n\nfunction findClosestMatch(\n messages: Message[],\n expectedPayload: Record<string, unknown>\n): { payload: Record<string, unknown>; matchedKeys: number; totalKeys: number } {\n const expectedKeys = Object.keys(expectedPayload);\n let bestMatch = {\n payload: messages[0]?.getPayload() ?? {},\n matchedKeys: 0,\n totalKeys: expectedKeys.length,\n };\n\n for (const msg of messages) {\n const actualPayload = msg.getPayload();\n const matchedKeys = countMatchedKeys(actualPayload, expectedPayload);\n\n if (matchedKeys > bestMatch.matchedKeys) {\n bestMatch = { payload: actualPayload, matchedKeys, totalKeys: expectedKeys.length };\n }\n }\n\n return bestMatch;\n}\n\nfunction countMatchedKeys(\n actual: Record<string, unknown>,\n expected: Record<string, unknown>\n): number {\n let matched = 0;\n for (const key of Object.keys(expected)) {\n if (_.isEqual(actual[key], expected[key])) {\n matched++;\n }\n }\n return matched;\n}\n","import _ from \"lodash\";\n\nexport const anyString = Symbol.for(\"string\");\n\nexport const anyDate = Symbol.for(\"date\");\n\nexport function partialMatch(\n source: Record<any, any>,\n target: Record<any, any>\n): boolean {\n for (const key of Object.keys(target)) {\n const shouldRecurse =\n _.isObject(source[key]) && _.isObject(target[key]);\n\n if (shouldRecurse) {\n if (!partialMatch(source[key], target[key])) {\n return false;\n }\n } else {\n if (target[key] === anyString && typeof source[key] === \"string\") {\n continue;\n }\n\n if (target[key] === anyDate && source[key] instanceof Date) {\n continue;\n }\n\n if (!_.isEqual(source[key], target[key])) {\n return false;\n }\n }\n }\n\n return true;\n}\n","export { partialMatch } from \"./partial-match\";\n\nexport async function waitForTicks(number = 10): Promise<void> {\n for (let i = 0; i < number; i++) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n}\n\nexport async function waitForMs(number = 10): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, number));\n}\n\nexport async function waitFor(\n type: \"ticks\" | \"ms\" = \"ticks\",\n number = 10\n): Promise<void> {\n if (type === \"ticks\") {\n await waitForTicks(number);\n } else {\n await waitForMs(number);\n }\n}\n","import { RUNNING_HEXAI_TEST } from \"@/config\";\n\nexport let expect!: any;\n\n// if (RUNNING_HEXAI_TEST) {\n// import(\"vitest\").then(({ expect: expectStatic }) => {\n// expect = expectStatic;\n// });\n// }\n\nexport function setExpect(expectStatic: any) {\n expect = expectStatic;\n}\n","import { v4 as uuid } from \"uuid\";\n\ntype Version = string | number | undefined;\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\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 _ from \"lodash\";\n\nimport { Message, MessageHeaders } from \"@/message\";\n\nexport class DummyMessage extends Message<Record<never, never>> {\n static type = \"test.dummy-message\";\n\n public static create() {\n return new this({});\n }\n\n public static createMany(number: number) {\n return _.times(number, () => this.create());\n }\n\n public static from(\n _: Record<never, never>,\n headers?: MessageHeaders\n ): DummyMessage {\n return new this({}, { headers });\n }\n}\n","import { EventStore, EventStoreFetchResult, StoredEvent } from \"@/event-store\";\nimport { Message } from \"@/message\";\n\nexport class InMemoryEventStore implements EventStore {\n private events: StoredEvent[] = [];\n\n async store(event: Message): Promise<StoredEvent> {\n const position = this.events.length + 1;\n const storedEvent: StoredEvent = { position, event };\n this.events.push(storedEvent);\n return storedEvent;\n }\n\n async storeAll(events: Message[]): Promise<StoredEvent[]> {\n const storedEvents: StoredEvent[] = [];\n for (const event of events) {\n storedEvents.push(await this.store(event));\n }\n return storedEvents;\n }\n\n async fetch(\n afterPosition: number,\n limit?: number\n ): Promise<EventStoreFetchResult> {\n const lastPosition = await this.getLastPosition();\n let filtered = this.events.filter((e) => e.position > afterPosition);\n\n if (limit !== undefined) {\n filtered = filtered.slice(0, limit);\n }\n\n return {\n events: filtered,\n lastPosition,\n };\n }\n\n async getLastPosition(): Promise<number> {\n return this.events.length;\n }\n\n clear(): void {\n this.events = [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/test/matchers.ts","../../src/test/partial-match.ts","../../src/test/utils.ts","../../src/test/expect.ts","../../src/message.ts","../../src/test/dummy-message.ts","../../src/test/in-memory-event-store.ts"],"names":["_","uuid"],"mappings":"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","file":"index.js","sourcesContent":["import _ from \"lodash\";\n\nimport { Message, MessageClass } from \"@/message\";\nimport { partialMatch } from \"./utils\";\nimport { expect } from \"./expect\";\n\nexport function expectMessagesToBeFullyEqual(\n messages: Message[],\n expectedMessages: Message[]\n): void {\n const actualSummary = formatMessagesSummary(messages);\n const expectedSummary = formatMessagesSummary(expectedMessages);\n\n expect(actualSummary, \"Messages should match\").toEqual(expectedSummary);\n\n for (let i = 0; i < Math.max(messages.length, expectedMessages.length); i++) {\n const actual = messages[i]?.serialize();\n const expected = expectedMessages[i]?.serialize();\n\n expect(actual, `message[${i}]`).toEqual(expected);\n }\n}\n\nexport function expectMessagesToContain(\n messages: Message[],\n expectedMessages: Message[]\n): void {\n for (const message of expectedMessages) {\n expectMessageToMatch(\n messages,\n message.getMessageType(),\n message.getPayload()\n );\n }\n}\n\nexport function expectMessageToMatch(\n messages: Message[],\n messageType: string | MessageClass<any>,\n payload: Record<string, unknown> = {}\n): void {\n const resolvedMessageType =\n typeof messageType === \"string\" ? messageType : messageType.getType();\n const sameTypeMessages = messages.filter(\n (msg) => msg.getMessageType() === resolvedMessageType\n );\n\n if (sameTypeMessages.length === 0) {\n const availableTypes = [...new Set(messages.map((m) => m.getMessageType()))];\n expect.fail(\n `Message not found: \"${resolvedMessageType}\"\\n\\n` +\n `Available message types:\\n` +\n (availableTypes.length === 0\n ? \" (none)\"\n : availableTypes.map((t) => ` - ${t}`).join(\"\\n\"))\n );\n }\n\n const found = sameTypeMessages.find((msg) =>\n partialMatch(msg.getPayload(), payload)\n );\n\n if (!found) {\n const closestMatch = findClosestMatch(sameTypeMessages, payload);\n\n expect(\n closestMatch.payload,\n `Found ${sameTypeMessages.length} message(s) of type \"${resolvedMessageType}\", ` +\n `but payload did not match.\\n` +\n `Showing closest match (${closestMatch.matchedKeys}/${closestMatch.totalKeys} keys matched):`\n ).toMatchObject(payload);\n }\n}\n\nfunction formatMessagesSummary(messages: Message[]): Array<{ type: string; payload: unknown }> {\n return messages.map((msg) => ({\n type: msg.getMessageType(),\n payload: msg.getPayload(),\n }));\n}\n\nfunction findClosestMatch(\n messages: Message[],\n expectedPayload: Record<string, unknown>\n): { payload: Record<string, unknown>; matchedKeys: number; totalKeys: number } {\n const expectedKeys = Object.keys(expectedPayload);\n let bestMatch = {\n payload: messages[0]?.getPayload() ?? {},\n matchedKeys: 0,\n totalKeys: expectedKeys.length,\n };\n\n for (const msg of messages) {\n const actualPayload = msg.getPayload();\n const matchedKeys = countMatchedKeys(actualPayload, expectedPayload);\n\n if (matchedKeys > bestMatch.matchedKeys) {\n bestMatch = { payload: actualPayload, matchedKeys, totalKeys: expectedKeys.length };\n }\n }\n\n return bestMatch;\n}\n\nfunction countMatchedKeys(\n actual: Record<string, unknown>,\n expected: Record<string, unknown>\n): number {\n let matched = 0;\n for (const key of Object.keys(expected)) {\n if (_.isEqual(actual[key], expected[key])) {\n matched++;\n }\n }\n return matched;\n}\n","import _ from \"lodash\";\n\nexport const anyString = Symbol.for(\"string\");\n\nexport const anyDate = Symbol.for(\"date\");\n\nexport function partialMatch(\n source: Record<any, any>,\n target: Record<any, any>\n): boolean {\n for (const key of Object.keys(target)) {\n const shouldRecurse =\n _.isObject(source[key]) && _.isObject(target[key]);\n\n if (shouldRecurse) {\n if (!partialMatch(source[key], target[key])) {\n return false;\n }\n } else {\n if (target[key] === anyString && typeof source[key] === \"string\") {\n continue;\n }\n\n if (target[key] === anyDate && source[key] instanceof Date) {\n continue;\n }\n\n if (!_.isEqual(source[key], target[key])) {\n return false;\n }\n }\n }\n\n return true;\n}\n","export { partialMatch } from \"./partial-match\";\n\nexport async function waitForTicks(number = 10): Promise<void> {\n for (let i = 0; i < number; i++) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n}\n\nexport async function waitForMs(number = 10): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, number));\n}\n\nexport async function waitFor(\n type: \"ticks\" | \"ms\" = \"ticks\",\n number = 10\n): Promise<void> {\n if (type === \"ticks\") {\n await waitForTicks(number);\n } else {\n await waitForMs(number);\n }\n}\n","import { RUNNING_HEXAI_TEST } from \"@/config\";\n\nexport let expect!: any;\n\n// if (RUNNING_HEXAI_TEST) {\n// import(\"vitest\").then(({ expect: expectStatic }) => {\n// expect = expectStatic;\n// });\n// }\n\nexport function setExpect(expectStatic: any) {\n expect = expectStatic;\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 _ from \"lodash\";\n\nimport { Message, MessageHeaders } from \"@/message\";\n\nexport class DummyMessage extends Message<Record<never, never>> {\n static type = \"test.dummy-message\";\n\n public static create() {\n return new this({});\n }\n\n public static createMany(number: number) {\n return _.times(number, () => this.create());\n }\n\n public static from(\n _: Record<never, never>,\n headers?: MessageHeaders\n ): DummyMessage {\n return new this({}, { headers });\n }\n}\n","import { EventStore, EventStoreFetchResult, StoredEvent } from \"@/event-store\";\nimport { Message } from \"@/message\";\n\nexport class InMemoryEventStore implements EventStore {\n private events: StoredEvent[] = [];\n\n async store(event: Message): Promise<StoredEvent> {\n const position = this.events.length + 1;\n const storedEvent: StoredEvent = { position, event };\n this.events.push(storedEvent);\n return storedEvent;\n }\n\n async storeAll(events: Message[]): Promise<StoredEvent[]> {\n const storedEvents: StoredEvent[] = [];\n for (const event of events) {\n storedEvents.push(await this.store(event));\n }\n return storedEvents;\n }\n\n async fetch(\n afterPosition: number,\n limit?: number\n ): Promise<EventStoreFetchResult> {\n const lastPosition = await this.getLastPosition();\n let filtered = this.events.filter((e) => e.position > afterPosition);\n\n if (limit !== undefined) {\n filtered = filtered.slice(0, limit);\n }\n\n return {\n events: filtered,\n lastPosition,\n };\n }\n\n async getLastPosition(): Promise<number> {\n return this.events.length;\n }\n\n clear(): void {\n this.events = [];\n }\n}\n"]}
|