abxbus 2.4.26 → 2.4.28

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 (178) hide show
  1. package/dist/cjs/BaseEvent.d.ts +211 -0
  2. package/dist/cjs/{base_event.js → BaseEvent.js} +38 -30
  3. package/dist/cjs/BaseEvent.js.map +7 -0
  4. package/dist/cjs/EventBridge.d.ts +34 -0
  5. package/dist/cjs/EventBridge.js +295 -0
  6. package/dist/cjs/EventBridge.js.map +7 -0
  7. package/dist/cjs/EventBus.d.ts +125 -0
  8. package/dist/cjs/{event_bus.js → EventBus.js} +21 -21
  9. package/dist/cjs/EventBus.js.map +7 -0
  10. package/dist/cjs/EventBusMiddleware.d.ts +13 -0
  11. package/dist/cjs/EventBusMiddleware.js +17 -0
  12. package/dist/cjs/EventBusMiddleware.js.map +7 -0
  13. package/dist/cjs/EventHandler.d.ts +140 -0
  14. package/dist/cjs/{event_handler.js → EventHandler.js} +4 -4
  15. package/dist/cjs/EventHandler.js.map +7 -0
  16. package/dist/cjs/EventHistory.d.ts +45 -0
  17. package/dist/cjs/{event_history.js → EventHistory.js} +4 -4
  18. package/dist/cjs/EventHistory.js.map +7 -0
  19. package/dist/cjs/EventResult.d.ts +86 -0
  20. package/dist/cjs/{event_result.js → EventResult.js} +18 -16
  21. package/dist/cjs/EventResult.js.map +7 -0
  22. package/dist/cjs/HTTPEventBridge.d.ts +10 -0
  23. package/dist/cjs/HTTPEventBridge.js +37 -0
  24. package/dist/cjs/HTTPEventBridge.js.map +7 -0
  25. package/dist/cjs/JSONLEventBridge.d.ts +26 -0
  26. package/dist/cjs/{bridge_jsonl.js → JSONLEventBridge.js} +8 -8
  27. package/dist/cjs/JSONLEventBridge.js.map +7 -0
  28. package/dist/cjs/LockManager.d.ts +70 -0
  29. package/dist/cjs/{lock_manager.js → LockManager.js} +4 -4
  30. package/dist/cjs/LockManager.js.map +7 -0
  31. package/dist/cjs/NATSEventBridge.d.ts +20 -0
  32. package/dist/cjs/{bridge_nats.js → NATSEventBridge.js} +8 -8
  33. package/dist/cjs/NATSEventBridge.js.map +7 -0
  34. package/dist/cjs/{middleware_otel_tracing.d.ts → OtelTracingMiddleware.d.ts} +4 -4
  35. package/dist/cjs/{middleware_otel_tracing.js → OtelTracingMiddleware.js} +4 -4
  36. package/dist/cjs/OtelTracingMiddleware.js.map +7 -0
  37. package/dist/cjs/PostgresEventBridge.d.ts +31 -0
  38. package/dist/cjs/{bridge_postgres.js → PostgresEventBridge.js} +8 -8
  39. package/dist/cjs/PostgresEventBridge.js.map +7 -0
  40. package/dist/cjs/RedisEventBridge.d.ts +34 -0
  41. package/dist/cjs/{bridge_redis.js → RedisEventBridge.js} +8 -8
  42. package/dist/cjs/RedisEventBridge.js.map +7 -0
  43. package/dist/cjs/SQLiteEventBridge.d.ts +30 -0
  44. package/dist/cjs/{bridge_sqlite.js → SQLiteEventBridge.js} +8 -8
  45. package/dist/cjs/SQLiteEventBridge.js.map +7 -0
  46. package/dist/cjs/SocketEventBridge.d.ts +4 -0
  47. package/dist/cjs/SocketEventBridge.js +35 -0
  48. package/dist/cjs/SocketEventBridge.js.map +7 -0
  49. package/dist/cjs/base_event.d.ts +2 -2
  50. package/dist/cjs/bridges.d.ts +9 -49
  51. package/dist/cjs/bridges.js +16 -303
  52. package/dist/cjs/bridges.js.map +2 -2
  53. package/dist/cjs/event_handler.d.ts +0 -1
  54. package/dist/cjs/events_suck.d.ts +2 -2
  55. package/dist/cjs/events_suck.js +4 -4
  56. package/dist/cjs/events_suck.js.map +2 -2
  57. package/dist/cjs/index.d.ts +15 -14
  58. package/dist/cjs/index.js +24 -24
  59. package/dist/cjs/index.js.map +2 -2
  60. package/dist/cjs/logging.d.ts +2 -2
  61. package/dist/cjs/logging.js +7 -7
  62. package/dist/cjs/logging.js.map +2 -2
  63. package/dist/cjs/middlewares.d.ts +1 -13
  64. package/dist/cjs/middlewares.js.map +1 -1
  65. package/dist/cjs/types.d.ts +1 -1
  66. package/dist/cjs/types.js.map +1 -1
  67. package/dist/esm/{base_event.js → BaseEvent.js} +20 -12
  68. package/dist/esm/BaseEvent.js.map +7 -0
  69. package/dist/esm/EventBridge.js +275 -0
  70. package/dist/esm/EventBridge.js.map +7 -0
  71. package/dist/esm/{event_bus.js → EventBus.js} +5 -5
  72. package/dist/esm/EventBus.js.map +7 -0
  73. package/dist/esm/EventBusMiddleware.js +1 -0
  74. package/dist/esm/EventBusMiddleware.js.map +7 -0
  75. package/dist/esm/{event_handler.js → EventHandler.js} +1 -1
  76. package/dist/esm/EventHandler.js.map +7 -0
  77. package/dist/esm/{event_history.js → EventHistory.js} +1 -1
  78. package/dist/esm/EventHistory.js.map +7 -0
  79. package/dist/esm/{event_result.js → EventResult.js} +7 -5
  80. package/dist/esm/EventResult.js.map +7 -0
  81. package/dist/esm/HTTPEventBridge.js +17 -0
  82. package/dist/esm/HTTPEventBridge.js.map +7 -0
  83. package/dist/esm/{bridge_jsonl.js → JSONLEventBridge.js} +3 -3
  84. package/dist/esm/JSONLEventBridge.js.map +7 -0
  85. package/dist/esm/{lock_manager.js → LockManager.js} +1 -1
  86. package/dist/esm/LockManager.js.map +7 -0
  87. package/dist/esm/{bridge_nats.js → NATSEventBridge.js} +3 -3
  88. package/dist/esm/NATSEventBridge.js.map +7 -0
  89. package/dist/esm/{middleware_otel_tracing.js → OtelTracingMiddleware.js} +1 -1
  90. package/dist/esm/OtelTracingMiddleware.js.map +7 -0
  91. package/dist/esm/{bridge_postgres.js → PostgresEventBridge.js} +3 -3
  92. package/dist/esm/PostgresEventBridge.js.map +7 -0
  93. package/dist/esm/{bridge_redis.js → RedisEventBridge.js} +3 -3
  94. package/dist/esm/RedisEventBridge.js.map +7 -0
  95. package/dist/esm/{bridge_sqlite.js → SQLiteEventBridge.js} +3 -3
  96. package/dist/esm/SQLiteEventBridge.js.map +7 -0
  97. package/dist/esm/SocketEventBridge.js +15 -0
  98. package/dist/esm/SocketEventBridge.js.map +7 -0
  99. package/dist/esm/bridges.js +9 -296
  100. package/dist/esm/bridges.js.map +2 -2
  101. package/dist/esm/events_suck.js +2 -2
  102. package/dist/esm/events_suck.js.map +1 -1
  103. package/dist/esm/index.js +11 -19
  104. package/dist/esm/index.js.map +2 -2
  105. package/dist/esm/logging.js +2 -2
  106. package/dist/esm/logging.js.map +1 -1
  107. package/dist/esm/types.js.map +1 -1
  108. package/dist/types/BaseEvent.d.ts +211 -0
  109. package/dist/types/EventBridge.d.ts +34 -0
  110. package/dist/types/EventBus.d.ts +125 -0
  111. package/dist/types/EventBusMiddleware.d.ts +13 -0
  112. package/dist/types/EventHandler.d.ts +140 -0
  113. package/dist/types/EventHistory.d.ts +45 -0
  114. package/dist/types/EventResult.d.ts +86 -0
  115. package/dist/types/HTTPEventBridge.d.ts +10 -0
  116. package/dist/types/JSONLEventBridge.d.ts +26 -0
  117. package/dist/types/LockManager.d.ts +70 -0
  118. package/dist/types/NATSEventBridge.d.ts +20 -0
  119. package/dist/types/{middleware_otel_tracing.d.ts → OtelTracingMiddleware.d.ts} +4 -4
  120. package/dist/types/PostgresEventBridge.d.ts +31 -0
  121. package/dist/types/RedisEventBridge.d.ts +34 -0
  122. package/dist/types/SQLiteEventBridge.d.ts +30 -0
  123. package/dist/types/SocketEventBridge.d.ts +4 -0
  124. package/dist/types/base_event.d.ts +2 -2
  125. package/dist/types/bridges.d.ts +9 -49
  126. package/dist/types/event_handler.d.ts +0 -1
  127. package/dist/types/events_suck.d.ts +2 -2
  128. package/dist/types/index.d.ts +15 -14
  129. package/dist/types/logging.d.ts +2 -2
  130. package/dist/types/middlewares.d.ts +1 -13
  131. package/dist/types/types.d.ts +1 -1
  132. package/package.json +52 -10
  133. package/src/{base_event.ts → BaseEvent.ts} +21 -13
  134. package/src/EventBridge.ts +332 -0
  135. package/src/{event_bus.ts → EventBus.ts} +6 -6
  136. package/src/EventBusMiddleware.ts +16 -0
  137. package/src/{event_handler.ts → EventHandler.ts} +2 -2
  138. package/src/{event_history.ts → EventHistory.ts} +1 -1
  139. package/src/{event_result.ts → EventResult.ts} +8 -6
  140. package/src/HTTPEventBridge.ts +27 -0
  141. package/src/{bridge_jsonl.ts → JSONLEventBridge.ts} +2 -2
  142. package/src/{lock_manager.ts → LockManager.ts} +2 -2
  143. package/src/{bridge_nats.ts → NATSEventBridge.ts} +2 -2
  144. package/src/{middleware_otel_tracing.ts → OtelTracingMiddleware.ts} +4 -4
  145. package/src/{bridge_postgres.ts → PostgresEventBridge.ts} +2 -2
  146. package/src/{bridge_redis.ts → RedisEventBridge.ts} +2 -2
  147. package/src/{bridge_sqlite.ts → SQLiteEventBridge.ts} +2 -2
  148. package/src/SocketEventBridge.ts +13 -0
  149. package/src/bridges.ts +9 -376
  150. package/src/events_suck.ts +2 -2
  151. package/src/index.ts +15 -22
  152. package/src/logging.ts +3 -3
  153. package/src/middlewares.ts +1 -16
  154. package/src/types.ts +1 -1
  155. package/dist/cjs/base_event.js.map +0 -7
  156. package/dist/cjs/bridge_jsonl.js.map +0 -7
  157. package/dist/cjs/bridge_nats.js.map +0 -7
  158. package/dist/cjs/bridge_postgres.js.map +0 -7
  159. package/dist/cjs/bridge_redis.js.map +0 -7
  160. package/dist/cjs/bridge_sqlite.js.map +0 -7
  161. package/dist/cjs/event_bus.js.map +0 -7
  162. package/dist/cjs/event_handler.js.map +0 -7
  163. package/dist/cjs/event_history.js.map +0 -7
  164. package/dist/cjs/event_result.js.map +0 -7
  165. package/dist/cjs/lock_manager.js.map +0 -7
  166. package/dist/cjs/middleware_otel_tracing.js.map +0 -7
  167. package/dist/esm/base_event.js.map +0 -7
  168. package/dist/esm/bridge_jsonl.js.map +0 -7
  169. package/dist/esm/bridge_nats.js.map +0 -7
  170. package/dist/esm/bridge_postgres.js.map +0 -7
  171. package/dist/esm/bridge_redis.js.map +0 -7
  172. package/dist/esm/bridge_sqlite.js.map +0 -7
  173. package/dist/esm/event_bus.js.map +0 -7
  174. package/dist/esm/event_handler.js.map +0 -7
  175. package/dist/esm/event_history.js.map +0 -7
  176. package/dist/esm/event_result.js.map +0 -7
  177. package/dist/esm/lock_manager.js.map +0 -7
  178. package/dist/esm/middleware_otel_tracing.js.map +0 -7
@@ -0,0 +1,332 @@
1
+ import { BaseEvent } from './BaseEvent.js'
2
+ import { EventBus } from './EventBus.js'
3
+ import type { EventClass, EventHandlerCallable, EventPattern, UntypedEventHandlerFunction } from './types.js'
4
+
5
+ export type EndpointScheme = 'unix' | 'http' | 'https'
6
+
7
+ export type ParsedEndpoint = {
8
+ raw: string
9
+ scheme: EndpointScheme
10
+ host?: string
11
+ port?: number
12
+ path?: string
13
+ }
14
+
15
+ const isNodeRuntime = (): boolean => {
16
+ const maybe_process = (globalThis as { process?: { versions?: { node?: string } } }).process
17
+ return typeof maybe_process?.versions?.node === 'string'
18
+ }
19
+
20
+ const isBrowserRuntime = (): boolean => !isNodeRuntime() && typeof globalThis.window !== 'undefined'
21
+
22
+ export const randomSuffix = (): string => Math.random().toString(36).slice(2, 10)
23
+ const UNIX_SOCKET_MAX_PATH_CHARS = 90
24
+
25
+ export const parseEndpoint = (raw_endpoint: string): ParsedEndpoint => {
26
+ let parsed: URL
27
+ try {
28
+ parsed = new URL(raw_endpoint)
29
+ } catch {
30
+ throw new Error(`Invalid endpoint URL: ${raw_endpoint}`)
31
+ }
32
+
33
+ const protocol = parsed.protocol.replace(/:$/, '').toLowerCase()
34
+ if (protocol !== 'unix' && protocol !== 'http' && protocol !== 'https') {
35
+ throw new Error(`Unsupported endpoint scheme: ${raw_endpoint}`)
36
+ }
37
+
38
+ if (protocol === 'unix') {
39
+ const socket_path = decodeURIComponent(parsed.pathname || '')
40
+ if (!socket_path) {
41
+ throw new Error(`Invalid unix endpoint (missing socket path): ${raw_endpoint}`)
42
+ }
43
+ const socket_path_len = new TextEncoder().encode(socket_path).length
44
+ if (socket_path_len > UNIX_SOCKET_MAX_PATH_CHARS) {
45
+ throw new Error(`Unix socket path is too long (${socket_path_len} chars), max is ${UNIX_SOCKET_MAX_PATH_CHARS}: ${socket_path}`)
46
+ }
47
+ return { raw: raw_endpoint, scheme: 'unix', path: socket_path }
48
+ }
49
+
50
+ if (!parsed.hostname) {
51
+ throw new Error(`Invalid HTTP endpoint (missing hostname): ${raw_endpoint}`)
52
+ }
53
+
54
+ const default_port = protocol === 'https' ? 443 : 80
55
+ return {
56
+ raw: raw_endpoint,
57
+ scheme: protocol,
58
+ host: parsed.hostname,
59
+ port: parsed.port ? Number(parsed.port) : default_port,
60
+ path: `${parsed.pathname || '/'}${parsed.search || ''}`,
61
+ }
62
+ }
63
+
64
+ const importNodeModule = async (specifier: string): Promise<any> => {
65
+ const dynamic_import = Function('module_name', 'return import(module_name)') as (module_name: string) => Promise<unknown>
66
+ return dynamic_import(specifier) as Promise<any>
67
+ }
68
+
69
+ export class EventBridge {
70
+ readonly send_to: ParsedEndpoint | null
71
+ readonly listen_on: ParsedEndpoint | null
72
+ readonly name: string
73
+
74
+ protected readonly inbound_bus: EventBus
75
+ private start_promise: Promise<void> | null
76
+ private node_server: any | null
77
+
78
+ constructor(send_to?: string | null, listen_on?: string | null, name?: string) {
79
+ this.send_to = send_to ? parseEndpoint(send_to) : null
80
+ this.listen_on = listen_on ? parseEndpoint(listen_on) : null
81
+ this.name = name ?? `EventBridge_${randomSuffix()}`
82
+ this.inbound_bus = new EventBus(this.name, { max_history_size: 0 })
83
+ this.start_promise = null
84
+ this.node_server = null
85
+
86
+ if (this.listen_on && isBrowserRuntime()) {
87
+ throw new Error(`${this.constructor.name} listen_on is not supported in browser runtimes`)
88
+ }
89
+
90
+ this.dispatch = this.dispatch.bind(this)
91
+ this.emit = this.emit.bind(this)
92
+ this.on = this.on.bind(this)
93
+ }
94
+
95
+ on<T extends BaseEvent>(event_pattern: EventClass<T>, handler: EventHandlerCallable<T>): void
96
+ on<T extends BaseEvent>(event_pattern: string | '*', handler: UntypedEventHandlerFunction<T>): void
97
+ on(event_pattern: EventPattern | '*', handler: EventHandlerCallable | UntypedEventHandlerFunction): void {
98
+ this.ensureListenerStarted()
99
+ if (typeof event_pattern === 'string') {
100
+ this.inbound_bus.on(event_pattern, handler as UntypedEventHandlerFunction<BaseEvent>)
101
+ return
102
+ }
103
+ this.inbound_bus.on(event_pattern as EventClass<BaseEvent>, handler as EventHandlerCallable<BaseEvent>)
104
+ }
105
+
106
+ async emit<T extends BaseEvent>(event: T): Promise<void> {
107
+ if (!this.send_to) {
108
+ throw new Error(`${this.constructor.name}.emit() requires send_to`)
109
+ }
110
+
111
+ const payload = event.toJSON()
112
+
113
+ if (this.send_to.scheme === 'unix') {
114
+ await this.sendUnix(this.send_to, payload)
115
+ return
116
+ }
117
+
118
+ await this.sendHttp(this.send_to, payload)
119
+ }
120
+
121
+ async dispatch<T extends BaseEvent>(event: T): Promise<void> {
122
+ return this.emit(event)
123
+ }
124
+
125
+ async start(): Promise<void> {
126
+ if (!this.listen_on) return
127
+ if (this.node_server) return
128
+ if (this.start_promise) {
129
+ await this.start_promise
130
+ return
131
+ }
132
+
133
+ if (!isNodeRuntime()) {
134
+ throw new Error(`${this.constructor.name} listen_on is only supported in Node.js runtimes`)
135
+ }
136
+
137
+ const launch = (async () => {
138
+ const endpoint = this.listen_on
139
+ if (!endpoint) return
140
+
141
+ if (endpoint.scheme === 'unix') {
142
+ await this.startUnixListener(endpoint)
143
+ return
144
+ }
145
+
146
+ if (endpoint.scheme !== 'http') {
147
+ throw new Error(`listen_on only supports unix:// or http:// endpoints, got: ${endpoint.raw}`)
148
+ }
149
+
150
+ await this.startHttpListener(endpoint)
151
+ })()
152
+ this.start_promise = launch
153
+
154
+ try {
155
+ await launch
156
+ } finally {
157
+ if (this.start_promise === launch) {
158
+ this.start_promise = null
159
+ }
160
+ }
161
+ }
162
+
163
+ async close(): Promise<void> {
164
+ if (this.start_promise) {
165
+ await Promise.allSettled([this.start_promise])
166
+ this.start_promise = null
167
+ }
168
+
169
+ if (this.node_server) {
170
+ const server = this.node_server
171
+ await new Promise<void>((resolve) => {
172
+ server.close(() => resolve())
173
+ })
174
+ this.node_server = null
175
+ }
176
+
177
+ this.inbound_bus.destroy()
178
+ }
179
+
180
+ private ensureListenerStarted(): void {
181
+ if (!this.listen_on || this.node_server || this.start_promise) {
182
+ return
183
+ }
184
+ void this.start().catch((error: unknown) => {
185
+ console.error('[abxbus] EventBridge failed to start listener', error)
186
+ })
187
+ }
188
+
189
+ private async handleIncomingPayload(payload: unknown): Promise<void> {
190
+ const event = BaseEvent.fromJSON(payload).eventReset()
191
+ this.inbound_bus.emit(event)
192
+ }
193
+
194
+ private async sendHttp(endpoint: ParsedEndpoint, payload: unknown): Promise<void> {
195
+ const response = await fetch(endpoint.raw, {
196
+ method: 'POST',
197
+ headers: { 'content-type': 'application/json' },
198
+ body: JSON.stringify(payload),
199
+ })
200
+ if (!response.ok) {
201
+ throw new Error(`IPC HTTP send failed with status ${response.status}: ${endpoint.raw}`)
202
+ }
203
+ }
204
+
205
+ private async sendUnix(endpoint: ParsedEndpoint, payload: unknown): Promise<void> {
206
+ if (!isNodeRuntime()) {
207
+ throw new Error('unix:// send_to is only supported in Node.js runtimes')
208
+ }
209
+
210
+ const socket_path = endpoint.path
211
+ if (!socket_path) {
212
+ throw new Error(`Invalid unix endpoint: ${endpoint.raw}`)
213
+ }
214
+
215
+ const node_net = await importNodeModule('node:net')
216
+ await new Promise<void>((resolve, reject) => {
217
+ const socket = node_net.createConnection(socket_path, () => {
218
+ socket.end(`${JSON.stringify(payload)}\n`)
219
+ })
220
+ socket.on('error', (error: unknown) => reject(error))
221
+ socket.on('close', () => resolve())
222
+ })
223
+ }
224
+
225
+ private async startHttpListener(endpoint: ParsedEndpoint): Promise<void> {
226
+ const node_http = await importNodeModule('node:http')
227
+ const expected_path = endpoint.path || '/'
228
+
229
+ this.node_server = node_http.createServer((req: any, res: any) => {
230
+ const method = (req.method || '').toUpperCase()
231
+ const request_url = String(req.url || '/')
232
+
233
+ if (method !== 'POST') {
234
+ res.statusCode = 405
235
+ res.end('method not allowed')
236
+ return
237
+ }
238
+ if (request_url !== expected_path) {
239
+ res.statusCode = 404
240
+ res.end('not found')
241
+ return
242
+ }
243
+
244
+ let body = ''
245
+ req.setEncoding('utf8')
246
+ req.on('data', (chunk: string) => {
247
+ body += chunk
248
+ })
249
+ req.on('end', () => {
250
+ let parsed_payload: unknown
251
+ try {
252
+ parsed_payload = JSON.parse(body)
253
+ } catch {
254
+ res.statusCode = 400
255
+ res.end('invalid json')
256
+ return
257
+ }
258
+
259
+ void this.handleIncomingPayload(parsed_payload)
260
+ .then(() => {
261
+ res.statusCode = 202
262
+ res.end('accepted')
263
+ })
264
+ .catch((error: unknown) => {
265
+ res.statusCode = 500
266
+ res.end('failed to process event')
267
+ console.error('[abxbus] EventBridge HTTP listener error', error)
268
+ })
269
+ })
270
+ })
271
+
272
+ await new Promise<void>((resolve, reject) => {
273
+ this.node_server.once('error', (error: unknown) => reject(error))
274
+ this.node_server.listen(endpoint.port, endpoint.host, () => resolve())
275
+ })
276
+ }
277
+
278
+ private async startUnixListener(endpoint: ParsedEndpoint): Promise<void> {
279
+ const socket_path = endpoint.path
280
+ if (!socket_path) {
281
+ throw new Error(`Invalid unix endpoint: ${endpoint.raw}`)
282
+ }
283
+
284
+ const node_net = await importNodeModule('node:net')
285
+ const node_fs = await importNodeModule('node:fs')
286
+
287
+ try {
288
+ await node_fs.promises.unlink(socket_path)
289
+ } catch (error: unknown) {
290
+ const code = (error as { code?: string }).code
291
+ if (code !== 'ENOENT') {
292
+ throw error
293
+ }
294
+ }
295
+
296
+ this.node_server = node_net.createServer((socket: any) => {
297
+ let buffer = ''
298
+ socket.setEncoding('utf8')
299
+ socket.on('data', (chunk: string) => {
300
+ buffer += chunk
301
+ while (true) {
302
+ const newline_index = buffer.indexOf('\n')
303
+ if (newline_index < 0) break
304
+ const line = buffer.slice(0, newline_index).trim()
305
+ buffer = buffer.slice(newline_index + 1)
306
+ if (!line) continue
307
+ try {
308
+ const parsed_payload = JSON.parse(line)
309
+ void this.handleIncomingPayload(parsed_payload)
310
+ } catch {
311
+ // Ignore malformed lines and continue reading next frames.
312
+ }
313
+ }
314
+ })
315
+ socket.on('end', () => {
316
+ const remainder = buffer.trim()
317
+ if (!remainder) return
318
+ try {
319
+ const parsed_payload = JSON.parse(remainder)
320
+ void this.handleIncomingPayload(parsed_payload)
321
+ } catch {
322
+ // Ignore malformed trailing frame.
323
+ }
324
+ })
325
+ })
326
+
327
+ await new Promise<void>((resolve, reject) => {
328
+ this.node_server.once('error', (error: unknown) => reject(error))
329
+ this.node_server.listen(socket_path, () => resolve())
330
+ })
331
+ }
332
+ }
@@ -1,6 +1,6 @@
1
- import { BaseEvent, type BaseEventJSON } from './base_event.js'
2
- import { EventHistory } from './event_history.js'
3
- import { EventResult } from './event_result.js'
1
+ import { BaseEvent, type BaseEventJSON } from './BaseEvent.js'
2
+ import { EventHistory } from './EventHistory.js'
3
+ import { EventResult } from './EventResult.js'
4
4
  import { captureAsyncContext } from './async_context.js'
5
5
  import { _runWithSlowMonitor, _runWithTimeout } from './timing.js'
6
6
  import {
@@ -9,7 +9,7 @@ import {
9
9
  type EventHandlerConcurrencyMode,
10
10
  type EventHandlerCompletionMode,
11
11
  LockManager,
12
- } from './lock_manager.js'
12
+ } from './LockManager.js'
13
13
  import {
14
14
  EventHandler,
15
15
  EventHandlerAbortedError,
@@ -17,8 +17,8 @@ import {
17
17
  EventHandlerTimeoutError,
18
18
  type EphemeralFindEventHandler,
19
19
  type EventHandlerJSON,
20
- } from './event_handler.js'
21
- import type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './middlewares.js'
20
+ } from './EventHandler.js'
21
+ import type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './EventBusMiddleware.js'
22
22
  import { logTree } from './logging.js'
23
23
  import { v7 as uuidv7 } from 'uuid'
24
24
  import { monotonicDatetime } from './helpers.js'
@@ -0,0 +1,16 @@
1
+ import type { BaseEvent } from './BaseEvent.js'
2
+ import type { EventBus } from './EventBus.js'
3
+ import type { EventHandler } from './EventHandler.js'
4
+ import type { EventResult } from './EventResult.js'
5
+ import type { EventStatus } from './types.js'
6
+
7
+ export type { EventStatus } from './types.js'
8
+
9
+ export interface EventBusMiddleware {
10
+ onEventChange?(eventbus: EventBus, event: BaseEvent, status: EventStatus): void | Promise<void>
11
+ onEventResultChange?(eventbus: EventBus, event: BaseEvent, event_result: EventResult, status: EventStatus): void | Promise<void>
12
+ onBusHandlersChange?(eventbus: EventBus, handler: EventHandler, registered: boolean): void | Promise<void>
13
+ }
14
+
15
+ export type EventBusMiddlewareCtor = new () => EventBusMiddleware
16
+ export type EventBusMiddlewareInput = EventBusMiddleware | EventBusMiddlewareCtor
@@ -2,8 +2,8 @@ import { z } from 'zod'
2
2
  import { v5 as uuidv5 } from 'uuid'
3
3
 
4
4
  import { normalizeEventPattern, type EventHandlerCallable, type EventPattern } from './types.js'
5
- import { BaseEvent } from './base_event.js'
6
- import type { EventResult } from './event_result.js'
5
+ import { BaseEvent } from './BaseEvent.js'
6
+ import type { EventResult } from './EventResult.js'
7
7
  import { monotonicDatetime } from './helpers.js'
8
8
 
9
9
  const HANDLER_ID_NAMESPACE = uuidv5('abxbus-handler', uuidv5.DNS)
@@ -1,4 +1,4 @@
1
- import { BaseEvent } from './base_event.js'
1
+ import { BaseEvent } from './BaseEvent.js'
2
2
  import type { EventPattern, FindWindow } from './types.js'
3
3
  import { normalizeEventPattern } from './types.js'
4
4
  import { monotonicDatetime } from './helpers.js'
@@ -2,11 +2,11 @@ import { v7 as uuidv7 } from 'uuid'
2
2
 
3
3
  import { z } from 'zod'
4
4
 
5
- import { BaseEvent } from './base_event.js'
6
- import type { EventBus } from './event_bus.js'
7
- import { EventHandler, EventHandlerCancelledError, EventHandlerResultSchemaError, EventHandlerTimeoutError } from './event_handler.js'
8
- import { withResolvers, type HandlerLock } from './lock_manager.js'
9
- import type { Deferred } from './lock_manager.js'
5
+ import { BaseEvent } from './BaseEvent.js'
6
+ import type { EventBus } from './EventBus.js'
7
+ import { EventHandler, EventHandlerCancelledError, EventHandlerResultSchemaError, EventHandlerTimeoutError } from './EventHandler.js'
8
+ import { withResolvers, type HandlerLock } from './LockManager.js'
9
+ import type { Deferred } from './LockManager.js'
10
10
  import type { EventHandlerCallable, EventResultType } from './types.js'
11
11
  import { isZodSchema } from './types.js'
12
12
  import { _runWithAsyncContext } from './async_context.js'
@@ -87,7 +87,9 @@ export class EventResult<TEvent extends BaseEvent = BaseEvent> {
87
87
  }
88
88
 
89
89
  get bus(): EventBus {
90
- return this.event.event_bus!
90
+ const original_event = this.event._event_original ?? this.event
91
+ const dispatch_bus = original_event.event_bus ?? this.event.event_bus!
92
+ return dispatch_bus.all_instances.findBusById(this.handler.eventbus_id) ?? dispatch_bus
91
93
  }
92
94
 
93
95
  get handler_id(): string {
@@ -0,0 +1,27 @@
1
+ import { EventBridge, parseEndpoint, randomSuffix } from './EventBridge.js'
2
+
3
+ export type HTTPEventBridgeOptions = {
4
+ send_to?: string | null
5
+ listen_on?: string | null
6
+ name?: string
7
+ }
8
+
9
+ export class HTTPEventBridge extends EventBridge {
10
+ constructor(send_to?: string | null, listen_on?: string | null, name?: string)
11
+ constructor(options?: HTTPEventBridgeOptions)
12
+ constructor(send_to_or_options?: string | null | HTTPEventBridgeOptions, listen_on?: string | null, name?: string) {
13
+ const options: HTTPEventBridgeOptions =
14
+ typeof send_to_or_options === 'object'
15
+ ? (send_to_or_options ?? {})
16
+ : { send_to: send_to_or_options ?? undefined, listen_on: listen_on ?? undefined, name }
17
+
18
+ if (options.send_to && parseEndpoint(options.send_to).scheme === 'unix') {
19
+ throw new Error('HTTPEventBridge send_to must be http:// or https://')
20
+ }
21
+ if (options.listen_on && parseEndpoint(options.listen_on).scheme !== 'http') {
22
+ throw new Error('HTTPEventBridge listen_on must be http://')
23
+ }
24
+
25
+ super(options.send_to, options.listen_on, options.name ?? `HTTPEventBridge_${randomSuffix()}`)
26
+ }
27
+ }
@@ -1,5 +1,5 @@
1
- import { BaseEvent } from './base_event.js'
2
- import { EventBus } from './event_bus.js'
1
+ import { BaseEvent } from './BaseEvent.js'
2
+ import { EventBus } from './EventBus.js'
3
3
  import type { EventClass, EventHandlerCallable, EventPattern, UntypedEventHandlerFunction } from './types.js'
4
4
 
5
5
  const isNodeRuntime = (): boolean => {
@@ -1,5 +1,5 @@
1
- import type { BaseEvent } from './base_event.js'
2
- import type { EventResult } from './event_result.js'
1
+ import type { BaseEvent } from './BaseEvent.js'
2
+ import type { EventResult } from './EventResult.js'
3
3
  import { createAsyncLocalStorage, type AsyncLocalStorageLike } from './async_context.js'
4
4
 
5
5
  // ─── Deferred / withResolvers ────────────────────────────────────────────────
@@ -1,5 +1,5 @@
1
- import { BaseEvent } from './base_event.js'
2
- import { EventBus } from './event_bus.js'
1
+ import { BaseEvent } from './BaseEvent.js'
2
+ import { EventBus } from './EventBus.js'
3
3
  import { assertOptionalDependencyAvailable, importOptionalDependency, isNodeRuntime } from './optional_deps.js'
4
4
  import type { EventClass, EventHandlerCallable, EventPattern, UntypedEventHandlerFunction } from './types.js'
5
5
 
@@ -17,10 +17,10 @@ import { BasicTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trac
17
17
  import type { SpanLimits, SpanProcessor } from '@opentelemetry/sdk-trace-base'
18
18
  import { SpanImpl } from '@opentelemetry/sdk-trace-base/build/src/Span.js'
19
19
 
20
- import type { BaseEvent } from './base_event.js'
21
- import type { EventBus } from './event_bus.js'
22
- import type { EventResult } from './event_result.js'
23
- import type { EventBusMiddleware } from './middlewares.js'
20
+ import type { BaseEvent } from './BaseEvent.js'
21
+ import type { EventBus } from './EventBus.js'
22
+ import type { EventResult } from './EventResult.js'
23
+ import type { EventBusMiddleware } from './EventBusMiddleware.js'
24
24
  import type { EventStatus } from './types.js'
25
25
 
26
26
  type OpenTelemetryTraceApi = Pick<typeof trace, 'getTracer' | 'setSpan'> & Partial<Pick<typeof trace, 'setSpanContext'>>
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * PostgreSQL LISTEN/NOTIFY + flat-table bridge for forwarding events.
3
3
  */
4
- import { BaseEvent } from './base_event.js'
5
- import { EventBus } from './event_bus.js'
4
+ import { BaseEvent } from './BaseEvent.js'
5
+ import { EventBus } from './EventBus.js'
6
6
  import { assertOptionalDependencyAvailable, importOptionalDependency, isNodeRuntime } from './optional_deps.js'
7
7
  import type { EventClass, EventHandlerCallable, EventPattern, UntypedEventHandlerFunction } from './types.js'
8
8
 
@@ -11,8 +11,8 @@
11
11
  * URL format:
12
12
  * redis://user:pass@host:6379/<db>/<optional_channel>
13
13
  */
14
- import { BaseEvent } from './base_event.js'
15
- import { EventBus } from './event_bus.js'
14
+ import { BaseEvent } from './BaseEvent.js'
15
+ import { EventBus } from './EventBus.js'
16
16
  import { assertOptionalDependencyAvailable, importOptionalDependency, isNodeRuntime } from './optional_deps.js'
17
17
  import type { EventClass, EventHandlerCallable, EventPattern, UntypedEventHandlerFunction } from './types.js'
18
18
 
@@ -1,5 +1,5 @@
1
- import { BaseEvent } from './base_event.js'
2
- import { EventBus } from './event_bus.js'
1
+ import { BaseEvent } from './BaseEvent.js'
2
+ import { EventBus } from './EventBus.js'
3
3
  import { isNodeRuntime } from './optional_deps.js'
4
4
  import type { EventClass, EventHandlerCallable, EventPattern, UntypedEventHandlerFunction } from './types.js'
5
5
 
@@ -0,0 +1,13 @@
1
+ import { EventBridge, randomSuffix } from './EventBridge.js'
2
+
3
+ export class SocketEventBridge extends EventBridge {
4
+ constructor(path?: string | null, name?: string) {
5
+ const normalized = path ? (path.startsWith('unix://') ? path.slice(7) : path) : null
6
+ if (normalized === '') {
7
+ throw new Error('SocketEventBridge path must not be empty')
8
+ }
9
+
10
+ const endpoint = normalized ? `unix://${normalized}` : null
11
+ super(endpoint, endpoint, name ?? `SocketEventBridge_${randomSuffix()}`)
12
+ }
13
+ }