abxbus 2.4.29 → 2.4.30

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 (48) hide show
  1. package/README.md +22 -1
  2. package/dist/cjs/EventBus.d.ts +5 -1
  3. package/dist/cjs/EventBus.js +20 -7
  4. package/dist/cjs/EventBus.js.map +2 -2
  5. package/dist/cjs/EventHistory.d.ts +5 -0
  6. package/dist/cjs/EventHistory.js +32 -5
  7. package/dist/cjs/EventHistory.js.map +2 -2
  8. package/dist/cjs/TachyonEventBridge.d.ts +25 -0
  9. package/dist/cjs/TachyonEventBridge.js +427 -0
  10. package/dist/cjs/TachyonEventBridge.js.map +7 -0
  11. package/dist/cjs/base_event.d.ts +2 -2
  12. package/dist/cjs/bridges.d.ts +1 -0
  13. package/dist/cjs/bridges.js +3 -1
  14. package/dist/cjs/bridges.js.map +2 -2
  15. package/dist/cjs/event_handler.d.ts +0 -1
  16. package/dist/cjs/index.d.ts +2 -2
  17. package/dist/cjs/index.js.map +2 -2
  18. package/dist/cjs/types.d.ts +3 -0
  19. package/dist/cjs/types.js.map +2 -2
  20. package/dist/esm/EventBus.js +20 -7
  21. package/dist/esm/EventBus.js.map +2 -2
  22. package/dist/esm/EventHistory.js +32 -5
  23. package/dist/esm/EventHistory.js.map +2 -2
  24. package/dist/esm/TachyonEventBridge.js +406 -0
  25. package/dist/esm/TachyonEventBridge.js.map +7 -0
  26. package/dist/esm/bridges.js +3 -1
  27. package/dist/esm/bridges.js.map +2 -2
  28. package/dist/esm/index.js.map +2 -2
  29. package/dist/esm/types.js.map +2 -2
  30. package/dist/types/EventBus.d.ts +5 -1
  31. package/dist/types/EventHistory.d.ts +5 -0
  32. package/dist/types/TachyonEventBridge.d.ts +25 -0
  33. package/dist/types/base_event.d.ts +2 -2
  34. package/dist/types/bridges.d.ts +1 -0
  35. package/dist/types/event_handler.d.ts +0 -1
  36. package/dist/types/index.d.ts +2 -2
  37. package/dist/types/types.d.ts +3 -0
  38. package/package.json +26 -20
  39. package/src/EventBus.ts +38 -10
  40. package/src/EventHistory.ts +47 -4
  41. package/src/TachyonEventBridge.ts +498 -0
  42. package/src/bridges.ts +1 -0
  43. package/src/index.ts +10 -2
  44. package/src/types.ts +2 -0
  45. package/dist/cjs/bridge_ipc.d.ts +0 -45
  46. package/dist/cjs/middleware_otel_tracing.d.ts +0 -49
  47. package/dist/types/bridge_ipc.d.ts +0 -45
  48. package/dist/types/middleware_otel_tracing.d.ts +0 -49
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/EventHistory.ts"],
4
- "sourcesContent": ["import { BaseEvent } from './BaseEvent.js'\nimport type { EventPattern, FindWindow } from './types.js'\nimport { normalizeEventPattern } from './types.js'\nimport { monotonicDatetime } from './helpers.js'\n\nexport type EventHistoryFindOptions = {\n past?: FindWindow\n future?: FindWindow\n child_of?: BaseEvent | null\n event_is_child_of?: (event: BaseEvent, ancestor: BaseEvent) => boolean\n wait_for_future_match?: (\n event_pattern: string | '*',\n matches: (event: BaseEvent) => boolean,\n future: FindWindow\n ) => Promise<BaseEvent | null>\n} & Record<string, unknown>\n\nexport type EventHistoryTrimOptions<TEvent extends BaseEvent = BaseEvent> = {\n is_event_complete?: (event: TEvent) => boolean\n on_remove?: (event: TEvent) => void\n owner_label?: string\n max_history_size?: number | null\n max_history_drop?: boolean\n}\n\nexport class EventHistory<TEvent extends BaseEvent = BaseEvent> implements Iterable<[string, TEvent]> {\n max_history_size: number | null\n max_history_drop: boolean\n\n private _events: Map<string, TEvent>\n private _warned_about_dropping_uncompleted_events: boolean\n\n constructor(options: { max_history_size?: number | null; max_history_drop?: boolean } = {}) {\n this.max_history_size = options.max_history_size === undefined ? 100 : options.max_history_size\n this.max_history_drop = options.max_history_drop ?? false\n this._events = new Map()\n this._warned_about_dropping_uncompleted_events = false\n }\n\n get size(): number {\n return this._events.size\n }\n\n [Symbol.iterator](): Iterator<[string, TEvent]> {\n return this._events[Symbol.iterator]()\n }\n\n entries(): IterableIterator<[string, TEvent]> {\n return this._events.entries()\n }\n\n keys(): IterableIterator<string> {\n return this._events.keys()\n }\n\n values(): IterableIterator<TEvent> {\n return this._events.values()\n }\n\n clear(): void {\n this._events.clear()\n }\n\n get(event_id: string): TEvent | undefined {\n return this._events.get(event_id)\n }\n\n set(event_id: string, event: TEvent): this {\n this._events.set(event_id, event)\n return this\n }\n\n has(event_id: string): boolean {\n return this._events.has(event_id)\n }\n\n delete(event_id: string): boolean {\n return this._events.delete(event_id)\n }\n\n addEvent(event: TEvent): void {\n this._events.set(event.event_id, event)\n }\n\n getEvent(event_id: string): TEvent | undefined {\n return this._events.get(event_id)\n }\n\n removeEvent(event_id: string): boolean {\n return this._events.delete(event_id)\n }\n\n hasEvent(event_id: string): boolean {\n return this._events.has(event_id)\n }\n\n static normalizeEventPattern(event_pattern: EventPattern | '*'): string | '*' {\n return normalizeEventPattern(event_pattern)\n }\n\n find(event_pattern: '*', where?: (event: TEvent) => boolean, options?: EventHistoryFindOptions): Promise<TEvent | null>\n find<TMatch extends TEvent>(\n event_pattern: EventPattern<TMatch>,\n where?: (event: TMatch) => boolean,\n options?: EventHistoryFindOptions\n ): Promise<TMatch | null>\n async find(\n event_pattern: EventPattern<TEvent> | '*',\n where: (event: TEvent) => boolean = () => true,\n options: EventHistoryFindOptions = {}\n ): Promise<TEvent | null> {\n const past = options.past ?? true\n const future = options.future ?? false\n const child_of = options.child_of ?? null\n const eventIsChildOf = options.event_is_child_of ?? ((event: BaseEvent, ancestor: BaseEvent) => this.eventIsChildOf(event, ancestor))\n const waitForFutureMatch = options.wait_for_future_match\n if (past === false && future === false) {\n return null\n }\n\n const event_key = EventHistory.normalizeEventPattern(event_pattern)\n const cutoff_at = past === true ? null : monotonicDatetime(new Date(Date.now() - Math.max(0, Number(past)) * 1000).toISOString())\n\n const event_field_filters = Object.entries(options).filter(\n ([key, value]) =>\n key !== 'past' &&\n key !== 'future' &&\n key !== 'child_of' &&\n key !== 'event_is_child_of' &&\n key !== 'wait_for_future_match' &&\n value !== undefined\n )\n\n const matches = (event: BaseEvent): boolean =>\n (event_key === '*' || event.event_type === event_key) &&\n (!child_of || eventIsChildOf(event, child_of)) &&\n event_field_filters.every(([field_name, expected]) => (event as unknown as Record<string, unknown>)[field_name] === expected) &&\n where(event as TEvent)\n\n if (past !== false) {\n const history_values = Array.from(this._events.values())\n for (let i = history_values.length - 1; i >= 0; i -= 1) {\n const event = history_values[i]\n if (cutoff_at !== null && event.event_created_at < cutoff_at) {\n continue\n }\n if (matches(event)) {\n return event\n }\n }\n }\n\n if (future === false || !waitForFutureMatch) {\n return null\n }\n\n return (await waitForFutureMatch(event_key, matches, future)) as TEvent | null\n }\n\n trimEventHistory(options: EventHistoryTrimOptions<TEvent> = {}): number {\n const max_history_size = options.max_history_size ?? this.max_history_size\n const max_history_drop = options.max_history_drop ?? this.max_history_drop\n if (max_history_size === null) {\n return 0\n }\n\n const is_event_complete = options.is_event_complete ?? ((event: TEvent) => event.event_status === 'completed')\n const on_remove = options.on_remove\n\n if (max_history_size === 0) {\n let removed_count = 0\n for (const [event_id, event] of Array.from(this._events.entries())) {\n if (!is_event_complete(event)) {\n continue\n }\n this._events.delete(event_id)\n on_remove?.(event)\n removed_count += 1\n }\n return removed_count\n }\n\n if (!max_history_drop || this.size <= max_history_size) {\n return 0\n }\n\n let remaining_overage = this.size - max_history_size\n let removed_count = 0\n const remove_event = (event_id: string, event: TEvent): void => {\n this._events.delete(event_id)\n on_remove?.(event)\n removed_count += 1\n }\n\n for (const [event_id, event] of Array.from(this._events.entries())) {\n if (remaining_overage <= 0) {\n break\n }\n if (!is_event_complete(event)) {\n continue\n }\n remove_event(event_id, event)\n remaining_overage -= 1\n }\n\n let dropped_uncompleted = 0\n for (const [event_id, event] of Array.from(this._events.entries())) {\n if (remaining_overage <= 0) {\n break\n }\n if (!is_event_complete(event)) {\n dropped_uncompleted += 1\n }\n remove_event(event_id, event)\n remaining_overage -= 1\n }\n\n if (dropped_uncompleted > 0 && !this._warned_about_dropping_uncompleted_events) {\n this._warned_about_dropping_uncompleted_events = true\n const owner_label = options.owner_label ?? 'EventBus'\n console.error(\n `[abxbus] \u26A0\uFE0F Bus ${owner_label} has exceeded max_history_size=${max_history_size} and is dropping oldest history entries (even uncompleted events). Increase max_history_size or set max_history_drop=false to reject.`\n )\n }\n\n return removed_count\n }\n\n private eventIsChildOf(event: BaseEvent, ancestor: BaseEvent): boolean {\n let current_parent_id = event.event_parent_id\n const visited = new Set<string>()\n\n while (current_parent_id && !visited.has(current_parent_id)) {\n if (current_parent_id === ancestor.event_id) {\n return true\n }\n visited.add(current_parent_id)\n const parent = this._events.get(current_parent_id)\n if (!parent) {\n return false\n }\n current_parent_id = parent.event_parent_id\n }\n\n return false\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { BaseEvent } from './BaseEvent.js'\nimport type { EventPattern, FindWindow } from './types.js'\nimport { normalizeEventPattern } from './types.js'\nimport { monotonicDatetime } from './helpers.js'\n\nexport type EventHistoryFindOptions = {\n past?: FindWindow\n future?: FindWindow\n child_of?: BaseEvent | null\n event_is_child_of?: (event: BaseEvent, ancestor: BaseEvent) => boolean\n wait_for_future_match?: (\n event_pattern: string | '*',\n matches: (event: BaseEvent) => boolean,\n future: FindWindow\n ) => Promise<BaseEvent | null>\n} & Record<string, unknown>\n\nexport type EventHistoryFilterOptions = EventHistoryFindOptions & { limit?: number | null }\n\nexport type EventHistoryTrimOptions<TEvent extends BaseEvent = BaseEvent> = {\n is_event_complete?: (event: TEvent) => boolean\n on_remove?: (event: TEvent) => void\n owner_label?: string\n max_history_size?: number | null\n max_history_drop?: boolean\n}\n\nexport class EventHistory<TEvent extends BaseEvent = BaseEvent> implements Iterable<[string, TEvent]> {\n max_history_size: number | null\n max_history_drop: boolean\n\n private _events: Map<string, TEvent>\n private _warned_about_dropping_uncompleted_events: boolean\n\n constructor(options: { max_history_size?: number | null; max_history_drop?: boolean } = {}) {\n this.max_history_size = options.max_history_size === undefined ? 100 : options.max_history_size\n this.max_history_drop = options.max_history_drop ?? false\n this._events = new Map()\n this._warned_about_dropping_uncompleted_events = false\n }\n\n get size(): number {\n return this._events.size\n }\n\n [Symbol.iterator](): Iterator<[string, TEvent]> {\n return this._events[Symbol.iterator]()\n }\n\n entries(): IterableIterator<[string, TEvent]> {\n return this._events.entries()\n }\n\n keys(): IterableIterator<string> {\n return this._events.keys()\n }\n\n values(): IterableIterator<TEvent> {\n return this._events.values()\n }\n\n clear(): void {\n this._events.clear()\n }\n\n get(event_id: string): TEvent | undefined {\n return this._events.get(event_id)\n }\n\n set(event_id: string, event: TEvent): this {\n this._events.set(event_id, event)\n return this\n }\n\n has(event_id: string): boolean {\n return this._events.has(event_id)\n }\n\n delete(event_id: string): boolean {\n return this._events.delete(event_id)\n }\n\n addEvent(event: TEvent): void {\n this._events.set(event.event_id, event)\n }\n\n getEvent(event_id: string): TEvent | undefined {\n return this._events.get(event_id)\n }\n\n removeEvent(event_id: string): boolean {\n return this._events.delete(event_id)\n }\n\n hasEvent(event_id: string): boolean {\n return this._events.has(event_id)\n }\n\n static normalizeEventPattern(event_pattern: EventPattern | '*'): string | '*' {\n return normalizeEventPattern(event_pattern)\n }\n\n find(event_pattern: '*', where?: (event: TEvent) => boolean, options?: EventHistoryFindOptions): Promise<TEvent | null>\n find<TMatch extends TEvent>(\n event_pattern: EventPattern<TMatch>,\n where?: (event: TMatch) => boolean,\n options?: EventHistoryFindOptions\n ): Promise<TMatch | null>\n async find(\n event_pattern: EventPattern<TEvent> | '*',\n where: (event: TEvent) => boolean = () => true,\n options: EventHistoryFindOptions = {}\n ): Promise<TEvent | null> {\n // `limit` field-equality filter would collide with filter()'s cap arg; route it through `where`.\n let effective_where = where\n let effective_options: EventHistoryFindOptions = options\n if (Object.prototype.hasOwnProperty.call(options, 'limit')) {\n const { limit: limit_field_value, ...rest } = options as EventHistoryFindOptions & { limit: unknown }\n const inner_where = where\n effective_where = (event: TEvent) => (event as unknown as Record<string, unknown>).limit === limit_field_value && inner_where(event)\n effective_options = rest as EventHistoryFindOptions\n }\n const results = await this.filter(event_pattern as EventPattern<TEvent> | '*', effective_where, {\n ...effective_options,\n limit: 1,\n })\n return results.length > 0 ? results[0] : null\n }\n\n filter(event_pattern: '*', where?: (event: TEvent) => boolean, options?: EventHistoryFilterOptions): Promise<TEvent[]>\n filter<TMatch extends TEvent>(\n event_pattern: EventPattern<TMatch>,\n where?: (event: TMatch) => boolean,\n options?: EventHistoryFilterOptions\n ): Promise<TMatch[]>\n async filter(\n event_pattern: EventPattern<TEvent> | '*',\n where: (event: TEvent) => boolean = () => true,\n options: EventHistoryFilterOptions = {}\n ): Promise<TEvent[]> {\n const past = options.past ?? true\n const future = options.future ?? false\n const child_of = options.child_of ?? null\n const eventIsChildOf = options.event_is_child_of ?? ((event: BaseEvent, ancestor: BaseEvent) => this.eventIsChildOf(event, ancestor))\n const waitForFutureMatch = options.wait_for_future_match\n const limit = options.limit ?? null\n if (past === false && future === false) {\n return []\n }\n\n if (limit !== null && limit <= 0) {\n return []\n }\n\n const event_key = EventHistory.normalizeEventPattern(event_pattern)\n const cutoff_at = past === true ? null : monotonicDatetime(new Date(Date.now() - Math.max(0, Number(past)) * 1000).toISOString())\n\n const event_field_filters = Object.entries(options).filter(\n ([key, value]) =>\n key !== 'past' &&\n key !== 'future' &&\n key !== 'child_of' &&\n key !== 'event_is_child_of' &&\n key !== 'wait_for_future_match' &&\n key !== 'limit' &&\n value !== undefined\n )\n\n const matches = (event: BaseEvent): boolean =>\n (event_key === '*' || event.event_type === event_key) &&\n (!child_of || eventIsChildOf(event, child_of)) &&\n event_field_filters.every(([field_name, expected]) => (event as unknown as Record<string, unknown>)[field_name] === expected) &&\n where(event as TEvent)\n\n const results: TEvent[] = []\n if (past !== false) {\n const history_values = Array.from(this._events.values())\n for (let i = history_values.length - 1; i >= 0; i -= 1) {\n const event = history_values[i]\n if (cutoff_at !== null && event.event_created_at < cutoff_at) {\n continue\n }\n if (matches(event)) {\n results.push(event)\n if (limit !== null && results.length >= limit) {\n return results\n }\n }\n }\n }\n\n if (future === false || !waitForFutureMatch) {\n return results\n }\n\n const future_match = (await waitForFutureMatch(event_key, matches, future)) as TEvent | null\n if (future_match !== null) {\n results.push(future_match)\n }\n return results\n }\n\n trimEventHistory(options: EventHistoryTrimOptions<TEvent> = {}): number {\n const max_history_size = options.max_history_size ?? this.max_history_size\n const max_history_drop = options.max_history_drop ?? this.max_history_drop\n if (max_history_size === null) {\n return 0\n }\n\n const is_event_complete = options.is_event_complete ?? ((event: TEvent) => event.event_status === 'completed')\n const on_remove = options.on_remove\n\n if (max_history_size === 0) {\n let removed_count = 0\n for (const [event_id, event] of Array.from(this._events.entries())) {\n if (!is_event_complete(event)) {\n continue\n }\n this._events.delete(event_id)\n on_remove?.(event)\n removed_count += 1\n }\n return removed_count\n }\n\n if (!max_history_drop || this.size <= max_history_size) {\n return 0\n }\n\n let remaining_overage = this.size - max_history_size\n let removed_count = 0\n const remove_event = (event_id: string, event: TEvent): void => {\n this._events.delete(event_id)\n on_remove?.(event)\n removed_count += 1\n }\n\n for (const [event_id, event] of Array.from(this._events.entries())) {\n if (remaining_overage <= 0) {\n break\n }\n if (!is_event_complete(event)) {\n continue\n }\n remove_event(event_id, event)\n remaining_overage -= 1\n }\n\n let dropped_uncompleted = 0\n for (const [event_id, event] of Array.from(this._events.entries())) {\n if (remaining_overage <= 0) {\n break\n }\n if (!is_event_complete(event)) {\n dropped_uncompleted += 1\n }\n remove_event(event_id, event)\n remaining_overage -= 1\n }\n\n if (dropped_uncompleted > 0 && !this._warned_about_dropping_uncompleted_events) {\n this._warned_about_dropping_uncompleted_events = true\n const owner_label = options.owner_label ?? 'EventBus'\n console.error(\n `[abxbus] \u26A0\uFE0F Bus ${owner_label} has exceeded max_history_size=${max_history_size} and is dropping oldest history entries (even uncompleted events). Increase max_history_size or set max_history_drop=false to reject.`\n )\n }\n\n return removed_count\n }\n\n private eventIsChildOf(event: BaseEvent, ancestor: BaseEvent): boolean {\n let current_parent_id = event.event_parent_id\n const visited = new Set<string>()\n\n while (current_parent_id && !visited.has(current_parent_id)) {\n if (current_parent_id === ancestor.event_id) {\n return true\n }\n visited.add(current_parent_id)\n const parent = this._events.get(current_parent_id)\n if (!parent) {\n return false\n }\n current_parent_id = parent.event_parent_id\n }\n\n return false\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["removed_count"]
7
7
  }
@@ -0,0 +1,406 @@
1
+ import { existsSync, symlinkSync, unlinkSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join } from "node:path";
4
+ import { Worker } from "node:worker_threads";
5
+ import { BaseEvent } from "./BaseEvent.js";
6
+ import { EventBus } from "./EventBus.js";
7
+ import { assertOptionalDependencyAvailable, isNodeRuntime } from "./optional_deps.js";
8
+ const randomSuffix = () => Math.random().toString(36).slice(2, 10);
9
+ const DEFAULT_TACHYON_CAPACITY = 1 << 20;
10
+ const TACHYON_CONNECT_TIMEOUT_MS = 5e3;
11
+ const TACHYON_LISTEN_TIMEOUT_MS = 5e3;
12
+ const TACHYON_SHUTDOWN_TYPE_ID = 57005;
13
+ const TACHYON_DATA_TYPE_ID = 1;
14
+ const requireForTachyon = createRequire(import.meta.url);
15
+ const ensureTachyonNativeLayout = () => {
16
+ try {
17
+ const package_json = requireForTachyon.resolve("@tachyon-ipc/core/package.json");
18
+ const core_dir = dirname(package_json);
19
+ const expected_build_dir = join(dirname(core_dir), "build");
20
+ const actual_build_dir = join(core_dir, "build");
21
+ if (!existsSync(expected_build_dir) && existsSync(actual_build_dir)) {
22
+ symlinkSync(actual_build_dir, expected_build_dir, "dir");
23
+ }
24
+ } catch {
25
+ }
26
+ };
27
+ const TACHYON_NATIVE_LAYOUT_FIX = `
28
+ const ensureTachyonNativeLayout = () => {
29
+ try {
30
+ const fs = require('node:fs')
31
+ const path = require('node:path')
32
+ const packageJson = require.resolve('@tachyon-ipc/core/package.json')
33
+ const coreDir = path.dirname(packageJson)
34
+ const expectedBuildDir = path.join(path.dirname(coreDir), 'build')
35
+ const actualBuildDir = path.join(coreDir, 'build')
36
+ if (!fs.existsSync(expectedBuildDir) && fs.existsSync(actualBuildDir)) {
37
+ fs.symlinkSync(actualBuildDir, expectedBuildDir, 'dir')
38
+ }
39
+ } catch {}
40
+ }
41
+ ensureTachyonNativeLayout()
42
+ `;
43
+ const TACHYON_LISTENER_WORKER_CODE = `
44
+ const { parentPort, workerData } = require('node:worker_threads')
45
+ ${TACHYON_NATIVE_LAYOUT_FIX}
46
+
47
+ const SHUTDOWN_TYPE_ID = ${TACHYON_SHUTDOWN_TYPE_ID}
48
+
49
+ const probeListenerAlive = (path) => new Promise((resolve) => {
50
+ const net = require('node:net')
51
+ const sock = net.createConnection(path)
52
+ const settle = (alive) => {
53
+ try { sock.destroy() } catch {}
54
+ resolve(alive)
55
+ }
56
+ sock.setTimeout(50, () => settle(false))
57
+ sock.once('connect', () => settle(true))
58
+ sock.once('error', () => settle(false))
59
+ })
60
+
61
+ const main = async () => {
62
+ const { Bus } = await import('@tachyon-ipc/core')
63
+ const fs = require('node:fs')
64
+ const { path, capacity } = workerData
65
+ let bus
66
+ try {
67
+ bus = Bus.listen(path, capacity)
68
+ } catch (firstErr) {
69
+ // The bind failed because the path is in use. If a live listener owns it, propagate
70
+ // the error so the user can resolve the conflict; if it's a stale socket from a
71
+ // previous crash, unlink it and retry exactly once. Refuse to unlink anything that
72
+ // isn't a unix socket \u2014 we have no business deleting an unrelated regular file.
73
+ const alive = await probeListenerAlive(path)
74
+ if (alive || !fs.existsSync(path)) throw firstErr
75
+ let st
76
+ try { st = fs.statSync(path) } catch { throw firstErr }
77
+ if (!st.isSocket()) throw firstErr
78
+ try { fs.unlinkSync(path) } catch {}
79
+ bus = Bus.listen(path, capacity)
80
+ }
81
+ parentPort.postMessage({ type: 'ready' })
82
+ while (true) {
83
+ let msg
84
+ try {
85
+ msg = bus.recv()
86
+ } catch (err) {
87
+ parentPort.postMessage({ type: 'error', message: err && err.message ? err.message : String(err) })
88
+ break
89
+ }
90
+ if (msg.typeId === SHUTDOWN_TYPE_ID) break
91
+ parentPort.postMessage({ type: 'message', data: msg.data, typeId: msg.typeId })
92
+ }
93
+ try { bus.close && bus.close() } catch {}
94
+ }
95
+
96
+ main().catch((err) => {
97
+ if (parentPort) {
98
+ parentPort.postMessage({ type: 'error', message: err && err.message ? err.message : String(err) })
99
+ }
100
+ }).finally(() => process.exit(0))
101
+ `;
102
+ const TACHYON_SENDER_WORKER_CODE = `
103
+ const { parentPort, workerData } = require('node:worker_threads')
104
+ ${TACHYON_NATIVE_LAYOUT_FIX}
105
+
106
+ const SHUTDOWN_TYPE_ID = ${TACHYON_SHUTDOWN_TYPE_ID}
107
+ const DATA_TYPE_ID = ${TACHYON_DATA_TYPE_ID}
108
+
109
+ const main = async () => {
110
+ const { Bus } = await import('@tachyon-ipc/core')
111
+ const { path, connect_timeout_ms } = workerData
112
+ let bus = null
113
+ let last_err = null
114
+ const deadline = Date.now() + connect_timeout_ms
115
+ while (Date.now() < deadline) {
116
+ try {
117
+ bus = Bus.connect(path)
118
+ break
119
+ } catch (err) {
120
+ last_err = err
121
+ await new Promise((resolve) => setTimeout(resolve, 10))
122
+ }
123
+ }
124
+ if (!bus) {
125
+ parentPort.postMessage({ type: 'error', message: 'TachyonEventBridge sender failed to connect: ' + (last_err && last_err.message ? last_err.message : String(last_err)) })
126
+ return
127
+ }
128
+ parentPort.postMessage({ type: 'ready' })
129
+ parentPort.on('message', (msg) => {
130
+ if (!msg) return
131
+ if (msg.type === 'send') {
132
+ try {
133
+ bus.send(Buffer.from(msg.payload), DATA_TYPE_ID)
134
+ parentPort.postMessage({ type: 'sent', id: msg.id })
135
+ } catch (err) {
136
+ parentPort.postMessage({ type: 'send_error', id: msg.id, message: err && err.message ? err.message : String(err) })
137
+ }
138
+ return
139
+ }
140
+ if (msg.type === 'close') {
141
+ try { bus.send(Buffer.alloc(0), SHUTDOWN_TYPE_ID) } catch {}
142
+ try { bus.close && bus.close() } catch {}
143
+ process.exit(0)
144
+ }
145
+ })
146
+ }
147
+
148
+ main().catch((err) => {
149
+ if (parentPort) {
150
+ parentPort.postMessage({ type: 'error', message: err && err.message ? err.message : String(err) })
151
+ }
152
+ })
153
+ `;
154
+ class TachyonEventBridge {
155
+ path;
156
+ capacity;
157
+ name;
158
+ inbound_bus;
159
+ listener_worker;
160
+ // Sticky: `listener_worker` may be cleared mid-session (graceful exit, retry path),
161
+ // but the socket on disk is still ours to unlink in close().
162
+ acted_as_listener;
163
+ listener_startup_error;
164
+ sender_worker;
165
+ sender_ready_promise;
166
+ send_seq;
167
+ pending_sends;
168
+ closed;
169
+ constructor(path, capacity = DEFAULT_TACHYON_CAPACITY, name) {
170
+ if (!path) throw new Error("TachyonEventBridge path must not be empty");
171
+ if (capacity <= 0 || (capacity & capacity - 1) !== 0) {
172
+ throw new Error(`TachyonEventBridge capacity must be a positive power of two, got: ${capacity}`);
173
+ }
174
+ assertOptionalDependencyAvailable("TachyonEventBridge", "@tachyon-ipc/core");
175
+ ensureTachyonNativeLayout();
176
+ this.path = path;
177
+ this.capacity = capacity;
178
+ this.name = name ?? `TachyonEventBridge_${randomSuffix()}`;
179
+ this.inbound_bus = new EventBus(this.name, { max_history_size: 0 });
180
+ this.listener_worker = null;
181
+ this.acted_as_listener = false;
182
+ this.listener_startup_error = null;
183
+ this.sender_worker = null;
184
+ this.sender_ready_promise = null;
185
+ this.send_seq = 0;
186
+ this.pending_sends = /* @__PURE__ */ new Map();
187
+ this.closed = false;
188
+ this.dispatch = this.dispatch.bind(this);
189
+ this.emit = this.emit.bind(this);
190
+ this.on = this.on.bind(this);
191
+ }
192
+ on(event_pattern, handler) {
193
+ this.ensureListenerStarted();
194
+ if (typeof event_pattern === "string") {
195
+ this.inbound_bus.on(event_pattern, handler);
196
+ return;
197
+ }
198
+ this.inbound_bus.on(event_pattern, handler);
199
+ }
200
+ async emit(event) {
201
+ if (this.closed) throw new Error("TachyonEventBridge is closed");
202
+ await this.ensureSenderConnected();
203
+ if (this.closed || !this.sender_worker) {
204
+ throw new Error("TachyonEventBridge is closed");
205
+ }
206
+ const payload = Buffer.from(JSON.stringify(event.toJSON()));
207
+ const id = ++this.send_seq;
208
+ await new Promise((resolve, reject) => {
209
+ this.pending_sends.set(id, { resolve, reject });
210
+ this.sender_worker.postMessage({ type: "send", id, payload });
211
+ });
212
+ }
213
+ async dispatch(event) {
214
+ return this.emit(event);
215
+ }
216
+ async start() {
217
+ const worker = this.listener_worker;
218
+ if (!worker) return;
219
+ const deadline = Date.now() + TACHYON_LISTEN_TIMEOUT_MS;
220
+ let path_first_seen_at = null;
221
+ while (Date.now() < deadline) {
222
+ if (this.listener_startup_error) throw this.listener_startup_error;
223
+ if (this.acted_as_listener) return;
224
+ if (existsSync(this.path)) {
225
+ if (path_first_seen_at === null) {
226
+ path_first_seen_at = Date.now();
227
+ } else if (Date.now() - path_first_seen_at >= 200) {
228
+ return;
229
+ }
230
+ }
231
+ await new Promise((resolve) => setTimeout(resolve, 5));
232
+ }
233
+ if (this.listener_startup_error) throw this.listener_startup_error;
234
+ worker.terminate().catch(() => {
235
+ });
236
+ if (this.listener_worker === worker) this.listener_worker = null;
237
+ throw new Error(`TachyonEventBridge listener did not bind socket ${this.path} within ${TACHYON_LISTEN_TIMEOUT_MS}ms`);
238
+ }
239
+ async close() {
240
+ this.closed = true;
241
+ if (this.sender_worker) {
242
+ const sender_exited = new Promise((resolve) => {
243
+ this.sender_worker.once("exit", () => resolve());
244
+ });
245
+ try {
246
+ this.sender_worker.postMessage({ type: "close" });
247
+ } catch {
248
+ }
249
+ await Promise.race([sender_exited, new Promise((resolve) => setTimeout(resolve, 1e3))]);
250
+ try {
251
+ await this.sender_worker.terminate();
252
+ } catch {
253
+ }
254
+ this.sender_worker = null;
255
+ }
256
+ if (this.listener_worker) {
257
+ const listener = this.listener_worker;
258
+ this.listener_worker = null;
259
+ const listener_exited = new Promise((resolve) => {
260
+ listener.once("exit", () => resolve());
261
+ });
262
+ await Promise.race([listener_exited, new Promise((resolve) => setTimeout(resolve, 1e3))]);
263
+ const terminate_promise = listener.terminate().catch(() => void 0);
264
+ const terminate_settled = await Promise.race([
265
+ terminate_promise.then(() => true),
266
+ new Promise((resolve) => setTimeout(() => resolve(false), 500))
267
+ ]);
268
+ if (!terminate_settled) {
269
+ try {
270
+ listener.unref();
271
+ } catch {
272
+ }
273
+ }
274
+ }
275
+ for (const pending of this.pending_sends.values()) {
276
+ pending.reject(new Error("TachyonEventBridge closed"));
277
+ }
278
+ this.pending_sends.clear();
279
+ if (this.acted_as_listener && existsSync(this.path)) {
280
+ try {
281
+ unlinkSync(this.path);
282
+ } catch {
283
+ }
284
+ }
285
+ this.inbound_bus.destroy();
286
+ }
287
+ ensureListenerStarted() {
288
+ if (this.closed) throw new Error("TachyonEventBridge is closed");
289
+ if (this.listener_worker) return;
290
+ if (!isNodeRuntime()) {
291
+ throw new Error("TachyonEventBridge is only supported in Node.js runtimes");
292
+ }
293
+ const worker = new Worker(TACHYON_LISTENER_WORKER_CODE, {
294
+ eval: true,
295
+ workerData: { path: this.path, capacity: this.capacity }
296
+ });
297
+ this.listener_startup_error = null;
298
+ worker.on("message", (msg) => {
299
+ if (msg.type === "ready") {
300
+ this.acted_as_listener = true;
301
+ } else if (msg.type === "message" && msg.data) {
302
+ try {
303
+ const text = Buffer.from(msg.data).toString("utf-8");
304
+ const payload = JSON.parse(text);
305
+ const event = BaseEvent.fromJSON(payload).eventReset();
306
+ this.inbound_bus.emit(event);
307
+ } catch {
308
+ }
309
+ } else if (msg.type === "error") {
310
+ this.listener_startup_error = new Error(msg.message ?? "TachyonEventBridge listener error");
311
+ console.error("[abxbus] TachyonEventBridge listener error:", msg.message);
312
+ }
313
+ });
314
+ worker.on("error", (err) => {
315
+ this.listener_startup_error = err instanceof Error ? err : new Error(String(err));
316
+ console.error("[abxbus] TachyonEventBridge listener worker crashed:", err);
317
+ });
318
+ worker.on("exit", () => {
319
+ if (this.listener_worker === worker) this.listener_worker = null;
320
+ if (!this.acted_as_listener && !this.listener_startup_error && !this.closed) {
321
+ this.listener_startup_error = new Error("TachyonEventBridge listener worker exited before binding");
322
+ }
323
+ });
324
+ this.listener_worker = worker;
325
+ }
326
+ async ensureSenderConnected() {
327
+ if (this.sender_ready_promise) {
328
+ await this.sender_ready_promise;
329
+ return;
330
+ }
331
+ if (!isNodeRuntime()) {
332
+ throw new Error("TachyonEventBridge is only supported in Node.js runtimes");
333
+ }
334
+ const promise = new Promise((resolve, reject) => {
335
+ const worker = new Worker(TACHYON_SENDER_WORKER_CODE, {
336
+ eval: true,
337
+ workerData: { path: this.path, connect_timeout_ms: TACHYON_CONNECT_TIMEOUT_MS }
338
+ });
339
+ let resolved = false;
340
+ worker.on("message", (msg) => {
341
+ if (msg.type === "ready") {
342
+ resolved = true;
343
+ resolve();
344
+ return;
345
+ }
346
+ if (msg.type === "sent" && typeof msg.id === "number") {
347
+ const pending = this.pending_sends.get(msg.id);
348
+ if (pending) {
349
+ this.pending_sends.delete(msg.id);
350
+ pending.resolve();
351
+ }
352
+ return;
353
+ }
354
+ if (msg.type === "send_error" && typeof msg.id === "number") {
355
+ const pending = this.pending_sends.get(msg.id);
356
+ if (pending) {
357
+ this.pending_sends.delete(msg.id);
358
+ pending.reject(new Error(msg.message ?? "TachyonEventBridge send failed"));
359
+ }
360
+ return;
361
+ }
362
+ if (msg.type === "error") {
363
+ const err = new Error(msg.message ?? "TachyonEventBridge sender error");
364
+ if (!resolved) {
365
+ reject(err);
366
+ return;
367
+ }
368
+ console.error("[abxbus] TachyonEventBridge sender error:", err);
369
+ }
370
+ });
371
+ worker.on("error", (err) => {
372
+ if (!resolved) reject(err instanceof Error ? err : new Error(String(err)));
373
+ else console.error("[abxbus] TachyonEventBridge sender worker crashed:", err);
374
+ });
375
+ worker.on("exit", (code) => {
376
+ if (code === 0 && this.closed) return;
377
+ const err = new Error(`TachyonEventBridge sender worker exited with code ${code}`);
378
+ for (const pending of this.pending_sends.values()) pending.reject(err);
379
+ this.pending_sends.clear();
380
+ if (this.sender_worker === worker) {
381
+ this.sender_worker = null;
382
+ this.sender_ready_promise = null;
383
+ }
384
+ });
385
+ this.sender_worker = worker;
386
+ });
387
+ this.sender_ready_promise = promise;
388
+ try {
389
+ await promise;
390
+ } catch (err) {
391
+ this.sender_ready_promise = null;
392
+ if (this.sender_worker) {
393
+ try {
394
+ await this.sender_worker.terminate();
395
+ } catch {
396
+ }
397
+ this.sender_worker = null;
398
+ }
399
+ throw err;
400
+ }
401
+ }
402
+ }
403
+ export {
404
+ TachyonEventBridge
405
+ };
406
+ //# sourceMappingURL=TachyonEventBridge.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/TachyonEventBridge.ts"],
4
+ "sourcesContent": ["/**\n * Tachyon SPSC IPC bridge for forwarding events between runtimes.\n *\n * Tachyon is a same-machine shared-memory ring buffer with single-producer/\n * single-consumer semantics. Each bridge instance plays exactly one role per\n * session (sender XOR listener) \u2014 the role is committed on the first call to\n * `emit()` (sender) or `on()` (listener).\n *\n * Usage:\n * const bridge = new TachyonEventBridge('/tmp/abxbus.sock')\n *\n * // listener side (creates the SHM arena, must exist before sender connects)\n * bridge.on('SomeEvent', handler)\n *\n * // sender side (separate process or instance)\n * const sender = new TachyonEventBridge('/tmp/abxbus.sock')\n * await sender.emit(event)\n */\nimport { existsSync, symlinkSync, unlinkSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join } from 'node:path'\nimport { Worker } from 'node:worker_threads'\n\nimport { BaseEvent } from './BaseEvent.js'\nimport { EventBus } from './EventBus.js'\nimport { assertOptionalDependencyAvailable, isNodeRuntime } from './optional_deps.js'\nimport type { EventClass, EventHandlerCallable, EventPattern, UntypedEventHandlerFunction } from './types.js'\n\nconst randomSuffix = (): string => Math.random().toString(36).slice(2, 10)\nconst DEFAULT_TACHYON_CAPACITY = 1 << 20\nconst TACHYON_CONNECT_TIMEOUT_MS = 5000\nconst TACHYON_LISTEN_TIMEOUT_MS = 5000\n// Tachyon recv() blocks on a futex that worker.terminate() cannot preempt; the\n// producer signals graceful shutdown by emitting one final message with this\n// reserved type id, which lets the consumer break out of its recv loop.\nconst TACHYON_SHUTDOWN_TYPE_ID = 0xdead\nconst TACHYON_DATA_TYPE_ID = 1\nconst requireForTachyon = createRequire(import.meta.url)\n\nconst ensureTachyonNativeLayout = (): void => {\n try {\n const package_json = requireForTachyon.resolve('@tachyon-ipc/core/package.json')\n const core_dir = dirname(package_json)\n const expected_build_dir = join(dirname(core_dir), 'build')\n const actual_build_dir = join(core_dir, 'build')\n if (!existsSync(expected_build_dir) && existsSync(actual_build_dir)) {\n symlinkSync(actual_build_dir, expected_build_dir, 'dir')\n }\n } catch {\n // Optional dependency availability and import errors are reported by the caller.\n }\n}\n\nconst TACHYON_NATIVE_LAYOUT_FIX = `\nconst ensureTachyonNativeLayout = () => {\n try {\n const fs = require('node:fs')\n const path = require('node:path')\n const packageJson = require.resolve('@tachyon-ipc/core/package.json')\n const coreDir = path.dirname(packageJson)\n const expectedBuildDir = path.join(path.dirname(coreDir), 'build')\n const actualBuildDir = path.join(coreDir, 'build')\n if (!fs.existsSync(expectedBuildDir) && fs.existsSync(actualBuildDir)) {\n fs.symlinkSync(actualBuildDir, expectedBuildDir, 'dir')\n }\n } catch {}\n}\nensureTachyonNativeLayout()\n`\n\nconst TACHYON_LISTENER_WORKER_CODE = `\nconst { parentPort, workerData } = require('node:worker_threads')\n${TACHYON_NATIVE_LAYOUT_FIX}\n\nconst SHUTDOWN_TYPE_ID = ${TACHYON_SHUTDOWN_TYPE_ID}\n\nconst probeListenerAlive = (path) => new Promise((resolve) => {\n const net = require('node:net')\n const sock = net.createConnection(path)\n const settle = (alive) => {\n try { sock.destroy() } catch {}\n resolve(alive)\n }\n sock.setTimeout(50, () => settle(false))\n sock.once('connect', () => settle(true))\n sock.once('error', () => settle(false))\n})\n\nconst main = async () => {\n const { Bus } = await import('@tachyon-ipc/core')\n const fs = require('node:fs')\n const { path, capacity } = workerData\n let bus\n try {\n bus = Bus.listen(path, capacity)\n } catch (firstErr) {\n // The bind failed because the path is in use. If a live listener owns it, propagate\n // the error so the user can resolve the conflict; if it's a stale socket from a\n // previous crash, unlink it and retry exactly once. Refuse to unlink anything that\n // isn't a unix socket \u2014 we have no business deleting an unrelated regular file.\n const alive = await probeListenerAlive(path)\n if (alive || !fs.existsSync(path)) throw firstErr\n let st\n try { st = fs.statSync(path) } catch { throw firstErr }\n if (!st.isSocket()) throw firstErr\n try { fs.unlinkSync(path) } catch {}\n bus = Bus.listen(path, capacity)\n }\n parentPort.postMessage({ type: 'ready' })\n while (true) {\n let msg\n try {\n msg = bus.recv()\n } catch (err) {\n parentPort.postMessage({ type: 'error', message: err && err.message ? err.message : String(err) })\n break\n }\n if (msg.typeId === SHUTDOWN_TYPE_ID) break\n parentPort.postMessage({ type: 'message', data: msg.data, typeId: msg.typeId })\n }\n try { bus.close && bus.close() } catch {}\n}\n\nmain().catch((err) => {\n if (parentPort) {\n parentPort.postMessage({ type: 'error', message: err && err.message ? err.message : String(err) })\n }\n}).finally(() => process.exit(0))\n`\n\nconst TACHYON_SENDER_WORKER_CODE = `\nconst { parentPort, workerData } = require('node:worker_threads')\n${TACHYON_NATIVE_LAYOUT_FIX}\n\nconst SHUTDOWN_TYPE_ID = ${TACHYON_SHUTDOWN_TYPE_ID}\nconst DATA_TYPE_ID = ${TACHYON_DATA_TYPE_ID}\n\nconst main = async () => {\n const { Bus } = await import('@tachyon-ipc/core')\n const { path, connect_timeout_ms } = workerData\n let bus = null\n let last_err = null\n const deadline = Date.now() + connect_timeout_ms\n while (Date.now() < deadline) {\n try {\n bus = Bus.connect(path)\n break\n } catch (err) {\n last_err = err\n await new Promise((resolve) => setTimeout(resolve, 10))\n }\n }\n if (!bus) {\n parentPort.postMessage({ type: 'error', message: 'TachyonEventBridge sender failed to connect: ' + (last_err && last_err.message ? last_err.message : String(last_err)) })\n return\n }\n parentPort.postMessage({ type: 'ready' })\n parentPort.on('message', (msg) => {\n if (!msg) return\n if (msg.type === 'send') {\n try {\n bus.send(Buffer.from(msg.payload), DATA_TYPE_ID)\n parentPort.postMessage({ type: 'sent', id: msg.id })\n } catch (err) {\n parentPort.postMessage({ type: 'send_error', id: msg.id, message: err && err.message ? err.message : String(err) })\n }\n return\n }\n if (msg.type === 'close') {\n try { bus.send(Buffer.alloc(0), SHUTDOWN_TYPE_ID) } catch {}\n try { bus.close && bus.close() } catch {}\n process.exit(0)\n }\n })\n}\n\nmain().catch((err) => {\n if (parentPort) {\n parentPort.postMessage({ type: 'error', message: err && err.message ? err.message : String(err) })\n }\n})\n`\n\ntype SendResolver = { resolve: () => void; reject: (err: Error) => void }\n\nexport class TachyonEventBridge {\n readonly path: string\n readonly capacity: number\n readonly name: string\n\n private readonly inbound_bus: EventBus\n private listener_worker: Worker | null\n // Sticky: `listener_worker` may be cleared mid-session (graceful exit, retry path),\n // but the socket on disk is still ours to unlink in close().\n private acted_as_listener: boolean\n private listener_startup_error: Error | null\n private sender_worker: Worker | null\n private sender_ready_promise: Promise<void> | null\n private send_seq: number\n private pending_sends: Map<number, SendResolver>\n private closed: boolean\n\n constructor(path: string, capacity: number = DEFAULT_TACHYON_CAPACITY, name?: string) {\n if (!path) throw new Error('TachyonEventBridge path must not be empty')\n if (capacity <= 0 || (capacity & (capacity - 1)) !== 0) {\n throw new Error(`TachyonEventBridge capacity must be a positive power of two, got: ${capacity}`)\n }\n assertOptionalDependencyAvailable('TachyonEventBridge', '@tachyon-ipc/core')\n ensureTachyonNativeLayout()\n\n this.path = path\n this.capacity = capacity\n this.name = name ?? `TachyonEventBridge_${randomSuffix()}`\n this.inbound_bus = new EventBus(this.name, { max_history_size: 0 })\n this.listener_worker = null\n this.acted_as_listener = false\n this.listener_startup_error = null\n this.sender_worker = null\n this.sender_ready_promise = null\n this.send_seq = 0\n this.pending_sends = new Map()\n this.closed = false\n\n this.dispatch = this.dispatch.bind(this)\n this.emit = this.emit.bind(this)\n this.on = this.on.bind(this)\n }\n\n on<T extends BaseEvent>(event_pattern: EventClass<T>, handler: EventHandlerCallable<T>): void\n on<T extends BaseEvent>(event_pattern: string | '*', handler: UntypedEventHandlerFunction<T>): void\n on(event_pattern: EventPattern | '*', handler: EventHandlerCallable | UntypedEventHandlerFunction): void {\n this.ensureListenerStarted()\n if (typeof event_pattern === 'string') {\n this.inbound_bus.on(event_pattern, handler as UntypedEventHandlerFunction<BaseEvent>)\n return\n }\n this.inbound_bus.on(event_pattern as EventClass<BaseEvent>, handler as EventHandlerCallable<BaseEvent>)\n }\n\n async emit<T extends BaseEvent>(event: T): Promise<void> {\n // Fail fast before spawning a sender worker / connecting to the listener so a\n // post-close emit() doesn't leak an extra worker for an instance that is going away.\n if (this.closed) throw new Error('TachyonEventBridge is closed')\n await this.ensureSenderConnected()\n if (this.closed || !this.sender_worker) {\n throw new Error('TachyonEventBridge is closed')\n }\n const payload = Buffer.from(JSON.stringify(event.toJSON()))\n const id = ++this.send_seq\n await new Promise<void>((resolve, reject) => {\n this.pending_sends.set(id, { resolve, reject })\n this.sender_worker!.postMessage({ type: 'send', id, payload })\n })\n }\n\n async dispatch<T extends BaseEvent>(event: T): Promise<void> {\n return this.emit(event)\n }\n\n async start(): Promise<void> {\n // Role is committed lazily on first on() / emit(). For listener-side bridges,\n // await the underlying socket bind so callers that need fail-fast readiness\n // (peers about to connect, tests writing a ready_path file) can rely on it.\n const worker = this.listener_worker\n if (!worker) return\n const deadline = Date.now() + TACHYON_LISTEN_TIMEOUT_MS\n let path_first_seen_at: number | null = null\n while (Date.now() < deadline) {\n if (this.listener_startup_error) throw this.listener_startup_error\n // acted_as_listener is set when the worker posts 'ready' \u2014 the only signal that\n // *we* (not some other process) actually own the socket file at `path`.\n if (this.acted_as_listener) return\n if (existsSync(this.path)) {\n if (path_first_seen_at === null) {\n path_first_seen_at = Date.now()\n } else if (Date.now() - path_first_seen_at >= 200) {\n // The path exists, but it might belong to another process. After a brief\n // grace window with no startup error and no ready signal, assume our worker\n // bound it (it would otherwise have raised EADDRINUSE almost immediately).\n return\n }\n }\n await new Promise((resolve) => setTimeout(resolve, 5))\n }\n if (this.listener_startup_error) throw this.listener_startup_error\n // Tear down the worker that never bound so a later on() can spawn a fresh one.\n // Use .catch() so a terminate() rejection doesn't bubble up as an unhandled\n // promise rejection (which crashes Node by default in newer versions).\n worker.terminate().catch(() => {\n /* ignore */\n })\n if (this.listener_worker === worker) this.listener_worker = null\n throw new Error(`TachyonEventBridge listener did not bind socket ${this.path} within ${TACHYON_LISTEN_TIMEOUT_MS}ms`)\n }\n\n async close(): Promise<void> {\n this.closed = true\n if (this.sender_worker) {\n const sender_exited = new Promise<void>((resolve) => {\n this.sender_worker!.once('exit', () => resolve())\n })\n try {\n this.sender_worker.postMessage({ type: 'close' })\n } catch {\n // worker may already be gone\n }\n // The sender flushes a SHUTDOWN_TYPE_ID sentinel and self-exits in response\n // to `close`; await that natural exit before terminating to avoid orphaning.\n await Promise.race([sender_exited, new Promise((resolve) => setTimeout(resolve, 1000))])\n try {\n await this.sender_worker.terminate()\n } catch {\n // ignore\n }\n this.sender_worker = null\n }\n if (this.listener_worker) {\n const listener = this.listener_worker\n this.listener_worker = null\n // The listener exits naturally once it consumes the shutdown sentinel.\n const listener_exited = new Promise<void>((resolve) => {\n listener.once('exit', () => resolve())\n })\n await Promise.race([listener_exited, new Promise((resolve) => setTimeout(resolve, 1000))])\n // worker.terminate() cannot preempt a futex-blocked Tachyon recv() (see the\n // SHUTDOWN_TYPE_ID comment above), so a listener-only instance that never\n // received a sentinel would hang forever if we awaited it. Fire-and-forget the\n // terminate and bound how long we'll wait for it to come back; if it doesn't,\n // unref the worker so the parent Node process can still exit (the worker is\n // daemon-mode and dies with the process either way).\n const terminate_promise = listener.terminate().catch(() => undefined)\n const terminate_settled = await Promise.race([\n terminate_promise.then(() => true),\n new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 500)),\n ])\n if (!terminate_settled) {\n try {\n listener.unref()\n } catch {\n // ignore\n }\n }\n }\n for (const pending of this.pending_sends.values()) {\n pending.reject(new Error('TachyonEventBridge closed'))\n }\n this.pending_sends.clear()\n // Only the side that bound the socket (the listener) owns the path on disk.\n // Sender-only instances must leave it alone so other senders/listeners can keep using it.\n if (this.acted_as_listener && existsSync(this.path)) {\n try {\n unlinkSync(this.path)\n } catch {\n // ignore\n }\n }\n this.inbound_bus.destroy()\n }\n\n private ensureListenerStarted(): void {\n if (this.closed) throw new Error('TachyonEventBridge is closed')\n if (this.listener_worker) return\n if (!isNodeRuntime()) {\n throw new Error('TachyonEventBridge is only supported in Node.js runtimes')\n }\n // The worker probes the path before unlinking \u2014 only stale sockets (no live\n // listener) get cleared. This avoids clobbering a listener owned by another\n // process when two bridges race on the same path.\n const worker = new Worker(TACHYON_LISTENER_WORKER_CODE, {\n eval: true,\n workerData: { path: this.path, capacity: this.capacity },\n })\n this.listener_startup_error = null\n worker.on('message', (msg: { type: string; data?: Uint8Array; typeId?: number; message?: string }) => {\n if (msg.type === 'ready') {\n // Bus.listen returning means *this* worker completed the bind+handshake; only\n // now can we safely claim socket ownership for close()'s unlink path.\n this.acted_as_listener = true\n } else if (msg.type === 'message' && msg.data) {\n try {\n const text = Buffer.from(msg.data).toString('utf-8')\n const payload = JSON.parse(text)\n const event = BaseEvent.fromJSON(payload).eventReset()\n this.inbound_bus.emit(event)\n } catch {\n // ignore malformed payloads\n }\n } else if (msg.type === 'error') {\n // Surface the failure so a concurrent start() can fail fast instead of\n // hanging until the bind-wait deadline.\n this.listener_startup_error = new Error(msg.message ?? 'TachyonEventBridge listener error')\n console.error('[abxbus] TachyonEventBridge listener error:', msg.message)\n }\n })\n worker.on('error', (err: unknown) => {\n this.listener_startup_error = err instanceof Error ? err : new Error(String(err))\n console.error('[abxbus] TachyonEventBridge listener worker crashed:', err)\n })\n // Drop the cached reference if the worker dies (crash, init failure, or natural exit\n // after consuming a shutdown sentinel) so a subsequent on() call can spin up a fresh one.\n worker.on('exit', () => {\n if (this.listener_worker === worker) this.listener_worker = null\n // Only flag a startup error if we never confirmed a bind AND the bridge isn't\n // shutting down; a normal shutdown exit (close(), or recv loop after consuming a\n // sentinel) shouldn't poison future start() calls.\n if (!this.acted_as_listener && !this.listener_startup_error && !this.closed) {\n this.listener_startup_error = new Error('TachyonEventBridge listener worker exited before binding')\n }\n })\n this.listener_worker = worker\n // on() returns immediately so the Node event loop isn't frozen on startup; peers\n // that try Bus.connect before this worker finishes binding are covered by the\n // sender-side connect retry loop (see TACHYON_CONNECT_TIMEOUT_MS) and by the\n // worker's 'ready' message which flips acted_as_listener once bind completes.\n }\n\n private async ensureSenderConnected(): Promise<void> {\n if (this.sender_ready_promise) {\n await this.sender_ready_promise\n return\n }\n if (!isNodeRuntime()) {\n throw new Error('TachyonEventBridge is only supported in Node.js runtimes')\n }\n const promise = new Promise<void>((resolve, reject) => {\n const worker = new Worker(TACHYON_SENDER_WORKER_CODE, {\n eval: true,\n workerData: { path: this.path, connect_timeout_ms: TACHYON_CONNECT_TIMEOUT_MS },\n })\n let resolved = false\n worker.on('message', (msg: { type: string; id?: number; message?: string }) => {\n if (msg.type === 'ready') {\n resolved = true\n resolve()\n return\n }\n if (msg.type === 'sent' && typeof msg.id === 'number') {\n const pending = this.pending_sends.get(msg.id)\n if (pending) {\n this.pending_sends.delete(msg.id)\n pending.resolve()\n }\n return\n }\n if (msg.type === 'send_error' && typeof msg.id === 'number') {\n const pending = this.pending_sends.get(msg.id)\n if (pending) {\n this.pending_sends.delete(msg.id)\n pending.reject(new Error(msg.message ?? 'TachyonEventBridge send failed'))\n }\n return\n }\n if (msg.type === 'error') {\n const err = new Error(msg.message ?? 'TachyonEventBridge sender error')\n if (!resolved) {\n reject(err)\n return\n }\n console.error('[abxbus] TachyonEventBridge sender error:', err)\n }\n })\n worker.on('error', (err) => {\n if (!resolved) reject(err instanceof Error ? err : new Error(String(err)))\n else console.error('[abxbus] TachyonEventBridge sender worker crashed:', err)\n })\n worker.on('exit', (code) => {\n // The sender worker exits with code 0 only when responding to our `close`\n // message; any other exit means the worker died unexpectedly and pending\n // sends would otherwise hang forever.\n if (code === 0 && this.closed) return\n const err = new Error(`TachyonEventBridge sender worker exited with code ${code}`)\n for (const pending of this.pending_sends.values()) pending.reject(err)\n this.pending_sends.clear()\n if (this.sender_worker === worker) {\n this.sender_worker = null\n this.sender_ready_promise = null\n }\n })\n this.sender_worker = worker\n })\n this.sender_ready_promise = promise\n try {\n await promise\n } catch (err) {\n // Allow a later emit() to retry once the listener becomes available.\n this.sender_ready_promise = null\n if (this.sender_worker) {\n try {\n await this.sender_worker.terminate()\n } catch {\n /* ignore */\n }\n this.sender_worker = null\n }\n throw err\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -6,6 +6,7 @@ import { SQLiteEventBridge } from "./SQLiteEventBridge.js";
6
6
  import { NATSEventBridge } from "./NATSEventBridge.js";
7
7
  import { RedisEventBridge } from "./RedisEventBridge.js";
8
8
  import { PostgresEventBridge } from "./PostgresEventBridge.js";
9
+ import { TachyonEventBridge } from "./TachyonEventBridge.js";
9
10
  export {
10
11
  EventBridge,
11
12
  HTTPEventBridge,
@@ -14,6 +15,7 @@ export {
14
15
  PostgresEventBridge,
15
16
  RedisEventBridge,
16
17
  SQLiteEventBridge,
17
- SocketEventBridge
18
+ SocketEventBridge,
19
+ TachyonEventBridge
18
20
  };
19
21
  //# sourceMappingURL=bridges.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/bridges.ts"],
4
- "sourcesContent": ["export { EventBridge } from './EventBridge.js'\nexport { HTTPEventBridge } from './HTTPEventBridge.js'\nexport type { HTTPEventBridgeOptions } from './HTTPEventBridge.js'\nexport { SocketEventBridge } from './SocketEventBridge.js'\nexport { JSONLEventBridge } from './JSONLEventBridge.js'\nexport { SQLiteEventBridge } from './SQLiteEventBridge.js'\nexport { NATSEventBridge } from './NATSEventBridge.js'\nexport { RedisEventBridge } from './RedisEventBridge.js'\nexport { PostgresEventBridge } from './PostgresEventBridge.js'\n"],
5
- "mappings": "AAAA,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEhC,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,2BAA2B;",
4
+ "sourcesContent": ["export { EventBridge } from './EventBridge.js'\nexport { HTTPEventBridge } from './HTTPEventBridge.js'\nexport type { HTTPEventBridgeOptions } from './HTTPEventBridge.js'\nexport { SocketEventBridge } from './SocketEventBridge.js'\nexport { JSONLEventBridge } from './JSONLEventBridge.js'\nexport { SQLiteEventBridge } from './SQLiteEventBridge.js'\nexport { NATSEventBridge } from './NATSEventBridge.js'\nexport { RedisEventBridge } from './RedisEventBridge.js'\nexport { PostgresEventBridge } from './PostgresEventBridge.js'\nexport { TachyonEventBridge } from './TachyonEventBridge.js'\n"],
5
+ "mappings": "AAAA,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEhC,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,2BAA2B;AACpC,SAAS,0BAA0B;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["export { BaseEvent, BaseEventSchema } from './BaseEvent.js'\nexport { EventHistory } from './EventHistory.js'\nexport type { EventHistoryFindOptions, EventHistoryTrimOptions } from './EventHistory.js'\nexport { EventResult } from './EventResult.js'\nexport { EventBus } from './EventBus.js'\nexport type { EventBusJSON, EventBusOptions } from './EventBus.js'\nexport { EventBridge } from './EventBridge.js'\nexport { HTTPEventBridge } from './HTTPEventBridge.js'\nexport type { HTTPEventBridgeOptions } from './HTTPEventBridge.js'\nexport { SocketEventBridge } from './SocketEventBridge.js'\nexport { JSONLEventBridge } from './JSONLEventBridge.js'\nexport { SQLiteEventBridge } from './SQLiteEventBridge.js'\nexport type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './EventBusMiddleware.js'\nexport { monotonicDatetime } from './helpers.js'\nexport {\n EventHandlerTimeoutError,\n EventHandlerCancelledError,\n EventHandlerAbortedError,\n EventHandlerResultSchemaError,\n} from './EventHandler.js'\nexport type {\n EventConcurrencyMode,\n EventHandlerConcurrencyMode,\n EventHandlerCompletionMode,\n EventBusInterfaceForLockManager,\n} from './LockManager.js'\nexport type { EventClass, EventHandlerCallable as EventHandler, EventPattern, EventStatus, FindOptions, FindWindow } from './types.js'\nexport { retry, clearSemaphoreRegistry, RetryTimeoutError, SemaphoreTimeoutError } from './retry.js'\nexport type { RetryOptions } from './retry.js'\nexport { events_suck } from './events_suck.js'\nexport type { EventsSuckClient, EventsSuckClientClass, GeneratedEvents } from './events_suck.js'\n"],
5
- "mappings": "AAAA,SAAS,WAAW,uBAAuB;AAC3C,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AAEzB,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEhC,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAElC,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAQP,SAAS,OAAO,wBAAwB,mBAAmB,6BAA6B;AAExF,SAAS,mBAAmB;",
4
+ "sourcesContent": ["export { BaseEvent, BaseEventSchema } from './BaseEvent.js'\nexport { EventHistory } from './EventHistory.js'\nexport type { EventHistoryFilterOptions, EventHistoryFindOptions, EventHistoryTrimOptions } from './EventHistory.js'\nexport { EventResult } from './EventResult.js'\nexport { EventBus } from './EventBus.js'\nexport type { EventBusJSON, EventBusOptions } from './EventBus.js'\nexport { EventBridge } from './EventBridge.js'\nexport { HTTPEventBridge } from './HTTPEventBridge.js'\nexport type { HTTPEventBridgeOptions } from './HTTPEventBridge.js'\nexport { SocketEventBridge } from './SocketEventBridge.js'\nexport { JSONLEventBridge } from './JSONLEventBridge.js'\nexport { SQLiteEventBridge } from './SQLiteEventBridge.js'\nexport type { EventBusMiddleware, EventBusMiddlewareCtor, EventBusMiddlewareInput } from './EventBusMiddleware.js'\nexport { monotonicDatetime } from './helpers.js'\nexport {\n EventHandlerTimeoutError,\n EventHandlerCancelledError,\n EventHandlerAbortedError,\n EventHandlerResultSchemaError,\n} from './EventHandler.js'\nexport type {\n EventConcurrencyMode,\n EventHandlerConcurrencyMode,\n EventHandlerCompletionMode,\n EventBusInterfaceForLockManager,\n} from './LockManager.js'\nexport type {\n EventClass,\n EventHandlerCallable as EventHandler,\n EventPattern,\n EventStatus,\n FilterOptions,\n FindOptions,\n FindWindow,\n} from './types.js'\nexport { retry, clearSemaphoreRegistry, RetryTimeoutError, SemaphoreTimeoutError } from './retry.js'\nexport type { RetryOptions } from './retry.js'\nexport { events_suck } from './events_suck.js'\nexport type { EventsSuckClient, EventsSuckClientClass, GeneratedEvents } from './events_suck.js'\n"],
5
+ "mappings": "AAAA,SAAS,WAAW,uBAAuB;AAC3C,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AAEzB,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEhC,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAElC,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAgBP,SAAS,OAAO,wBAAwB,mBAAmB,6BAA6B;AAExF,SAAS,mBAAmB;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/types.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport type { BaseEvent } from './BaseEvent.js'\n\nexport type EventStatus = 'pending' | 'started' | 'completed'\n\nexport type EventClass<T extends BaseEvent = BaseEvent> = { event_type?: string } & (new (...args: any[]) => T)\n\nexport type EventPattern<T extends BaseEvent = BaseEvent> = string | EventClass<T>\n\nexport type EventWithResultSchema<TResult> = BaseEvent & { __event_result_type__?: TResult }\n\nexport type EventResultType<TEvent extends BaseEvent> = TEvent extends { __event_result_type__?: infer TResult } ? TResult : unknown\n\nexport type EventResultTypeConstructor = StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor\n\nexport type EventResultTypeInput = z.ZodTypeAny | EventResultTypeConstructor | unknown\n\nexport type EventHandlerReturn<T extends BaseEvent = BaseEvent> = EventResultType<T> | BaseEvent | null | void\n\nexport type EventHandlerCallable<T extends BaseEvent = BaseEvent> = (event: T) => EventHandlerReturn<T> | Promise<EventHandlerReturn<T>>\n\n// For string and wildcard subscriptions we cannot reliably infer which event\n// type will arrive, so return type checking intentionally degrades to unknown.\nexport type UntypedEventHandlerFunction<T extends BaseEvent = BaseEvent> = (\n event: T\n) => EventHandlerReturn<T> | unknown | Promise<EventHandlerReturn<T> | unknown>\n\nexport type FindWindow = boolean | number\n\ntype FindReservedOptionKeys = 'past' | 'future' | 'child_of'\n\ntype EventFilterFields<T extends BaseEvent> = {\n [K in keyof T as string extends K\n ? never\n : number extends K\n ? never\n : symbol extends K\n ? never\n : K extends FindReservedOptionKeys\n ? never\n : T[K] extends (...args: any[]) => any\n ? never\n : K]?: T[K]\n}\n\nexport type FindOptions<T extends BaseEvent = BaseEvent> = {\n past?: FindWindow\n future?: FindWindow\n child_of?: BaseEvent | null\n} & EventFilterFields<T> &\n Record<string, unknown>\n\nexport const normalizeEventPattern = (event_pattern: EventPattern | '*'): string | '*' => {\n if (event_pattern === '*') {\n return '*'\n }\n if (typeof event_pattern === 'string') {\n return event_pattern\n }\n const event_type = (event_pattern as { event_type?: unknown }).event_type\n if (typeof event_type === 'string' && event_type.length > 0 && event_type !== 'BaseEvent') {\n return event_type\n }\n const class_name = (event_pattern as { name?: unknown }).name\n if (typeof class_name === 'string' && class_name.length > 0 && class_name !== 'BaseEvent') {\n return class_name\n }\n let preview: string\n try {\n const encoded = JSON.stringify(event_pattern)\n preview = typeof encoded === 'string' ? encoded.slice(0, 30) : String(event_pattern).slice(0, 30)\n } catch {\n preview = String(event_pattern).slice(0, 30)\n }\n throw new Error('bus.on(match_pattern, ...) must be a string event type, \"*\", or a BaseEvent class, got: ' + preview)\n}\n\nexport const isZodSchema = (value: unknown): value is z.ZodTypeAny => !!value && typeof (value as z.ZodTypeAny).safeParse === 'function'\n\nexport const eventResultTypeFromConstructor = (value: unknown): z.ZodTypeAny | undefined => {\n if (value === String) {\n return z.string()\n }\n if (value === Number) {\n return z.number()\n }\n if (value === Boolean) {\n return z.boolean()\n }\n if (value === Array) {\n return z.array(z.unknown())\n }\n if (value === Object) {\n return z.record(z.string(), z.unknown())\n }\n return undefined\n}\n\nexport const extractZodShape = (raw: Record<string, unknown>): z.ZodRawShape => {\n const shape: Record<string, z.ZodTypeAny> = {}\n for (const [key, value] of Object.entries(raw)) {\n if (key === 'event_result_type') continue\n if (isZodSchema(value)) shape[key] = value\n }\n return shape as z.ZodRawShape\n}\n\nexport const toJsonSchema = (schema: unknown): unknown => {\n if (!schema || !isZodSchema(schema)) return schema\n const zod_any = z as unknown as { toJSONSchema: (input: z.ZodTypeAny) => unknown }\n // Cross-language roundtrips preserve core structural types; constraint keywords may not roundtrip exactly.\n return zod_any.toJSONSchema(schema)\n}\n\nexport const fromJsonSchema = (schema: unknown): z.ZodTypeAny => {\n const zod_any = z as unknown as { fromJSONSchema: (input: unknown) => z.ZodTypeAny }\n return zod_any.fromJSONSchema(schema)\n}\n\nexport const normalizeEventResultType = (value: EventResultTypeInput): z.ZodTypeAny | undefined => {\n if (value === undefined || value === null) {\n return undefined\n }\n if (isZodSchema(value)) {\n return value\n }\n const constructor_schema = eventResultTypeFromConstructor(value)\n if (constructor_schema) {\n return constructor_schema\n }\n return fromJsonSchema(value)\n}\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAoDX,MAAM,wBAAwB,CAAC,kBAAoD;AACxF,MAAI,kBAAkB,KAAK;AACzB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,kBAAkB,UAAU;AACrC,WAAO;AAAA,EACT;AACA,QAAM,aAAc,cAA2C;AAC/D,MAAI,OAAO,eAAe,YAAY,WAAW,SAAS,KAAK,eAAe,aAAa;AACzF,WAAO;AAAA,EACT;AACA,QAAM,aAAc,cAAqC;AACzD,MAAI,OAAO,eAAe,YAAY,WAAW,SAAS,KAAK,eAAe,aAAa;AACzF,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,KAAK,UAAU,aAAa;AAC5C,cAAU,OAAO,YAAY,WAAW,QAAQ,MAAM,GAAG,EAAE,IAAI,OAAO,aAAa,EAAE,MAAM,GAAG,EAAE;AAAA,EAClG,QAAQ;AACN,cAAU,OAAO,aAAa,EAAE,MAAM,GAAG,EAAE;AAAA,EAC7C;AACA,QAAM,IAAI,MAAM,6FAA6F,OAAO;AACtH;AAEO,MAAM,cAAc,CAAC,UAA0C,CAAC,CAAC,SAAS,OAAQ,MAAuB,cAAc;AAEvH,MAAM,iCAAiC,CAAC,UAA6C;AAC1F,MAAI,UAAU,QAAQ;AACpB,WAAO,EAAE,OAAO;AAAA,EAClB;AACA,MAAI,UAAU,QAAQ;AACpB,WAAO,EAAE,OAAO;AAAA,EAClB;AACA,MAAI,UAAU,SAAS;AACrB,WAAO,EAAE,QAAQ;AAAA,EACnB;AACA,MAAI,UAAU,OAAO;AACnB,WAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;AAAA,EAC5B;AACA,MAAI,UAAU,QAAQ;AACpB,WAAO,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AAEO,MAAM,kBAAkB,CAAC,QAAgD;AAC9E,QAAM,QAAsC,CAAC;AAC7C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,QAAI,QAAQ,oBAAqB;AACjC,QAAI,YAAY,KAAK,EAAG,OAAM,GAAG,IAAI;AAAA,EACvC;AACA,SAAO;AACT;AAEO,MAAM,eAAe,CAAC,WAA6B;AACxD,MAAI,CAAC,UAAU,CAAC,YAAY,MAAM,EAAG,QAAO;AAC5C,QAAM,UAAU;AAEhB,SAAO,QAAQ,aAAa,MAAM;AACpC;AAEO,MAAM,iBAAiB,CAAC,WAAkC;AAC/D,QAAM,UAAU;AAChB,SAAO,QAAQ,eAAe,MAAM;AACtC;AAEO,MAAM,2BAA2B,CAAC,UAA0D;AACjG,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AACA,MAAI,YAAY,KAAK,GAAG;AACtB,WAAO;AAAA,EACT;AACA,QAAM,qBAAqB,+BAA+B,KAAK;AAC/D,MAAI,oBAAoB;AACtB,WAAO;AAAA,EACT;AACA,SAAO,eAAe,KAAK;AAC7B;",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport type { BaseEvent } from './BaseEvent.js'\n\nexport type EventStatus = 'pending' | 'started' | 'completed'\n\nexport type EventClass<T extends BaseEvent = BaseEvent> = { event_type?: string } & (new (...args: any[]) => T)\n\nexport type EventPattern<T extends BaseEvent = BaseEvent> = string | EventClass<T>\n\nexport type EventWithResultSchema<TResult> = BaseEvent & { __event_result_type__?: TResult }\n\nexport type EventResultType<TEvent extends BaseEvent> = TEvent extends { __event_result_type__?: infer TResult } ? TResult : unknown\n\nexport type EventResultTypeConstructor = StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor\n\nexport type EventResultTypeInput = z.ZodTypeAny | EventResultTypeConstructor | unknown\n\nexport type EventHandlerReturn<T extends BaseEvent = BaseEvent> = EventResultType<T> | BaseEvent | null | void\n\nexport type EventHandlerCallable<T extends BaseEvent = BaseEvent> = (event: T) => EventHandlerReturn<T> | Promise<EventHandlerReturn<T>>\n\n// For string and wildcard subscriptions we cannot reliably infer which event\n// type will arrive, so return type checking intentionally degrades to unknown.\nexport type UntypedEventHandlerFunction<T extends BaseEvent = BaseEvent> = (\n event: T\n) => EventHandlerReturn<T> | unknown | Promise<EventHandlerReturn<T> | unknown>\n\nexport type FindWindow = boolean | number\n\ntype FindReservedOptionKeys = 'past' | 'future' | 'child_of'\n\ntype EventFilterFields<T extends BaseEvent> = {\n [K in keyof T as string extends K\n ? never\n : number extends K\n ? never\n : symbol extends K\n ? never\n : K extends FindReservedOptionKeys\n ? never\n : T[K] extends (...args: any[]) => any\n ? never\n : K]?: T[K]\n}\n\nexport type FindOptions<T extends BaseEvent = BaseEvent> = {\n past?: FindWindow\n future?: FindWindow\n child_of?: BaseEvent | null\n} & EventFilterFields<T> &\n Record<string, unknown>\n\nexport type FilterOptions<T extends BaseEvent = BaseEvent> = FindOptions<T> & { limit?: number | null }\n\nexport const normalizeEventPattern = (event_pattern: EventPattern | '*'): string | '*' => {\n if (event_pattern === '*') {\n return '*'\n }\n if (typeof event_pattern === 'string') {\n return event_pattern\n }\n const event_type = (event_pattern as { event_type?: unknown }).event_type\n if (typeof event_type === 'string' && event_type.length > 0 && event_type !== 'BaseEvent') {\n return event_type\n }\n const class_name = (event_pattern as { name?: unknown }).name\n if (typeof class_name === 'string' && class_name.length > 0 && class_name !== 'BaseEvent') {\n return class_name\n }\n let preview: string\n try {\n const encoded = JSON.stringify(event_pattern)\n preview = typeof encoded === 'string' ? encoded.slice(0, 30) : String(event_pattern).slice(0, 30)\n } catch {\n preview = String(event_pattern).slice(0, 30)\n }\n throw new Error('bus.on(match_pattern, ...) must be a string event type, \"*\", or a BaseEvent class, got: ' + preview)\n}\n\nexport const isZodSchema = (value: unknown): value is z.ZodTypeAny => !!value && typeof (value as z.ZodTypeAny).safeParse === 'function'\n\nexport const eventResultTypeFromConstructor = (value: unknown): z.ZodTypeAny | undefined => {\n if (value === String) {\n return z.string()\n }\n if (value === Number) {\n return z.number()\n }\n if (value === Boolean) {\n return z.boolean()\n }\n if (value === Array) {\n return z.array(z.unknown())\n }\n if (value === Object) {\n return z.record(z.string(), z.unknown())\n }\n return undefined\n}\n\nexport const extractZodShape = (raw: Record<string, unknown>): z.ZodRawShape => {\n const shape: Record<string, z.ZodTypeAny> = {}\n for (const [key, value] of Object.entries(raw)) {\n if (key === 'event_result_type') continue\n if (isZodSchema(value)) shape[key] = value\n }\n return shape as z.ZodRawShape\n}\n\nexport const toJsonSchema = (schema: unknown): unknown => {\n if (!schema || !isZodSchema(schema)) return schema\n const zod_any = z as unknown as { toJSONSchema: (input: z.ZodTypeAny) => unknown }\n // Cross-language roundtrips preserve core structural types; constraint keywords may not roundtrip exactly.\n return zod_any.toJSONSchema(schema)\n}\n\nexport const fromJsonSchema = (schema: unknown): z.ZodTypeAny => {\n const zod_any = z as unknown as { fromJSONSchema: (input: unknown) => z.ZodTypeAny }\n return zod_any.fromJSONSchema(schema)\n}\n\nexport const normalizeEventResultType = (value: EventResultTypeInput): z.ZodTypeAny | undefined => {\n if (value === undefined || value === null) {\n return undefined\n }\n if (isZodSchema(value)) {\n return value\n }\n const constructor_schema = eventResultTypeFromConstructor(value)\n if (constructor_schema) {\n return constructor_schema\n }\n return fromJsonSchema(value)\n}\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAsDX,MAAM,wBAAwB,CAAC,kBAAoD;AACxF,MAAI,kBAAkB,KAAK;AACzB,WAAO;AAAA,EACT;AACA,MAAI,OAAO,kBAAkB,UAAU;AACrC,WAAO;AAAA,EACT;AACA,QAAM,aAAc,cAA2C;AAC/D,MAAI,OAAO,eAAe,YAAY,WAAW,SAAS,KAAK,eAAe,aAAa;AACzF,WAAO;AAAA,EACT;AACA,QAAM,aAAc,cAAqC;AACzD,MAAI,OAAO,eAAe,YAAY,WAAW,SAAS,KAAK,eAAe,aAAa;AACzF,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,KAAK,UAAU,aAAa;AAC5C,cAAU,OAAO,YAAY,WAAW,QAAQ,MAAM,GAAG,EAAE,IAAI,OAAO,aAAa,EAAE,MAAM,GAAG,EAAE;AAAA,EAClG,QAAQ;AACN,cAAU,OAAO,aAAa,EAAE,MAAM,GAAG,EAAE;AAAA,EAC7C;AACA,QAAM,IAAI,MAAM,6FAA6F,OAAO;AACtH;AAEO,MAAM,cAAc,CAAC,UAA0C,CAAC,CAAC,SAAS,OAAQ,MAAuB,cAAc;AAEvH,MAAM,iCAAiC,CAAC,UAA6C;AAC1F,MAAI,UAAU,QAAQ;AACpB,WAAO,EAAE,OAAO;AAAA,EAClB;AACA,MAAI,UAAU,QAAQ;AACpB,WAAO,EAAE,OAAO;AAAA,EAClB;AACA,MAAI,UAAU,SAAS;AACrB,WAAO,EAAE,QAAQ;AAAA,EACnB;AACA,MAAI,UAAU,OAAO;AACnB,WAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;AAAA,EAC5B;AACA,MAAI,UAAU,QAAQ;AACpB,WAAO,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AAEO,MAAM,kBAAkB,CAAC,QAAgD;AAC9E,QAAM,QAAsC,CAAC;AAC7C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,QAAI,QAAQ,oBAAqB;AACjC,QAAI,YAAY,KAAK,EAAG,OAAM,GAAG,IAAI;AAAA,EACvC;AACA,SAAO;AACT;AAEO,MAAM,eAAe,CAAC,WAA6B;AACxD,MAAI,CAAC,UAAU,CAAC,YAAY,MAAM,EAAG,QAAO;AAC5C,QAAM,UAAU;AAEhB,SAAO,QAAQ,aAAa,MAAM;AACpC;AAEO,MAAM,iBAAiB,CAAC,WAAkC;AAC/D,QAAM,UAAU;AAChB,SAAO,QAAQ,eAAe,MAAM;AACtC;AAEO,MAAM,2BAA2B,CAAC,UAA0D;AACjG,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AACA,MAAI,YAAY,KAAK,GAAG;AACtB,WAAO;AAAA,EACT;AACA,QAAM,qBAAqB,+BAA+B,KAAK;AAC/D,MAAI,oBAAoB;AACtB,WAAO;AAAA,EACT;AACA,SAAO,eAAe,KAAK;AAC7B;",
6
6
  "names": []
7
7
  }