@contractspec/lib.bus 1.56.1 → 1.58.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.
Files changed (42) hide show
  1. package/dist/auditableBus.d.ts +76 -81
  2. package/dist/auditableBus.d.ts.map +1 -1
  3. package/dist/auditableBus.js +149 -132
  4. package/dist/browser/auditableBus.js +153 -0
  5. package/dist/browser/eventBus.js +28 -0
  6. package/dist/browser/filtering.js +147 -0
  7. package/dist/browser/inMemoryBus.js +25 -0
  8. package/dist/browser/index.js +373 -0
  9. package/dist/browser/metadata.js +60 -0
  10. package/dist/browser/subscriber.js +37 -0
  11. package/dist/eventBus.d.ts +9 -14
  12. package/dist/eventBus.d.ts.map +1 -1
  13. package/dist/eventBus.js +23 -26
  14. package/dist/filtering.d.ts +53 -58
  15. package/dist/filtering.d.ts.map +1 -1
  16. package/dist/filtering.js +139 -125
  17. package/dist/inMemoryBus.d.ts +6 -11
  18. package/dist/inMemoryBus.d.ts.map +1 -1
  19. package/dist/inMemoryBus.js +25 -28
  20. package/dist/index.d.ts +7 -7
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +374 -8
  23. package/dist/metadata.d.ts +60 -63
  24. package/dist/metadata.d.ts.map +1 -1
  25. package/dist/metadata.js +55 -76
  26. package/dist/node/auditableBus.js +153 -0
  27. package/dist/node/eventBus.js +28 -0
  28. package/dist/node/filtering.js +147 -0
  29. package/dist/node/inMemoryBus.js +25 -0
  30. package/dist/node/index.js +373 -0
  31. package/dist/node/metadata.js +60 -0
  32. package/dist/node/subscriber.js +37 -0
  33. package/dist/subscriber.d.ts +6 -10
  34. package/dist/subscriber.d.ts.map +1 -1
  35. package/dist/subscriber.js +35 -16
  36. package/package.json +69 -28
  37. package/dist/auditableBus.js.map +0 -1
  38. package/dist/eventBus.js.map +0 -1
  39. package/dist/filtering.js.map +0 -1
  40. package/dist/inMemoryBus.js.map +0 -1
  41. package/dist/metadata.js.map +0 -1
  42. package/dist/subscriber.js.map +0 -1
@@ -1,117 +1,112 @@
1
- import { EventBus } from "./eventBus.js";
2
- import { EventMetadata } from "./metadata.js";
3
- import { EventEnvelope, EventKey, EventSpec } from "@contractspec/lib.contracts";
4
- import { AnySchemaModel } from "@contractspec/lib.schema";
5
-
6
- //#region src/auditableBus.d.ts
7
-
1
+ import { type EventEnvelope, type EventKey, type EventSpec } from '@contractspec/lib.contracts';
2
+ import type { AnySchemaModel } from '@contractspec/lib.schema';
3
+ import type { EventBus } from './eventBus';
4
+ import type { EventMetadata } from './metadata';
8
5
  /**
9
6
  * Extended event envelope with metadata for auditing.
10
7
  */
11
- interface AuditableEventEnvelope<T = unknown> extends EventEnvelope<T> {
12
- metadata?: EventMetadata;
8
+ export interface AuditableEventEnvelope<T = unknown> extends EventEnvelope<T> {
9
+ metadata?: EventMetadata;
13
10
  }
14
11
  /**
15
12
  * Audit record for persisting event history.
16
13
  */
17
- interface AuditRecord {
18
- id: string;
19
- eventKey: string;
20
- eventVersion: string;
21
- payload: unknown;
22
- metadata?: EventMetadata;
23
- occurredAt: string;
24
- traceId?: string;
25
- recordedAt: Date;
14
+ export interface AuditRecord {
15
+ id: string;
16
+ eventKey: string;
17
+ eventVersion: string;
18
+ payload: unknown;
19
+ metadata?: EventMetadata;
20
+ occurredAt: string;
21
+ traceId?: string;
22
+ recordedAt: Date;
26
23
  }
27
24
  /**
28
25
  * Audit storage adapter interface.
29
26
  */
30
- interface AuditStorage {
31
- /** Store an audit record */
32
- store(record: AuditRecord): Promise<void>;
33
- /** Query audit records */
34
- query?(options: AuditQueryOptions): Promise<AuditRecord[]>;
27
+ export interface AuditStorage {
28
+ /** Store an audit record */
29
+ store(record: AuditRecord): Promise<void>;
30
+ /** Query audit records */
31
+ query?(options: AuditQueryOptions): Promise<AuditRecord[]>;
35
32
  }
36
33
  /**
37
34
  * Options for querying audit records.
38
35
  */
39
- interface AuditQueryOptions {
40
- eventKey?: 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;
36
+ export interface AuditQueryOptions {
37
+ eventKey?: string;
38
+ actorId?: string;
39
+ targetId?: string;
40
+ orgId?: string;
41
+ traceId?: string;
42
+ from?: Date;
43
+ to?: Date;
44
+ limit?: number;
45
+ offset?: number;
49
46
  }
50
47
  /**
51
48
  * Options for creating an auditable event bus.
52
49
  */
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?: (eventKey: string, envelope: AuditableEventEnvelope) => boolean;
62
- /** Transform function for audit records */
63
- transformAuditRecord?: (record: AuditRecord) => AuditRecord;
50
+ export interface AuditableEventBusOptions {
51
+ /** Underlying event bus for publishing */
52
+ bus: EventBus;
53
+ /** Audit storage adapter */
54
+ storage?: AuditStorage;
55
+ /** Default metadata to include with all events */
56
+ defaultMetadata?: EventMetadata;
57
+ /** Filter function to decide which events to audit */
58
+ shouldAudit?: (eventKey: string, envelope: AuditableEventEnvelope) => boolean;
59
+ /** Transform function for audit records */
60
+ transformAuditRecord?: (record: AuditRecord) => AuditRecord;
64
61
  }
65
62
  /**
66
63
  * AuditableEventBus wraps an EventBus to automatically record events for audit trail.
67
64
  */
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[]>;
65
+ export declare class AuditableEventBus implements EventBus {
66
+ private readonly bus;
67
+ private readonly storage?;
68
+ private readonly defaultMetadata;
69
+ private readonly shouldAudit;
70
+ private readonly transformAuditRecord?;
71
+ constructor(options: AuditableEventBusOptions);
72
+ /**
73
+ * Publish an event and optionally record it for audit.
74
+ */
75
+ publish(topic: EventKey, bytes: Uint8Array): Promise<void>;
76
+ /**
77
+ * Subscribe to events from the underlying bus.
78
+ */
79
+ subscribe(topic: EventKey | string, handler: (bytes: Uint8Array) => Promise<void>): Promise<() => Promise<void>>;
80
+ /**
81
+ * Query audit records (if storage supports it).
82
+ */
83
+ queryAudit(options: AuditQueryOptions): Promise<AuditRecord[]>;
87
84
  }
88
85
  /**
89
86
  * Create an auditable publisher that includes metadata.
90
87
  */
91
- declare function makeAuditablePublisher<T extends AnySchemaModel>(bus: AuditableEventBus | EventBus, spec: EventSpec<T>, defaultMetadata?: EventMetadata): (payload: T, options?: {
92
- traceId?: string;
93
- metadata?: EventMetadata;
88
+ export declare function makeAuditablePublisher<T extends AnySchemaModel>(bus: AuditableEventBus | EventBus, spec: EventSpec<T>, defaultMetadata?: EventMetadata): (payload: T, options?: {
89
+ traceId?: string;
90
+ metadata?: EventMetadata;
94
91
  }) => Promise<void>;
95
92
  /**
96
93
  * In-memory audit storage for development/testing.
97
94
  */
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;
95
+ export declare class InMemoryAuditStorage implements AuditStorage {
96
+ private records;
97
+ store(record: AuditRecord): Promise<void>;
98
+ query(options: AuditQueryOptions): Promise<AuditRecord[]>;
99
+ /**
100
+ * Get all records (for testing).
101
+ */
102
+ getAll(): AuditRecord[];
103
+ /**
104
+ * Clear all records (for testing).
105
+ */
106
+ clear(): void;
110
107
  }
111
108
  /**
112
109
  * Create an auditable event bus with in-memory storage.
113
110
  */
114
- declare function createAuditableEventBus(bus: EventBus, options?: Omit<AuditableEventBusOptions, 'bus'>): AuditableEventBus;
115
- //#endregion
116
- export { AuditQueryOptions, AuditRecord, AuditStorage, AuditableEventBus, AuditableEventBusOptions, AuditableEventEnvelope, InMemoryAuditStorage, createAuditableEventBus, makeAuditablePublisher };
111
+ export declare function createAuditableEventBus(bus: EventBus, options?: Omit<AuditableEventBusOptions, 'bus'>): AuditableEventBus;
117
112
  //# sourceMappingURL=auditableBus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auditableBus.d.ts","names":[],"sources":["../src/auditableBus.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAcA;AAA2E,UAA1D,sBAA0D,CAAA,IAAA,OAAA,CAAA,SAAd,aAAc,CAAA,CAAA,CAAA,CAAA;EAC9D,QAAA,CAAA,EAAA,aAAA;;;AAMb;AAcA;AAEgB,UAhBC,WAAA,CAgBD;EAAc,EAAA,EAAA,MAAA;EAEZ,QAAA,EAAA,MAAA;EAA4B,YAAA,EAAA,MAAA;EAAR,OAAA,EAAA,OAAA;EAAO,QAAA,CAAA,EAbhC,aAagC;EAM5B,UAAA,EAAA,MAAA;EAeA,OAAA,CAAA,EAAA,MAAA;EAEV,UAAA,EAjCO,IAiCP;;;;;AAQ2C,UAnCjC,YAAA,CAmCiC;EAAW;EAMhD,KAAA,CAAA,MAAA,EAvCG,WAuCe,CAAA,EAvCD,OAuCC,CAAA,IAAA,CAAA;EAUR;EAWA,KAAA,EAAA,OAAA,EA1DL,iBA0DK,CAAA,EA1De,OA0Df,CA1DuB,WA0DvB,EAAA,CAAA;;;;;AA0Ca,UA9FnB,iBAAA,CA8FmB;EACjB,QAAA,CAAA,EAAA,MAAA;EAAd,OAAA,CAAA,EAAA,MAAA;EAOuB,QAAA,CAAA,EAAA,MAAA;EAA4B,KAAA,CAAA,EAAA,MAAA;EAAR,OAAA,CAAA,EAAA,MAAA;EAvEN,IAAA,CAAA,EAzBjC,IAyBiC;EAAQ,EAAA,CAAA,EAxB3C,IAwB2C;EAkFlC,KAAA,CAAA,EAAA,MAAA;EAAiC,MAAA,CAAA,EAAA,MAAA;;;;;AAG7B,UArGH,wBAAA,CAqGG;EAGP;EACgC,GAAA,EAvGtC,QAuGsC;EAAe;EAAA,OAAA,CAAA,EArGhD,YAqGgD;EAwB/C;EAGS,eAAA,CAAA,EA9HF,aA8HE;EAAc;EAIb,WAAA,CAAA,EAAA,CAAA,QAAA,EAAA,MAAA,EAAA,QAAA,EAhIsB,sBAgItB,EAAA,GAAA,OAAA;EAA4B;EAAR,oBAAA,CAAA,EAAA,CAAA,MAAA,EA9HT,WA8HS,EAAA,GA9HO,WA8HP;;;;AAiE3C;AACO,cA1LM,iBAAA,YAA6B,QA0LnC,CAAA;EACU,iBAAA,GAAA;EAAL,iBAAA,OAAA;EACT,iBAAA,eAAA;EAAiB,iBAAA,WAAA;;uBAlLG;;;;iBAWA,iBAAiB,aAAa;;;;mBAyC1C,oCACU,eAAe,gBAC/B,cAAc;;;;sBAOS,oBAAoB,QAAQ;;;;;iBAWxC,iCAAiC,qBAC1C,oBAAoB,gBACnB,UAAU,sBACE,0BAGP;;aACgC;MAAe;;;;cAwB/C,oBAAA,YAAgC;;gBAGvB,cAAc;iBAIb,oBAAoB,QAAQ;;;;YAkDvC;;;;;;;;;iBAeI,uBAAA,MACT,oBACK,KAAK,mCACd"}
1
+ {"version":3,"file":"auditableBus.d.ts","sourceRoot":"","sources":["../src/auditableBus.ts"],"names":[],"mappings":"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"}
@@ -1,137 +1,154 @@
1
- import { decodeEvent, encodeEvent } from "./eventBus.js";
2
- import { eventKey } from "@contractspec/lib.contracts";
1
+ // @bun
2
+ // src/eventBus.ts
3
+ import {
4
+ eventKey
5
+ } from "@contractspec/lib.contracts";
6
+ function encodeEvent(envelope) {
7
+ return new TextEncoder().encode(JSON.stringify(envelope));
8
+ }
9
+ function decodeEvent(data) {
10
+ return JSON.parse(new TextDecoder().decode(data));
11
+ }
12
+ function makePublisher(bus, spec) {
13
+ return async (payload, traceId) => {
14
+ const envelope = {
15
+ id: crypto.randomUUID(),
16
+ occurredAt: new Date().toISOString(),
17
+ key: spec.meta.key,
18
+ version: spec.meta.version,
19
+ payload,
20
+ traceId
21
+ };
22
+ await bus.publish(eventKey(spec.meta.key, spec.meta.version), encodeEvent(envelope));
23
+ };
24
+ }
3
25
 
4
- //#region src/auditableBus.ts
5
- /**
6
- * AuditableEventBus wraps an EventBus to automatically record events for audit trail.
7
- */
8
- var AuditableEventBus = class {
9
- bus;
10
- storage;
11
- defaultMetadata;
12
- shouldAudit;
13
- transformAuditRecord;
14
- constructor(options) {
15
- this.bus = options.bus;
16
- this.storage = options.storage;
17
- this.defaultMetadata = options.defaultMetadata ?? {};
18
- this.shouldAudit = options.shouldAudit ?? (() => true);
19
- this.transformAuditRecord = options.transformAuditRecord;
20
- }
21
- /**
22
- * Publish an event and optionally record it for audit.
23
- */
24
- async publish(topic, bytes) {
25
- await this.bus.publish(topic, bytes);
26
- if (this.storage) try {
27
- const envelope = decodeEvent(bytes);
28
- if (this.shouldAudit(envelope.key, envelope)) {
29
- let record = {
30
- id: crypto.randomUUID(),
31
- eventKey: envelope.key,
32
- eventVersion: envelope.version,
33
- payload: envelope.payload,
34
- metadata: {
35
- ...this.defaultMetadata,
36
- ...envelope.metadata
37
- },
38
- occurredAt: envelope.occurredAt,
39
- traceId: envelope.traceId,
40
- recordedAt: /* @__PURE__ */ new Date()
41
- };
42
- if (this.transformAuditRecord) record = this.transformAuditRecord(record);
43
- await this.storage.store(record);
44
- }
45
- } catch (error) {
46
- console.error("Failed to record audit:", error);
47
- }
48
- }
49
- /**
50
- * Subscribe to events from the underlying bus.
51
- */
52
- async subscribe(topic, handler) {
53
- return this.bus.subscribe(topic, handler);
54
- }
55
- /**
56
- * Query audit records (if storage supports it).
57
- */
58
- async queryAudit(options) {
59
- if (!this.storage?.query) throw new Error("Audit storage does not support querying");
60
- return this.storage.query(options);
61
- }
62
- };
63
- /**
64
- * Create an auditable publisher that includes metadata.
65
- */
26
+ // src/auditableBus.ts
27
+ import {
28
+ eventKey as eventKey2
29
+ } from "@contractspec/lib.contracts";
30
+ class AuditableEventBus {
31
+ bus;
32
+ storage;
33
+ defaultMetadata;
34
+ shouldAudit;
35
+ transformAuditRecord;
36
+ constructor(options) {
37
+ this.bus = options.bus;
38
+ this.storage = options.storage;
39
+ this.defaultMetadata = options.defaultMetadata ?? {};
40
+ this.shouldAudit = options.shouldAudit ?? (() => true);
41
+ this.transformAuditRecord = options.transformAuditRecord;
42
+ }
43
+ async publish(topic, bytes) {
44
+ await this.bus.publish(topic, bytes);
45
+ if (this.storage) {
46
+ try {
47
+ const envelope = decodeEvent(bytes);
48
+ if (this.shouldAudit(envelope.key, envelope)) {
49
+ let record = {
50
+ id: crypto.randomUUID(),
51
+ eventKey: envelope.key,
52
+ eventVersion: envelope.version,
53
+ payload: envelope.payload,
54
+ metadata: {
55
+ ...this.defaultMetadata,
56
+ ...envelope.metadata
57
+ },
58
+ occurredAt: envelope.occurredAt,
59
+ traceId: envelope.traceId,
60
+ recordedAt: new Date
61
+ };
62
+ if (this.transformAuditRecord) {
63
+ record = this.transformAuditRecord(record);
64
+ }
65
+ await this.storage.store(record);
66
+ }
67
+ } catch (error) {
68
+ console.error("Failed to record audit:", error);
69
+ }
70
+ }
71
+ }
72
+ async subscribe(topic, handler) {
73
+ return this.bus.subscribe(topic, handler);
74
+ }
75
+ async queryAudit(options) {
76
+ if (!this.storage?.query) {
77
+ throw new Error("Audit storage does not support querying");
78
+ }
79
+ return this.storage.query(options);
80
+ }
81
+ }
66
82
  function makeAuditablePublisher(bus, spec, defaultMetadata) {
67
- return async (payload, options) => {
68
- const envelope = {
69
- id: crypto.randomUUID(),
70
- occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
71
- key: spec.meta.key,
72
- version: spec.meta.version,
73
- payload,
74
- traceId: options?.traceId,
75
- metadata: {
76
- ...defaultMetadata,
77
- ...options?.metadata
78
- }
79
- };
80
- await bus.publish(eventKey(spec.meta.key, spec.meta.version), encodeEvent(envelope));
81
- };
83
+ return async (payload, options) => {
84
+ const envelope = {
85
+ id: crypto.randomUUID(),
86
+ occurredAt: new Date().toISOString(),
87
+ key: spec.meta.key,
88
+ version: spec.meta.version,
89
+ payload,
90
+ traceId: options?.traceId,
91
+ metadata: {
92
+ ...defaultMetadata,
93
+ ...options?.metadata
94
+ }
95
+ };
96
+ await bus.publish(eventKey2(spec.meta.key, spec.meta.version), encodeEvent(envelope));
97
+ };
98
+ }
99
+
100
+ class InMemoryAuditStorage {
101
+ records = [];
102
+ async store(record) {
103
+ this.records.push(record);
104
+ }
105
+ async query(options) {
106
+ let results = [...this.records];
107
+ if (options.eventKey) {
108
+ results = results.filter((r) => r.eventKey === options.eventKey);
109
+ }
110
+ if (options.actorId) {
111
+ results = results.filter((r) => r.metadata?.actorId === options.actorId);
112
+ }
113
+ if (options.targetId) {
114
+ results = results.filter((r) => r.metadata?.targetId === options.targetId);
115
+ }
116
+ if (options.orgId) {
117
+ results = results.filter((r) => r.metadata?.orgId === options.orgId);
118
+ }
119
+ if (options.traceId) {
120
+ results = results.filter((r) => r.traceId === options.traceId);
121
+ }
122
+ if (options.from) {
123
+ const from = options.from;
124
+ results = results.filter((r) => new Date(r.occurredAt) >= from);
125
+ }
126
+ if (options.to) {
127
+ const to = options.to;
128
+ results = results.filter((r) => new Date(r.occurredAt) <= to);
129
+ }
130
+ results.sort((a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime());
131
+ const offset = options.offset ?? 0;
132
+ const limit = options.limit ?? 100;
133
+ return results.slice(offset, offset + limit);
134
+ }
135
+ getAll() {
136
+ return [...this.records];
137
+ }
138
+ clear() {
139
+ this.records = [];
140
+ }
82
141
  }
83
- /**
84
- * In-memory audit storage for development/testing.
85
- */
86
- var InMemoryAuditStorage = class {
87
- records = [];
88
- async store(record) {
89
- this.records.push(record);
90
- }
91
- async query(options) {
92
- let results = [...this.records];
93
- if (options.eventKey) results = results.filter((r) => r.eventKey === options.eventKey);
94
- if (options.actorId) results = results.filter((r) => r.metadata?.actorId === options.actorId);
95
- if (options.targetId) results = results.filter((r) => r.metadata?.targetId === options.targetId);
96
- if (options.orgId) results = results.filter((r) => r.metadata?.orgId === options.orgId);
97
- if (options.traceId) results = results.filter((r) => r.traceId === options.traceId);
98
- if (options.from) {
99
- const from = options.from;
100
- results = results.filter((r) => new Date(r.occurredAt) >= from);
101
- }
102
- if (options.to) {
103
- const to = options.to;
104
- results = results.filter((r) => new Date(r.occurredAt) <= to);
105
- }
106
- results.sort((a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime());
107
- const offset = options.offset ?? 0;
108
- const limit = options.limit ?? 100;
109
- return results.slice(offset, offset + limit);
110
- }
111
- /**
112
- * Get all records (for testing).
113
- */
114
- getAll() {
115
- return [...this.records];
116
- }
117
- /**
118
- * Clear all records (for testing).
119
- */
120
- clear() {
121
- this.records = [];
122
- }
123
- };
124
- /**
125
- * Create an auditable event bus with in-memory storage.
126
- */
127
142
  function createAuditableEventBus(bus, options) {
128
- return new AuditableEventBus({
129
- bus,
130
- storage: options?.storage ?? new InMemoryAuditStorage(),
131
- ...options
132
- });
143
+ return new AuditableEventBus({
144
+ bus,
145
+ storage: options?.storage ?? new InMemoryAuditStorage,
146
+ ...options
147
+ });
133
148
  }
134
-
135
- //#endregion
136
- export { AuditableEventBus, InMemoryAuditStorage, createAuditableEventBus, makeAuditablePublisher };
137
- //# sourceMappingURL=auditableBus.js.map
149
+ export {
150
+ makeAuditablePublisher,
151
+ createAuditableEventBus,
152
+ InMemoryAuditStorage,
153
+ AuditableEventBus
154
+ };
@@ -0,0 +1,153 @@
1
+ // src/eventBus.ts
2
+ import {
3
+ eventKey
4
+ } from "@contractspec/lib.contracts";
5
+ function encodeEvent(envelope) {
6
+ return new TextEncoder().encode(JSON.stringify(envelope));
7
+ }
8
+ function decodeEvent(data) {
9
+ return JSON.parse(new TextDecoder().decode(data));
10
+ }
11
+ function makePublisher(bus, spec) {
12
+ return async (payload, traceId) => {
13
+ const envelope = {
14
+ id: crypto.randomUUID(),
15
+ occurredAt: new Date().toISOString(),
16
+ key: spec.meta.key,
17
+ version: spec.meta.version,
18
+ payload,
19
+ traceId
20
+ };
21
+ await bus.publish(eventKey(spec.meta.key, spec.meta.version), encodeEvent(envelope));
22
+ };
23
+ }
24
+
25
+ // src/auditableBus.ts
26
+ import {
27
+ eventKey as eventKey2
28
+ } from "@contractspec/lib.contracts";
29
+ class AuditableEventBus {
30
+ bus;
31
+ storage;
32
+ defaultMetadata;
33
+ shouldAudit;
34
+ transformAuditRecord;
35
+ constructor(options) {
36
+ this.bus = options.bus;
37
+ this.storage = options.storage;
38
+ this.defaultMetadata = options.defaultMetadata ?? {};
39
+ this.shouldAudit = options.shouldAudit ?? (() => true);
40
+ this.transformAuditRecord = options.transformAuditRecord;
41
+ }
42
+ async publish(topic, bytes) {
43
+ await this.bus.publish(topic, bytes);
44
+ if (this.storage) {
45
+ try {
46
+ const envelope = decodeEvent(bytes);
47
+ if (this.shouldAudit(envelope.key, envelope)) {
48
+ let record = {
49
+ id: crypto.randomUUID(),
50
+ eventKey: envelope.key,
51
+ eventVersion: envelope.version,
52
+ payload: envelope.payload,
53
+ metadata: {
54
+ ...this.defaultMetadata,
55
+ ...envelope.metadata
56
+ },
57
+ occurredAt: envelope.occurredAt,
58
+ traceId: envelope.traceId,
59
+ recordedAt: new Date
60
+ };
61
+ if (this.transformAuditRecord) {
62
+ record = this.transformAuditRecord(record);
63
+ }
64
+ await this.storage.store(record);
65
+ }
66
+ } catch (error) {
67
+ console.error("Failed to record audit:", error);
68
+ }
69
+ }
70
+ }
71
+ async subscribe(topic, handler) {
72
+ return this.bus.subscribe(topic, handler);
73
+ }
74
+ async queryAudit(options) {
75
+ if (!this.storage?.query) {
76
+ throw new Error("Audit storage does not support querying");
77
+ }
78
+ return this.storage.query(options);
79
+ }
80
+ }
81
+ function makeAuditablePublisher(bus, spec, defaultMetadata) {
82
+ return async (payload, options) => {
83
+ const envelope = {
84
+ id: crypto.randomUUID(),
85
+ occurredAt: new Date().toISOString(),
86
+ key: spec.meta.key,
87
+ version: spec.meta.version,
88
+ payload,
89
+ traceId: options?.traceId,
90
+ metadata: {
91
+ ...defaultMetadata,
92
+ ...options?.metadata
93
+ }
94
+ };
95
+ await bus.publish(eventKey2(spec.meta.key, spec.meta.version), encodeEvent(envelope));
96
+ };
97
+ }
98
+
99
+ class InMemoryAuditStorage {
100
+ records = [];
101
+ async store(record) {
102
+ this.records.push(record);
103
+ }
104
+ async query(options) {
105
+ let results = [...this.records];
106
+ if (options.eventKey) {
107
+ results = results.filter((r) => r.eventKey === options.eventKey);
108
+ }
109
+ if (options.actorId) {
110
+ results = results.filter((r) => r.metadata?.actorId === options.actorId);
111
+ }
112
+ if (options.targetId) {
113
+ results = results.filter((r) => r.metadata?.targetId === options.targetId);
114
+ }
115
+ if (options.orgId) {
116
+ results = results.filter((r) => r.metadata?.orgId === options.orgId);
117
+ }
118
+ if (options.traceId) {
119
+ results = results.filter((r) => r.traceId === options.traceId);
120
+ }
121
+ if (options.from) {
122
+ const from = options.from;
123
+ results = results.filter((r) => new Date(r.occurredAt) >= from);
124
+ }
125
+ if (options.to) {
126
+ const to = options.to;
127
+ results = results.filter((r) => new Date(r.occurredAt) <= to);
128
+ }
129
+ results.sort((a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime());
130
+ const offset = options.offset ?? 0;
131
+ const limit = options.limit ?? 100;
132
+ return results.slice(offset, offset + limit);
133
+ }
134
+ getAll() {
135
+ return [...this.records];
136
+ }
137
+ clear() {
138
+ this.records = [];
139
+ }
140
+ }
141
+ function createAuditableEventBus(bus, options) {
142
+ return new AuditableEventBus({
143
+ bus,
144
+ storage: options?.storage ?? new InMemoryAuditStorage,
145
+ ...options
146
+ });
147
+ }
148
+ export {
149
+ makeAuditablePublisher,
150
+ createAuditableEventBus,
151
+ InMemoryAuditStorage,
152
+ AuditableEventBus
153
+ };