storion 0.8.3 → 0.10.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/CHANGELOG.md +127 -21
- package/README.md +42 -2021
- package/dist/async/abortable.d.ts +295 -0
- package/dist/async/abortable.d.ts.map +1 -0
- package/dist/async/async.d.ts +86 -5
- package/dist/async/async.d.ts.map +1 -1
- package/dist/async/context.d.ts +15 -0
- package/dist/async/context.d.ts.map +1 -0
- package/dist/async/index.d.ts +16 -3
- package/dist/async/index.d.ts.map +1 -1
- package/dist/async/index.js +407 -137
- package/dist/async/safe.d.ts +221 -0
- package/dist/async/safe.d.ts.map +1 -0
- package/dist/async/types.d.ts +77 -29
- package/dist/async/types.d.ts.map +1 -1
- package/dist/async/wrappers.d.ts +217 -0
- package/dist/async/wrappers.d.ts.map +1 -0
- package/dist/core/effect.d.ts +34 -26
- package/dist/core/effect.d.ts.map +1 -1
- package/dist/core/equality.d.ts +25 -0
- package/dist/core/equality.d.ts.map +1 -1
- package/dist/core/focus.d.ts +20 -0
- package/dist/core/focus.d.ts.map +1 -0
- package/dist/core/focusHelpers.d.ts +258 -0
- package/dist/core/focusHelpers.d.ts.map +1 -0
- package/dist/core/middleware.d.ts +4 -4
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/storeContext.d.ts +2 -9
- package/dist/core/storeContext.d.ts.map +1 -1
- package/dist/dev.d.ts +0 -10
- package/dist/dev.d.ts.map +1 -1
- package/dist/{index-C8B6Mo8r.js → effect-BDQU8Voz.js} +1241 -583
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/network/index.d.ts +69 -0
- package/dist/network/index.d.ts.map +1 -0
- package/dist/network/retry.d.ts +53 -0
- package/dist/network/retry.d.ts.map +1 -0
- package/dist/network/services.d.ts +58 -0
- package/dist/network/services.d.ts.map +1 -0
- package/dist/network/store.d.ts +36 -0
- package/dist/network/store.d.ts.map +1 -0
- package/dist/network/utils.d.ts +9 -0
- package/dist/network/utils.d.ts.map +1 -0
- package/dist/persist/index.d.ts +1 -1
- package/dist/persist/index.d.ts.map +1 -1
- package/dist/persist/index.js +55 -31
- package/dist/persist/persist.d.ts +119 -62
- package/dist/persist/persist.d.ts.map +1 -1
- package/dist/pool.d.ts +77 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +245 -244
- package/dist/react/stable.d.ts +27 -0
- package/dist/react/stable.d.ts.map +1 -0
- package/dist/react/useStore.d.ts +38 -13
- package/dist/react/useStore.d.ts.map +1 -1
- package/dist/react/withStore.d.ts.map +1 -1
- package/dist/storion.js +911 -37
- package/dist/trigger.d.ts +12 -7
- package/dist/trigger.d.ts.map +1 -1
- package/dist/types.d.ts +133 -22
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/storeTuple.d.ts +7 -0
- package/dist/utils/storeTuple.d.ts.map +1 -0
- package/package.json +5 -1
- package/dist/collection.d.ts +0 -34
- package/dist/collection.d.ts.map +0 -1
- package/dist/core/proxy.d.ts +0 -47
- package/dist/core/proxy.d.ts.map +0 -1
- package/dist/effect-C6h0PDDI.js +0 -446
- package/dist/isPromiseLike-bFkfHAbm.js +0 -6
- package/dist/react/useLocalStore.d.ts +0 -48
- package/dist/react/useLocalStore.d.ts.map +0 -1
package/dist/async/index.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
|
-
import { e as effect, u as untrack,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { c as createSafe, i as isSpec, s as storeTuple, a as store, A as AsyncNotReadyError, b as AsyncAggregateError, e as effect, u as untrack, d as AsyncFunctionError, S as SetupPhaseError, f as isAbortable, r as retryStrategy } from "../effect-BDQU8Voz.js";
|
|
2
|
+
import { g } from "../effect-BDQU8Voz.js";
|
|
3
|
+
function createAsyncContext(abortController, isCancelledOrAborted, cancel, resolver) {
|
|
4
|
+
const safe = createSafe(
|
|
5
|
+
() => abortController.signal,
|
|
6
|
+
isCancelledOrAborted
|
|
7
|
+
);
|
|
8
|
+
return {
|
|
9
|
+
signal: abortController.signal,
|
|
10
|
+
get(specOrFactory) {
|
|
11
|
+
const instance = resolver.get(specOrFactory);
|
|
12
|
+
if (isSpec(specOrFactory)) {
|
|
13
|
+
const store2 = instance;
|
|
14
|
+
return storeTuple(store2);
|
|
15
|
+
}
|
|
16
|
+
return instance;
|
|
17
|
+
},
|
|
18
|
+
safe,
|
|
19
|
+
cancel
|
|
20
|
+
};
|
|
15
21
|
}
|
|
16
22
|
const pendingPromises = /* @__PURE__ */ new WeakMap();
|
|
17
23
|
function getPendingPromise(state) {
|
|
@@ -39,95 +45,67 @@ function stateToJSON() {
|
|
|
39
45
|
}
|
|
40
46
|
return null;
|
|
41
47
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
function wrapAbortable(fn) {
|
|
49
|
+
return (ctx, ...args) => {
|
|
50
|
+
return fn.withSignal(ctx.signal, ...args);
|
|
51
|
+
};
|
|
46
52
|
}
|
|
47
|
-
function
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
function asyncWithFocus(focus, handler, options) {
|
|
54
|
+
if (!focus._storeContext.isSetupPhase()) {
|
|
55
|
+
throw new SetupPhaseError(
|
|
56
|
+
"async.action",
|
|
57
|
+
"async.action() must be called during store setup phase."
|
|
58
|
+
);
|
|
52
59
|
}
|
|
53
|
-
return 1e3;
|
|
54
|
-
}
|
|
55
|
-
function async(focus, handler, options) {
|
|
56
60
|
const [getState, setState] = focus;
|
|
57
61
|
const asyncKey = {};
|
|
58
62
|
let lastCancel = null;
|
|
59
63
|
let lastArgs = null;
|
|
60
64
|
let invocationCount = 0;
|
|
61
65
|
function dispatch(...args) {
|
|
62
|
-
|
|
63
|
-
lastCancel()
|
|
64
|
-
|
|
65
|
-
const abortController = new AbortController();
|
|
66
|
-
let isCancelled = false;
|
|
67
|
-
const requestId = {};
|
|
68
|
-
let rejectOnCancel = null;
|
|
69
|
-
const cancelPromise = new Promise((_, reject) => {
|
|
70
|
-
rejectOnCancel = reject;
|
|
71
|
-
});
|
|
72
|
-
const cancel2 = () => {
|
|
73
|
-
if (!isCancelled) {
|
|
74
|
-
isCancelled = true;
|
|
75
|
-
abortController.abort();
|
|
76
|
-
pendingPromises.delete(asyncKey);
|
|
77
|
-
rejectOnCancel == null ? void 0 : rejectOnCancel(new DOMException("Aborted", "AbortError"));
|
|
66
|
+
return untrack(() => {
|
|
67
|
+
if (lastCancel && (options == null ? void 0 : options.autoCancel) !== false) {
|
|
68
|
+
lastCancel();
|
|
78
69
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
70
|
+
const abortController = new AbortController();
|
|
71
|
+
let isCancelled = false;
|
|
72
|
+
const requestId = {};
|
|
73
|
+
let rejectOnCancel = null;
|
|
74
|
+
const cancelPromise = new Promise((_, reject) => {
|
|
75
|
+
rejectOnCancel = reject;
|
|
76
|
+
});
|
|
77
|
+
const cancel2 = () => {
|
|
78
|
+
if (!isCancelled) {
|
|
79
|
+
isCancelled = true;
|
|
80
|
+
abortController.abort();
|
|
81
|
+
pendingPromises.delete(asyncKey);
|
|
82
|
+
rejectOnCancel == null ? void 0 : rejectOnCancel(new DOMException("Aborted", "AbortError"));
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
lastCancel = cancel2;
|
|
86
|
+
lastArgs = args;
|
|
87
|
+
invocationCount++;
|
|
88
|
+
const prevState = getState();
|
|
89
|
+
const mode = prevState.mode;
|
|
90
|
+
const staleData = mode === "stale" ? prevState.data : prevState.status === "success" ? prevState.data : void 0;
|
|
91
|
+
const autoCancel = (options == null ? void 0 : options.autoCancel) !== false;
|
|
92
|
+
const isStateExternallyModified = () => {
|
|
93
|
+
if (!autoCancel) {
|
|
94
|
+
const currentState2 = getState();
|
|
95
|
+
return currentState2.__requestId === void 0;
|
|
96
|
+
}
|
|
97
|
+
const currentState = getState();
|
|
98
|
+
return currentState.__requestId !== requestId;
|
|
99
|
+
};
|
|
100
|
+
const execute = async () => {
|
|
99
101
|
try {
|
|
100
102
|
const isCancelledOrAborted = () => isCancelled || abortController.signal.aborted;
|
|
101
|
-
const asyncContext =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
(value) => {
|
|
108
|
-
if (!isCancelledOrAborted()) {
|
|
109
|
-
resolve(value);
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
(error) => {
|
|
113
|
-
if (!isCancelledOrAborted()) {
|
|
114
|
-
reject(error);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
return (...args2) => {
|
|
121
|
-
if (!isCancelledOrAborted()) {
|
|
122
|
-
return promiseOrCallback(
|
|
123
|
-
...args2
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
return void 0;
|
|
127
|
-
};
|
|
128
|
-
},
|
|
129
|
-
cancel: cancel2
|
|
130
|
-
};
|
|
103
|
+
const asyncContext = createAsyncContext(
|
|
104
|
+
abortController,
|
|
105
|
+
isCancelledOrAborted,
|
|
106
|
+
cancel2,
|
|
107
|
+
focus._resolver
|
|
108
|
+
);
|
|
131
109
|
const result = await promiseTry(() => handler(asyncContext, ...args));
|
|
132
110
|
if (isCancelled) {
|
|
133
111
|
throw new DOMException("Aborted", "AbortError");
|
|
@@ -152,55 +130,47 @@ function async(focus, handler, options) {
|
|
|
152
130
|
if (isCancelled || abortController.signal.aborted) {
|
|
153
131
|
throw error instanceof Error ? error : new DOMException("Aborted", "AbortError");
|
|
154
132
|
}
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
133
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
134
|
+
if (!abortController.signal.aborted) {
|
|
135
|
+
abortController.abort();
|
|
136
|
+
}
|
|
137
|
+
if (isStateExternallyModified()) {
|
|
138
|
+
throw errorObj;
|
|
139
|
+
}
|
|
140
|
+
setState({
|
|
141
|
+
status: "error",
|
|
142
|
+
mode,
|
|
143
|
+
data: mode === "stale" ? staleData : void 0,
|
|
144
|
+
error: errorObj,
|
|
145
|
+
timestamp: void 0,
|
|
146
|
+
__requestId: requestId,
|
|
147
|
+
toJSON: stateToJSON
|
|
148
|
+
});
|
|
149
|
+
if (lastCancel === cancel2) {
|
|
150
|
+
lastCancel = null;
|
|
160
151
|
}
|
|
152
|
+
throw errorObj;
|
|
161
153
|
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (isStateExternallyModified()) {
|
|
167
|
-
throw lastError;
|
|
168
|
-
}
|
|
154
|
+
};
|
|
155
|
+
const executionPromise = execute();
|
|
156
|
+
const promise = Promise.race([executionPromise, cancelPromise]);
|
|
157
|
+
pendingPromises.set(asyncKey, executionPromise);
|
|
169
158
|
setState({
|
|
170
|
-
status: "
|
|
159
|
+
status: "pending",
|
|
171
160
|
mode,
|
|
172
161
|
data: mode === "stale" ? staleData : void 0,
|
|
173
|
-
error:
|
|
162
|
+
error: void 0,
|
|
174
163
|
timestamp: void 0,
|
|
164
|
+
__key: asyncKey,
|
|
175
165
|
__requestId: requestId,
|
|
176
166
|
toJSON: stateToJSON
|
|
177
167
|
});
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
throw lastError;
|
|
185
|
-
};
|
|
186
|
-
const executionPromise = executeWithRetry();
|
|
187
|
-
const promise = Promise.race([executionPromise, cancelPromise]);
|
|
188
|
-
pendingPromises.set(asyncKey, executionPromise);
|
|
189
|
-
setState({
|
|
190
|
-
status: "pending",
|
|
191
|
-
mode,
|
|
192
|
-
data: mode === "stale" ? staleData : void 0,
|
|
193
|
-
error: void 0,
|
|
194
|
-
timestamp: void 0,
|
|
195
|
-
__key: asyncKey,
|
|
196
|
-
__requestId: requestId,
|
|
197
|
-
toJSON: stateToJSON
|
|
168
|
+
promise.then(
|
|
169
|
+
() => pendingPromises.delete(asyncKey),
|
|
170
|
+
() => pendingPromises.delete(asyncKey)
|
|
171
|
+
);
|
|
172
|
+
return createCancellablePromise(promise, cancel2);
|
|
198
173
|
});
|
|
199
|
-
promise.then(
|
|
200
|
-
() => pendingPromises.delete(asyncKey),
|
|
201
|
-
() => pendingPromises.delete(asyncKey)
|
|
202
|
-
);
|
|
203
|
-
return createCancellablePromise(promise, cancel2);
|
|
204
174
|
}
|
|
205
175
|
function refresh() {
|
|
206
176
|
if (lastArgs === null) {
|
|
@@ -254,6 +224,11 @@ function async(focus, handler, options) {
|
|
|
254
224
|
state: getState()
|
|
255
225
|
};
|
|
256
226
|
}
|
|
227
|
+
if (options == null ? void 0 : options.autoCancel) {
|
|
228
|
+
focus._storeContext.onDispose(() => {
|
|
229
|
+
cancel();
|
|
230
|
+
});
|
|
231
|
+
}
|
|
257
232
|
return {
|
|
258
233
|
dispatch,
|
|
259
234
|
refresh,
|
|
@@ -390,6 +365,7 @@ function asyncStateFrom(prev, status, dataOrError) {
|
|
|
390
365
|
}
|
|
391
366
|
}
|
|
392
367
|
asyncState.from = asyncStateFrom;
|
|
368
|
+
var async;
|
|
393
369
|
((async2) => {
|
|
394
370
|
function fresh() {
|
|
395
371
|
return asyncState("fresh", "idle");
|
|
@@ -399,14 +375,44 @@ asyncState.from = asyncStateFrom;
|
|
|
399
375
|
return asyncState("stale", "idle", initialData);
|
|
400
376
|
}
|
|
401
377
|
async2.stale = stale;
|
|
378
|
+
function action(focus, handlerOrAbortable, options) {
|
|
379
|
+
const handler = isAbortable(handlerOrAbortable) ? wrapAbortable(handlerOrAbortable) : handlerOrAbortable;
|
|
380
|
+
return asyncWithFocus(focus, handler, options);
|
|
381
|
+
}
|
|
382
|
+
async2.action = action;
|
|
383
|
+
function mixin(handlerOrAbortable, options) {
|
|
384
|
+
const handler = isAbortable(handlerOrAbortable) ? wrapAbortable(handlerOrAbortable) : handlerOrAbortable;
|
|
385
|
+
const initialState = (options == null ? void 0 : options.initial) ?? asyncState("fresh", "idle");
|
|
386
|
+
const asyncSpec = store({
|
|
387
|
+
name: (options == null ? void 0 : options.name) ?? `async:${handler.name || "anonymous"}`,
|
|
388
|
+
state: { result: initialState },
|
|
389
|
+
meta: options == null ? void 0 : options.meta,
|
|
390
|
+
setup(storeContext) {
|
|
391
|
+
const { focus } = storeContext;
|
|
392
|
+
const actions = asyncWithFocus(
|
|
393
|
+
focus("result"),
|
|
394
|
+
(asyncContext, ...args) => {
|
|
395
|
+
return handler(asyncContext, ...args);
|
|
396
|
+
},
|
|
397
|
+
options
|
|
398
|
+
);
|
|
399
|
+
return actions;
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
return (context) => {
|
|
403
|
+
const [state, actions] = context.scoped(asyncSpec);
|
|
404
|
+
return [state.result, actions];
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
async2.mixin = mixin;
|
|
402
408
|
function delay(ms, resolved) {
|
|
403
|
-
let
|
|
409
|
+
let timeout2;
|
|
404
410
|
return createCancellablePromise(
|
|
405
411
|
new Promise((resolve) => {
|
|
406
|
-
|
|
412
|
+
timeout2 = setTimeout(resolve, ms, resolved);
|
|
407
413
|
}),
|
|
408
414
|
() => {
|
|
409
|
-
clearTimeout(
|
|
415
|
+
clearTimeout(timeout2);
|
|
410
416
|
}
|
|
411
417
|
);
|
|
412
418
|
}
|
|
@@ -587,11 +593,275 @@ asyncState.from = asyncStateFrom;
|
|
|
587
593
|
}
|
|
588
594
|
async2.derive = derive;
|
|
589
595
|
})(async || (async = {}));
|
|
596
|
+
function retry(retriesOrStrategyOrOptions) {
|
|
597
|
+
const options = typeof retriesOrStrategyOrOptions === "number" ? { retries: retriesOrStrategyOrOptions } : typeof retriesOrStrategyOrOptions === "string" ? { delay: retriesOrStrategyOrOptions } : retriesOrStrategyOrOptions ?? {};
|
|
598
|
+
const retries = options.retries ?? 3;
|
|
599
|
+
const delayOption = options.delay ?? "backoff";
|
|
600
|
+
const getDelay = typeof delayOption === "function" ? delayOption : typeof delayOption === "number" ? () => delayOption : retryStrategy[delayOption];
|
|
601
|
+
return (next) => async (ctx, ...args) => {
|
|
602
|
+
let lastError;
|
|
603
|
+
for (let attempt = 0; attempt < retries; attempt++) {
|
|
604
|
+
try {
|
|
605
|
+
return await next(ctx, ...args);
|
|
606
|
+
} catch (error) {
|
|
607
|
+
lastError = error;
|
|
608
|
+
if (ctx.signal.aborted) {
|
|
609
|
+
throw error;
|
|
610
|
+
}
|
|
611
|
+
if (attempt < retries - 1) {
|
|
612
|
+
const delayResult = getDelay(attempt, lastError);
|
|
613
|
+
if (typeof delayResult === "number") {
|
|
614
|
+
await new Promise((resolve, reject) => {
|
|
615
|
+
const timer = setTimeout(resolve, delayResult);
|
|
616
|
+
const onAbort = () => {
|
|
617
|
+
clearTimeout(timer);
|
|
618
|
+
reject(ctx.signal.reason ?? new Error("Aborted"));
|
|
619
|
+
};
|
|
620
|
+
if (ctx.signal.aborted) {
|
|
621
|
+
onAbort();
|
|
622
|
+
} else {
|
|
623
|
+
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
} else {
|
|
627
|
+
await delayResult;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
throw lastError;
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function catchError(callback) {
|
|
636
|
+
return (next) => async (ctx, ...args) => {
|
|
637
|
+
try {
|
|
638
|
+
return await next(ctx, ...args);
|
|
639
|
+
} catch (error) {
|
|
640
|
+
callback(error, ctx, ...args);
|
|
641
|
+
throw error;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function timeout(ms, message = "Operation timed out") {
|
|
646
|
+
return (next) => async (ctx, ...args) => {
|
|
647
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
648
|
+
const timer = setTimeout(() => {
|
|
649
|
+
reject(new Error(message));
|
|
650
|
+
}, ms);
|
|
651
|
+
ctx.signal.addEventListener("abort", () => clearTimeout(timer));
|
|
652
|
+
});
|
|
653
|
+
return ctx.safe.race([next(ctx, ...args), timeoutPromise]);
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function logging(name, logger = console) {
|
|
657
|
+
return (next) => async (ctx, ...args) => {
|
|
658
|
+
logger.log(`[${name}] calling with:`, args);
|
|
659
|
+
try {
|
|
660
|
+
const result = await next(ctx, ...args);
|
|
661
|
+
logger.log(`[${name}] success:`, result);
|
|
662
|
+
return result;
|
|
663
|
+
} catch (error) {
|
|
664
|
+
logger.error(`[${name}] error:`, error);
|
|
665
|
+
throw error;
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function debounce(ms) {
|
|
670
|
+
let timeoutId;
|
|
671
|
+
return (next) => async (ctx, ...args) => {
|
|
672
|
+
if (timeoutId) {
|
|
673
|
+
clearTimeout(timeoutId);
|
|
674
|
+
}
|
|
675
|
+
return new Promise((resolve, reject) => {
|
|
676
|
+
timeoutId = setTimeout(async () => {
|
|
677
|
+
try {
|
|
678
|
+
const result = await next(ctx, ...args);
|
|
679
|
+
resolve(result);
|
|
680
|
+
} catch (error) {
|
|
681
|
+
reject(error);
|
|
682
|
+
}
|
|
683
|
+
}, ms);
|
|
684
|
+
ctx.signal.addEventListener("abort", () => {
|
|
685
|
+
if (timeoutId) {
|
|
686
|
+
clearTimeout(timeoutId);
|
|
687
|
+
reject(new Error("Debounced operation cancelled"));
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
function throttle(ms) {
|
|
694
|
+
let lastCall = 0;
|
|
695
|
+
let lastResult;
|
|
696
|
+
let pending;
|
|
697
|
+
return (next) => async (ctx, ...args) => {
|
|
698
|
+
const now = Date.now();
|
|
699
|
+
if (now - lastCall < ms) {
|
|
700
|
+
if (pending) {
|
|
701
|
+
return pending;
|
|
702
|
+
}
|
|
703
|
+
if (lastResult !== void 0) {
|
|
704
|
+
return lastResult;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
lastCall = now;
|
|
708
|
+
pending = next(ctx, ...args);
|
|
709
|
+
try {
|
|
710
|
+
lastResult = await pending;
|
|
711
|
+
return lastResult;
|
|
712
|
+
} finally {
|
|
713
|
+
pending = void 0;
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
function fallback(value) {
|
|
718
|
+
return (next) => async (ctx, ...args) => {
|
|
719
|
+
try {
|
|
720
|
+
return await next(ctx, ...args);
|
|
721
|
+
} catch (error) {
|
|
722
|
+
if (ctx.signal.aborted) {
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
return typeof value === "function" ? value(error, ctx, ...args) : value;
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function cache(ttlOrOptions) {
|
|
730
|
+
const options = typeof ttlOrOptions === "number" ? { ttl: ttlOrOptions } : ttlOrOptions;
|
|
731
|
+
const { ttl, key: keyFn = (...args) => JSON.stringify(args) } = options;
|
|
732
|
+
const cacheMap = /* @__PURE__ */ new Map();
|
|
733
|
+
return (next) => async (ctx, ...args) => {
|
|
734
|
+
const cacheKey = keyFn(...args);
|
|
735
|
+
const now = Date.now();
|
|
736
|
+
const cached = cacheMap.get(cacheKey);
|
|
737
|
+
if (cached && cached.expires > now) {
|
|
738
|
+
return cached.value;
|
|
739
|
+
}
|
|
740
|
+
const result = await next(ctx, ...args);
|
|
741
|
+
cacheMap.set(cacheKey, { value: result, expires: now + ttl });
|
|
742
|
+
return result;
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function rateLimit(options) {
|
|
746
|
+
const { limit, window } = options;
|
|
747
|
+
const timestamps = [];
|
|
748
|
+
const queue = [];
|
|
749
|
+
let processing = false;
|
|
750
|
+
const processQueue = async () => {
|
|
751
|
+
if (processing || queue.length === 0) return;
|
|
752
|
+
processing = true;
|
|
753
|
+
while (queue.length > 0) {
|
|
754
|
+
const now = Date.now();
|
|
755
|
+
while (timestamps.length > 0 && timestamps[0] <= now - window) {
|
|
756
|
+
timestamps.shift();
|
|
757
|
+
}
|
|
758
|
+
if (timestamps.length < limit) {
|
|
759
|
+
const item = queue.shift();
|
|
760
|
+
if (item.ctx.signal.aborted) {
|
|
761
|
+
item.reject(item.ctx.signal.reason ?? new Error("Aborted"));
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
timestamps.push(now);
|
|
765
|
+
try {
|
|
766
|
+
const result = await item.next(item.ctx, ...item.args);
|
|
767
|
+
item.resolve(result);
|
|
768
|
+
} catch (error) {
|
|
769
|
+
item.reject(error);
|
|
770
|
+
}
|
|
771
|
+
} else {
|
|
772
|
+
const waitTime = timestamps[0] + window - now;
|
|
773
|
+
await new Promise((r) => setTimeout(r, waitTime));
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
processing = false;
|
|
777
|
+
};
|
|
778
|
+
return (next) => async (ctx, ...args) => {
|
|
779
|
+
const now = Date.now();
|
|
780
|
+
while (timestamps.length > 0 && timestamps[0] <= now - window) {
|
|
781
|
+
timestamps.shift();
|
|
782
|
+
}
|
|
783
|
+
if (timestamps.length < limit) {
|
|
784
|
+
timestamps.push(now);
|
|
785
|
+
return next(ctx, ...args);
|
|
786
|
+
}
|
|
787
|
+
return new Promise((resolve, reject) => {
|
|
788
|
+
queue.push({ resolve, reject, ctx, args, next });
|
|
789
|
+
ctx.signal.addEventListener(
|
|
790
|
+
"abort",
|
|
791
|
+
() => {
|
|
792
|
+
const index = queue.findIndex((item) => item.ctx === ctx);
|
|
793
|
+
if (index !== -1) {
|
|
794
|
+
queue.splice(index, 1);
|
|
795
|
+
reject(ctx.signal.reason ?? new Error("Aborted"));
|
|
796
|
+
}
|
|
797
|
+
},
|
|
798
|
+
{ once: true }
|
|
799
|
+
);
|
|
800
|
+
processQueue();
|
|
801
|
+
});
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
function circuitBreaker(options = {}) {
|
|
805
|
+
const { threshold = 5, resetTimeout = 3e4 } = options;
|
|
806
|
+
let state = "closed";
|
|
807
|
+
let failures = 0;
|
|
808
|
+
let lastFailure = 0;
|
|
809
|
+
return (next) => async (ctx, ...args) => {
|
|
810
|
+
const now = Date.now();
|
|
811
|
+
if (state === "open" && now - lastFailure >= resetTimeout) {
|
|
812
|
+
state = "half-open";
|
|
813
|
+
}
|
|
814
|
+
if (state === "open") {
|
|
815
|
+
throw new Error(
|
|
816
|
+
`Circuit breaker is open. Retry after ${Math.ceil(
|
|
817
|
+
(lastFailure + resetTimeout - now) / 1e3
|
|
818
|
+
)}s`
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
try {
|
|
822
|
+
const result = await next(ctx, ...args);
|
|
823
|
+
if (state === "half-open") {
|
|
824
|
+
state = "closed";
|
|
825
|
+
failures = 0;
|
|
826
|
+
} else if (failures > 0) {
|
|
827
|
+
failures--;
|
|
828
|
+
}
|
|
829
|
+
return result;
|
|
830
|
+
} catch (error) {
|
|
831
|
+
if (ctx.signal.aborted) {
|
|
832
|
+
throw error;
|
|
833
|
+
}
|
|
834
|
+
failures++;
|
|
835
|
+
lastFailure = now;
|
|
836
|
+
if (failures >= threshold) {
|
|
837
|
+
state = "open";
|
|
838
|
+
}
|
|
839
|
+
throw error;
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
function map(mapper) {
|
|
844
|
+
return (next) => async (ctx, ...newArgs) => {
|
|
845
|
+
return mapper((...args) => next(ctx, ...args), ...newArgs);
|
|
846
|
+
};
|
|
847
|
+
}
|
|
590
848
|
export {
|
|
591
849
|
AsyncAggregateError,
|
|
592
850
|
AsyncNotReadyError,
|
|
851
|
+
g as abortable,
|
|
593
852
|
async,
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
853
|
+
cache,
|
|
854
|
+
catchError,
|
|
855
|
+
circuitBreaker,
|
|
856
|
+
createSafe,
|
|
857
|
+
debounce,
|
|
858
|
+
fallback,
|
|
859
|
+
isAbortable,
|
|
860
|
+
logging,
|
|
861
|
+
map,
|
|
862
|
+
rateLimit,
|
|
863
|
+
retry,
|
|
864
|
+
retryStrategy,
|
|
865
|
+
throttle,
|
|
866
|
+
timeout
|
|
597
867
|
};
|