storion 0.9.0 → 0.10.1

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 (77) hide show
  1. package/CHANGELOG.md +62 -4
  2. package/README.md +65 -2004
  3. package/dist/async/abortable.d.ts +295 -0
  4. package/dist/async/abortable.d.ts.map +1 -0
  5. package/dist/async/async.d.ts +86 -5
  6. package/dist/async/async.d.ts.map +1 -1
  7. package/dist/async/context.d.ts +15 -0
  8. package/dist/async/context.d.ts.map +1 -0
  9. package/dist/async/index.d.ts +16 -3
  10. package/dist/async/index.d.ts.map +1 -1
  11. package/dist/async/index.js +407 -137
  12. package/dist/async/safe.d.ts +221 -0
  13. package/dist/async/safe.d.ts.map +1 -0
  14. package/dist/async/types.d.ts +77 -29
  15. package/dist/async/types.d.ts.map +1 -1
  16. package/dist/async/wrappers.d.ts +217 -0
  17. package/dist/async/wrappers.d.ts.map +1 -0
  18. package/dist/core/effect.d.ts +34 -26
  19. package/dist/core/effect.d.ts.map +1 -1
  20. package/dist/core/equality.d.ts +25 -0
  21. package/dist/core/equality.d.ts.map +1 -1
  22. package/dist/core/focus.d.ts +20 -0
  23. package/dist/core/focus.d.ts.map +1 -0
  24. package/dist/core/focusHelpers.d.ts +258 -0
  25. package/dist/core/focusHelpers.d.ts.map +1 -0
  26. package/dist/core/middleware.d.ts +4 -4
  27. package/dist/core/store.d.ts.map +1 -1
  28. package/dist/core/storeContext.d.ts +2 -9
  29. package/dist/core/storeContext.d.ts.map +1 -1
  30. package/dist/dev.d.ts +0 -10
  31. package/dist/dev.d.ts.map +1 -1
  32. package/dist/{index-C8B6Mo8r.js → effect-BDQU8Voz.js} +1241 -583
  33. package/dist/errors.d.ts +6 -0
  34. package/dist/errors.d.ts.map +1 -1
  35. package/dist/index.d.ts +5 -4
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/network/index.d.ts +69 -0
  38. package/dist/network/index.d.ts.map +1 -0
  39. package/dist/network/retry.d.ts +53 -0
  40. package/dist/network/retry.d.ts.map +1 -0
  41. package/dist/network/services.d.ts +58 -0
  42. package/dist/network/services.d.ts.map +1 -0
  43. package/dist/network/store.d.ts +36 -0
  44. package/dist/network/store.d.ts.map +1 -0
  45. package/dist/network/utils.d.ts +9 -0
  46. package/dist/network/utils.d.ts.map +1 -0
  47. package/dist/persist/index.d.ts +1 -1
  48. package/dist/persist/index.d.ts.map +1 -1
  49. package/dist/persist/index.js +11 -9
  50. package/dist/persist/persist.d.ts +14 -14
  51. package/dist/persist/persist.d.ts.map +1 -1
  52. package/dist/pool.d.ts +77 -0
  53. package/dist/pool.d.ts.map +1 -0
  54. package/dist/react/index.d.ts +2 -2
  55. package/dist/react/index.d.ts.map +1 -1
  56. package/dist/react/index.js +245 -244
  57. package/dist/react/stable.d.ts +27 -0
  58. package/dist/react/stable.d.ts.map +1 -0
  59. package/dist/react/useStore.d.ts +38 -13
  60. package/dist/react/useStore.d.ts.map +1 -1
  61. package/dist/react/withStore.d.ts.map +1 -1
  62. package/dist/storion.js +911 -37
  63. package/dist/trigger.d.ts +12 -7
  64. package/dist/trigger.d.ts.map +1 -1
  65. package/dist/types.d.ts +133 -22
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/utils/storeTuple.d.ts +7 -0
  68. package/dist/utils/storeTuple.d.ts.map +1 -0
  69. package/package.json +5 -1
  70. package/dist/collection.d.ts +0 -34
  71. package/dist/collection.d.ts.map +0 -1
  72. package/dist/core/proxy.d.ts +0 -47
  73. package/dist/core/proxy.d.ts.map +0 -1
  74. package/dist/effect-C6h0PDDI.js +0 -446
  75. package/dist/isPromiseLike-bFkfHAbm.js +0 -6
  76. package/dist/react/useLocalStore.d.ts +0 -48
  77. package/dist/react/useLocalStore.d.ts.map +0 -1
@@ -1,17 +1,23 @@
1
- import { e as effect, u as untrack, A as AsyncFunctionError } from "../effect-C6h0PDDI.js";
2
- class AsyncNotReadyError extends Error {
3
- constructor(message, status) {
4
- super(message);
5
- this.status = status;
6
- this.name = "AsyncNotReadyError";
7
- }
8
- }
9
- class AsyncAggregateError extends Error {
10
- constructor(message, errors) {
11
- super(message);
12
- this.errors = errors;
13
- this.name = "AsyncAggregateError";
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 getRetryCount(retry) {
43
- if (typeof retry === "number") return retry;
44
- if (retry && typeof retry === "object") return retry.count;
45
- return 0;
48
+ function wrapAbortable(fn) {
49
+ return (ctx, ...args) => {
50
+ return fn.withSignal(ctx.signal, ...args);
51
+ };
46
52
  }
47
- function getRetryDelay(retry, attempt, error) {
48
- if (typeof retry === "number") return 1e3;
49
- if (retry && typeof retry === "object") {
50
- if (typeof retry.delay === "function") return retry.delay(attempt, error);
51
- return retry.delay ?? 1e3;
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
- if (lastCancel && (options == null ? void 0 : options.autoCancel) !== false) {
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
- lastCancel = cancel2;
81
- lastArgs = args;
82
- invocationCount++;
83
- const prevState = getState();
84
- const mode = prevState.mode;
85
- const staleData = mode === "stale" ? prevState.data : prevState.status === "success" ? prevState.data : void 0;
86
- const autoCancel = (options == null ? void 0 : options.autoCancel) !== false;
87
- const isStateExternallyModified = () => {
88
- if (!autoCancel) {
89
- const currentState2 = getState();
90
- return currentState2.__requestId === void 0;
91
- }
92
- const currentState = getState();
93
- return currentState.__requestId !== requestId;
94
- };
95
- const retryCount = getRetryCount(options == null ? void 0 : options.retry);
96
- const executeWithRetry = async () => {
97
- let lastError = null;
98
- for (let attempt = 0; attempt <= retryCount; attempt++) {
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
- signal: abortController.signal,
103
- safe(promiseOrCallback) {
104
- if (promiseOrCallback instanceof Promise) {
105
- return new Promise((resolve, reject) => {
106
- promiseOrCallback.then(
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
- lastError = error instanceof Error ? error : new Error(String(error));
156
- if (attempt < retryCount) {
157
- const delay = getRetryDelay(options == null ? void 0 : options.retry, attempt + 1, lastError);
158
- await new Promise((resolve) => setTimeout(resolve, delay));
159
- continue;
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
- if (!abortController.signal.aborted) {
164
- abortController.abort();
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: "error",
159
+ status: "pending",
171
160
  mode,
172
161
  data: mode === "stale" ? staleData : void 0,
173
- error: lastError,
162
+ error: void 0,
174
163
  timestamp: void 0,
164
+ __key: asyncKey,
175
165
  __requestId: requestId,
176
166
  toJSON: stateToJSON
177
167
  });
178
- if (lastCancel === cancel2) {
179
- lastCancel = null;
180
- }
181
- if ((options == null ? void 0 : options.onError) && lastError) {
182
- options.onError(lastError);
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 timeout;
409
+ let timeout2;
404
410
  return createCancellablePromise(
405
411
  new Promise((resolve) => {
406
- timeout = setTimeout(resolve, ms, resolved);
412
+ timeout2 = setTimeout(resolve, ms, resolved);
407
413
  }),
408
414
  () => {
409
- clearTimeout(timeout);
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
- asyncState,
595
- asyncStateFrom,
596
- getPendingPromise
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
  };