@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 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: { correlationId: "corr-abc" } }
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
- .withHeader("correlationId", "corr-abc")
124
+ .withCorrelation({ id: "corr-abc", type: "HttpRequest" })
125
125
  .withHeader("source", "api-gateway");
126
126
 
127
- command.getHeader("correlationId"); // "corr-abc"
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. Controls transaction propagation and provides access to the underlying database client.
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 // Start new transaction
267
- Propagation.EXISTING // Use existing transaction (error if none)
268
- Propagation.NESTED // Nested transaction (savepoint)
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-DQXtakLF.js';
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-DQXtakLF.js';
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"]}
@@ -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-DQXtakLF.js';
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;
@@ -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 = {};
@@ -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"]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.5.1",
6
+ "version": "0.7.0",
7
7
  "type": "module",
8
8
  "description": "Core utilities/types/base classes for hexai projects",
9
9
  "license": "MIT",