@lssm/lib.bus 0.0.0-canary-20251217052941 → 0.0.0-canary-20251217060433
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auditableBus.d.ts +116 -0
- package/dist/eventBus.d.ts +21 -0
- package/dist/filtering.d.ts +82 -0
- package/dist/inMemoryBus.d.ts +16 -0
- package/dist/index.d.ts +7 -0
- package/dist/metadata.d.ts +81 -0
- package/dist/subscriber.d.ts +12 -0
- package/package.json +12 -12
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { EventBus } from "./eventBus.js";
|
|
2
|
+
import { EventMetadata } from "./metadata.js";
|
|
3
|
+
import { EventEnvelope, EventKey, EventSpec } from "@lssm/lib.contracts";
|
|
4
|
+
import { AnySchemaModel } from "@lssm/lib.schema";
|
|
5
|
+
|
|
6
|
+
//#region src/auditableBus.d.ts
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extended event envelope with metadata for auditing.
|
|
10
|
+
*/
|
|
11
|
+
interface AuditableEventEnvelope<T = unknown> extends EventEnvelope<T> {
|
|
12
|
+
metadata?: EventMetadata;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Audit record for persisting event history.
|
|
16
|
+
*/
|
|
17
|
+
interface AuditRecord {
|
|
18
|
+
id: string;
|
|
19
|
+
eventName: string;
|
|
20
|
+
eventVersion: number;
|
|
21
|
+
payload: unknown;
|
|
22
|
+
metadata?: EventMetadata;
|
|
23
|
+
occurredAt: string;
|
|
24
|
+
traceId?: string;
|
|
25
|
+
recordedAt: Date;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Audit storage adapter interface.
|
|
29
|
+
*/
|
|
30
|
+
interface AuditStorage {
|
|
31
|
+
/** Store an audit record */
|
|
32
|
+
store(record: AuditRecord): Promise<void>;
|
|
33
|
+
/** Query audit records */
|
|
34
|
+
query?(options: AuditQueryOptions): Promise<AuditRecord[]>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Options for querying audit records.
|
|
38
|
+
*/
|
|
39
|
+
interface AuditQueryOptions {
|
|
40
|
+
eventName?: string;
|
|
41
|
+
actorId?: string;
|
|
42
|
+
targetId?: string;
|
|
43
|
+
orgId?: string;
|
|
44
|
+
traceId?: string;
|
|
45
|
+
from?: Date;
|
|
46
|
+
to?: Date;
|
|
47
|
+
limit?: number;
|
|
48
|
+
offset?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Options for creating an auditable event bus.
|
|
52
|
+
*/
|
|
53
|
+
interface AuditableEventBusOptions {
|
|
54
|
+
/** Underlying event bus for publishing */
|
|
55
|
+
bus: EventBus;
|
|
56
|
+
/** Audit storage adapter */
|
|
57
|
+
storage?: AuditStorage;
|
|
58
|
+
/** Default metadata to include with all events */
|
|
59
|
+
defaultMetadata?: EventMetadata;
|
|
60
|
+
/** Filter function to decide which events to audit */
|
|
61
|
+
shouldAudit?: (eventName: string, envelope: AuditableEventEnvelope) => boolean;
|
|
62
|
+
/** Transform function for audit records */
|
|
63
|
+
transformAuditRecord?: (record: AuditRecord) => AuditRecord;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* AuditableEventBus wraps an EventBus to automatically record events for audit trail.
|
|
67
|
+
*/
|
|
68
|
+
declare class AuditableEventBus implements EventBus {
|
|
69
|
+
private readonly bus;
|
|
70
|
+
private readonly storage?;
|
|
71
|
+
private readonly defaultMetadata;
|
|
72
|
+
private readonly shouldAudit;
|
|
73
|
+
private readonly transformAuditRecord?;
|
|
74
|
+
constructor(options: AuditableEventBusOptions);
|
|
75
|
+
/**
|
|
76
|
+
* Publish an event and optionally record it for audit.
|
|
77
|
+
*/
|
|
78
|
+
publish(topic: EventKey, bytes: Uint8Array): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Subscribe to events from the underlying bus.
|
|
81
|
+
*/
|
|
82
|
+
subscribe(topic: EventKey | string, handler: (bytes: Uint8Array) => Promise<void>): Promise<() => Promise<void>>;
|
|
83
|
+
/**
|
|
84
|
+
* Query audit records (if storage supports it).
|
|
85
|
+
*/
|
|
86
|
+
queryAudit(options: AuditQueryOptions): Promise<AuditRecord[]>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create an auditable publisher that includes metadata.
|
|
90
|
+
*/
|
|
91
|
+
declare function makeAuditablePublisher<T extends AnySchemaModel>(bus: AuditableEventBus | EventBus, spec: EventSpec<T>, defaultMetadata?: EventMetadata): (payload: T, options?: {
|
|
92
|
+
traceId?: string;
|
|
93
|
+
metadata?: EventMetadata;
|
|
94
|
+
}) => Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* In-memory audit storage for development/testing.
|
|
97
|
+
*/
|
|
98
|
+
declare class InMemoryAuditStorage implements AuditStorage {
|
|
99
|
+
private records;
|
|
100
|
+
store(record: AuditRecord): Promise<void>;
|
|
101
|
+
query(options: AuditQueryOptions): Promise<AuditRecord[]>;
|
|
102
|
+
/**
|
|
103
|
+
* Get all records (for testing).
|
|
104
|
+
*/
|
|
105
|
+
getAll(): AuditRecord[];
|
|
106
|
+
/**
|
|
107
|
+
* Clear all records (for testing).
|
|
108
|
+
*/
|
|
109
|
+
clear(): void;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Create an auditable event bus with in-memory storage.
|
|
113
|
+
*/
|
|
114
|
+
declare function createAuditableEventBus(bus: EventBus, options?: Omit<AuditableEventBusOptions, 'bus'>): AuditableEventBus;
|
|
115
|
+
//#endregion
|
|
116
|
+
export { AuditQueryOptions, AuditRecord, AuditStorage, AuditableEventBus, AuditableEventBusOptions, AuditableEventEnvelope, InMemoryAuditStorage, createAuditableEventBus, makeAuditablePublisher };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { EventEnvelope, EventKey, EventSpec } from "@lssm/lib.contracts";
|
|
2
|
+
import { AnySchemaModel } from "@lssm/lib.schema";
|
|
3
|
+
|
|
4
|
+
//#region src/eventBus.d.ts
|
|
5
|
+
interface EventBus {
|
|
6
|
+
publish: (topic: EventKey, bytes: Uint8Array) => Promise<void>;
|
|
7
|
+
subscribe: (topic: EventKey | string,
|
|
8
|
+
// allow wildcard if your broker supports it
|
|
9
|
+
handler: (bytes: Uint8Array) => Promise<void>) => Promise<() => Promise<void>>;
|
|
10
|
+
}
|
|
11
|
+
/** Helper to encode a typed event envelope into JSON string */
|
|
12
|
+
declare function encodeEvent<T>(envelope: EventEnvelope<T>): Uint8Array;
|
|
13
|
+
/** Helper to decode JSON string into a typed event envelope */
|
|
14
|
+
declare function decodeEvent<T>(data: Uint8Array): EventEnvelope<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Create a typed publisher function for a given event spec.
|
|
17
|
+
* It ensures payload conformance at compile time and builds the correct topic.
|
|
18
|
+
*/
|
|
19
|
+
declare function makePublisher<T extends AnySchemaModel>(bus: EventBus, spec: EventSpec<T>): (payload: T, traceId?: string) => Promise<void>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { EventBus, decodeEvent, encodeEvent, makePublisher };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { EventBus } from "./eventBus.js";
|
|
2
|
+
import { AuditableEventEnvelope } from "./auditableBus.js";
|
|
3
|
+
import { EventKey, EventSpec } from "@lssm/lib.contracts";
|
|
4
|
+
import { AnySchemaModel } from "@lssm/lib.schema";
|
|
5
|
+
|
|
6
|
+
//#region src/filtering.d.ts
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Event filter configuration.
|
|
10
|
+
*/
|
|
11
|
+
interface EventFilter {
|
|
12
|
+
/** Filter by event name pattern (supports * wildcard) */
|
|
13
|
+
eventName?: string;
|
|
14
|
+
/** Filter by domain prefix */
|
|
15
|
+
domain?: string;
|
|
16
|
+
/** Filter by version */
|
|
17
|
+
version?: number;
|
|
18
|
+
/** Filter by actor ID */
|
|
19
|
+
actorId?: string;
|
|
20
|
+
/** Filter by organization ID */
|
|
21
|
+
orgId?: string;
|
|
22
|
+
/** Filter by custom tags */
|
|
23
|
+
tags?: Record<string, string>;
|
|
24
|
+
/** Custom predicate function */
|
|
25
|
+
predicate?: (envelope: AuditableEventEnvelope) => boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check if an event matches a filter.
|
|
29
|
+
*/
|
|
30
|
+
declare function matchesFilter(envelope: AuditableEventEnvelope, filter: EventFilter): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Create a filtered subscriber that only receives matching events.
|
|
33
|
+
*/
|
|
34
|
+
declare function createFilteredSubscriber(bus: EventBus, filter: EventFilter, handler: (envelope: AuditableEventEnvelope) => Promise<void>): (topic: EventKey | string) => Promise<() => Promise<void>>;
|
|
35
|
+
/**
|
|
36
|
+
* Domain-specific event bus that filters by domain prefix.
|
|
37
|
+
*/
|
|
38
|
+
declare class DomainEventBus {
|
|
39
|
+
private readonly bus;
|
|
40
|
+
private readonly domain;
|
|
41
|
+
constructor(bus: EventBus, domain: string);
|
|
42
|
+
/**
|
|
43
|
+
* Publish a domain event.
|
|
44
|
+
*/
|
|
45
|
+
publish<T extends AnySchemaModel>(spec: EventSpec<T>, payload: T, metadata?: AuditableEventEnvelope['metadata']): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Subscribe to all domain events.
|
|
48
|
+
*/
|
|
49
|
+
subscribeAll(handler: (envelope: AuditableEventEnvelope) => Promise<void>): Promise<() => Promise<void>>;
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe with filter.
|
|
52
|
+
*/
|
|
53
|
+
subscribeFiltered(filter: Omit<EventFilter, 'domain'>, handler: (envelope: AuditableEventEnvelope) => Promise<void>): Promise<() => Promise<void>>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a domain-scoped event bus.
|
|
57
|
+
*/
|
|
58
|
+
declare function createDomainBus(bus: EventBus, domain: string): DomainEventBus;
|
|
59
|
+
/**
|
|
60
|
+
* Event router that routes events to different handlers based on filters.
|
|
61
|
+
*/
|
|
62
|
+
declare class EventRouter {
|
|
63
|
+
private routes;
|
|
64
|
+
/**
|
|
65
|
+
* Add a route.
|
|
66
|
+
*/
|
|
67
|
+
route(filter: EventFilter, handler: (envelope: AuditableEventEnvelope) => Promise<void>): this;
|
|
68
|
+
/**
|
|
69
|
+
* Route an event to matching handlers.
|
|
70
|
+
*/
|
|
71
|
+
dispatch(envelope: AuditableEventEnvelope): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Create a subscriber that routes events.
|
|
74
|
+
*/
|
|
75
|
+
createSubscriber(bus: EventBus): (topic: EventKey | string) => Promise<() => Promise<void>>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create an event router.
|
|
79
|
+
*/
|
|
80
|
+
declare function createEventRouter(): EventRouter;
|
|
81
|
+
//#endregion
|
|
82
|
+
export { DomainEventBus, EventFilter, EventRouter, createDomainBus, createEventRouter, createFilteredSubscriber, matchesFilter };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EventBus } from "./eventBus.js";
|
|
2
|
+
import { EventKey } from "@lssm/lib.contracts";
|
|
3
|
+
|
|
4
|
+
//#region src/inMemoryBus.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* In-memory bus for dev/test. Not for production scale.
|
|
8
|
+
* Subscribers receive events synchronously in the order they were published.
|
|
9
|
+
*/
|
|
10
|
+
declare class InMemoryBus implements EventBus {
|
|
11
|
+
private listeners;
|
|
12
|
+
publish(topic: EventKey, payload: Uint8Array): Promise<void>;
|
|
13
|
+
subscribe(topic: string | RegExp, handler: (payload: Uint8Array) => Promise<void>): Promise<() => Promise<void>>;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { InMemoryBus };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { EventBus, decodeEvent, encodeEvent, makePublisher } from "./eventBus.js";
|
|
2
|
+
import { EventMetadata, MetadataContext, createMetadataContext, createMetadataFromContext, mergeMetadata } from "./metadata.js";
|
|
3
|
+
import { AuditQueryOptions, AuditRecord, AuditStorage, AuditableEventBus, AuditableEventBusOptions, AuditableEventEnvelope, InMemoryAuditStorage, createAuditableEventBus, makeAuditablePublisher } from "./auditableBus.js";
|
|
4
|
+
import { DomainEventBus, EventFilter, EventRouter, createDomainBus, createEventRouter, createFilteredSubscriber, matchesFilter } from "./filtering.js";
|
|
5
|
+
import { InMemoryBus } from "./inMemoryBus.js";
|
|
6
|
+
import { subscribeEvent } from "./subscriber.js";
|
|
7
|
+
export { AuditQueryOptions, AuditRecord, AuditStorage, AuditableEventBus, AuditableEventBusOptions, AuditableEventEnvelope, DomainEventBus, EventBus, EventFilter, EventMetadata, EventRouter, InMemoryAuditStorage, InMemoryBus, MetadataContext, createAuditableEventBus, createDomainBus, createEventRouter, createFilteredSubscriber, createMetadataContext, createMetadataFromContext, decodeEvent, encodeEvent, makeAuditablePublisher, makePublisher, matchesFilter, mergeMetadata, subscribeEvent };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//#region src/metadata.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Event metadata that enriches events with contextual information.
|
|
4
|
+
*/
|
|
5
|
+
interface EventMetadata {
|
|
6
|
+
/** ID of the actor (user) who triggered the event */
|
|
7
|
+
actorId?: string;
|
|
8
|
+
/** Type of actor (user, system, service) */
|
|
9
|
+
actorType?: 'user' | 'system' | 'service';
|
|
10
|
+
/** Target resource ID */
|
|
11
|
+
targetId?: string;
|
|
12
|
+
/** Target resource type */
|
|
13
|
+
targetType?: string;
|
|
14
|
+
/** Organization context */
|
|
15
|
+
orgId?: string;
|
|
16
|
+
/** Tenant context (if different from org) */
|
|
17
|
+
tenantId?: string;
|
|
18
|
+
/** Distributed trace ID */
|
|
19
|
+
traceId?: string;
|
|
20
|
+
/** Parent span ID */
|
|
21
|
+
spanId?: string;
|
|
22
|
+
/** Client IP address */
|
|
23
|
+
clientIp?: string;
|
|
24
|
+
/** Client user agent */
|
|
25
|
+
userAgent?: string;
|
|
26
|
+
/** Session ID */
|
|
27
|
+
sessionId?: string;
|
|
28
|
+
/** Request ID */
|
|
29
|
+
requestId?: string;
|
|
30
|
+
/** Source service name */
|
|
31
|
+
source?: string;
|
|
32
|
+
/** Correlation ID for related events */
|
|
33
|
+
correlationId?: string;
|
|
34
|
+
/** Custom tags for filtering/routing */
|
|
35
|
+
tags?: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create metadata from request context.
|
|
39
|
+
*/
|
|
40
|
+
declare function createMetadataFromContext(context: {
|
|
41
|
+
userId?: string;
|
|
42
|
+
orgId?: string;
|
|
43
|
+
sessionId?: string;
|
|
44
|
+
requestId?: string;
|
|
45
|
+
traceId?: string;
|
|
46
|
+
clientIp?: string;
|
|
47
|
+
userAgent?: string;
|
|
48
|
+
}): EventMetadata;
|
|
49
|
+
/**
|
|
50
|
+
* Merge metadata with overrides.
|
|
51
|
+
*/
|
|
52
|
+
declare function mergeMetadata(base: EventMetadata, overrides: Partial<EventMetadata>): EventMetadata;
|
|
53
|
+
/**
|
|
54
|
+
* Create a metadata context that can be passed through operations.
|
|
55
|
+
*/
|
|
56
|
+
declare class MetadataContext {
|
|
57
|
+
private metadata;
|
|
58
|
+
constructor(initial?: EventMetadata);
|
|
59
|
+
/**
|
|
60
|
+
* Get the current metadata.
|
|
61
|
+
*/
|
|
62
|
+
get(): EventMetadata;
|
|
63
|
+
/**
|
|
64
|
+
* Set a metadata value.
|
|
65
|
+
*/
|
|
66
|
+
set<K extends keyof EventMetadata>(key: K, value: EventMetadata[K]): this;
|
|
67
|
+
/**
|
|
68
|
+
* Add a tag.
|
|
69
|
+
*/
|
|
70
|
+
tag(key: string, value: string): this;
|
|
71
|
+
/**
|
|
72
|
+
* Create a child context with the same trace.
|
|
73
|
+
*/
|
|
74
|
+
child(overrides?: Partial<EventMetadata>): MetadataContext;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a new metadata context.
|
|
78
|
+
*/
|
|
79
|
+
declare function createMetadataContext(initial?: EventMetadata): MetadataContext;
|
|
80
|
+
//#endregion
|
|
81
|
+
export { EventMetadata, MetadataContext, createMetadataContext, createMetadataFromContext, mergeMetadata };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { EventBus } from "./eventBus.js";
|
|
2
|
+
import { EventSpec } from "@lssm/lib.contracts";
|
|
3
|
+
import { AnySchemaModel } from "@lssm/lib.schema";
|
|
4
|
+
|
|
5
|
+
//#region src/subscriber.d.ts
|
|
6
|
+
/** Typed subscription using your EventSpec */
|
|
7
|
+
declare function subscribeEvent<T extends AnySchemaModel>(bus: EventBus, spec: EventSpec<T>, handler: (payload: T, ctx: {
|
|
8
|
+
traceId?: string;
|
|
9
|
+
deliveryId?: string;
|
|
10
|
+
}) => Promise<void>): Promise<() => Promise<void>>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { subscribeEvent };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lssm/lib.bus",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-20251217060433",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
6
6
|
"publish:pkg:canary": "bun publish:pkg --tag canary",
|
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
"lint:check": "eslint src"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@lssm/tool.tsdown": "0.0.0-canary-
|
|
18
|
-
"@lssm/tool.typescript": "0.0.0-canary-
|
|
17
|
+
"@lssm/tool.tsdown": "0.0.0-canary-20251217060433",
|
|
18
|
+
"@lssm/tool.typescript": "0.0.0-canary-20251217060433",
|
|
19
19
|
"@types/express": "^5.0.3",
|
|
20
20
|
"tsdown": "^0.17.4",
|
|
21
21
|
"typescript": "^5.9.3"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@lssm/lib.schema": "0.0.0-canary-
|
|
25
|
-
"@lssm/lib.contracts": "0.0.0-canary-
|
|
24
|
+
"@lssm/lib.schema": "0.0.0-canary-20251217060433",
|
|
25
|
+
"@lssm/lib.contracts": "0.0.0-canary-20251217060433"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"elysia": "^1.4.19",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"README.md"
|
|
39
39
|
],
|
|
40
40
|
"exports": {
|
|
41
|
-
".": "./
|
|
42
|
-
"./auditableBus": "./
|
|
43
|
-
"./eventBus": "./
|
|
44
|
-
"./filtering": "./
|
|
45
|
-
"./inMemoryBus": "./
|
|
46
|
-
"./metadata": "./
|
|
47
|
-
"./subscriber": "./
|
|
41
|
+
".": "./dist/index.js",
|
|
42
|
+
"./auditableBus": "./dist/auditableBus.js",
|
|
43
|
+
"./eventBus": "./dist/eventBus.js",
|
|
44
|
+
"./filtering": "./dist/filtering.js",
|
|
45
|
+
"./inMemoryBus": "./dist/inMemoryBus.js",
|
|
46
|
+
"./metadata": "./dist/metadata.js",
|
|
47
|
+
"./subscriber": "./dist/subscriber.js",
|
|
48
48
|
"./*": "./*"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|