🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@git-stunts/alfred

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@git-stunts/alfred - npm Package Compare versions

Comparing version
0.10.3
to
0.10.4
+8
-0
CHANGELOG.md

@@ -8,2 +8,10 @@ # Changelog

## [0.10.4] - 2026-06-24
### Fixed
- **Timeout cleanup**: `timeout()` now clears the default runtime timer when the
wrapped operation resolves or rejects before the deadline, while preserving
injected-clock behavior for deterministic tests.
## [0.10.3] - 2026-02-08

@@ -10,0 +18,0 @@

+1
-1
{
"name": "@git-stunts/alfred",
"version": "0.10.3",
"version": "0.10.4",
"description": "Production-grade resilience patterns for async ops: retry/backoff+jitter, circuit breaker, bulkhead, timeout.",

@@ -5,0 +5,0 @@ "license": "Apache-2.0",

{
"name": "@git-stunts/alfred",
"version": "0.10.3",
"version": "0.10.4",
"description": "Production-grade resilience patterns for async ops: retry/backoff+jitter, circuit breaker, bulkhead, timeout.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -22,2 +22,51 @@ /**

function rejectWithTimeout(context) {
const { controller, clock, isCompleted, onTimeout, reject, startTime, telemetry, timeoutMs } =
context;
if (isCompleted()) {
return;
}
controller.abort();
const elapsed = clock.now() - startTime;
notifyTimeout({ elapsed, onTimeout });
emitTimeoutTelemetry({ clock, elapsed, telemetry, timeoutMs });
reject(new TimeoutError(timeoutMs, elapsed));
}
function notifyTimeout({ elapsed, onTimeout }) {
if (onTimeout) {
try {
onTimeout(elapsed);
} catch {
// Timeout side effects must not replace the TimeoutError result.
}
}
}
function emitTimeoutTelemetry({ clock, elapsed, telemetry, timeoutMs }) {
try {
telemetry.emit({
type: 'timeout',
timestamp: clock.now(),
timeout: timeoutMs,
elapsed,
metrics: { timeouts: 1, failures: 1 },
});
} catch {
// Timeout telemetry must not replace the TimeoutError result.
}
}
function scheduleTimeout(context) {
if (context.usesInjectedClock) {
context.clock.sleep(context.timeoutMs).then(() => rejectWithTimeout(context));
return null;
}
return setTimeout(() => rejectWithTimeout(context), context.timeoutMs);
}
/**

@@ -57,3 +106,4 @@ * Executes a function with a timeout limit.

export async function timeout(ms, fn, options = {}) {
const { onTimeout, telemetry = new NoopSink(), clock = new SystemClock() } = options;
const { onTimeout, telemetry = new NoopSink() } = options;
const clock = options.clock || new SystemClock();
const timeoutMs = resolve(ms);

@@ -64,25 +114,15 @@ const controller = new AbortController();

let completed = false;
let timeoutHandle = null;
const timeoutPromise = new Promise((_, reject) => {
clock.sleep(timeoutMs).then(() => {
if (completed) {
return;
}
controller.abort();
const elapsed = clock.now() - startTime;
if (onTimeout) {
onTimeout(elapsed);
}
telemetry.emit({
type: 'timeout',
timestamp: clock.now(),
timeout: timeoutMs,
elapsed,
metrics: { timeouts: 1, failures: 1 },
});
reject(new TimeoutError(timeoutMs, elapsed));
timeoutHandle = scheduleTimeout({
controller,
clock,
isCompleted: () => completed,
onTimeout,
reject,
startTime,
telemetry,
timeoutMs,
usesInjectedClock: Boolean(options.clock),
});

@@ -96,9 +136,10 @@ });

const result = await Promise.race([operationPromise, timeoutPromise]);
return await Promise.race([operationPromise, timeoutPromise]);
} finally {
completed = true;
return result;
} catch (error) {
completed = true;
throw error;
if (timeoutHandle !== null) {
clearTimeout(timeoutHandle);
timeoutHandle = null;
}
}
}