flowx-control 1.0.0
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 +443 -0
- package/dist/batch.d.mts +37 -0
- package/dist/batch.d.ts +37 -0
- package/dist/batch.js +75 -0
- package/dist/batch.js.map +1 -0
- package/dist/batch.mjs +73 -0
- package/dist/batch.mjs.map +1 -0
- package/dist/bulkhead.d.mts +30 -0
- package/dist/bulkhead.d.ts +30 -0
- package/dist/bulkhead.js +85 -0
- package/dist/bulkhead.js.map +1 -0
- package/dist/bulkhead.mjs +83 -0
- package/dist/bulkhead.mjs.map +1 -0
- package/dist/circuit-breaker.d.mts +44 -0
- package/dist/circuit-breaker.d.ts +44 -0
- package/dist/circuit-breaker.js +132 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/circuit-breaker.mjs +130 -0
- package/dist/circuit-breaker.mjs.map +1 -0
- package/dist/debounce.d.mts +30 -0
- package/dist/debounce.d.ts +30 -0
- package/dist/debounce.js +96 -0
- package/dist/debounce.js.map +1 -0
- package/dist/debounce.mjs +94 -0
- package/dist/debounce.mjs.map +1 -0
- package/dist/deferred.d.mts +27 -0
- package/dist/deferred.d.ts +27 -0
- package/dist/deferred.js +42 -0
- package/dist/deferred.js.map +1 -0
- package/dist/deferred.mjs +40 -0
- package/dist/deferred.mjs.map +1 -0
- package/dist/fallback.d.mts +33 -0
- package/dist/fallback.d.ts +33 -0
- package/dist/fallback.js +43 -0
- package/dist/fallback.js.map +1 -0
- package/dist/fallback.mjs +40 -0
- package/dist/fallback.mjs.map +1 -0
- package/dist/hedge.d.mts +18 -0
- package/dist/hedge.d.ts +18 -0
- package/dist/hedge.js +47 -0
- package/dist/hedge.js.map +1 -0
- package/dist/hedge.mjs +45 -0
- package/dist/hedge.mjs.map +1 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1151 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1122 -0
- package/dist/index.mjs.map +1 -0
- package/dist/memo.d.mts +35 -0
- package/dist/memo.d.ts +35 -0
- package/dist/memo.js +74 -0
- package/dist/memo.js.map +1 -0
- package/dist/memo.mjs +72 -0
- package/dist/memo.mjs.map +1 -0
- package/dist/mutex.d.mts +24 -0
- package/dist/mutex.d.ts +24 -0
- package/dist/mutex.js +46 -0
- package/dist/mutex.js.map +1 -0
- package/dist/mutex.mjs +44 -0
- package/dist/mutex.mjs.map +1 -0
- package/dist/pipeline.d.mts +42 -0
- package/dist/pipeline.d.ts +42 -0
- package/dist/pipeline.js +30 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/pipeline.mjs +27 -0
- package/dist/pipeline.mjs.map +1 -0
- package/dist/poll.d.mts +35 -0
- package/dist/poll.d.ts +35 -0
- package/dist/poll.js +111 -0
- package/dist/poll.js.map +1 -0
- package/dist/poll.mjs +109 -0
- package/dist/poll.mjs.map +1 -0
- package/dist/queue.d.mts +47 -0
- package/dist/queue.d.ts +47 -0
- package/dist/queue.js +121 -0
- package/dist/queue.js.map +1 -0
- package/dist/queue.mjs +119 -0
- package/dist/queue.mjs.map +1 -0
- package/dist/rate-limit.d.mts +28 -0
- package/dist/rate-limit.d.ts +28 -0
- package/dist/rate-limit.js +94 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/rate-limit.mjs +92 -0
- package/dist/rate-limit.mjs.map +1 -0
- package/dist/retry.d.mts +45 -0
- package/dist/retry.d.ts +45 -0
- package/dist/retry.js +111 -0
- package/dist/retry.js.map +1 -0
- package/dist/retry.mjs +108 -0
- package/dist/retry.mjs.map +1 -0
- package/dist/semaphore.d.mts +27 -0
- package/dist/semaphore.d.ts +27 -0
- package/dist/semaphore.js +47 -0
- package/dist/semaphore.js.map +1 -0
- package/dist/semaphore.mjs +45 -0
- package/dist/semaphore.mjs.map +1 -0
- package/dist/throttle.d.mts +25 -0
- package/dist/throttle.d.ts +25 -0
- package/dist/throttle.js +97 -0
- package/dist/throttle.js.map +1 -0
- package/dist/throttle.mjs +95 -0
- package/dist/throttle.mjs.map +1 -0
- package/dist/timeout.d.mts +19 -0
- package/dist/timeout.d.ts +19 -0
- package/dist/timeout.js +79 -0
- package/dist/timeout.js.map +1 -0
- package/dist/timeout.mjs +77 -0
- package/dist/timeout.mjs.map +1 -0
- package/dist/types-BsCO2J40.d.mts +35 -0
- package/dist/types-BsCO2J40.d.ts +35 -0
- package/package.json +167 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var FlowXError = class extends Error {
|
|
3
|
+
constructor(message, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "FlowXError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var CircuitBreakerError = class extends FlowXError {
|
|
11
|
+
constructor(message = "Circuit breaker is open") {
|
|
12
|
+
super(message, "ERR_CIRCUIT_OPEN");
|
|
13
|
+
this.name = "CircuitBreakerError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/circuit-breaker.ts
|
|
18
|
+
function createCircuitBreaker(fn, options) {
|
|
19
|
+
const {
|
|
20
|
+
failureThreshold = 5,
|
|
21
|
+
resetTimeout = 3e4,
|
|
22
|
+
halfOpenLimit = 1,
|
|
23
|
+
shouldTrip,
|
|
24
|
+
onStateChange,
|
|
25
|
+
successThreshold = 1
|
|
26
|
+
} = options ?? {};
|
|
27
|
+
let state = "closed";
|
|
28
|
+
let failureCount = 0;
|
|
29
|
+
let successCount = 0;
|
|
30
|
+
let halfOpenActive = 0;
|
|
31
|
+
let resetTimer = null;
|
|
32
|
+
function transition(to) {
|
|
33
|
+
if (state === to) return;
|
|
34
|
+
const from = state;
|
|
35
|
+
state = to;
|
|
36
|
+
onStateChange?.(from, to);
|
|
37
|
+
}
|
|
38
|
+
function scheduleReset() {
|
|
39
|
+
if (resetTimer) clearTimeout(resetTimer);
|
|
40
|
+
resetTimer = setTimeout(() => {
|
|
41
|
+
resetTimer = null;
|
|
42
|
+
transition("half-open");
|
|
43
|
+
halfOpenActive = 0;
|
|
44
|
+
successCount = 0;
|
|
45
|
+
}, resetTimeout);
|
|
46
|
+
}
|
|
47
|
+
function reset() {
|
|
48
|
+
if (resetTimer) {
|
|
49
|
+
clearTimeout(resetTimer);
|
|
50
|
+
resetTimer = null;
|
|
51
|
+
}
|
|
52
|
+
failureCount = 0;
|
|
53
|
+
successCount = 0;
|
|
54
|
+
halfOpenActive = 0;
|
|
55
|
+
transition("closed");
|
|
56
|
+
}
|
|
57
|
+
function manualOpen() {
|
|
58
|
+
if (resetTimer) {
|
|
59
|
+
clearTimeout(resetTimer);
|
|
60
|
+
resetTimer = null;
|
|
61
|
+
}
|
|
62
|
+
failureCount = 0;
|
|
63
|
+
successCount = 0;
|
|
64
|
+
halfOpenActive = 0;
|
|
65
|
+
transition("open");
|
|
66
|
+
scheduleReset();
|
|
67
|
+
}
|
|
68
|
+
async function fire(...args) {
|
|
69
|
+
if (state === "open") {
|
|
70
|
+
throw new CircuitBreakerError();
|
|
71
|
+
}
|
|
72
|
+
if (state === "half-open" && halfOpenActive >= halfOpenLimit) {
|
|
73
|
+
throw new CircuitBreakerError("Circuit breaker is half-open \u2014 limit reached");
|
|
74
|
+
}
|
|
75
|
+
if (state === "half-open") {
|
|
76
|
+
halfOpenActive++;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const result = await fn(...args);
|
|
80
|
+
if (state === "half-open") {
|
|
81
|
+
successCount++;
|
|
82
|
+
halfOpenActive--;
|
|
83
|
+
if (successCount >= successThreshold) {
|
|
84
|
+
reset();
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
failureCount = 0;
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
92
|
+
if (shouldTrip && !shouldTrip(err)) {
|
|
93
|
+
if (state === "half-open") {
|
|
94
|
+
halfOpenActive--;
|
|
95
|
+
}
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
if (state === "half-open") {
|
|
99
|
+
halfOpenActive--;
|
|
100
|
+
transition("open");
|
|
101
|
+
scheduleReset();
|
|
102
|
+
} else {
|
|
103
|
+
failureCount++;
|
|
104
|
+
if (failureCount >= failureThreshold) {
|
|
105
|
+
transition("open");
|
|
106
|
+
scheduleReset();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
fire,
|
|
114
|
+
get state() {
|
|
115
|
+
return state;
|
|
116
|
+
},
|
|
117
|
+
get failureCount() {
|
|
118
|
+
return failureCount;
|
|
119
|
+
},
|
|
120
|
+
get successCount() {
|
|
121
|
+
return successCount;
|
|
122
|
+
},
|
|
123
|
+
reset,
|
|
124
|
+
open: manualOpen
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { createCircuitBreaker };
|
|
129
|
+
//# sourceMappingURL=circuit-breaker.mjs.map
|
|
130
|
+
//# sourceMappingURL=circuit-breaker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/circuit-breaker.ts"],"names":[],"mappings":"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","file":"circuit-breaker.mjs","sourcesContent":["// ============================================================================\n// FlowX — Types & Error Hierarchy\n// ============================================================================\n\n/** Generic async function signature */\nexport type AsyncFn<TArgs extends any[] = any[], TReturn = any> = (\n ...args: TArgs\n) => Promise<TReturn>;\n\n/** Backoff strategy for retry/poll operations */\nexport type BackoffStrategy =\n | 'fixed'\n | 'linear'\n | 'exponential'\n | ((attempt: number, delay: number) => number);\n\n// ── Error Classes ───────────────────────────────────────────────────────────\n\n/** Base error class for all FlowX errors */\nexport class FlowXError extends Error {\n public readonly code: string;\n constructor(message: string, code: string) {\n super(message);\n this.name = 'FlowXError';\n this.code = code;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a promise exceeds its timeout */\nexport class TimeoutError extends FlowXError {\n constructor(message = 'Operation timed out') {\n super(message, 'ERR_TIMEOUT');\n this.name = 'TimeoutError';\n }\n}\n\n/** Thrown when a circuit breaker is open */\nexport class CircuitBreakerError extends FlowXError {\n constructor(message = 'Circuit breaker is open') {\n super(message, 'ERR_CIRCUIT_OPEN');\n this.name = 'CircuitBreakerError';\n }\n}\n\n/** Thrown when a bulkhead rejects due to capacity */\nexport class BulkheadError extends FlowXError {\n constructor(message = 'Bulkhead capacity exceeded') {\n super(message, 'ERR_BULKHEAD_FULL');\n this.name = 'BulkheadError';\n }\n}\n\n/** Thrown when an operation is aborted */\nexport class AbortError extends FlowXError {\n constructor(message = 'Operation aborted') {\n super(message, 'ERR_ABORTED');\n this.name = 'AbortError';\n }\n}\n\n/** Thrown when rate limit is exceeded */\nexport class RateLimitError extends FlowXError {\n constructor(message = 'Rate limit exceeded') {\n super(message, 'ERR_RATE_LIMIT');\n this.name = 'RateLimitError';\n }\n}\n\n// ── Utility Helpers ─────────────────────────────────────────────────────────\n\n/** Sleep for the specified duration, respecting AbortSignal */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new AbortError());\n return;\n }\n\n let onAbort: (() => void) | undefined;\n\n const timer = setTimeout(() => {\n if (signal && onAbort) {\n signal.removeEventListener('abort', onAbort);\n }\n resolve();\n }, ms);\n\n if (signal) {\n onAbort = () => {\n clearTimeout(timer);\n reject(new AbortError());\n };\n signal.addEventListener('abort', onAbort, { once: true });\n }\n });\n}\n\n/** Calculate delay based on backoff strategy */\nexport function calculateDelay(\n attempt: number,\n baseDelay: number,\n strategy: BackoffStrategy,\n jitter: boolean | number = false,\n): number {\n let delay: number;\n\n if (typeof strategy === 'function') {\n delay = strategy(attempt, baseDelay);\n } else {\n switch (strategy) {\n case 'fixed':\n delay = baseDelay;\n break;\n case 'linear':\n delay = baseDelay * attempt;\n break;\n case 'exponential':\n delay = baseDelay * Math.pow(2, attempt - 1);\n break;\n }\n }\n\n if (jitter) {\n const factor = typeof jitter === 'number' ? jitter : 1;\n delay = delay * (1 - factor * 0.5 + Math.random() * factor);\n }\n\n return Math.max(0, Math.floor(delay));\n}\n","// ============================================================================\n// FlowX — Circuit Breaker Pattern\n// ============================================================================\nimport { CircuitBreakerError } from './types';\n\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\nexport interface CircuitBreakerOptions {\n /** Number of failures before opening the circuit (default: 5) */\n failureThreshold?: number;\n /** Time in ms before attempting half-open (default: 30000) */\n resetTimeout?: number;\n /** Max concurrent calls in half-open state (default: 1) */\n halfOpenLimit?: number;\n /** Custom predicate to decide if an error should count as a failure */\n shouldTrip?: (error: Error) => boolean;\n /** Callback fired on state changes */\n onStateChange?: (from: CircuitState, to: CircuitState) => void;\n /** Number of successes in half-open to close the circuit (default: 1) */\n successThreshold?: number;\n}\n\nexport interface CircuitBreaker<TArgs extends any[], TReturn> {\n /** Execute the protected function */\n fire: (...args: TArgs) => Promise<TReturn>;\n /** Get the current circuit state */\n readonly state: CircuitState;\n /** Get the current failure count */\n readonly failureCount: number;\n /** Get the current success count (in half-open) */\n readonly successCount: number;\n /** Manually reset the circuit to closed */\n reset: () => void;\n /** Manually open the circuit */\n open: () => void;\n}\n\n/**\n * Create a circuit breaker to protect against cascading failures.\n *\n * @example\n * ```ts\n * const breaker = createCircuitBreaker(callExternalApi, {\n * failureThreshold: 5,\n * resetTimeout: 30000,\n * });\n * const data = await breaker.fire('arg1');\n * ```\n */\nexport function createCircuitBreaker<TArgs extends any[], TReturn>(\n fn: (...args: TArgs) => Promise<TReturn>,\n options?: CircuitBreakerOptions,\n): CircuitBreaker<TArgs, TReturn> {\n const {\n failureThreshold = 5,\n resetTimeout = 30000,\n halfOpenLimit = 1,\n shouldTrip,\n onStateChange,\n successThreshold = 1,\n } = options ?? {};\n\n let state: CircuitState = 'closed';\n let failureCount = 0;\n let successCount = 0;\n let halfOpenActive = 0;\n let resetTimer: ReturnType<typeof setTimeout> | null = null;\n\n function transition(to: CircuitState): void {\n if (state === to) return;\n const from = state;\n state = to;\n onStateChange?.(from, to);\n }\n\n function scheduleReset(): void {\n /* istanbul ignore next -- defensive guard; timer always fires before reschedule in normal flow */\n if (resetTimer) clearTimeout(resetTimer);\n resetTimer = setTimeout(() => {\n resetTimer = null;\n transition('half-open');\n halfOpenActive = 0;\n successCount = 0;\n }, resetTimeout);\n }\n\n function reset(): void {\n if (resetTimer) {\n clearTimeout(resetTimer);\n resetTimer = null;\n }\n failureCount = 0;\n successCount = 0;\n halfOpenActive = 0;\n transition('closed');\n }\n\n function manualOpen(): void {\n if (resetTimer) {\n clearTimeout(resetTimer);\n resetTimer = null;\n }\n failureCount = 0;\n successCount = 0;\n halfOpenActive = 0;\n transition('open');\n scheduleReset();\n }\n\n async function fire(...args: TArgs): Promise<TReturn> {\n if (state === 'open') {\n throw new CircuitBreakerError();\n }\n\n if (state === 'half-open' && halfOpenActive >= halfOpenLimit) {\n throw new CircuitBreakerError('Circuit breaker is half-open — limit reached');\n }\n\n if (state === 'half-open') {\n halfOpenActive++;\n }\n\n try {\n const result = await fn(...args);\n\n if (state === 'half-open') {\n successCount++;\n halfOpenActive--;\n if (successCount >= successThreshold) {\n reset();\n }\n } else {\n // In closed state, reset failure count on success\n failureCount = 0;\n }\n\n return result;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (shouldTrip && !shouldTrip(err)) {\n if (state === 'half-open') {\n halfOpenActive--;\n }\n throw err;\n }\n\n if (state === 'half-open') {\n halfOpenActive--;\n transition('open');\n scheduleReset();\n } else {\n failureCount++;\n if (failureCount >= failureThreshold) {\n transition('open');\n scheduleReset();\n }\n }\n\n throw err;\n }\n }\n\n return {\n fire,\n get state() {\n return state;\n },\n get failureCount() {\n return failureCount;\n },\n get successCount() {\n return successCount;\n },\n reset,\n open: manualOpen,\n };\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
interface DebounceOptions {
|
|
2
|
+
/** Invoke on the leading edge (default: false) */
|
|
3
|
+
leading?: boolean;
|
|
4
|
+
/** Invoke on the trailing edge (default: true) */
|
|
5
|
+
trailing?: boolean;
|
|
6
|
+
/** Maximum wait time before forced invocation in ms */
|
|
7
|
+
maxWait?: number;
|
|
8
|
+
}
|
|
9
|
+
interface DebouncedFunction<TArgs extends any[], TReturn> {
|
|
10
|
+
/** Call the debounced function */
|
|
11
|
+
(...args: TArgs): Promise<TReturn>;
|
|
12
|
+
/** Cancel any pending invocation */
|
|
13
|
+
cancel: () => void;
|
|
14
|
+
/** Immediately invoke any pending call */
|
|
15
|
+
flush: () => Promise<TReturn | undefined>;
|
|
16
|
+
/** Whether there is a pending invocation */
|
|
17
|
+
readonly pending: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a debounced version of an async function.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const debouncedSearch = debounce(searchApi, 300);
|
|
25
|
+
* await debouncedSearch('query');
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
declare function debounce<TArgs extends any[], TReturn>(fn: (...args: TArgs) => TReturn | Promise<TReturn>, wait: number, options?: DebounceOptions): DebouncedFunction<TArgs, TReturn>;
|
|
29
|
+
|
|
30
|
+
export { type DebounceOptions, type DebouncedFunction, debounce };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
interface DebounceOptions {
|
|
2
|
+
/** Invoke on the leading edge (default: false) */
|
|
3
|
+
leading?: boolean;
|
|
4
|
+
/** Invoke on the trailing edge (default: true) */
|
|
5
|
+
trailing?: boolean;
|
|
6
|
+
/** Maximum wait time before forced invocation in ms */
|
|
7
|
+
maxWait?: number;
|
|
8
|
+
}
|
|
9
|
+
interface DebouncedFunction<TArgs extends any[], TReturn> {
|
|
10
|
+
/** Call the debounced function */
|
|
11
|
+
(...args: TArgs): Promise<TReturn>;
|
|
12
|
+
/** Cancel any pending invocation */
|
|
13
|
+
cancel: () => void;
|
|
14
|
+
/** Immediately invoke any pending call */
|
|
15
|
+
flush: () => Promise<TReturn | undefined>;
|
|
16
|
+
/** Whether there is a pending invocation */
|
|
17
|
+
readonly pending: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a debounced version of an async function.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const debouncedSearch = debounce(searchApi, 300);
|
|
25
|
+
* await debouncedSearch('query');
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
declare function debounce<TArgs extends any[], TReturn>(fn: (...args: TArgs) => TReturn | Promise<TReturn>, wait: number, options?: DebounceOptions): DebouncedFunction<TArgs, TReturn>;
|
|
29
|
+
|
|
30
|
+
export { type DebounceOptions, type DebouncedFunction, debounce };
|
package/dist/debounce.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/debounce.ts
|
|
4
|
+
function debounce(fn, wait, options) {
|
|
5
|
+
const { leading = false, trailing = true, maxWait } = options ?? {};
|
|
6
|
+
if (wait < 0) throw new RangeError("wait must be >= 0");
|
|
7
|
+
let timer = null;
|
|
8
|
+
let maxTimer = null;
|
|
9
|
+
let lastArgs = null;
|
|
10
|
+
const pendingResolvers = [];
|
|
11
|
+
let isPending = false;
|
|
12
|
+
async function invoke() {
|
|
13
|
+
const args = lastArgs;
|
|
14
|
+
const resolvers = pendingResolvers.splice(0);
|
|
15
|
+
lastArgs = null;
|
|
16
|
+
isPending = false;
|
|
17
|
+
if (timer) {
|
|
18
|
+
clearTimeout(timer);
|
|
19
|
+
timer = null;
|
|
20
|
+
}
|
|
21
|
+
if (maxTimer) {
|
|
22
|
+
clearTimeout(maxTimer);
|
|
23
|
+
maxTimer = null;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const result = await fn(...args);
|
|
27
|
+
for (const r of resolvers) r.resolve(result);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
30
|
+
for (const r of resolvers) r.reject(err);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function debounced(...args) {
|
|
34
|
+
lastArgs = args;
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
pendingResolvers.push({ resolve, reject });
|
|
37
|
+
if (leading && !isPending) {
|
|
38
|
+
isPending = true;
|
|
39
|
+
invoke();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
isPending = true;
|
|
43
|
+
if (timer) clearTimeout(timer);
|
|
44
|
+
if (trailing) {
|
|
45
|
+
timer = setTimeout(() => {
|
|
46
|
+
timer = null;
|
|
47
|
+
invoke();
|
|
48
|
+
}, wait);
|
|
49
|
+
}
|
|
50
|
+
if (maxWait !== void 0 && !maxTimer) {
|
|
51
|
+
maxTimer = setTimeout(() => {
|
|
52
|
+
maxTimer = null;
|
|
53
|
+
if (timer) {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
timer = null;
|
|
56
|
+
}
|
|
57
|
+
invoke();
|
|
58
|
+
}, maxWait);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
debounced.cancel = () => {
|
|
63
|
+
if (timer) {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
timer = null;
|
|
66
|
+
}
|
|
67
|
+
if (maxTimer) {
|
|
68
|
+
clearTimeout(maxTimer);
|
|
69
|
+
maxTimer = null;
|
|
70
|
+
}
|
|
71
|
+
lastArgs = null;
|
|
72
|
+
isPending = false;
|
|
73
|
+
const resolvers = pendingResolvers.splice(0);
|
|
74
|
+
for (const r of resolvers) {
|
|
75
|
+
r.reject(new Error("Debounced call cancelled"));
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
debounced.flush = async () => {
|
|
79
|
+
if (!isPending) return void 0;
|
|
80
|
+
const promise = new Promise((resolve, reject) => {
|
|
81
|
+
pendingResolvers.push({ resolve, reject });
|
|
82
|
+
});
|
|
83
|
+
await invoke();
|
|
84
|
+
return promise;
|
|
85
|
+
};
|
|
86
|
+
Object.defineProperty(debounced, "pending", {
|
|
87
|
+
get() {
|
|
88
|
+
return isPending;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return debounced;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
exports.debounce = debounce;
|
|
95
|
+
//# sourceMappingURL=debounce.js.map
|
|
96
|
+
//# sourceMappingURL=debounce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/debounce.ts"],"names":[],"mappings":"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","file":"debounce.js","sourcesContent":["// ============================================================================\n// FlowX — Async-Aware Debounce\n// ============================================================================\n\nexport interface DebounceOptions {\n /** Invoke on the leading edge (default: false) */\n leading?: boolean;\n /** Invoke on the trailing edge (default: true) */\n trailing?: boolean;\n /** Maximum wait time before forced invocation in ms */\n maxWait?: number;\n}\n\nexport interface DebouncedFunction<TArgs extends any[], TReturn> {\n /** Call the debounced function */\n (...args: TArgs): Promise<TReturn>;\n /** Cancel any pending invocation */\n cancel: () => void;\n /** Immediately invoke any pending call */\n flush: () => Promise<TReturn | undefined>;\n /** Whether there is a pending invocation */\n readonly pending: boolean;\n}\n\n/**\n * Create a debounced version of an async function.\n *\n * @example\n * ```ts\n * const debouncedSearch = debounce(searchApi, 300);\n * await debouncedSearch('query');\n * ```\n */\nexport function debounce<TArgs extends any[], TReturn>(\n fn: (...args: TArgs) => TReturn | Promise<TReturn>,\n wait: number,\n options?: DebounceOptions,\n): DebouncedFunction<TArgs, TReturn> {\n const { leading = false, trailing = true, maxWait } = options ?? {};\n\n if (wait < 0) throw new RangeError('wait must be >= 0');\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let maxTimer: ReturnType<typeof setTimeout> | null = null;\n let lastArgs: TArgs | null = null;\n const pendingResolvers: Array<{\n resolve: (value: TReturn) => void;\n reject: (error: Error) => void;\n }> = [];\n let isPending = false;\n\n async function invoke(): Promise<void> {\n\n const args = lastArgs!;\n const resolvers = pendingResolvers.splice(0);\n lastArgs = null;\n isPending = false;\n\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (maxTimer) {\n clearTimeout(maxTimer);\n maxTimer = null;\n }\n\n try {\n const result = await fn(...args);\n for (const r of resolvers) r.resolve(result);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const r of resolvers) r.reject(err);\n }\n }\n\n function debounced(...args: TArgs): Promise<TReturn> {\n lastArgs = args;\n\n return new Promise<TReturn>((resolve, reject) => {\n pendingResolvers.push({ resolve, reject });\n\n // Leading edge\n if (leading && !isPending) {\n isPending = true;\n invoke();\n return;\n }\n\n isPending = true;\n\n // Reset trailing timer\n if (timer) clearTimeout(timer);\n\n if (trailing) {\n timer = setTimeout(() => {\n timer = null;\n invoke();\n }, wait);\n }\n\n // Set max wait timer\n if (maxWait !== undefined && !maxTimer) {\n maxTimer = setTimeout(() => {\n maxTimer = null;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n invoke();\n }, maxWait);\n }\n });\n }\n\n debounced.cancel = (): void => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (maxTimer) {\n clearTimeout(maxTimer);\n maxTimer = null;\n }\n lastArgs = null;\n isPending = false;\n // Reject all pending resolvers\n const resolvers = pendingResolvers.splice(0);\n for (const r of resolvers) {\n r.reject(new Error('Debounced call cancelled'));\n }\n };\n\n debounced.flush = async (): Promise<TReturn | undefined> => {\n if (!isPending) return undefined;\n const promise = new Promise<TReturn>((resolve, reject) => {\n pendingResolvers.push({ resolve, reject });\n });\n await invoke();\n return promise;\n };\n\n Object.defineProperty(debounced, 'pending', {\n get() {\n return isPending;\n },\n });\n\n return debounced as DebouncedFunction<TArgs, TReturn>;\n}\n"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/debounce.ts
|
|
2
|
+
function debounce(fn, wait, options) {
|
|
3
|
+
const { leading = false, trailing = true, maxWait } = options ?? {};
|
|
4
|
+
if (wait < 0) throw new RangeError("wait must be >= 0");
|
|
5
|
+
let timer = null;
|
|
6
|
+
let maxTimer = null;
|
|
7
|
+
let lastArgs = null;
|
|
8
|
+
const pendingResolvers = [];
|
|
9
|
+
let isPending = false;
|
|
10
|
+
async function invoke() {
|
|
11
|
+
const args = lastArgs;
|
|
12
|
+
const resolvers = pendingResolvers.splice(0);
|
|
13
|
+
lastArgs = null;
|
|
14
|
+
isPending = false;
|
|
15
|
+
if (timer) {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
timer = null;
|
|
18
|
+
}
|
|
19
|
+
if (maxTimer) {
|
|
20
|
+
clearTimeout(maxTimer);
|
|
21
|
+
maxTimer = null;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const result = await fn(...args);
|
|
25
|
+
for (const r of resolvers) r.resolve(result);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
28
|
+
for (const r of resolvers) r.reject(err);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function debounced(...args) {
|
|
32
|
+
lastArgs = args;
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
pendingResolvers.push({ resolve, reject });
|
|
35
|
+
if (leading && !isPending) {
|
|
36
|
+
isPending = true;
|
|
37
|
+
invoke();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
isPending = true;
|
|
41
|
+
if (timer) clearTimeout(timer);
|
|
42
|
+
if (trailing) {
|
|
43
|
+
timer = setTimeout(() => {
|
|
44
|
+
timer = null;
|
|
45
|
+
invoke();
|
|
46
|
+
}, wait);
|
|
47
|
+
}
|
|
48
|
+
if (maxWait !== void 0 && !maxTimer) {
|
|
49
|
+
maxTimer = setTimeout(() => {
|
|
50
|
+
maxTimer = null;
|
|
51
|
+
if (timer) {
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
timer = null;
|
|
54
|
+
}
|
|
55
|
+
invoke();
|
|
56
|
+
}, maxWait);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
debounced.cancel = () => {
|
|
61
|
+
if (timer) {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
timer = null;
|
|
64
|
+
}
|
|
65
|
+
if (maxTimer) {
|
|
66
|
+
clearTimeout(maxTimer);
|
|
67
|
+
maxTimer = null;
|
|
68
|
+
}
|
|
69
|
+
lastArgs = null;
|
|
70
|
+
isPending = false;
|
|
71
|
+
const resolvers = pendingResolvers.splice(0);
|
|
72
|
+
for (const r of resolvers) {
|
|
73
|
+
r.reject(new Error("Debounced call cancelled"));
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
debounced.flush = async () => {
|
|
77
|
+
if (!isPending) return void 0;
|
|
78
|
+
const promise = new Promise((resolve, reject) => {
|
|
79
|
+
pendingResolvers.push({ resolve, reject });
|
|
80
|
+
});
|
|
81
|
+
await invoke();
|
|
82
|
+
return promise;
|
|
83
|
+
};
|
|
84
|
+
Object.defineProperty(debounced, "pending", {
|
|
85
|
+
get() {
|
|
86
|
+
return isPending;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return debounced;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { debounce };
|
|
93
|
+
//# sourceMappingURL=debounce.mjs.map
|
|
94
|
+
//# sourceMappingURL=debounce.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/debounce.ts"],"names":[],"mappings":"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","file":"debounce.mjs","sourcesContent":["// ============================================================================\n// FlowX — Async-Aware Debounce\n// ============================================================================\n\nexport interface DebounceOptions {\n /** Invoke on the leading edge (default: false) */\n leading?: boolean;\n /** Invoke on the trailing edge (default: true) */\n trailing?: boolean;\n /** Maximum wait time before forced invocation in ms */\n maxWait?: number;\n}\n\nexport interface DebouncedFunction<TArgs extends any[], TReturn> {\n /** Call the debounced function */\n (...args: TArgs): Promise<TReturn>;\n /** Cancel any pending invocation */\n cancel: () => void;\n /** Immediately invoke any pending call */\n flush: () => Promise<TReturn | undefined>;\n /** Whether there is a pending invocation */\n readonly pending: boolean;\n}\n\n/**\n * Create a debounced version of an async function.\n *\n * @example\n * ```ts\n * const debouncedSearch = debounce(searchApi, 300);\n * await debouncedSearch('query');\n * ```\n */\nexport function debounce<TArgs extends any[], TReturn>(\n fn: (...args: TArgs) => TReturn | Promise<TReturn>,\n wait: number,\n options?: DebounceOptions,\n): DebouncedFunction<TArgs, TReturn> {\n const { leading = false, trailing = true, maxWait } = options ?? {};\n\n if (wait < 0) throw new RangeError('wait must be >= 0');\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let maxTimer: ReturnType<typeof setTimeout> | null = null;\n let lastArgs: TArgs | null = null;\n const pendingResolvers: Array<{\n resolve: (value: TReturn) => void;\n reject: (error: Error) => void;\n }> = [];\n let isPending = false;\n\n async function invoke(): Promise<void> {\n\n const args = lastArgs!;\n const resolvers = pendingResolvers.splice(0);\n lastArgs = null;\n isPending = false;\n\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (maxTimer) {\n clearTimeout(maxTimer);\n maxTimer = null;\n }\n\n try {\n const result = await fn(...args);\n for (const r of resolvers) r.resolve(result);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const r of resolvers) r.reject(err);\n }\n }\n\n function debounced(...args: TArgs): Promise<TReturn> {\n lastArgs = args;\n\n return new Promise<TReturn>((resolve, reject) => {\n pendingResolvers.push({ resolve, reject });\n\n // Leading edge\n if (leading && !isPending) {\n isPending = true;\n invoke();\n return;\n }\n\n isPending = true;\n\n // Reset trailing timer\n if (timer) clearTimeout(timer);\n\n if (trailing) {\n timer = setTimeout(() => {\n timer = null;\n invoke();\n }, wait);\n }\n\n // Set max wait timer\n if (maxWait !== undefined && !maxTimer) {\n maxTimer = setTimeout(() => {\n maxTimer = null;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n invoke();\n }, maxWait);\n }\n });\n }\n\n debounced.cancel = (): void => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (maxTimer) {\n clearTimeout(maxTimer);\n maxTimer = null;\n }\n lastArgs = null;\n isPending = false;\n // Reject all pending resolvers\n const resolvers = pendingResolvers.splice(0);\n for (const r of resolvers) {\n r.reject(new Error('Debounced call cancelled'));\n }\n };\n\n debounced.flush = async (): Promise<TReturn | undefined> => {\n if (!isPending) return undefined;\n const promise = new Promise<TReturn>((resolve, reject) => {\n pendingResolvers.push({ resolve, reject });\n });\n await invoke();\n return promise;\n };\n\n Object.defineProperty(debounced, 'pending', {\n get() {\n return isPending;\n },\n });\n\n return debounced as DebouncedFunction<TArgs, TReturn>;\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface Deferred<T> {
|
|
2
|
+
/** The underlying promise */
|
|
3
|
+
promise: Promise<T>;
|
|
4
|
+
/** Resolve the deferred */
|
|
5
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
6
|
+
/** Reject the deferred */
|
|
7
|
+
reject: (reason?: any) => void;
|
|
8
|
+
/** Whether the deferred has been settled */
|
|
9
|
+
readonly isSettled: boolean;
|
|
10
|
+
/** Whether the deferred was resolved */
|
|
11
|
+
readonly isResolved: boolean;
|
|
12
|
+
/** Whether the deferred was rejected */
|
|
13
|
+
readonly isRejected: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a deferred promise — a promise whose resolve/reject are externally accessible.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const deferred = createDeferred<string>();
|
|
21
|
+
* setTimeout(() => deferred.resolve('done'), 1000);
|
|
22
|
+
* const result = await deferred.promise; // 'done'
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function createDeferred<T = void>(): Deferred<T>;
|
|
26
|
+
|
|
27
|
+
export { type Deferred, createDeferred };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface Deferred<T> {
|
|
2
|
+
/** The underlying promise */
|
|
3
|
+
promise: Promise<T>;
|
|
4
|
+
/** Resolve the deferred */
|
|
5
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
6
|
+
/** Reject the deferred */
|
|
7
|
+
reject: (reason?: any) => void;
|
|
8
|
+
/** Whether the deferred has been settled */
|
|
9
|
+
readonly isSettled: boolean;
|
|
10
|
+
/** Whether the deferred was resolved */
|
|
11
|
+
readonly isResolved: boolean;
|
|
12
|
+
/** Whether the deferred was rejected */
|
|
13
|
+
readonly isRejected: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a deferred promise — a promise whose resolve/reject are externally accessible.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const deferred = createDeferred<string>();
|
|
21
|
+
* setTimeout(() => deferred.resolve('done'), 1000);
|
|
22
|
+
* const result = await deferred.promise; // 'done'
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function createDeferred<T = void>(): Deferred<T>;
|
|
26
|
+
|
|
27
|
+
export { type Deferred, createDeferred };
|
package/dist/deferred.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/deferred.ts
|
|
4
|
+
function createDeferred() {
|
|
5
|
+
let isSettled = false;
|
|
6
|
+
let isResolved = false;
|
|
7
|
+
let isRejected = false;
|
|
8
|
+
let resolveFn;
|
|
9
|
+
let rejectFn;
|
|
10
|
+
const promise = new Promise((resolve, reject) => {
|
|
11
|
+
resolveFn = resolve;
|
|
12
|
+
rejectFn = reject;
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
promise,
|
|
16
|
+
resolve: (value) => {
|
|
17
|
+
if (isSettled) return;
|
|
18
|
+
isSettled = true;
|
|
19
|
+
isResolved = true;
|
|
20
|
+
resolveFn(value);
|
|
21
|
+
},
|
|
22
|
+
reject: (reason) => {
|
|
23
|
+
if (isSettled) return;
|
|
24
|
+
isSettled = true;
|
|
25
|
+
isRejected = true;
|
|
26
|
+
rejectFn(reason);
|
|
27
|
+
},
|
|
28
|
+
get isSettled() {
|
|
29
|
+
return isSettled;
|
|
30
|
+
},
|
|
31
|
+
get isResolved() {
|
|
32
|
+
return isResolved;
|
|
33
|
+
},
|
|
34
|
+
get isRejected() {
|
|
35
|
+
return isRejected;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
exports.createDeferred = createDeferred;
|
|
41
|
+
//# sourceMappingURL=deferred.js.map
|
|
42
|
+
//# sourceMappingURL=deferred.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/deferred.ts"],"names":[],"mappings":";;;AA6BO,SAAS,cAAA,GAAwC;AACtD,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,UAAA,GAAa,KAAA;AACjB,EAAA,IAAI,UAAA,GAAa,KAAA;AACjB,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,QAAA;AAEJ,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAW,CAAC,SAAS,MAAA,KAAW;AAClD,IAAA,SAAA,GAAY,OAAA;AACZ,IAAA,QAAA,GAAW,MAAA;AAAA,EACb,CAAC,CAAA;AAED,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,OAAA,EAAS,CAAC,KAAA,KAA8B;AACtC,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,UAAA,GAAa,IAAA;AACb,MAAA,SAAA,CAAU,KAAK,CAAA;AAAA,IACjB,CAAA;AAAA,IACA,MAAA,EAAQ,CAAC,MAAA,KAAiB;AACxB,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,UAAA,GAAa,IAAA;AACb,MAAA,QAAA,CAAS,MAAM,CAAA;AAAA,IACjB,CAAA;AAAA,IACA,IAAI,SAAA,GAAY;AACd,MAAA,OAAO,SAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,UAAA,GAAa;AACf,MAAA,OAAO,UAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,UAAA,GAAa;AACf,MAAA,OAAO,UAAA;AAAA,IACT;AAAA,GACF;AACF","file":"deferred.js","sourcesContent":["// ============================================================================\n// FlowX — Deferred Promise\n// ============================================================================\n\nexport interface Deferred<T> {\n /** The underlying promise */\n promise: Promise<T>;\n /** Resolve the deferred */\n resolve: (value: T | PromiseLike<T>) => void;\n /** Reject the deferred */\n reject: (reason?: any) => void;\n /** Whether the deferred has been settled */\n readonly isSettled: boolean;\n /** Whether the deferred was resolved */\n readonly isResolved: boolean;\n /** Whether the deferred was rejected */\n readonly isRejected: boolean;\n}\n\n/**\n * Create a deferred promise — a promise whose resolve/reject are externally accessible.\n *\n * @example\n * ```ts\n * const deferred = createDeferred<string>();\n * setTimeout(() => deferred.resolve('done'), 1000);\n * const result = await deferred.promise; // 'done'\n * ```\n */\nexport function createDeferred<T = void>(): Deferred<T> {\n let isSettled = false;\n let isResolved = false;\n let isRejected = false;\n let resolveFn!: (value: T | PromiseLike<T>) => void;\n let rejectFn!: (reason?: any) => void;\n\n const promise = new Promise<T>((resolve, reject) => {\n resolveFn = resolve;\n rejectFn = reject;\n });\n\n return {\n promise,\n resolve: (value: T | PromiseLike<T>) => {\n if (isSettled) return;\n isSettled = true;\n isResolved = true;\n resolveFn(value);\n },\n reject: (reason?: any) => {\n if (isSettled) return;\n isSettled = true;\n isRejected = true;\n rejectFn(reason);\n },\n get isSettled() {\n return isSettled;\n },\n get isResolved() {\n return isResolved;\n },\n get isRejected() {\n return isRejected;\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/deferred.ts
|
|
2
|
+
function createDeferred() {
|
|
3
|
+
let isSettled = false;
|
|
4
|
+
let isResolved = false;
|
|
5
|
+
let isRejected = false;
|
|
6
|
+
let resolveFn;
|
|
7
|
+
let rejectFn;
|
|
8
|
+
const promise = new Promise((resolve, reject) => {
|
|
9
|
+
resolveFn = resolve;
|
|
10
|
+
rejectFn = reject;
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
promise,
|
|
14
|
+
resolve: (value) => {
|
|
15
|
+
if (isSettled) return;
|
|
16
|
+
isSettled = true;
|
|
17
|
+
isResolved = true;
|
|
18
|
+
resolveFn(value);
|
|
19
|
+
},
|
|
20
|
+
reject: (reason) => {
|
|
21
|
+
if (isSettled) return;
|
|
22
|
+
isSettled = true;
|
|
23
|
+
isRejected = true;
|
|
24
|
+
rejectFn(reason);
|
|
25
|
+
},
|
|
26
|
+
get isSettled() {
|
|
27
|
+
return isSettled;
|
|
28
|
+
},
|
|
29
|
+
get isResolved() {
|
|
30
|
+
return isResolved;
|
|
31
|
+
},
|
|
32
|
+
get isRejected() {
|
|
33
|
+
return isRejected;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { createDeferred };
|
|
39
|
+
//# sourceMappingURL=deferred.mjs.map
|
|
40
|
+
//# sourceMappingURL=deferred.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/deferred.ts"],"names":[],"mappings":";AA6BO,SAAS,cAAA,GAAwC;AACtD,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,UAAA,GAAa,KAAA;AACjB,EAAA,IAAI,UAAA,GAAa,KAAA;AACjB,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,QAAA;AAEJ,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAW,CAAC,SAAS,MAAA,KAAW;AAClD,IAAA,SAAA,GAAY,OAAA;AACZ,IAAA,QAAA,GAAW,MAAA;AAAA,EACb,CAAC,CAAA;AAED,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,OAAA,EAAS,CAAC,KAAA,KAA8B;AACtC,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,UAAA,GAAa,IAAA;AACb,MAAA,SAAA,CAAU,KAAK,CAAA;AAAA,IACjB,CAAA;AAAA,IACA,MAAA,EAAQ,CAAC,MAAA,KAAiB;AACxB,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,UAAA,GAAa,IAAA;AACb,MAAA,QAAA,CAAS,MAAM,CAAA;AAAA,IACjB,CAAA;AAAA,IACA,IAAI,SAAA,GAAY;AACd,MAAA,OAAO,SAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,UAAA,GAAa;AACf,MAAA,OAAO,UAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAI,UAAA,GAAa;AACf,MAAA,OAAO,UAAA;AAAA,IACT;AAAA,GACF;AACF","file":"deferred.mjs","sourcesContent":["// ============================================================================\n// FlowX — Deferred Promise\n// ============================================================================\n\nexport interface Deferred<T> {\n /** The underlying promise */\n promise: Promise<T>;\n /** Resolve the deferred */\n resolve: (value: T | PromiseLike<T>) => void;\n /** Reject the deferred */\n reject: (reason?: any) => void;\n /** Whether the deferred has been settled */\n readonly isSettled: boolean;\n /** Whether the deferred was resolved */\n readonly isResolved: boolean;\n /** Whether the deferred was rejected */\n readonly isRejected: boolean;\n}\n\n/**\n * Create a deferred promise — a promise whose resolve/reject are externally accessible.\n *\n * @example\n * ```ts\n * const deferred = createDeferred<string>();\n * setTimeout(() => deferred.resolve('done'), 1000);\n * const result = await deferred.promise; // 'done'\n * ```\n */\nexport function createDeferred<T = void>(): Deferred<T> {\n let isSettled = false;\n let isResolved = false;\n let isRejected = false;\n let resolveFn!: (value: T | PromiseLike<T>) => void;\n let rejectFn!: (reason?: any) => void;\n\n const promise = new Promise<T>((resolve, reject) => {\n resolveFn = resolve;\n rejectFn = reject;\n });\n\n return {\n promise,\n resolve: (value: T | PromiseLike<T>) => {\n if (isSettled) return;\n isSettled = true;\n isResolved = true;\n resolveFn(value);\n },\n reject: (reason?: any) => {\n if (isSettled) return;\n isSettled = true;\n isRejected = true;\n rejectFn(reason);\n },\n get isSettled() {\n return isSettled;\n },\n get isResolved() {\n return isResolved;\n },\n get isRejected() {\n return isRejected;\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface FallbackOptions {
|
|
2
|
+
/** Predicate to determine if the error warrants a fallback */
|
|
3
|
+
shouldFallback?: (error: Error) => boolean;
|
|
4
|
+
/** Callback fired when the primary function fails and fallback is used */
|
|
5
|
+
onFallback?: (error: Error, fallbackIndex: number) => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Execute a function with a fallback value or function if it fails.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const data = await withFallback(
|
|
13
|
+
* () => fetch('/primary-api'),
|
|
14
|
+
* 'default-value',
|
|
15
|
+
* );
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare function withFallback<T>(fn: () => T | Promise<T>, fallback: T | (() => T | Promise<T>), options?: FallbackOptions): Promise<T>;
|
|
19
|
+
/**
|
|
20
|
+
* Execute the first successful function from a chain of fallbacks.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const data = await fallbackChain([
|
|
25
|
+
* () => fetch('/primary'),
|
|
26
|
+
* () => fetch('/secondary'),
|
|
27
|
+
* () => fetch('/tertiary'),
|
|
28
|
+
* ]);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function fallbackChain<T>(fns: Array<() => T | Promise<T>>, options?: FallbackOptions): Promise<T>;
|
|
32
|
+
|
|
33
|
+
export { type FallbackOptions, fallbackChain, withFallback };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface FallbackOptions {
|
|
2
|
+
/** Predicate to determine if the error warrants a fallback */
|
|
3
|
+
shouldFallback?: (error: Error) => boolean;
|
|
4
|
+
/** Callback fired when the primary function fails and fallback is used */
|
|
5
|
+
onFallback?: (error: Error, fallbackIndex: number) => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Execute a function with a fallback value or function if it fails.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const data = await withFallback(
|
|
13
|
+
* () => fetch('/primary-api'),
|
|
14
|
+
* 'default-value',
|
|
15
|
+
* );
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare function withFallback<T>(fn: () => T | Promise<T>, fallback: T | (() => T | Promise<T>), options?: FallbackOptions): Promise<T>;
|
|
19
|
+
/**
|
|
20
|
+
* Execute the first successful function from a chain of fallbacks.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const data = await fallbackChain([
|
|
25
|
+
* () => fetch('/primary'),
|
|
26
|
+
* () => fetch('/secondary'),
|
|
27
|
+
* () => fetch('/tertiary'),
|
|
28
|
+
* ]);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function fallbackChain<T>(fns: Array<() => T | Promise<T>>, options?: FallbackOptions): Promise<T>;
|
|
32
|
+
|
|
33
|
+
export { type FallbackOptions, fallbackChain, withFallback };
|