abxbus 2.5.6 → 2.5.11

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 (62) hide show
  1. package/dist/cjs/BaseEvent.d.ts +12 -32
  2. package/dist/cjs/BaseEvent.js +20 -17
  3. package/dist/cjs/BaseEvent.js.map +2 -2
  4. package/dist/cjs/LockManager.js +1 -1
  5. package/dist/cjs/LockManager.js.map +2 -2
  6. package/dist/cjs/events_suck.d.ts +7 -14
  7. package/dist/cjs/events_suck.js +1 -1
  8. package/dist/cjs/events_suck.js.map +2 -2
  9. package/dist/cjs/retry.d.ts +2 -0
  10. package/dist/cjs/retry.js +121 -38
  11. package/dist/cjs/retry.js.map +3 -3
  12. package/dist/cjs/types.d.ts +3 -6
  13. package/dist/cjs/types.js +1 -1
  14. package/dist/cjs/types.js.map +2 -2
  15. package/dist/esm/BaseEvent.js +20 -17
  16. package/dist/esm/BaseEvent.js.map +2 -2
  17. package/dist/esm/LockManager.js +1 -1
  18. package/dist/esm/LockManager.js.map +2 -2
  19. package/dist/esm/events_suck.js +1 -1
  20. package/dist/esm/events_suck.js.map +2 -2
  21. package/dist/esm/retry.js +121 -38
  22. package/dist/esm/retry.js.map +3 -3
  23. package/dist/esm/types.js +1 -1
  24. package/dist/esm/types.js.map +2 -2
  25. package/dist/types/BaseEvent.d.ts +12 -32
  26. package/dist/types/events_suck.d.ts +7 -14
  27. package/dist/types/retry.d.ts +2 -0
  28. package/dist/types/types.d.ts +3 -6
  29. package/package.json +42 -24
  30. package/src/BaseEvent.ts +93 -75
  31. package/src/LockManager.ts +1 -1
  32. package/src/events_suck.ts +17 -20
  33. package/src/retry.ts +146 -41
  34. package/src/types.ts +4 -5
  35. package/dist/cjs/base_event.d.ts +0 -211
  36. package/dist/cjs/bridge_ipc.d.ts +0 -45
  37. package/dist/cjs/bridge_jsonl.d.ts +0 -26
  38. package/dist/cjs/bridge_nats.d.ts +0 -20
  39. package/dist/cjs/bridge_postgres.d.ts +0 -31
  40. package/dist/cjs/bridge_redis.d.ts +0 -34
  41. package/dist/cjs/bridge_sqlite.d.ts +0 -30
  42. package/dist/cjs/event_bus.d.ts +0 -125
  43. package/dist/cjs/event_handler.d.ts +0 -140
  44. package/dist/cjs/event_history.d.ts +0 -45
  45. package/dist/cjs/event_result.d.ts +0 -86
  46. package/dist/cjs/lock_manager.d.ts +0 -70
  47. package/dist/cjs/middleware_otel_tracing.d.ts +0 -49
  48. package/dist/cjs/type_inference.test.d.ts +0 -1
  49. package/dist/types/base_event.d.ts +0 -211
  50. package/dist/types/bridge_ipc.d.ts +0 -45
  51. package/dist/types/bridge_jsonl.d.ts +0 -26
  52. package/dist/types/bridge_nats.d.ts +0 -20
  53. package/dist/types/bridge_postgres.d.ts +0 -31
  54. package/dist/types/bridge_redis.d.ts +0 -34
  55. package/dist/types/bridge_sqlite.d.ts +0 -30
  56. package/dist/types/event_bus.d.ts +0 -125
  57. package/dist/types/event_handler.d.ts +0 -140
  58. package/dist/types/event_history.d.ts +0 -45
  59. package/dist/types/event_result.d.ts +0 -86
  60. package/dist/types/lock_manager.d.ts +0 -70
  61. package/dist/types/middleware_otel_tracing.d.ts +0 -49
  62. package/dist/types/type_inference.test.d.ts +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/LockManager.ts"],
4
- "sourcesContent": ["import type { BaseEvent } from './BaseEvent.js'\nimport type { EventResult } from './EventResult.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 = this._getRawActiveHandlerResultForCurrentAsyncContext()\n return result?.status === 'started' ? result : undefined\n }\n\n _getRawActiveHandlerResultForCurrentAsyncContext(): EventResult | undefined {\n return handler_context_storage?.getStore() as EventResult | 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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",
4
+ "sourcesContent": ["import type { BaseEvent } from './BaseEvent.js'\nimport type { EventResult } from './EventResult.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 = this._getRawActiveHandlerResultForCurrentAsyncContext()\n return result?.status === 'started' ? result : undefined\n }\n\n _getRawActiveHandlerResultForCurrentAsyncContext(): EventResult | undefined {\n return handler_context_storage?.getStore() as EventResult | 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 || timeout_seconds <= 0) {\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
  }
@@ -22,7 +22,7 @@ const wrap = (class_name, methods) => {
22
22
  Object.defineProperty(WrappedClient.prototype, method_name, {
23
23
  value: async function(init, extra) {
24
24
  const payload = { ...init ?? {}, ...extra ?? {} };
25
- return await this.bus.emit(new EventCtor(payload)).now({ first_result: true }).eventResult();
25
+ return await this.bus.emit(EventCtor(payload)).now({ first_result: true }).eventResult();
26
26
  },
27
27
  writable: true,
28
28
  configurable: true
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/events_suck.ts"],
4
- "sourcesContent": ["import { EventBus } from './EventBus.js'\nimport { BaseEvent } from './BaseEvent.js'\n\nimport type { EventClass, EventResultType } from './types.js'\n\ntype EventMap = Record<string, EventClass<BaseEvent>>\ntype AnyFn = (...args: any[]) => any\ntype FunctionMap = Record<string, AnyFn>\ntype ExtraDict = Record<string, unknown>\n\ntype EventFieldsFromFn<TFunc extends AnyFn> =\n Parameters<TFunc> extends [infer TArg] ? (TArg extends Record<string, unknown> ? TArg : ExtraDict) : ExtraDict\n\ntype GeneratedEvent<TFunc extends AnyFn> = {\n (\n data: EventFieldsFromFn<TFunc> & ExtraDict\n ): BaseEvent & EventFieldsFromFn<TFunc> & { __event_result_type__?: Awaited<ReturnType<TFunc>> }\n new (\n data: EventFieldsFromFn<TFunc> & ExtraDict\n ): BaseEvent & EventFieldsFromFn<TFunc> & { __event_result_type__?: Awaited<ReturnType<TFunc>> }\n event_type?: string\n}\n\nexport type GeneratedEvents<TEvents extends FunctionMap> = {\n by_name: { [K in keyof TEvents]: GeneratedEvent<TEvents[K]> }\n} & {\n [K in keyof TEvents]: GeneratedEvent<TEvents[K]>\n}\n\ntype EventInit<TEventClass extends EventClass<BaseEvent>> = [ConstructorParameters<TEventClass>[0]] extends [undefined]\n ? {}\n : NonNullable<ConstructorParameters<TEventClass>[0]>\n\ntype EventMethodArgs<TEventClass extends EventClass<BaseEvent>> =\n {} extends EventInit<TEventClass>\n ? [init?: EventInit<TEventClass>, extra?: Record<string, unknown>]\n : [init: EventInit<TEventClass>, extra?: Record<string, unknown>]\n\ntype EventMethodResult<TEventClass extends EventClass<BaseEvent>> = EventResultType<InstanceType<TEventClass>> | undefined\n\nexport type EventsSuckClient<TEvents extends EventMap> = {\n bus: EventBus\n} & {\n [K in keyof TEvents]: (...args: EventMethodArgs<TEvents[K]>) => Promise<EventMethodResult<TEvents[K]>>\n}\n\nexport type EventsSuckClientClass<TEvents extends EventMap> = new (bus?: EventBus) => EventsSuckClient<TEvents>\n\ntype DynamicWrappedClient = {\n bus: EventBus\n} & Record<string, (...args: unknown[]) => Promise<unknown>>\n\nexport const make_events = <TEvents extends FunctionMap>(events: TEvents): GeneratedEvents<TEvents> => {\n const by_name = {} as { [K in keyof TEvents]: GeneratedEvent<TEvents[K]> }\n for (const [event_name] of Object.entries(events) as Array<[keyof TEvents, TEvents[keyof TEvents]]>) {\n if (!/^[A-Za-z_$][\\w$]*$/.test(String(event_name))) {\n throw new Error(`Invalid event name: ${String(event_name)}`)\n }\n by_name[event_name] = BaseEvent.extend(String(event_name), {}) as unknown as GeneratedEvent<TEvents[keyof TEvents]>\n }\n return Object.assign({ by_name }, by_name) as GeneratedEvents<TEvents>\n}\n\nexport const wrap = <TEvents extends EventMap>(class_name: string, methods: TEvents): EventsSuckClientClass<TEvents> => {\n class WrappedClient {\n bus: EventBus\n\n constructor(bus?: EventBus) {\n this.bus = bus ?? new EventBus(`${class_name}Bus`)\n }\n }\n\n Object.defineProperty(WrappedClient, 'name', { value: class_name })\n\n for (const [method_name, EventCtor] of Object.entries(methods)) {\n Object.defineProperty(WrappedClient.prototype, method_name, {\n value: async function (this: DynamicWrappedClient, init?: Record<string, unknown>, extra?: Record<string, unknown>) {\n const payload = { ...(init ?? {}), ...(extra ?? {}) }\n return await this.bus.emit(new EventCtor(payload)).now({ first_result: true }).eventResult()\n },\n writable: true,\n configurable: true,\n })\n }\n\n return WrappedClient as unknown as EventsSuckClientClass<TEvents>\n}\n\n// Intentionally no make_event()/make_handler() helpers in TypeScript.\n// Prefer the explicit inline pattern:\n// const FooCreateEvent = BaseEvent.extend('FooCreateEvent', {\n// id: z.string().nullable().optional(),\n// name: z.string(),\n// age: z.number(),\n// })\n// bus.on(FooCreateEvent, ({ id, name, age, ...extra }) => impl.create(id, { name, age }))\nexport const events_suck = { make_events, wrap } as const\n"],
5
- "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAmDnB,MAAM,cAAc,CAA8B,WAA8C;AACrG,QAAM,UAAU,CAAC;AACjB,aAAW,CAAC,UAAU,KAAK,OAAO,QAAQ,MAAM,GAAqD;AACnG,QAAI,CAAC,qBAAqB,KAAK,OAAO,UAAU,CAAC,GAAG;AAClD,YAAM,IAAI,MAAM,uBAAuB,OAAO,UAAU,CAAC,EAAE;AAAA,IAC7D;AACA,YAAQ,UAAU,IAAI,UAAU,OAAO,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,EAC/D;AACA,SAAO,OAAO,OAAO,EAAE,QAAQ,GAAG,OAAO;AAC3C;AAEO,MAAM,OAAO,CAA2B,YAAoB,YAAqD;AAAA,EACtH,MAAM,cAAc;AAAA,IAClB;AAAA,IAEA,YAAY,KAAgB;AAC1B,WAAK,MAAM,OAAO,IAAI,SAAS,GAAG,UAAU,KAAK;AAAA,IACnD;AAAA,EACF;AAEA,SAAO,eAAe,eAAe,QAAQ,EAAE,OAAO,WAAW,CAAC;AAElE,aAAW,CAAC,aAAa,SAAS,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC9D,WAAO,eAAe,cAAc,WAAW,aAAa;AAAA,MAC1D,OAAO,eAA4C,MAAgC,OAAiC;AAClH,cAAM,UAAU,EAAE,GAAI,QAAQ,CAAC,GAAI,GAAI,SAAS,CAAC,EAAG;AACpD,eAAO,MAAM,KAAK,IAAI,KAAK,IAAI,UAAU,OAAO,CAAC,EAAE,IAAI,EAAE,cAAc,KAAK,CAAC,EAAE,YAAY;AAAA,MAC7F;AAAA,MACA,UAAU;AAAA,MACV,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAUO,MAAM,cAAc,EAAE,aAAa,KAAK;",
4
+ "sourcesContent": ["import { EventBus } from './EventBus.js'\nimport { BaseEvent } from './BaseEvent.js'\n\nimport type { EventClass, EventResultType } from './types.js'\n\ntype EventMap = Record<string, EventClass<BaseEvent, never>>\ntype FunctionMap = Record<string, (...args: never[]) => unknown>\ntype ExtraDict = Record<string, unknown>\n\ntype EventFieldsFromFn<TFunc extends FunctionMap[string]> =\n Parameters<TFunc> extends [infer TArg, ...unknown[]] ? (TArg extends Record<string, unknown> ? TArg : ExtraDict) : ExtraDict\n\ntype EventFromFn<TFunc extends FunctionMap[string]> = BaseEvent &\n EventFieldsFromFn<TFunc> & {\n __event_result_type__?: Awaited<ReturnType<TFunc>>\n }\n\nexport type GeneratedEvents<TEvents extends FunctionMap> = {\n by_name: { [K in keyof TEvents]: EventClass<EventFromFn<TEvents[K]>, EventFieldsFromFn<TEvents[K]> & ExtraDict> }\n} & {\n [K in keyof TEvents]: EventClass<EventFromFn<TEvents[K]>, EventFieldsFromFn<TEvents[K]> & ExtraDict>\n}\n\ntype EventInit<TEventClass extends EventClass<BaseEvent>> = [ConstructorParameters<TEventClass>[0]] extends [undefined]\n ? {}\n : NonNullable<ConstructorParameters<TEventClass>[0]>\n\ntype EventMethodArgs<TEventClass extends EventClass<BaseEvent>> =\n {} extends EventInit<TEventClass>\n ? [init?: EventInit<TEventClass>, extra?: Record<string, unknown>]\n : [init: EventInit<TEventClass>, extra?: Record<string, unknown>]\n\ntype EventMethodResult<TEventClass extends EventClass<BaseEvent>> = EventResultType<InstanceType<TEventClass>> | undefined\n\nexport type EventsSuckClient<TEvents extends EventMap> = {\n bus: EventBus\n} & {\n [K in keyof TEvents]: (...args: EventMethodArgs<TEvents[K]>) => Promise<EventMethodResult<TEvents[K]>>\n}\n\nexport type EventsSuckClientClass<TEvents extends EventMap> = new (bus?: EventBus) => EventsSuckClient<TEvents>\n\ntype DynamicWrappedClient = {\n bus: EventBus\n} & Record<string, (...args: unknown[]) => Promise<unknown>>\n\nexport const make_events = <TEvents extends FunctionMap>(events: TEvents): GeneratedEvents<TEvents> => {\n const by_name = {} as GeneratedEvents<TEvents>['by_name']\n for (const [event_name] of Object.entries(events) as Array<[keyof TEvents, TEvents[keyof TEvents]]>) {\n if (!/^[A-Za-z_$][\\w$]*$/.test(String(event_name))) {\n throw new Error(`Invalid event name: ${String(event_name)}`)\n }\n by_name[event_name] = BaseEvent.extend(String(event_name), {}) as unknown as GeneratedEvents<TEvents>['by_name'][typeof event_name]\n }\n return Object.assign({ by_name }, by_name) as GeneratedEvents<TEvents>\n}\n\nexport const wrap = <TEvents extends EventMap>(class_name: string, methods: TEvents): EventsSuckClientClass<TEvents> => {\n class WrappedClient {\n bus: EventBus\n\n constructor(bus?: EventBus) {\n this.bus = bus ?? new EventBus(`${class_name}Bus`)\n }\n }\n\n Object.defineProperty(WrappedClient, 'name', { value: class_name })\n\n for (const [method_name, EventCtor] of Object.entries(methods)) {\n Object.defineProperty(WrappedClient.prototype, method_name, {\n value: async function (this: DynamicWrappedClient, init?: Record<string, unknown>, extra?: Record<string, unknown>) {\n const payload = { ...(init ?? {}), ...(extra ?? {}) }\n return await this.bus\n .emit((EventCtor as EventClass<BaseEvent, Record<string, unknown>>)(payload))\n .now({ first_result: true })\n .eventResult()\n },\n writable: true,\n configurable: true,\n })\n }\n\n return WrappedClient as unknown as EventsSuckClientClass<TEvents>\n}\n\n// Intentionally no make_event()/make_handler() helpers in TypeScript.\n// Prefer the explicit inline pattern:\n// const FooCreateEvent = BaseEvent.extend('FooCreateEvent', {\n// id: z.string().nullable().optional(),\n// name: z.string(),\n// age: z.number(),\n// })\n// bus.on(FooCreateEvent, ({ id, name, age, ...extra }) => impl.create(id, { name, age }))\nexport const events_suck = { make_events, wrap } as const\n"],
5
+ "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AA6CnB,MAAM,cAAc,CAA8B,WAA8C;AACrG,QAAM,UAAU,CAAC;AACjB,aAAW,CAAC,UAAU,KAAK,OAAO,QAAQ,MAAM,GAAqD;AACnG,QAAI,CAAC,qBAAqB,KAAK,OAAO,UAAU,CAAC,GAAG;AAClD,YAAM,IAAI,MAAM,uBAAuB,OAAO,UAAU,CAAC,EAAE;AAAA,IAC7D;AACA,YAAQ,UAAU,IAAI,UAAU,OAAO,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,EAC/D;AACA,SAAO,OAAO,OAAO,EAAE,QAAQ,GAAG,OAAO;AAC3C;AAEO,MAAM,OAAO,CAA2B,YAAoB,YAAqD;AAAA,EACtH,MAAM,cAAc;AAAA,IAClB;AAAA,IAEA,YAAY,KAAgB;AAC1B,WAAK,MAAM,OAAO,IAAI,SAAS,GAAG,UAAU,KAAK;AAAA,IACnD;AAAA,EACF;AAEA,SAAO,eAAe,eAAe,QAAQ,EAAE,OAAO,WAAW,CAAC;AAElE,aAAW,CAAC,aAAa,SAAS,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC9D,WAAO,eAAe,cAAc,WAAW,aAAa;AAAA,MAC1D,OAAO,eAA4C,MAAgC,OAAiC;AAClH,cAAM,UAAU,EAAE,GAAI,QAAQ,CAAC,GAAI,GAAI,SAAS,CAAC,EAAG;AACpD,eAAO,MAAM,KAAK,IACf,KAAM,UAA6D,OAAO,CAAC,EAC3E,IAAI,EAAE,cAAc,KAAK,CAAC,EAC1B,YAAY;AAAA,MACjB;AAAA,MACA,UAAU;AAAA,MACV,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAUO,MAAM,cAAc,EAAE,aAAa,KAAK;",
6
6
  "names": []
7
7
  }
package/dist/esm/retry.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { createAsyncLocalStorage } from "./async_context.js";
2
2
  import { isNodeRuntime } from "./optional_deps.js";
3
- const MULTIPROCESS_SEMAPHORE_DIRNAME = "browser_use_semaphores";
4
3
  const MULTIPROCESS_STALE_LOCK_MS = 5 * 60 * 1e3;
4
+ const RETRY_SLOW_WARNING_THROTTLE_MS = 2e3;
5
+ const RETRY_SLOW_WARNING_ARGS_MAX_LENGTH = 80;
5
6
  let multiprocess_fallback_reason_logged = null;
6
7
  class RetryTimeoutError extends Error {
7
8
  timeout_seconds;
@@ -107,16 +108,20 @@ function retry(options = {}) {
107
108
  retry_backoff_factor = 1,
108
109
  retry_on_errors,
109
110
  timeout,
111
+ slow_timeout,
110
112
  semaphore_limit,
111
113
  semaphore_name: semaphore_name_option,
112
114
  semaphore_lax = true,
113
115
  semaphore_scope = "global",
114
116
  semaphore_timeout
115
117
  } = options;
116
- const decorateFunction = (target, _context) => {
117
- const fn_name = target.name || _context?.name || "anonymous";
118
+ const decorateFunction = (target, _context, owner_name) => {
119
+ const base_fn_name = target.name || _context?.name || "anonymous";
120
+ let fn_name = owner_name ? `${owner_name}.${base_fn_name}` : base_fn_name;
118
121
  const effective_max_attempts = Math.max(1, max_attempts);
119
122
  const effective_retry_after = Math.max(0, retry_after);
123
+ const effective_slow_timeout_ms = slow_timeout != null && slow_timeout > 0 ? slow_timeout * 1e3 : null;
124
+ let last_slow_warning_at = 0;
120
125
  const shouldRetry = (error) => {
121
126
  if (!retry_on_errors || retry_on_errors.length === 0) return true;
122
127
  return retry_on_errors.some(
@@ -135,6 +140,13 @@ function retry(options = {}) {
135
140
  sleepSync(delay_seconds * 1e3);
136
141
  }
137
142
  };
143
+ const emitSlowWarningIfDue = (args, start_time) => {
144
+ if (effective_slow_timeout_ms == null) return;
145
+ const now = Date.now();
146
+ if (now - last_slow_warning_at < RETRY_SLOW_WARNING_THROTTLE_MS) return;
147
+ last_slow_warning_at = now;
148
+ console.warn(`Warning: ${fn_name}(${formatRetrySlowWarningArgs(args)}) slow (${((now - start_time) / 1e3).toFixed(1)}s)`);
149
+ };
138
150
  const runRetryLoopFromThenable = async (this_arg, args, first_thenable, first_attempt) => {
139
151
  let current_result = first_thenable;
140
152
  for (let attempt = first_attempt; attempt <= effective_max_attempts; attempt++) {
@@ -214,20 +226,35 @@ function retry(options = {}) {
214
226
  new_held.add(scoped_key);
215
227
  }
216
228
  const runRetryLoop = async () => {
217
- for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
218
- try {
219
- if (timeout != null && timeout > 0) {
220
- return await _runWithTimeout(() => Promise.resolve(target.apply(this, args)), timeout * 1e3, attempt);
221
- } else {
222
- return await Promise.resolve(target.apply(this, args));
229
+ const call_started_at = Date.now();
230
+ const warning_args = [...args];
231
+ const slow_warning_timer = effective_slow_timeout_ms == null ? null : setTimeout(() => emitSlowWarningIfDue(warning_args, call_started_at), effective_slow_timeout_ms);
232
+ const finishSlowWarning = () => {
233
+ if (slow_warning_timer !== null) {
234
+ clearTimeout(slow_warning_timer);
235
+ }
236
+ if (effective_slow_timeout_ms != null && Date.now() - call_started_at >= effective_slow_timeout_ms) {
237
+ emitSlowWarningIfDue(warning_args, call_started_at);
238
+ }
239
+ };
240
+ try {
241
+ for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
242
+ try {
243
+ if (timeout != null && timeout > 0) {
244
+ return await _runWithTimeout(() => Promise.resolve(target.apply(this, args)), timeout * 1e3, attempt);
245
+ } else {
246
+ return await Promise.resolve(target.apply(this, args));
247
+ }
248
+ } catch (error) {
249
+ if (!shouldRetry(error)) throw error;
250
+ if (attempt >= effective_max_attempts) throw error;
251
+ await asyncRetryDelay(attempt);
223
252
  }
224
- } catch (error) {
225
- if (!shouldRetry(error)) throw error;
226
- if (attempt >= effective_max_attempts) throw error;
227
- await asyncRetryDelay(attempt);
228
253
  }
254
+ throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
255
+ } finally {
256
+ finishSlowWarning();
229
257
  }
230
- throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
231
258
  };
232
259
  try {
233
260
  return await runWithHeldSemaphores(new_held, runRetryLoop);
@@ -277,27 +304,48 @@ function retry(options = {}) {
277
304
  }
278
305
  };
279
306
  const runRetryLoop = () => {
280
- for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
281
- const attempt_started_at = Date.now();
282
- try {
283
- const result = target.apply(this, args);
284
- if (isThenable(result)) {
285
- return runRetryLoopFromThenable(this, args, result, attempt);
286
- }
287
- if (timeout != null && timeout > 0 && Date.now() - attempt_started_at > timeout * 1e3) {
288
- throw new RetryTimeoutError(`Timed out after ${timeout}s (attempt ${attempt})`, {
289
- timeout_seconds: timeout,
290
- attempt
291
- });
307
+ const call_started_at = Date.now();
308
+ const warning_args = [...args];
309
+ const slow_warning_timer = effective_slow_timeout_ms == null ? null : setTimeout(() => emitSlowWarningIfDue(warning_args, call_started_at), effective_slow_timeout_ms);
310
+ const finishSlowWarning = () => {
311
+ if (slow_warning_timer !== null) {
312
+ clearTimeout(slow_warning_timer);
313
+ }
314
+ if (effective_slow_timeout_ms != null && Date.now() - call_started_at >= effective_slow_timeout_ms) {
315
+ emitSlowWarningIfDue(warning_args, call_started_at);
316
+ }
317
+ };
318
+ let finish_on_return = true;
319
+ try {
320
+ for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
321
+ const attempt_started_at = Date.now();
322
+ try {
323
+ const result = target.apply(this, args);
324
+ if (isThenable(result)) {
325
+ finish_on_return = false;
326
+ return runRetryLoopFromThenable(this, args, result, attempt).finally(finishSlowWarning);
327
+ }
328
+ if (timeout != null && timeout > 0 && Date.now() - attempt_started_at > timeout * 1e3) {
329
+ throw new RetryTimeoutError(`Timed out after ${timeout}s (attempt ${attempt})`, {
330
+ timeout_seconds: timeout,
331
+ attempt
332
+ });
333
+ }
334
+ return result;
335
+ } catch (error) {
336
+ if (!shouldRetry(error)) throw error;
337
+ if (attempt >= effective_max_attempts) {
338
+ throw error;
339
+ }
340
+ syncRetryDelay(attempt);
292
341
  }
293
- return result;
294
- } catch (error) {
295
- if (!shouldRetry(error)) throw error;
296
- if (attempt >= effective_max_attempts) throw error;
297
- syncRetryDelay(attempt);
342
+ }
343
+ throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
344
+ } finally {
345
+ if (finish_on_return) {
346
+ finishSlowWarning();
298
347
  }
299
348
  }
300
- throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
301
349
  };
302
350
  try {
303
351
  const result = runWithHeldSemaphores(new_held, runRetryLoop);
@@ -315,9 +363,10 @@ function retry(options = {}) {
315
363
  Object.defineProperty(retryWrapper, "name", { value: fn_name, configurable: true });
316
364
  if (_context?.kind === "method" && typeof _context.addInitializer === "function") {
317
365
  _context.addInitializer(function() {
318
- const owner_name = findDecoratedMethodOwnerName(this, _context, retryWrapper);
319
- if (owner_name) {
320
- Object.defineProperty(retryWrapper, "name", { value: `${owner_name}.${fn_name}`, configurable: true });
366
+ const owner_name2 = findDecoratedMethodOwnerName(this, _context, retryWrapper);
367
+ if (owner_name2) {
368
+ fn_name = `${owner_name2}.${target.name || _context.name || "anonymous"}`;
369
+ Object.defineProperty(retryWrapper, "name", { value: fn_name, configurable: true });
321
370
  }
322
371
  });
323
372
  }
@@ -325,7 +374,8 @@ function retry(options = {}) {
325
374
  };
326
375
  function decorator(target, context_or_property_key, descriptor) {
327
376
  if (descriptor?.value && typeof descriptor.value === "function") {
328
- descriptor.value = decorateFunction(descriptor.value);
377
+ const owner_name = target && (typeof target === "object" || typeof target === "function") ? typeof target === "function" ? target.name : target.constructor?.name : null;
378
+ descriptor.value = decorateFunction(descriptor.value, void 0, owner_name);
329
379
  return descriptor;
330
380
  }
331
381
  if (typeof target === "function") {
@@ -372,6 +422,30 @@ function isAsyncFunction(fn) {
372
422
  function isThenable(value) {
373
423
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
374
424
  }
425
+ function formatRetrySlowWarningArgs(args) {
426
+ const preview = args.map(formatRetrySlowWarningValue).join(", ");
427
+ if (preview.length > RETRY_SLOW_WARNING_ARGS_MAX_LENGTH) {
428
+ return `${preview.slice(0, RETRY_SLOW_WARNING_ARGS_MAX_LENGTH - 3).replace(/,?\s*$/, "")}...`;
429
+ }
430
+ return preview;
431
+ }
432
+ function formatRetrySlowWarningValue(value) {
433
+ let text;
434
+ if (typeof value === "string") {
435
+ text = value;
436
+ } else if (value === null || value === void 0) {
437
+ text = String(value);
438
+ } else if (typeof value === "object") {
439
+ try {
440
+ text = JSON.stringify(value);
441
+ } catch {
442
+ text = String(value);
443
+ }
444
+ } else {
445
+ text = String(value);
446
+ }
447
+ return text.replace(/['"]/g, "").slice(0, 3);
448
+ }
375
449
  async function acquireWithTimeout(semaphore, timeout_ms) {
376
450
  return new Promise((resolve) => {
377
451
  let settled = false;
@@ -441,6 +515,15 @@ async function importNodeModule(specifier) {
441
515
  const dynamic_import = Function("module_name", "return import(module_name)");
442
516
  return dynamic_import(specifier);
443
517
  }
518
+ function resolveMultiprocessSemaphoreDirectory(path, os) {
519
+ const configured = process.env.ABXBUS_MULTIPROCESS_SEMAPHORE_DIR;
520
+ if (configured) return path.resolve(configured.replace(/^~(?=$|[/\\])/, os.homedir()));
521
+ const runtime_dir = process.env.XDG_RUNTIME_DIR;
522
+ if (runtime_dir) return path.join(path.resolve(runtime_dir.replace(/^~(?=$|[/\\])/, os.homedir())), "abxbus", "semaphores");
523
+ const cache_home = process.env.XDG_CACHE_HOME;
524
+ const cache_dir = cache_home ? path.resolve(cache_home.replace(/^~(?=$|[/\\])/, os.homedir())) : path.join(os.homedir(), ".cache");
525
+ return path.join(cache_dir, "abxbus", "semaphores");
526
+ }
444
527
  async function acquireMultiprocessSemaphore(scoped_key, semaphore_limit, semaphore_timeout_seconds, semaphore_lax) {
445
528
  const [crypto, fs, os, path] = await Promise.all([
446
529
  importNodeModule("node:crypto"),
@@ -448,7 +531,7 @@ async function acquireMultiprocessSemaphore(scoped_key, semaphore_limit, semapho
448
531
  importNodeModule("node:os"),
449
532
  importNodeModule("node:path")
450
533
  ]);
451
- const semaphore_directory = path.join(os.tmpdir(), MULTIPROCESS_SEMAPHORE_DIRNAME);
534
+ const semaphore_directory = resolveMultiprocessSemaphoreDirectory(path, os);
452
535
  const lock_prefix = crypto.createHash("sha256").update(scoped_key).digest("hex").slice(0, 40);
453
536
  fs.mkdirSync(semaphore_directory, { recursive: true });
454
537
  const start = Date.now();
@@ -532,7 +615,7 @@ function acquireMultiprocessSemaphoreSync(scoped_key, semaphore_limit, semaphore
532
615
  const fs = importNodeModuleSync("node:fs");
533
616
  const os = importNodeModuleSync("node:os");
534
617
  const path = importNodeModuleSync("node:path");
535
- const semaphore_directory = path.join(os.tmpdir(), MULTIPROCESS_SEMAPHORE_DIRNAME);
618
+ const semaphore_directory = resolveMultiprocessSemaphoreDirectory(path, os);
536
619
  const lock_prefix = crypto.createHash("sha256").update(scoped_key).digest("hex").slice(0, 40);
537
620
  fs.mkdirSync(semaphore_directory, { recursive: true });
538
621
  const start = Date.now();