abxbus 2.4.1 → 2.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -7
- package/dist/cjs/base_event.d.ts +8 -4
- package/dist/cjs/base_event.js +98 -38
- package/dist/cjs/base_event.js.map +2 -2
- package/dist/cjs/event_bus.d.ts +0 -2
- package/dist/cjs/event_bus.js +13 -46
- package/dist/cjs/event_bus.js.map +2 -2
- package/dist/cjs/event_result.js +2 -2
- package/dist/cjs/event_result.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/lock_manager.d.ts +1 -1
- package/dist/cjs/lock_manager.js +9 -6
- package/dist/cjs/lock_manager.js.map +2 -2
- package/dist/cjs/logging.js +5 -2
- package/dist/cjs/logging.js.map +2 -2
- package/dist/cjs/middleware_otel_tracing.d.ts +28 -0
- package/dist/cjs/middleware_otel_tracing.js +189 -0
- package/dist/cjs/middleware_otel_tracing.js.map +7 -0
- package/dist/cjs/retry.d.ts +4 -1
- package/dist/cjs/retry.js +139 -12
- package/dist/cjs/retry.js.map +2 -2
- package/dist/esm/base_event.js +98 -38
- package/dist/esm/base_event.js.map +2 -2
- package/dist/esm/event_bus.js +13 -46
- package/dist/esm/event_bus.js.map +2 -2
- package/dist/esm/event_result.js +2 -2
- package/dist/esm/event_result.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/lock_manager.js +9 -6
- package/dist/esm/lock_manager.js.map +2 -2
- package/dist/esm/logging.js +6 -3
- package/dist/esm/logging.js.map +2 -2
- package/dist/esm/middleware_otel_tracing.js +169 -0
- package/dist/esm/middleware_otel_tracing.js.map +7 -0
- package/dist/esm/retry.js +139 -12
- package/dist/esm/retry.js.map +2 -2
- package/dist/types/base_event.d.ts +8 -4
- package/dist/types/event_bus.d.ts +0 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lock_manager.d.ts +1 -1
- package/dist/types/middleware_otel_tracing.d.ts +28 -0
- package/dist/types/retry.d.ts +4 -1
- package/package.json +4 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lock_manager.ts"],
|
|
4
|
-
"sourcesContent": ["import type { BaseEvent } from './base_event.js'\nimport type { EventResult } from './event_result.js'\n\n// \u2500\u2500\u2500 Deferred / withResolvers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type Deferred<T> = {\n promise: Promise<T>\n resolve: (value: T | PromiseLike<T>) => void\n reject: (reason?: unknown) => void\n}\n\nexport const withResolvers = <T>(): Deferred<T> => {\n if (typeof Promise.withResolvers === 'function') {\n return Promise.withResolvers<T>()\n }\n let resolve!: (value: T | PromiseLike<T>) => void\n let reject!: (reason?: unknown) => void\n const promise = new Promise<T>((resolve_fn, reject_fn) => {\n resolve = resolve_fn\n reject = reject_fn\n })\n return { promise, resolve, reject }\n}\n\n// \u2500\u2500\u2500 Concurrency modes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const EVENT_CONCURRENCY_MODES = ['global-serial', 'bus-serial', 'parallel'] as const\nexport type EventConcurrencyMode = (typeof EVENT_CONCURRENCY_MODES)[number]\n\nexport const EVENT_HANDLER_CONCURRENCY_MODES = ['serial', 'parallel'] as const\nexport type EventHandlerConcurrencyMode = (typeof EVENT_HANDLER_CONCURRENCY_MODES)[number]\n\nexport const EVENT_HANDLER_COMPLETION_MODES = ['all', 'first'] as const\nexport type EventHandlerCompletionMode = (typeof EVENT_HANDLER_COMPLETION_MODES)[number]\n\n// \u2500\u2500\u2500 AsyncLock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class AsyncLock {\n size: number\n in_use: number\n waiters: Array<() => void>\n\n constructor(size: number) {\n this.size = size\n this.in_use = 0\n this.waiters = []\n }\n\n async acquire(): Promise<void> {\n if (this.size === Infinity) {\n return\n }\n if (this.in_use < this.size) {\n this.in_use += 1\n return\n }\n await new Promise<void>((resolve) => {\n this.waiters.push(resolve)\n })\n }\n\n release(): void {\n if (this.size === Infinity) {\n return\n }\n const next = this.waiters.shift()\n if (next) {\n // Handoff: keep permit accounted for and transfer directly to next waiter.\n next()\n return\n }\n this.in_use = Math.max(0, this.in_use - 1)\n }\n}\n\nexport const runWithLock = async <T>(lock: AsyncLock | null, fn: () => Promise<T>): Promise<T> => {\n if (!lock) {\n return await fn()\n }\n await lock.acquire()\n try {\n return await fn()\n } finally {\n lock.release()\n }\n}\n\n// \u2500\u2500\u2500 HandlerLock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type HandlerExecutionState = 'held' | 'yielded' | 'closed'\n\n// Tracks a single handler execution's ownership of a handler lock.\n// Reacquire is race-safe: if the handler exits while waiting to reclaim,\n// the reclaimed lock is immediately released to avoid leaks.\nexport class HandlerLock {\n private lock: AsyncLock | null\n private state: HandlerExecutionState\n\n constructor(lock: AsyncLock | null) {\n this.lock = lock\n this.state = 'held'\n }\n\n // used by EventBus._processEventImmediately to yield the parent handler's lock to the child event so it can be processed immediately\n yieldHandlerLockForChildRun(): boolean {\n if (!this.lock || this.state !== 'held') {\n return false\n }\n this.state = 'yielded'\n this.lock.release()\n return true\n }\n\n // used by EventBus._processEventImmediately to reacquire the handler lock after the child event has been processed\n async reclaimHandlerLockIfRunning(): Promise<boolean> {\n if (!this.lock || this.state !== 'yielded') {\n return false\n }\n await this.lock.acquire()\n if (this.state !== 'yielded') {\n // Handler exited while this reacquire was pending.\n this.lock.release()\n return false\n }\n this.state = 'held'\n return true\n }\n\n // used by EventResult.runHandler to exit the handler lock after the handler has finished executing\n exitHandlerRun(): void {\n if (this.state === 'closed') {\n return\n }\n const should_release = !!this.lock && this.state === 'held'\n this.state = 'closed'\n if (should_release) {\n this.lock!.release()\n }\n }\n\n // used by EventBus._processEventImmediately to yield the handler lock and reacquire it after the child event has been processed\n async runQueueJump<T>(fn: () => Promise<T>): Promise<T> {\n const yielded = this.yieldHandlerLockForChildRun()\n try {\n return await fn()\n } finally {\n if (yielded) {\n await this.reclaimHandlerLockIfRunning()\n }\n }\n }\n}\n\n// \u2500\u2500\u2500 LockManager \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Interface that must be implemented by the EventBus class to be used by the LockManager\nexport type EventBusInterfaceForLockManager = {\n isIdleAndQueueEmpty: () => boolean\n event_concurrency: EventConcurrencyMode\n _lock_for_event_global_serial: AsyncLock\n}\n\nexport type LockManagerOptions = {\n auto_schedule_idle_checks?: boolean\n}\n\n// The LockManager is responsible for managing the concurrency of events and handlers\nexport class LockManager {\n private bus: EventBusInterfaceForLockManager // Live bus reference; used to read defaults and idle state.\n private auto_schedule_idle_checks: boolean\n\n readonly bus_event_lock: AsyncLock // Per-bus event lock; created with LockManager and never swapped.\n private pause_depth: number // Re-entrant pause counter; increments on _requestRunloopPause, decrements on release.\n private pause_waiters: Array<() => void> // Resolvers for _waitUntilRunloopResumed; drained when pause_depth hits 0.\n private active_handler_results: EventResult[] // Stack of active handler results for \"inside handler\" detection.\n\n private idle_waiters: Array<(became_idle: boolean) => void> // Resolvers waiting for stable idle; cleared when idle confirmed.\n private idle_check_pending: boolean // Debounce flag to avoid scheduling redundant idle checks.\n private idle_check_streak: number // Counts consecutive idle checks; used to require two ticks of idle.\n\n constructor(bus: EventBusInterfaceForLockManager, options: LockManagerOptions = {}) {\n this.bus = bus\n this.auto_schedule_idle_checks = options.auto_schedule_idle_checks ?? true\n this.bus_event_lock = new AsyncLock(1) // used for the bus-serial concurrency mode\n\n this.pause_depth = 0\n this.pause_waiters = []\n this.active_handler_results = []\n\n this.idle_waiters = []\n this.idle_check_pending = false\n this.idle_check_streak = 0\n }\n\n // Low-level runloop pause: increments a re-entrant counter and returns a release\n // function. Used for broad, bus-scoped pauses during queue-jump across buses.\n _requestRunloopPause(): () => void {\n this.pause_depth += 1\n let released = false\n return () => {\n if (released) {\n return\n }\n released = true\n this.pause_depth = Math.max(0, this.pause_depth - 1)\n if (this.pause_depth !== 0) {\n return\n }\n const waiters = this.pause_waiters\n this.pause_waiters = []\n for (const resolve of waiters) {\n resolve()\n }\n }\n }\n\n _waitUntilRunloopResumed(): Promise<void> {\n if (this.pause_depth === 0) {\n return Promise.resolve()\n }\n return new Promise((resolve) => {\n this.pause_waiters.push(resolve)\n })\n }\n\n _isPaused(): boolean {\n return this.pause_depth > 0\n }\n\n async _runWithHandlerDispatchContext<T>(result: EventResult, fn: () => Promise<T>): Promise<T> {\n this.active_handler_results.push(result)\n try {\n return await fn()\n } finally {\n const idx = this.active_handler_results.indexOf(result)\n if (idx >= 0) {\n this.active_handler_results.splice(idx, 1)\n }\n }\n }\n\n _getActiveHandlerResult(): EventResult | undefined {\n return this.active_handler_results[this.active_handler_results.length - 1]\n }\n\n _getActiveHandlerResults(): EventResult[] {\n return [...this.active_handler_results]\n }\n\n // Per-bus check: true only if this specific bus has a handler on its stack.\n // For cross-bus queue-jumping, EventBus._processEventImmediately uses getParentEventResultAcrossAllBuses()\n // to walk up the parent event tree, and the bus proxy passes handler_result\n // to _processEventImmediately so it can yield/reacquire the correct lock.\n _isAnyHandlerActive(): boolean {\n return this.active_handler_results.length > 0\n }\n\n waitForIdle(timeout_seconds: number | null = null): Promise<boolean> {\n return new Promise((resolve) => {\n let done = false\n let timeout_id: ReturnType<typeof setTimeout> | null = null\n\n const finish = (became_idle: boolean): void => {\n if (done) {\n return\n }\n done = true\n if (timeout_id !== null) {\n clearTimeout(timeout_id)\n timeout_id = null\n }\n resolve(became_idle)\n }\n\n this.idle_waiters.push(finish)\n this.scheduleIdleCheck()\n\n if (timeout_seconds === null || timeout_seconds === undefined) {\n return\n }\n\n const timeout_ms = Math.max(0, Number(timeout_seconds)) * 1000\n if (!Number.isFinite(timeout_ms)) {\n return\n }\n\n timeout_id = setTimeout(() => {\n const index = this.idle_waiters.indexOf(finish)\n if (index >= 0) {\n this.idle_waiters.splice(index, 1)\n }\n finish(false)\n }, timeout_ms)\n })\n }\n\n // Called by EventBus.markEventCompleted and EventBus.markHandlerCompleted to notify\n // waitUntilIdle() callers that the bus may now be idle.\n _notifyIdleListeners(): void {\n // Fast-path: most completions have no waitUntilIdle() callers waiting,\n // so skip expensive idle snapshot scans in that common case.\n if (this.idle_waiters.length === 0) {\n this.idle_check_streak = 0\n return\n }\n\n if (!this.bus.isIdleAndQueueEmpty()) {\n this.idle_check_streak = 0\n if (this.idle_waiters.length > 0) {\n this.scheduleIdleCheck()\n }\n return\n }\n\n this.idle_check_streak += 1\n if (this.idle_check_streak < 2) {\n if (this.idle_waiters.length > 0) {\n this.scheduleIdleCheck()\n }\n return\n }\n\n this.idle_check_streak = 0\n const waiters = this.idle_waiters\n this.idle_waiters = []\n for (const resolve of waiters) {\n resolve(true)\n }\n }\n\n // get the bus-level lock that prevents/allows multiple events to be processed concurrently on the same bus\n getLockForEvent(event: BaseEvent): AsyncLock | null {\n const resolved = event.event_concurrency ?? this.bus.event_concurrency\n if (resolved === 'parallel') {\n return null\n }\n if (resolved === 'global-serial') {\n return this.bus._lock_for_event_global_serial\n }\n return this.bus_event_lock\n }\n\n async _runWithEventLock<T>(\n event: BaseEvent,\n fn: () => Promise<T>,\n options: { bypass_event_locks?: boolean; pre_acquired_lock?: AsyncLock | null } = {}\n ): Promise<T> {\n const pre_acquired = options.pre_acquired_lock ?? null\n if (options.bypass_event_locks || pre_acquired) {\n return await fn()\n }\n return await runWithLock(this.getLockForEvent(event), fn)\n }\n\n async _runWithHandlerLock<T>(\n event: BaseEvent,\n default_handler_concurrency: EventHandlerConcurrencyMode | undefined,\n fn: (lock: HandlerLock | null) => Promise<T>\n ): Promise<T> {\n const lock = event._getHandlerLock(default_handler_concurrency)\n if (lock) {\n await lock.acquire()\n }\n const handler_lock = lock ? new HandlerLock(lock) : null\n try {\n return await fn(handler_lock)\n } finally {\n handler_lock?.exitHandlerRun()\n }\n }\n\n // Schedules a debounced idle check to run after a short delay. Used to gate\n // waitUntilIdle() calls during handler execution and after event completion.\n private scheduleIdleCheck(): void {\n if (!this.auto_schedule_idle_checks) {\n return\n }\n if (this.idle_check_pending) {\n return\n }\n this.idle_check_pending = true\n setTimeout(() => {\n this.idle_check_pending = false\n this._notifyIdleListeners()\n }, 0)\n }\n\n // Reset all state to initial values\n clear(): void {\n this.pause_depth = 0\n this.pause_waiters = []\n this.active_handler_results = []\n this.idle_waiters = []\n this.idle_check_pending = false\n this.idle_check_streak = 0\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["import type { BaseEvent } from './base_event.js'\nimport type { EventResult } from './event_result.js'\nimport { createAsyncLocalStorage, type AsyncLocalStorageLike } from './async_context.js'\n\n// \u2500\u2500\u2500 Deferred / withResolvers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type Deferred<T> = {\n promise: Promise<T>\n resolve: (value: T | PromiseLike<T>) => void\n reject: (reason?: unknown) => void\n}\n\nexport const withResolvers = <T>(): Deferred<T> => {\n if (typeof Promise.withResolvers === 'function') {\n return Promise.withResolvers<T>()\n }\n let resolve!: (value: T | PromiseLike<T>) => void\n let reject!: (reason?: unknown) => void\n const promise = new Promise<T>((resolve_fn, reject_fn) => {\n resolve = resolve_fn\n reject = reject_fn\n })\n return { promise, resolve, reject }\n}\n\n// \u2500\u2500\u2500 Concurrency modes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const EVENT_CONCURRENCY_MODES = ['global-serial', 'bus-serial', 'parallel'] as const\nexport type EventConcurrencyMode = (typeof EVENT_CONCURRENCY_MODES)[number]\n\nexport const EVENT_HANDLER_CONCURRENCY_MODES = ['serial', 'parallel'] as const\nexport type EventHandlerConcurrencyMode = (typeof EVENT_HANDLER_CONCURRENCY_MODES)[number]\n\nexport const EVENT_HANDLER_COMPLETION_MODES = ['all', 'first'] as const\nexport type EventHandlerCompletionMode = (typeof EVENT_HANDLER_COMPLETION_MODES)[number]\n\n// \u2500\u2500\u2500 AsyncLock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class AsyncLock {\n size: number\n in_use: number\n waiters: Array<() => void>\n\n constructor(size: number) {\n this.size = size\n this.in_use = 0\n this.waiters = []\n }\n\n async acquire(): Promise<void> {\n if (this.size === Infinity) {\n return\n }\n if (this.in_use < this.size) {\n this.in_use += 1\n return\n }\n await new Promise<void>((resolve) => {\n this.waiters.push(resolve)\n })\n }\n\n release(): void {\n if (this.size === Infinity) {\n return\n }\n const next = this.waiters.shift()\n if (next) {\n // Handoff: keep permit accounted for and transfer directly to next waiter.\n next()\n return\n }\n this.in_use = Math.max(0, this.in_use - 1)\n }\n}\n\nexport const runWithLock = async <T>(lock: AsyncLock | null, fn: () => Promise<T>): Promise<T> => {\n if (!lock) {\n return await fn()\n }\n await lock.acquire()\n try {\n return await fn()\n } finally {\n lock.release()\n }\n}\n\nconst handler_context_storage: AsyncLocalStorageLike | null = createAsyncLocalStorage()\n\n// \u2500\u2500\u2500 HandlerLock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type HandlerExecutionState = 'held' | 'yielded' | 'closed'\n\n// Tracks a single handler execution's ownership of a handler lock.\n// Reacquire is race-safe: if the handler exits while waiting to reclaim,\n// the reclaimed lock is immediately released to avoid leaks.\nexport class HandlerLock {\n private lock: AsyncLock | null\n private state: HandlerExecutionState\n\n constructor(lock: AsyncLock | null) {\n this.lock = lock\n this.state = 'held'\n }\n\n // used by EventBus._processEventImmediately to yield the parent handler's lock to the child event so it can be processed immediately\n yieldHandlerLockForChildRun(): boolean {\n if (!this.lock || this.state !== 'held') {\n return false\n }\n this.state = 'yielded'\n this.lock.release()\n return true\n }\n\n // used by EventBus._processEventImmediately to reacquire the handler lock after the child event has been processed\n async reclaimHandlerLockIfRunning(): Promise<boolean> {\n if (!this.lock || this.state !== 'yielded') {\n return false\n }\n await this.lock.acquire()\n if (this.state !== 'yielded') {\n // Handler exited while this reacquire was pending.\n this.lock.release()\n return false\n }\n this.state = 'held'\n return true\n }\n\n // used by EventResult.runHandler to exit the handler lock after the handler has finished executing\n exitHandlerRun(): void {\n if (this.state === 'closed') {\n return\n }\n const should_release = !!this.lock && this.state === 'held'\n this.state = 'closed'\n if (should_release) {\n this.lock!.release()\n }\n }\n\n // used by EventBus._processEventImmediately to yield the handler lock and reacquire it after the child event has been processed\n async runQueueJump<T>(fn: () => Promise<T>): Promise<T> {\n const yielded = this.yieldHandlerLockForChildRun()\n try {\n return await fn()\n } finally {\n if (yielded) {\n await this.reclaimHandlerLockIfRunning()\n }\n }\n }\n}\n\n// \u2500\u2500\u2500 LockManager \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Interface that must be implemented by the EventBus class to be used by the LockManager\nexport type EventBusInterfaceForLockManager = {\n isIdleAndQueueEmpty: () => boolean\n event_concurrency: EventConcurrencyMode\n _lock_for_event_global_serial: AsyncLock\n}\n\nexport type LockManagerOptions = {\n auto_schedule_idle_checks?: boolean\n}\n\n// The LockManager is responsible for managing the concurrency of events and handlers\nexport class LockManager {\n private bus: EventBusInterfaceForLockManager // Live bus reference; used to read defaults and idle state.\n private auto_schedule_idle_checks: boolean\n\n readonly bus_event_lock: AsyncLock // Per-bus event lock; created with LockManager and never swapped.\n private pause_depth: number // Re-entrant pause counter; increments on _requestRunloopPause, decrements on release.\n private pause_waiters: Array<() => void> // Resolvers for _waitUntilRunloopResumed; drained when pause_depth hits 0.\n private active_handler_results: EventResult[] // Stack of active handler results for \"inside handler\" detection.\n\n private idle_waiters: Array<(became_idle: boolean) => void> // Resolvers waiting for stable idle; cleared when idle confirmed.\n private idle_check_pending: boolean // Debounce flag to avoid scheduling redundant idle checks.\n private idle_check_streak: number // Counts consecutive idle checks; used to require two ticks of idle.\n\n constructor(bus: EventBusInterfaceForLockManager, options: LockManagerOptions = {}) {\n this.bus = bus\n this.auto_schedule_idle_checks = options.auto_schedule_idle_checks ?? true\n this.bus_event_lock = new AsyncLock(1) // used for the bus-serial concurrency mode\n\n this.pause_depth = 0\n this.pause_waiters = []\n this.active_handler_results = []\n\n this.idle_waiters = []\n this.idle_check_pending = false\n this.idle_check_streak = 0\n }\n\n // Low-level runloop pause: increments a re-entrant counter and returns a release\n // function. Used for broad, bus-scoped pauses during queue-jump across buses.\n _requestRunloopPause(): () => void {\n this.pause_depth += 1\n let released = false\n return () => {\n if (released) {\n return\n }\n released = true\n this.pause_depth = Math.max(0, this.pause_depth - 1)\n if (this.pause_depth !== 0) {\n return\n }\n const waiters = this.pause_waiters\n this.pause_waiters = []\n for (const resolve of waiters) {\n resolve()\n }\n }\n }\n\n _waitUntilRunloopResumed(): Promise<void> {\n if (this.pause_depth === 0) {\n return Promise.resolve()\n }\n return new Promise((resolve) => {\n this.pause_waiters.push(resolve)\n })\n }\n\n _isPaused(): boolean {\n return this.pause_depth > 0\n }\n\n async _runWithHandlerDispatchContext<T>(result: EventResult, fn: () => Promise<T>): Promise<T> {\n this.active_handler_results.push(result)\n try {\n if (!handler_context_storage) {\n return await fn()\n }\n return await handler_context_storage.run(result, fn)\n } finally {\n const idx = this.active_handler_results.indexOf(result)\n if (idx >= 0) {\n this.active_handler_results.splice(idx, 1)\n }\n }\n }\n\n _getActiveHandlerResultForCurrentAsyncContext(): EventResult | undefined {\n const result = handler_context_storage?.getStore() as EventResult | undefined\n return result?.status === 'started' ? result : undefined\n }\n\n _getActiveHandlerResults(): EventResult[] {\n return [...this.active_handler_results]\n }\n\n // Per-bus check: true only if this specific bus has a handler on its stack.\n _isAnyHandlerActive(): boolean {\n return this.active_handler_results.length > 0\n }\n\n waitForIdle(timeout_seconds: number | null = null): Promise<boolean> {\n return new Promise((resolve) => {\n let done = false\n let timeout_id: ReturnType<typeof setTimeout> | null = null\n\n const finish = (became_idle: boolean): void => {\n if (done) {\n return\n }\n done = true\n if (timeout_id !== null) {\n clearTimeout(timeout_id)\n timeout_id = null\n }\n resolve(became_idle)\n }\n\n this.idle_waiters.push(finish)\n this.scheduleIdleCheck()\n\n if (timeout_seconds === null || timeout_seconds === undefined) {\n return\n }\n\n const timeout_ms = Math.max(0, Number(timeout_seconds)) * 1000\n if (!Number.isFinite(timeout_ms)) {\n return\n }\n\n timeout_id = setTimeout(() => {\n const index = this.idle_waiters.indexOf(finish)\n if (index >= 0) {\n this.idle_waiters.splice(index, 1)\n }\n finish(false)\n }, timeout_ms)\n })\n }\n\n // Called by EventBus.markEventCompleted and EventBus.markHandlerCompleted to notify\n // waitUntilIdle() callers that the bus may now be idle.\n _notifyIdleListeners(): void {\n // Fast-path: most completions have no waitUntilIdle() callers waiting,\n // so skip expensive idle snapshot scans in that common case.\n if (this.idle_waiters.length === 0) {\n this.idle_check_streak = 0\n return\n }\n\n if (!this.bus.isIdleAndQueueEmpty()) {\n this.idle_check_streak = 0\n if (this.idle_waiters.length > 0) {\n this.scheduleIdleCheck()\n }\n return\n }\n\n this.idle_check_streak += 1\n if (this.idle_check_streak < 2) {\n if (this.idle_waiters.length > 0) {\n this.scheduleIdleCheck()\n }\n return\n }\n\n this.idle_check_streak = 0\n const waiters = this.idle_waiters\n this.idle_waiters = []\n for (const resolve of waiters) {\n resolve(true)\n }\n }\n\n // get the bus-level lock that prevents/allows multiple events to be processed concurrently on the same bus\n getLockForEvent(event: BaseEvent): AsyncLock | null {\n const resolved = event.event_concurrency ?? this.bus.event_concurrency\n if (resolved === 'parallel') {\n return null\n }\n if (resolved === 'global-serial') {\n return this.bus._lock_for_event_global_serial\n }\n return this.bus_event_lock\n }\n\n async _runWithEventLock<T>(\n event: BaseEvent,\n fn: () => Promise<T>,\n options: { bypass_event_locks?: boolean; pre_acquired_lock?: AsyncLock | null } = {}\n ): Promise<T> {\n const pre_acquired = options.pre_acquired_lock ?? null\n if (options.bypass_event_locks || pre_acquired) {\n return await fn()\n }\n return await runWithLock(this.getLockForEvent(event), fn)\n }\n\n async _runWithHandlerLock<T>(\n event: BaseEvent,\n default_handler_concurrency: EventHandlerConcurrencyMode | undefined,\n fn: (lock: HandlerLock | null) => Promise<T>\n ): Promise<T> {\n const lock = event._getHandlerLock(default_handler_concurrency)\n if (lock) {\n await lock.acquire()\n }\n const handler_lock = lock ? new HandlerLock(lock) : null\n try {\n return await fn(handler_lock)\n } finally {\n handler_lock?.exitHandlerRun()\n }\n }\n\n // Schedules a debounced idle check to run after a short delay. Used to gate\n // waitUntilIdle() calls during handler execution and after event completion.\n private scheduleIdleCheck(): void {\n if (!this.auto_schedule_idle_checks) {\n return\n }\n if (this.idle_check_pending) {\n return\n }\n this.idle_check_pending = true\n setTimeout(() => {\n this.idle_check_pending = false\n this._notifyIdleListeners()\n }, 0)\n }\n\n // Reset all state to initial values\n clear(): void {\n this.pause_depth = 0\n this.pause_waiters = []\n this.active_handler_results = []\n this.idle_waiters = []\n this.idle_check_pending = false\n this.idle_check_streak = 0\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cjs/logging.js
CHANGED
|
@@ -129,7 +129,7 @@ const buildTreeLine = (event, indent, is_last, parent_to_children, visited) => {
|
|
|
129
129
|
};
|
|
130
130
|
const buildResultLine = (result, indent, is_last, parent_to_children, visited) => {
|
|
131
131
|
const connector = is_last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
132
|
-
const status_icon = result.status === "completed" ? "\u2705" : result.status === "error" ? "\u274C" : result.status === "started" ? "\u{1F3C3}" : "\u23F3";
|
|
132
|
+
const status_icon = result.status === "completed" ? "\u2705" : result.status === "error" && isCancellationControlError(result.error) ? "\u{1F6AB}" : result.status === "error" ? "\u274C" : result.status === "started" ? "\u{1F3C3}" : "\u23F3";
|
|
133
133
|
const handler_label = result.handler_name && result.handler_name !== "anonymous" ? result.handler_name : result.handler_file_path ? result.handler_file_path : "anonymous";
|
|
134
134
|
const handler_display = `${result.eventbus_label}.${handler_label}#${result.handler_id.slice(-4)}`;
|
|
135
135
|
let line = `${indent}${connector}${status_icon} ${handler_display}`;
|
|
@@ -149,7 +149,9 @@ const buildResultLine = (result, indent, is_last, parent_to_children, visited) =
|
|
|
149
149
|
if (result.error instanceof import_event_handler.EventHandlerTimeoutError) {
|
|
150
150
|
line += ` \u23F1\uFE0F Timeout: ${result.error.message}`;
|
|
151
151
|
} else if (result.error instanceof import_event_handler.EventHandlerCancelledError) {
|
|
152
|
-
line += `
|
|
152
|
+
line += ` Cancelled: ${result.error.message}`;
|
|
153
|
+
} else if (result.error instanceof import_event_handler.EventHandlerAbortedError) {
|
|
154
|
+
line += ` Aborted: ${result.error.message}`;
|
|
153
155
|
} else {
|
|
154
156
|
const error_name = result.error instanceof Error ? result.error.name : "Error";
|
|
155
157
|
const error_message = result.error instanceof Error ? result.error.message : String(result.error);
|
|
@@ -182,6 +184,7 @@ const buildResultLine = (result, indent, is_last, parent_to_children, visited) =
|
|
|
182
184
|
});
|
|
183
185
|
return [line, ...child_lines].join("\n");
|
|
184
186
|
};
|
|
187
|
+
const isCancellationControlError = (error) => error instanceof import_event_handler.EventHandlerCancelledError || error instanceof import_event_handler.EventHandlerAbortedError;
|
|
185
188
|
const formatTimestamp = (value) => {
|
|
186
189
|
if (!value) {
|
|
187
190
|
return "N/A";
|
package/dist/cjs/logging.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/logging.ts"],
|
|
4
|
-
"sourcesContent": ["import { BaseEvent } from './base_event.js'\nimport { EventResult } from './event_result.js'\nimport { EventHandlerCancelledError, EventHandlerTimeoutError } from './event_handler.js'\n\ntype LogTreeBus = {\n name: string\n event_history: {\n values(): IterableIterator<BaseEvent>\n has(event_id: string): boolean\n }\n toString?: () => string\n}\n\nexport const logTree = (bus: LogTreeBus): string => {\n const parent_to_children = new Map<string, BaseEvent[]>()\n\n const addChild = (parent_id: string, child: BaseEvent): void => {\n const existing = parent_to_children.get(parent_id) ?? []\n existing.push(child)\n parent_to_children.set(parent_id, existing)\n }\n\n const root_events: BaseEvent[] = []\n const seen = new Set<string>()\n\n for (const event of bus.event_history.values()) {\n const parent_id = event.event_parent_id\n if (!parent_id || parent_id === event.event_id || !bus.event_history.has(parent_id)) {\n if (!seen.has(event.event_id)) {\n root_events.push(event)\n seen.add(event.event_id)\n }\n }\n }\n\n if (root_events.length === 0) {\n return '(No events in history)'\n }\n\n const nodes_by_id = new Map<string, BaseEvent>()\n for (const root of root_events) {\n nodes_by_id.set(root.event_id, root)\n for (const descendant of root.event_descendants) {\n nodes_by_id.set(descendant.event_id, descendant)\n }\n }\n\n for (const node of nodes_by_id.values()) {\n const parent_id = node.event_parent_id\n if (!parent_id || parent_id === node.event_id) {\n continue\n }\n if (!nodes_by_id.has(parent_id)) {\n continue\n }\n addChild(parent_id, node)\n }\n\n for (const children of parent_to_children.values()) {\n children.sort((a, b) => (a.event_created_at < b.event_created_at ? -1 : a.event_created_at > b.event_created_at ? 1 : 0))\n }\n\n const lines: string[] = []\n const bus_label = typeof bus.toString === 'function' ? bus.toString() : bus.name\n lines.push(`\uD83D\uDCCA Event History Tree for ${bus_label}`)\n lines.push('='.repeat(80))\n\n root_events.sort((a, b) => (a.event_created_at < b.event_created_at ? -1 : a.event_created_at > b.event_created_at ? 1 : 0))\n const visited = new Set<string>()\n root_events.forEach((event, index) => {\n lines.push(buildTreeLine(event, '', index === root_events.length - 1, parent_to_children, visited))\n })\n\n lines.push('='.repeat(80))\n\n return lines.join('\\n')\n}\n\nexport const buildTreeLine = (\n event: BaseEvent,\n indent: string,\n is_last: boolean,\n parent_to_children: Map<string, BaseEvent[]>,\n visited: Set<string>\n): string => {\n const connector = is_last ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 '\n const status_icon = event.event_status === 'completed' ? '\u2705' : event.event_status === 'started' ? '\uD83C\uDFC3' : '\u23F3'\n\n const created_at = formatTimestamp(event.event_created_at)\n let timing = `[${created_at}`\n if (event.event_completed_at) {\n const created_ms = Date.parse(event.event_created_at)\n const completed_ms = Date.parse(event.event_completed_at)\n if (!Number.isNaN(created_ms) && !Number.isNaN(completed_ms)) {\n const duration = (completed_ms - created_ms) / 1000\n timing += ` (${duration.toFixed(3)}s)`\n }\n }\n timing += ']'\n\n const line = `${indent}${connector}${status_icon} ${event.event_type}#${event.event_id.slice(-4)} ${timing}`\n\n if (visited.has(event.event_id)) {\n return line\n }\n visited.add(event.event_id)\n\n const extension = is_last ? ' ' : '\u2502 '\n const new_indent = indent + extension\n\n const result_items: Array<{ type: 'result'; result: EventResult } | { type: 'child'; child: BaseEvent }> = []\n for (const result of event.event_results.values()) {\n result_items.push({ type: 'result', result })\n }\n const children = parent_to_children.get(event.event_id) ?? []\n const printed_child_ids = new Set<string>(event.event_results.size > 0 ? event.event_results.keys() : [])\n for (const child of children) {\n if (!printed_child_ids.has(child.event_id) && !child.event_emitted_by_handler_id) {\n result_items.push({ type: 'child', child })\n printed_child_ids.add(child.event_id)\n }\n }\n\n if (result_items.length === 0) {\n return line\n }\n\n const child_lines: string[] = []\n result_items.forEach((item, index) => {\n const is_last_item = index === result_items.length - 1\n if (item.type === 'result') {\n child_lines.push(buildResultLine(item.result, new_indent, is_last_item, parent_to_children, visited))\n } else {\n child_lines.push(buildTreeLine(item.child, new_indent, is_last_item, parent_to_children, visited))\n }\n })\n\n return [line, ...child_lines].join('\\n')\n}\n\nexport const buildResultLine = (\n result: EventResult,\n indent: string,\n is_last: boolean,\n parent_to_children: Map<string, BaseEvent[]>,\n visited: Set<string>\n): string => {\n const connector = is_last ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 '\n const status_icon
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAA0B;AAE1B,
|
|
4
|
+
"sourcesContent": ["import { BaseEvent } from './base_event.js'\nimport { EventResult } from './event_result.js'\nimport { EventHandlerAbortedError, EventHandlerCancelledError, EventHandlerTimeoutError } from './event_handler.js'\n\ntype LogTreeBus = {\n name: string\n event_history: {\n values(): IterableIterator<BaseEvent>\n has(event_id: string): boolean\n }\n toString?: () => string\n}\n\nexport const logTree = (bus: LogTreeBus): string => {\n const parent_to_children = new Map<string, BaseEvent[]>()\n\n const addChild = (parent_id: string, child: BaseEvent): void => {\n const existing = parent_to_children.get(parent_id) ?? []\n existing.push(child)\n parent_to_children.set(parent_id, existing)\n }\n\n const root_events: BaseEvent[] = []\n const seen = new Set<string>()\n\n for (const event of bus.event_history.values()) {\n const parent_id = event.event_parent_id\n if (!parent_id || parent_id === event.event_id || !bus.event_history.has(parent_id)) {\n if (!seen.has(event.event_id)) {\n root_events.push(event)\n seen.add(event.event_id)\n }\n }\n }\n\n if (root_events.length === 0) {\n return '(No events in history)'\n }\n\n const nodes_by_id = new Map<string, BaseEvent>()\n for (const root of root_events) {\n nodes_by_id.set(root.event_id, root)\n for (const descendant of root.event_descendants) {\n nodes_by_id.set(descendant.event_id, descendant)\n }\n }\n\n for (const node of nodes_by_id.values()) {\n const parent_id = node.event_parent_id\n if (!parent_id || parent_id === node.event_id) {\n continue\n }\n if (!nodes_by_id.has(parent_id)) {\n continue\n }\n addChild(parent_id, node)\n }\n\n for (const children of parent_to_children.values()) {\n children.sort((a, b) => (a.event_created_at < b.event_created_at ? -1 : a.event_created_at > b.event_created_at ? 1 : 0))\n }\n\n const lines: string[] = []\n const bus_label = typeof bus.toString === 'function' ? bus.toString() : bus.name\n lines.push(`\uD83D\uDCCA Event History Tree for ${bus_label}`)\n lines.push('='.repeat(80))\n\n root_events.sort((a, b) => (a.event_created_at < b.event_created_at ? -1 : a.event_created_at > b.event_created_at ? 1 : 0))\n const visited = new Set<string>()\n root_events.forEach((event, index) => {\n lines.push(buildTreeLine(event, '', index === root_events.length - 1, parent_to_children, visited))\n })\n\n lines.push('='.repeat(80))\n\n return lines.join('\\n')\n}\n\nexport const buildTreeLine = (\n event: BaseEvent,\n indent: string,\n is_last: boolean,\n parent_to_children: Map<string, BaseEvent[]>,\n visited: Set<string>\n): string => {\n const connector = is_last ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 '\n const status_icon = event.event_status === 'completed' ? '\u2705' : event.event_status === 'started' ? '\uD83C\uDFC3' : '\u23F3'\n\n const created_at = formatTimestamp(event.event_created_at)\n let timing = `[${created_at}`\n if (event.event_completed_at) {\n const created_ms = Date.parse(event.event_created_at)\n const completed_ms = Date.parse(event.event_completed_at)\n if (!Number.isNaN(created_ms) && !Number.isNaN(completed_ms)) {\n const duration = (completed_ms - created_ms) / 1000\n timing += ` (${duration.toFixed(3)}s)`\n }\n }\n timing += ']'\n\n const line = `${indent}${connector}${status_icon} ${event.event_type}#${event.event_id.slice(-4)} ${timing}`\n\n if (visited.has(event.event_id)) {\n return line\n }\n visited.add(event.event_id)\n\n const extension = is_last ? ' ' : '\u2502 '\n const new_indent = indent + extension\n\n const result_items: Array<{ type: 'result'; result: EventResult } | { type: 'child'; child: BaseEvent }> = []\n for (const result of event.event_results.values()) {\n result_items.push({ type: 'result', result })\n }\n const children = parent_to_children.get(event.event_id) ?? []\n const printed_child_ids = new Set<string>(event.event_results.size > 0 ? event.event_results.keys() : [])\n for (const child of children) {\n if (!printed_child_ids.has(child.event_id) && !child.event_emitted_by_handler_id) {\n result_items.push({ type: 'child', child })\n printed_child_ids.add(child.event_id)\n }\n }\n\n if (result_items.length === 0) {\n return line\n }\n\n const child_lines: string[] = []\n result_items.forEach((item, index) => {\n const is_last_item = index === result_items.length - 1\n if (item.type === 'result') {\n child_lines.push(buildResultLine(item.result, new_indent, is_last_item, parent_to_children, visited))\n } else {\n child_lines.push(buildTreeLine(item.child, new_indent, is_last_item, parent_to_children, visited))\n }\n })\n\n return [line, ...child_lines].join('\\n')\n}\n\nexport const buildResultLine = (\n result: EventResult,\n indent: string,\n is_last: boolean,\n parent_to_children: Map<string, BaseEvent[]>,\n visited: Set<string>\n): string => {\n const connector = is_last ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 '\n const status_icon =\n result.status === 'completed'\n ? '\u2705'\n : result.status === 'error' && isCancellationControlError(result.error)\n ? '\uD83D\uDEAB'\n : result.status === 'error'\n ? '\u274C'\n : result.status === 'started'\n ? '\uD83C\uDFC3'\n : '\u23F3'\n\n const handler_label =\n result.handler_name && result.handler_name !== 'anonymous'\n ? result.handler_name\n : result.handler_file_path\n ? result.handler_file_path\n : 'anonymous'\n const handler_display = `${result.eventbus_label}.${handler_label}#${result.handler_id.slice(-4)}`\n let line = `${indent}${connector}${status_icon} ${handler_display}`\n\n if (result.started_at) {\n line += ` [${formatTimestamp(result.started_at)}`\n if (result.completed_at) {\n const started_ms = Date.parse(result.started_at)\n const completed_ms = Date.parse(result.completed_at)\n if (!Number.isNaN(started_ms) && !Number.isNaN(completed_ms)) {\n const duration = (completed_ms - started_ms) / 1000\n line += ` (${duration.toFixed(3)}s)`\n }\n }\n line += ']'\n }\n\n if (result.status === 'error' && result.error) {\n if (result.error instanceof EventHandlerTimeoutError) {\n line += ` \u23F1\uFE0F Timeout: ${result.error.message}`\n } else if (result.error instanceof EventHandlerCancelledError) {\n line += ` Cancelled: ${result.error.message}`\n } else if (result.error instanceof EventHandlerAbortedError) {\n line += ` Aborted: ${result.error.message}`\n } else {\n const error_name = result.error instanceof Error ? result.error.name : 'Error'\n const error_message = result.error instanceof Error ? result.error.message : String(result.error)\n line += ` \u2620\uFE0F ${error_name}: ${error_message}`\n }\n } else if (result.status === 'completed') {\n line += ` \u2192 ${formatResultValue(result.result)}`\n }\n\n const extension = is_last ? ' ' : '\u2502 '\n const new_indent = indent + extension\n\n const direct_children = result.event_children\n if (direct_children.length === 0) {\n return line\n }\n\n const child_lines: string[] = []\n const parent_children = parent_to_children.get(result.event_id) ?? []\n const emitted_children = parent_children.filter((child) => child.event_emitted_by_handler_id === result.handler_id)\n const children_by_id = new Map<string, BaseEvent>()\n direct_children.forEach((child) => {\n children_by_id.set(child.event_id, child)\n })\n emitted_children.forEach((child) => {\n if (!children_by_id.has(child.event_id)) {\n children_by_id.set(child.event_id, child)\n }\n })\n const children_to_print = Array.from(children_by_id.values()).filter((child) => !visited.has(child.event_id))\n\n children_to_print.forEach((child, index) => {\n child_lines.push(buildTreeLine(child, new_indent, index === children_to_print.length - 1, parent_to_children, visited))\n })\n\n return [line, ...child_lines].join('\\n')\n}\n\nconst isCancellationControlError = (error: unknown): boolean =>\n error instanceof EventHandlerCancelledError || error instanceof EventHandlerAbortedError\n\nexport const formatTimestamp = (value?: string): string => {\n if (!value) {\n return 'N/A'\n }\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) {\n return 'N/A'\n }\n return date.toISOString().slice(11, 23)\n}\n\nexport const formatResultValue = (value: unknown): string => {\n if (value === null || value === undefined) {\n return 'None'\n }\n if (value instanceof BaseEvent) {\n return `Event(${value.event_type}#${value.event_id.slice(-4)})`\n }\n if (typeof value === 'string') {\n return JSON.stringify(value)\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n if (Array.isArray(value)) {\n return `list(${value.length} items)`\n }\n if (typeof value === 'object') {\n return `dict(${Object.keys(value as Record<string, unknown>).length} items)`\n }\n return `${typeof value}(...)`\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { trace, type Tracer } from '@opentelemetry/api';
|
|
2
|
+
import type { BaseEvent } from './base_event.js';
|
|
3
|
+
import type { EventBus } from './event_bus.js';
|
|
4
|
+
import type { EventResult } from './event_result.js';
|
|
5
|
+
import type { EventBusMiddleware } from './middlewares.js';
|
|
6
|
+
import type { EventStatus } from './types.js';
|
|
7
|
+
type OpenTelemetryTraceApi = Pick<typeof trace, 'getTracer' | 'setSpan'>;
|
|
8
|
+
export type OtelTracingMiddlewareOptions = {
|
|
9
|
+
tracer?: Tracer;
|
|
10
|
+
trace_api?: OpenTelemetryTraceApi;
|
|
11
|
+
};
|
|
12
|
+
export declare class OtelTracingMiddleware implements EventBusMiddleware {
|
|
13
|
+
private readonly tracer;
|
|
14
|
+
private readonly trace_api;
|
|
15
|
+
private readonly event_spans;
|
|
16
|
+
private readonly event_contexts;
|
|
17
|
+
private readonly handler_spans;
|
|
18
|
+
private readonly handler_contexts;
|
|
19
|
+
constructor(options?: OtelTracingMiddlewareOptions);
|
|
20
|
+
onEventChange(eventbus: EventBus, event: BaseEvent, status: EventStatus): void;
|
|
21
|
+
onEventResultChange(eventbus: EventBus, event: BaseEvent, event_result: EventResult, status: EventStatus): void;
|
|
22
|
+
private startEventSpan;
|
|
23
|
+
private completeEventSpan;
|
|
24
|
+
private startHandlerSpan;
|
|
25
|
+
private completeHandlerSpan;
|
|
26
|
+
private parentContextForEvent;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var middleware_otel_tracing_exports = {};
|
|
20
|
+
__export(middleware_otel_tracing_exports, {
|
|
21
|
+
OtelTracingMiddleware: () => OtelTracingMiddleware
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(middleware_otel_tracing_exports);
|
|
24
|
+
var import_api = require("@opentelemetry/api");
|
|
25
|
+
class OtelTracingMiddleware {
|
|
26
|
+
tracer;
|
|
27
|
+
trace_api;
|
|
28
|
+
event_spans = /* @__PURE__ */ new Map();
|
|
29
|
+
event_contexts = /* @__PURE__ */ new Map();
|
|
30
|
+
handler_spans = /* @__PURE__ */ new Map();
|
|
31
|
+
handler_contexts = /* @__PURE__ */ new Map();
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
this.trace_api = options.trace_api ?? import_api.trace;
|
|
34
|
+
this.tracer = options.tracer ?? this.trace_api.getTracer("abxbus");
|
|
35
|
+
}
|
|
36
|
+
onEventChange(eventbus, event, status) {
|
|
37
|
+
if (status === "started") {
|
|
38
|
+
this.startEventSpan(eventbus, event);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (status === "completed") {
|
|
42
|
+
this.completeEventSpan(eventbus, event);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
onEventResultChange(eventbus, event, event_result, status) {
|
|
46
|
+
if (status === "started") {
|
|
47
|
+
this.startHandlerSpan(eventbus, event, event_result);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (status === "completed") {
|
|
51
|
+
this.completeHandlerSpan(event_result);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
startEventSpan(eventbus, event) {
|
|
55
|
+
const existing = this.event_spans.get(event.event_id);
|
|
56
|
+
if (existing) {
|
|
57
|
+
return existing;
|
|
58
|
+
}
|
|
59
|
+
const parent_context = this.parentContextForEvent(event);
|
|
60
|
+
const span = this.tracer.startSpan(
|
|
61
|
+
`abxbus.event ${event.event_type}`,
|
|
62
|
+
{
|
|
63
|
+
attributes: compactAttributes({
|
|
64
|
+
"abxbus.bus.id": eventbus.id,
|
|
65
|
+
"abxbus.bus.name": eventbus.name,
|
|
66
|
+
"abxbus.event.id": event.event_id,
|
|
67
|
+
"abxbus.event.type": event.event_type,
|
|
68
|
+
"abxbus.event.version": event.event_version,
|
|
69
|
+
"abxbus.event.parent_id": event.event_parent_id,
|
|
70
|
+
"abxbus.event.emitted_by_handler_id": event.event_emitted_by_handler_id,
|
|
71
|
+
"abxbus.event.path": event.event_path.join(" ")
|
|
72
|
+
}),
|
|
73
|
+
startTime: dateFromIso(event.event_started_at)
|
|
74
|
+
},
|
|
75
|
+
parent_context
|
|
76
|
+
);
|
|
77
|
+
const span_context = this.trace_api.setSpan(parent_context ?? import_api.context.active(), span);
|
|
78
|
+
this.event_spans.set(event.event_id, span);
|
|
79
|
+
this.event_contexts.set(event.event_id, span_context);
|
|
80
|
+
return span;
|
|
81
|
+
}
|
|
82
|
+
completeEventSpan(eventbus, event) {
|
|
83
|
+
const span = this.event_spans.get(event.event_id) ?? this.startEventSpan(eventbus, event);
|
|
84
|
+
if (event.event_errors.length > 0) {
|
|
85
|
+
recordSpanError(span, event.event_errors[0]);
|
|
86
|
+
} else {
|
|
87
|
+
span.setStatus({ code: import_api.SpanStatusCode.OK });
|
|
88
|
+
}
|
|
89
|
+
span.setAttributes(
|
|
90
|
+
compactAttributes({
|
|
91
|
+
"abxbus.event.status": event.event_status,
|
|
92
|
+
"abxbus.event.result_count": event.event_results.size,
|
|
93
|
+
"abxbus.event.error_count": event.event_errors.length,
|
|
94
|
+
"abxbus.event.child_count": event.event_children.length
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
span.end(dateFromIso(event.event_completed_at));
|
|
98
|
+
this.event_spans.delete(event.event_id);
|
|
99
|
+
this.event_contexts.delete(event.event_id);
|
|
100
|
+
}
|
|
101
|
+
startHandlerSpan(eventbus, event, event_result) {
|
|
102
|
+
const existing = this.handler_spans.get(event_result.id);
|
|
103
|
+
if (existing) {
|
|
104
|
+
return existing;
|
|
105
|
+
}
|
|
106
|
+
const parent_context = this.event_contexts.get(event.event_id) ?? this.trace_api.setSpan(import_api.context.active(), this.startEventSpan(eventbus, event));
|
|
107
|
+
const span = this.tracer.startSpan(
|
|
108
|
+
`abxbus.handler ${event.event_type} ${event_result.handler_name}`,
|
|
109
|
+
{
|
|
110
|
+
attributes: compactAttributes({
|
|
111
|
+
"abxbus.bus.id": eventbus.id,
|
|
112
|
+
"abxbus.bus.name": eventbus.name,
|
|
113
|
+
"abxbus.event.id": event.event_id,
|
|
114
|
+
"abxbus.event.type": event.event_type,
|
|
115
|
+
"abxbus.handler.id": event_result.handler_id,
|
|
116
|
+
"abxbus.handler.name": event_result.handler_name,
|
|
117
|
+
"abxbus.handler.file_path": event_result.handler_file_path,
|
|
118
|
+
"abxbus.handler.event_pattern": event_result.handler.event_pattern,
|
|
119
|
+
"abxbus.event_result.id": event_result.id
|
|
120
|
+
}),
|
|
121
|
+
startTime: dateFromIso(event_result.started_at)
|
|
122
|
+
},
|
|
123
|
+
parent_context
|
|
124
|
+
);
|
|
125
|
+
const span_context = this.trace_api.setSpan(parent_context, span);
|
|
126
|
+
this.handler_spans.set(event_result.id, span);
|
|
127
|
+
this.handler_contexts.set(handlerSpanKey(event_result.event_id, event_result.handler_id), span_context);
|
|
128
|
+
return span;
|
|
129
|
+
}
|
|
130
|
+
completeHandlerSpan(event_result) {
|
|
131
|
+
const span = this.handler_spans.get(event_result.id);
|
|
132
|
+
if (!span) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (event_result.error !== void 0) {
|
|
136
|
+
recordSpanError(span, event_result.error);
|
|
137
|
+
} else {
|
|
138
|
+
span.setStatus({ code: import_api.SpanStatusCode.OK });
|
|
139
|
+
}
|
|
140
|
+
span.setAttributes(
|
|
141
|
+
compactAttributes({
|
|
142
|
+
"abxbus.event_result.status": event_result.status,
|
|
143
|
+
"abxbus.handler.child_count": event_result.event_children.length
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
span.end(dateFromIso(event_result.completed_at));
|
|
147
|
+
this.handler_spans.delete(event_result.id);
|
|
148
|
+
this.handler_contexts.delete(handlerSpanKey(event_result.event_id, event_result.handler_id));
|
|
149
|
+
}
|
|
150
|
+
parentContextForEvent(event) {
|
|
151
|
+
if (event.event_parent_id && event.event_emitted_by_handler_id) {
|
|
152
|
+
const handler_context = this.handler_contexts.get(handlerSpanKey(event.event_parent_id, event.event_emitted_by_handler_id));
|
|
153
|
+
if (handler_context) {
|
|
154
|
+
return handler_context;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return event.event_parent_id ? this.event_contexts.get(event.event_parent_id) : void 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function handlerSpanKey(event_id, handler_id) {
|
|
161
|
+
return `${event_id}:${handler_id}`;
|
|
162
|
+
}
|
|
163
|
+
function dateFromIso(value) {
|
|
164
|
+
if (value == null) {
|
|
165
|
+
return void 0;
|
|
166
|
+
}
|
|
167
|
+
const date = new Date(value);
|
|
168
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
169
|
+
}
|
|
170
|
+
function compactAttributes(attributes) {
|
|
171
|
+
const compacted = {};
|
|
172
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
173
|
+
if (value !== null && value !== void 0) {
|
|
174
|
+
compacted[key] = value;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return compacted;
|
|
178
|
+
}
|
|
179
|
+
function recordSpanError(span, error) {
|
|
180
|
+
if (error instanceof Error) {
|
|
181
|
+
span.recordException(error);
|
|
182
|
+
span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const message = typeof error === "string" ? error : "Unknown abxbus handler error";
|
|
186
|
+
span.recordException(message);
|
|
187
|
+
span.setStatus({ code: import_api.SpanStatusCode.ERROR, message });
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=middleware_otel_tracing.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/middleware_otel_tracing.ts"],
|
|
4
|
+
"sourcesContent": ["import { context, SpanStatusCode, trace, type Context, type Span, type Tracer } from '@opentelemetry/api'\n\nimport type { BaseEvent } from './base_event.js'\nimport type { EventBus } from './event_bus.js'\nimport type { EventResult } from './event_result.js'\nimport type { EventBusMiddleware } from './middlewares.js'\nimport type { EventStatus } from './types.js'\n\ntype OpenTelemetryTraceApi = Pick<typeof trace, 'getTracer' | 'setSpan'>\n\nexport type OtelTracingMiddlewareOptions = {\n tracer?: Tracer\n trace_api?: OpenTelemetryTraceApi\n}\n\nexport class OtelTracingMiddleware implements EventBusMiddleware {\n private readonly tracer: Tracer\n private readonly trace_api: OpenTelemetryTraceApi\n private readonly event_spans = new Map<string, Span>()\n private readonly event_contexts = new Map<string, Context>()\n private readonly handler_spans = new Map<string, Span>()\n private readonly handler_contexts = new Map<string, Context>()\n\n constructor(options: OtelTracingMiddlewareOptions = {}) {\n this.trace_api = options.trace_api ?? trace\n this.tracer = options.tracer ?? this.trace_api.getTracer('abxbus')\n }\n\n onEventChange(eventbus: EventBus, event: BaseEvent, status: EventStatus): void {\n if (status === 'started') {\n this.startEventSpan(eventbus, event)\n return\n }\n\n if (status === 'completed') {\n this.completeEventSpan(eventbus, event)\n }\n }\n\n onEventResultChange(eventbus: EventBus, event: BaseEvent, event_result: EventResult, status: EventStatus): void {\n if (status === 'started') {\n this.startHandlerSpan(eventbus, event, event_result)\n return\n }\n\n if (status === 'completed') {\n this.completeHandlerSpan(event_result)\n }\n }\n\n private startEventSpan(eventbus: EventBus, event: BaseEvent): Span {\n const existing = this.event_spans.get(event.event_id)\n if (existing) {\n return existing\n }\n\n const parent_context = this.parentContextForEvent(event)\n const span = this.tracer.startSpan(\n `abxbus.event ${event.event_type}`,\n {\n attributes: compactAttributes({\n 'abxbus.bus.id': eventbus.id,\n 'abxbus.bus.name': eventbus.name,\n 'abxbus.event.id': event.event_id,\n 'abxbus.event.type': event.event_type,\n 'abxbus.event.version': event.event_version,\n 'abxbus.event.parent_id': event.event_parent_id,\n 'abxbus.event.emitted_by_handler_id': event.event_emitted_by_handler_id,\n 'abxbus.event.path': event.event_path.join(' '),\n }),\n startTime: dateFromIso(event.event_started_at),\n },\n parent_context\n )\n const span_context = this.trace_api.setSpan(parent_context ?? context.active(), span)\n this.event_spans.set(event.event_id, span)\n this.event_contexts.set(event.event_id, span_context)\n return span\n }\n\n private completeEventSpan(eventbus: EventBus, event: BaseEvent): void {\n const span = this.event_spans.get(event.event_id) ?? this.startEventSpan(eventbus, event)\n if (event.event_errors.length > 0) {\n recordSpanError(span, event.event_errors[0])\n } else {\n span.setStatus({ code: SpanStatusCode.OK })\n }\n span.setAttributes(\n compactAttributes({\n 'abxbus.event.status': event.event_status,\n 'abxbus.event.result_count': event.event_results.size,\n 'abxbus.event.error_count': event.event_errors.length,\n 'abxbus.event.child_count': event.event_children.length,\n })\n )\n span.end(dateFromIso(event.event_completed_at))\n this.event_spans.delete(event.event_id)\n this.event_contexts.delete(event.event_id)\n }\n\n private startHandlerSpan(eventbus: EventBus, event: BaseEvent, event_result: EventResult): Span {\n const existing = this.handler_spans.get(event_result.id)\n if (existing) {\n return existing\n }\n\n const parent_context =\n this.event_contexts.get(event.event_id) ?? this.trace_api.setSpan(context.active(), this.startEventSpan(eventbus, event))\n const span = this.tracer.startSpan(\n `abxbus.handler ${event.event_type} ${event_result.handler_name}`,\n {\n attributes: compactAttributes({\n 'abxbus.bus.id': eventbus.id,\n 'abxbus.bus.name': eventbus.name,\n 'abxbus.event.id': event.event_id,\n 'abxbus.event.type': event.event_type,\n 'abxbus.handler.id': event_result.handler_id,\n 'abxbus.handler.name': event_result.handler_name,\n 'abxbus.handler.file_path': event_result.handler_file_path,\n 'abxbus.handler.event_pattern': event_result.handler.event_pattern,\n 'abxbus.event_result.id': event_result.id,\n }),\n startTime: dateFromIso(event_result.started_at),\n },\n parent_context\n )\n const span_context = this.trace_api.setSpan(parent_context, span)\n this.handler_spans.set(event_result.id, span)\n this.handler_contexts.set(handlerSpanKey(event_result.event_id, event_result.handler_id), span_context)\n return span\n }\n\n private completeHandlerSpan(event_result: EventResult): void {\n const span = this.handler_spans.get(event_result.id)\n if (!span) {\n return\n }\n\n if (event_result.error !== undefined) {\n recordSpanError(span, event_result.error)\n } else {\n span.setStatus({ code: SpanStatusCode.OK })\n }\n span.setAttributes(\n compactAttributes({\n 'abxbus.event_result.status': event_result.status,\n 'abxbus.handler.child_count': event_result.event_children.length,\n })\n )\n span.end(dateFromIso(event_result.completed_at))\n this.handler_spans.delete(event_result.id)\n this.handler_contexts.delete(handlerSpanKey(event_result.event_id, event_result.handler_id))\n }\n\n private parentContextForEvent(event: BaseEvent): Context | undefined {\n if (event.event_parent_id && event.event_emitted_by_handler_id) {\n const handler_context = this.handler_contexts.get(handlerSpanKey(event.event_parent_id, event.event_emitted_by_handler_id))\n if (handler_context) {\n return handler_context\n }\n }\n\n return event.event_parent_id ? this.event_contexts.get(event.event_parent_id) : undefined\n }\n}\n\nfunction handlerSpanKey(event_id: string, handler_id: string): string {\n return `${event_id}:${handler_id}`\n}\n\nfunction dateFromIso(value: string | null | undefined): Date | undefined {\n if (value == null) {\n return undefined\n }\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? undefined : date\n}\n\nfunction compactAttributes(\n attributes: Record<string, string | number | boolean | null | undefined>\n): Record<string, string | number | boolean> {\n const compacted: Record<string, string | number | boolean> = {}\n for (const [key, value] of Object.entries(attributes)) {\n if (value !== null && value !== undefined) {\n compacted[key] = value\n }\n }\n return compacted\n}\n\nfunction recordSpanError(span: Span, error: unknown): void {\n if (error instanceof Error) {\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n return\n }\n\n const message = typeof error === 'string' ? error : 'Unknown abxbus handler error'\n span.recordException(message)\n span.setStatus({ code: SpanStatusCode.ERROR, message })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/cjs/retry.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
type SemaphoreScope = 'multiprocess' | 'global' | 'class' | 'instance';
|
|
1
2
|
export interface RetryOptions {
|
|
2
3
|
/** Total number of attempts including the initial call (1 = no retry, 3 = up to 2 retries). Default: 1 */
|
|
3
4
|
max_attempts?: number;
|
|
@@ -19,11 +20,12 @@ export interface RetryOptions {
|
|
|
19
20
|
/** If true, proceed without concurrency limit when semaphore acquisition times out. Default: true */
|
|
20
21
|
semaphore_lax?: boolean;
|
|
21
22
|
/** Semaphore scoping strategy. Default: 'global'
|
|
23
|
+
* - 'multiprocess': all processes on the machine share one semaphore (Node.js only)
|
|
22
24
|
* - 'global': all calls share one semaphore (keyed by semaphore_name)
|
|
23
25
|
* - 'class': all instances of the same class share one semaphore (keyed by className.semaphore_name)
|
|
24
26
|
* - 'instance': each object instance gets its own semaphore (keyed by instanceId.semaphore_name)
|
|
25
27
|
* 'class' and 'instance' require `this` to be an object; they fall back to 'global' for standalone calls. */
|
|
26
|
-
semaphore_scope?:
|
|
28
|
+
semaphore_scope?: SemaphoreScope;
|
|
27
29
|
/** Maximum seconds to wait for semaphore acquisition. Default: undefined → timeout * max(1, limit - 1) */
|
|
28
30
|
semaphore_timeout?: number | null;
|
|
29
31
|
}
|
|
@@ -50,3 +52,4 @@ export declare class SemaphoreTimeoutError extends Error {
|
|
|
50
52
|
/** Reset the global semaphore registry. Useful in tests. */
|
|
51
53
|
export declare function clearSemaphoreRegistry(): void;
|
|
52
54
|
export declare function retry(options?: RetryOptions): <T extends (...args: any[]) => any>(target: T, _context?: ClassMethodDecoratorContext) => T;
|
|
55
|
+
export {};
|