Conversation
Standalone higher-order function / TC39 decorator that adds configurable retry logic and semaphore-based concurrency limiting to any async function. Works independently of the event bus (on plain functions, class methods, or event handlers). Features: - max_attempts, retry_after, retry_backoff_factor, retry_on_errors, timeout - Global semaphore registry (semaphore_limit, semaphore_name, semaphore_lax) - AsyncLocalStorage-based re-entrancy tracking to prevent deadlocks when nested/recursive calls share the same semaphore - 30 tests covering retry logic, backoff, error filtering, timeouts, semaphore concurrency, re-entrancy, and event bus integration https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
Switch from a separate node:async_hooks import to the existing createAsyncLocalStorage() factory from async_context.ts. This ensures browser compatibility by gracefully degrading to a no-op when AsyncLocalStorage is unavailable. https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
retry_on_errors now accepts a mix of: - Error class constructors (instanceof check) - String error names (matched against error.name) - RegExp patterns (tested against String(error)) https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
- 'global': all calls share one semaphore (default, existing behavior) - 'class': keyed by constructor.name — all instances of a class share one - 'instance': keyed by WeakMap identity — each object gets its own Falls back to 'global' when `this` is not an object (standalone calls). Multiprocess scope is not supported (single-process JS runtime). https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Tests verify: @Retry() works with native TC39 Stage 3 decorator syntax on class methods, preserves `this` context, composes with semaphore_scope (class/instance), works with bus.on() via .bind(), and class/instance scopes correctly fall back to global for standalone functions. https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Tests added (12 new, 51 total retry tests): - TC39 @Retry() decorator on class methods with all 3 scopes - @Retry + bus.on via .bind(this) for class/instance/global scopes - HOF retry()(fn).bind(instance) pattern (bind after wrap) - HOF retry()(fn.bind(instance)) → verifies scope falls back to global - Standalone functions with class/instance scope → fall back to global README updated: - TC39 decorator syntax examples with bus.on + .bind(this) - HOF .bind() ordering requirement documented - Note on scope fallback for standalone/unbound functions Also fixed flaky bus tests caused by handler ID collision (bus uses ms-precision timestamps in handler ID hash — added 2ms delay between same-millisecond handler registrations). https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
…ents Verifies the pattern where retry() wraps the full bus.emit→event.done() cycle so each retry dispatches a fresh event, while other events race in parallel via Promise.all. https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
… wrapping Rewrite README retry section to establish @Retry() on class methods as the primary recommended pattern. Explain why retry/timeout is a handler-level concern (handlers fail, events don't), why emit-level retry hurts replayability/determinism, and how retry semaphores are orthogonal to bus concurrency options. Mark the emit→done wrapping pattern as technically supported but not recommended, with clear rationale. Reorganize test section headers to reflect the recommended pattern hierarchy. https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Implements event.first() which returns the first non-undefined handler result value, then cancels remaining handlers (pending: cancelled, started: aborted via signalAbort, plus their child events). Works with all concurrency modes: parallel races all handlers, serial short-circuits after first success. - BaseEvent: add _first_mode, _first_result, first() method - EventBus.processEvent: monitor handler completions in first mode, cancel losers via new cancelEventHandlersForFirstMode() method - 19 tests: parallel/serial, falsy values, @Retry integration, screenshot service pattern, error handling, child event cancellation - README: document first() with examples and comparison to done() https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Remove the internal _first_result property in favor of a computed getter that reads from event_results sorted by completed_ts. This is cleaner — the result is derived from the source of truth rather than duplicated. https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
…sibility
Replaces the internal _first_mode boolean with a proper event_handler_completion
field ('all' | 'first') that is part of the Zod schema, included in toJSON(),
and visible in replay logs. The field is orthogonal to event_handler_concurrency
(scheduling vs completion strategy).
https://claude.ai/code/session_01TyuqFQFwDXa4h5QzQDCUsv
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
No description provided.