saga-toolkit 2.1.2 → 2.2.2

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 CHANGED
@@ -1,135 +1,149 @@
1
1
  # Saga Toolkit
2
2
 
3
- An extension for [redux-toolkit][redux-toolkit] that allows sagas to resolve async thunk actions. 🌝
3
+ **Seamlessly integrate Redux Toolkit with Redux Saga.**
4
4
 
5
- ## install
5
+ `saga-toolkit` acts as a bridge between Redux Toolkit's `createAsyncThunk` and Redux Saga. It allows you to dispatch actions that trigger Sagas, while retaining the ability to `await` the result (promise) directly in your components or thunks.
6
6
 
7
- `npm i saga-toolkit`
7
+ If you love the "fire-and-forget" nature of Sagas for complex flows but miss the convenience of `await dispatch(action)` for simple request/response patterns (like form submissions), this library is for you.
8
8
 
9
- ## example - takeEveryAsync
9
+ ## Features
10
10
 
11
- With `takeEveryAsync` each saga action dispatched runs their saga.
11
+ - 🤝 **Bridge Pattern**: Connects `createAsyncThunk` (RTK) with `takeEvery` / `takeLatest` (Saga).
12
+ - 🔄 **Promise Support**: `await` your Saga actions in React components.
13
+ - ⚡ **Reduce Boilerplate**: Easily handle loading/success/error states in slices using standard RTK patterns.
14
+ - 🛑 **Cancellation**: Propagates cancellation from the promise to the Saga.
12
15
 
13
- `slice.js`
14
- ```js
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install saga-toolkit
20
+ # or
21
+ yarn add saga-toolkit
22
+ ```
23
+
24
+ *Peer Dependencies: `@reduxjs/toolkit`, `redux-saga`*
25
+
26
+ ## Usage Guide
27
+
28
+ ### 1. Create a "Saga Action"
29
+
30
+ Instead of `createAsyncThunk` or standard standard action creators, use `createSagaAction`. This creates a thunk that returns a promise which your Saga will resolve or reject.
31
+
32
+ ```javascript
33
+ /* slice.js */
15
34
  import { createSlice } from '@reduxjs/toolkit'
16
- import { createSagaAction } from 'saga-toolkit'
35
+ import { createSagaAction } from 'saga-toolkit'
36
+
37
+ const name = 'users'
17
38
 
18
- const name = 'example'
39
+ // Define the action
40
+ export const fetchUser = createSagaAction(`${name}/fetchUser`)
19
41
 
20
42
  const initialState = {
21
- started: false,
22
- result: null,
43
+ data: null,
23
44
  loading: false,
24
45
  error: null,
25
46
  }
26
47
 
27
- export const appStart = createSagaAction(`${name}/appStart`)
28
- export const fetchThings = createSagaAction(`${name}/fetchThings`)
29
-
30
48
  const slice = createSlice({
31
49
  name,
32
50
  initialState,
33
- extraReducers: {
34
- [fetchThings.pending]: state => ({
35
- ...state,
36
- loading: true,
37
- }),
38
- [fetchThings.fulfilled]: (state, { payload }) => ({
39
- ...state,
40
- result: payload,
41
- loading: false,
42
- }),
43
- [fetchThings.rejected]: (state, { error }) => ({
44
- ...state,
45
- error,
46
- loading: false,
47
- }),
48
- [appStart.fulfilled]: state => {
49
- state.started = true // immer allows this
50
- return state
51
- },
51
+ extraReducers: (builder) => {
52
+ builder
53
+ .addCase(fetchUser.pending, (state) => {
54
+ state.loading = true
55
+ state.error = null
56
+ })
57
+ .addCase(fetchUser.fulfilled, (state, { payload }) => {
58
+ state.loading = false
59
+ state.data = payload
60
+ })
61
+ .addCase(fetchUser.rejected, (state, { error }) => {
62
+ state.loading = false
63
+ state.error = error
64
+ })
52
65
  },
53
66
  })
54
67
 
55
68
  export default slice.reducer
56
69
  ```
57
70
 
58
- `sagas.js`
59
- ```js
60
- import { put, call } from 'redux-saga/effects'
61
- import { takeEveryAsync, putAsync } from 'saga-toolkit'
62
- import API from 'hyper-super-api'
63
- import * as actions from './slice'
64
-
65
- function* appStart() {
66
- const promise = yield put(fetchThings({ someArg: 'example' }))
67
- try {
68
- const fetchThingsActionFulfulled = yield promise // optionally we can wait for an action to finish and get its result
69
- } catch(error) {
70
- // we can handle error to avoid appStart to get rejected if we want
71
- }
72
- return result
73
- }
71
+ ### 2. Connect to a Saga
74
72
 
75
- // or using putAsync
76
- function* appStart() {
77
- try {
78
- const payload = yield putAsync(fetchThings({ someArg: 'example' }))
79
- } catch(error) {
80
- // handle uncatched error from saga
81
- }
82
- return result
83
- }
73
+ Use `takeEveryAsync` (or `takeLatestAsync`, etc.) to listen for the action. The return value of your saga becomes the `fulfilled` payload. Throwing an error becomes the `rejected` payload.
74
+
75
+ ```javascript
76
+ /* sagas.js */
77
+ import { call } from 'redux-saga/effects'
78
+ import { takeEveryAsync } from 'saga-toolkit'
79
+ import { fetchUser } from './slice'
80
+ import API from './api'
84
81
 
85
- function* fetchThings({ meta }) {
86
- const { someArg } = meta
87
- const result = yield call(() => API.get('/things', { body: someArg }))
88
- return result
82
+ function* fetchUserSaga({ meta }) {
83
+ // meta.arg contains the argument passed to the dispatch
84
+ const userId = meta.arg
85
+
86
+ // The return value here resolves the promise!
87
+ const user = yield call(API.getUser, userId)
88
+ return user
89
89
  }
90
90
 
91
- export default [
92
- takeEveryAsync(actions.appStart.type, appStart),
93
- takeEveryAsync(actions.fetchThings.type, fetchThings),
94
- ]
91
+ export default function* rootSaga() {
92
+ yield takeEveryAsync(fetchUser.type, fetchUserSaga)
93
+ }
95
94
  ```
96
95
 
97
- `SomeComponent.js`
98
- ```js
99
- // import things
96
+ ### 3. Dispatch and Await in Component
97
+
98
+ Now you can treat the Saga logic as if it were a simple async function.
100
99
 
101
- const SomeComponent() {
100
+ ```javascript
101
+ /* UserComponent.jsx */
102
+ import { useDispatch } from 'react-redux'
103
+ import { useEffect } from 'react'
104
+ import { fetchUser } from './slice'
105
+
106
+ const UserComponent = ({ id }) => {
102
107
  const dispatch = useDispatch()
103
108
 
104
- useEffect(() => {
105
- const promise = dispatch(appStart()) // dispatch action from component
106
- promise.then(...).catch(...) // optionally do something with promise
107
- }, [])
109
+ const handleFetch = async () => {
110
+ try {
111
+ // This waits for the Saga to finish!
112
+ const user = await dispatch(fetchUser(id)).unwrap()
113
+ console.log('Got user:', user)
114
+ } catch (error) {
115
+ console.error('Failed to fetch:', error)
116
+ }
117
+ }
108
118
 
109
- return (
110
- ...
111
- )
119
+ return <button onClick={handleFetch}>Load User</button>
112
120
  }
113
121
  ```
114
122
 
115
- ## example2 - takeLatestAsync
123
+ ## API Reference
116
124
 
117
- With `takeLatestAsync` the latter saga cancels the previous one in case that has not finished running. Action will be rejected with error message 'Saga cancelled'.
125
+ ### `createSagaAction(typePrefix)`
126
+ Creates a Redux Toolkit Async Thunk that is specially designed to work with the effects below.
127
+ - **Returns**: An enhanced thunk action creator.
118
128
 
119
- ```js
120
- export default [
121
- takeLatestAsync(actions.fetchThings.type, fetchThings),
122
- ]
123
- ```
129
+ ### `takeEveryAsync(pattern, saga, ...args)`
130
+ Spawns a `saga` on each action dispatched to the Store that matches `pattern`.
131
+ - Automatically resolves the promise associated with the action when the saga returns.
132
+ - Automatically rejects the promise if the saga errors.
124
133
 
125
- ## example3 - takeAggregateAsync
134
+ ### `takeLatestAsync(pattern, saga, ...args)`
135
+ Same as `takeEveryAsync`, but cancels any previous running task if a new matching action is dispatched.
136
+ - **Note**: Cancelled tasks will reject the promise with an "Aborted" error (or similar).
126
137
 
127
- With `takeAggregateAsync` the underlying promise that is created by the first action dispatch will be used and shared accross all subsequent actions. Each subsequent action will be resolved / rejected with the result of the first action. In other words, one single saga runs at a time that is passed to this effect.
138
+ ### `takeAggregateAsync(pattern, saga, ...args)`
139
+ Wait for the saga to finish for the first action. If subsequent actions with the same pattern are dispatched *while only one is running*, they will all share the **same promise result** as the first one.
140
+ - Useful for de-duplicating identical requests (e.g., multiple components requesting "load config" simultaneously).
128
141
 
129
- ```js
130
- export default [
131
- takeAggregateAsync(actions.fetchThings.type, fetchThings),
132
- ]
133
- ```
142
+ ### `putAsync(action)`
143
+ Dispatches an action to the store and waits for the result.
144
+ - Useful when you want to call another saga-action from within a saga and wait for it (composition).
145
+ - **Example**: `const result = yield putAsync(otherAction())`
146
+
147
+ ## License
134
148
 
135
- [redux-toolkit]: https://redux-toolkit.js.org/ "redux-toolkit"
149
+ ISC
@@ -0,0 +1,28 @@
1
+ import { AsyncThunk, AsyncThunkAction } from '@reduxjs/toolkit';
2
+ import * as redux_saga_effects from 'redux-saga/effects';
3
+ import { ActionPattern, PutEffect } from 'redux-saga/effects';
4
+ import { Channel } from 'redux-saga';
5
+ import { Action } from 'redux';
6
+
7
+ type SagaWorker = (...args: unknown[]) => unknown;
8
+ interface Deferred<T = unknown> {
9
+ resolve: (value: T) => void;
10
+ reject: (error: unknown) => void;
11
+ promise: Promise<T>;
12
+ }
13
+ interface Request {
14
+ requestId?: string;
15
+ deferred?: Deferred;
16
+ onAdd?: (request: Request) => void;
17
+ abort?: () => void;
18
+ }
19
+ type SagaAction<Returned, ThunkArg = void> = AsyncThunk<Returned, ThunkArg, object>;
20
+
21
+ declare function takeEveryAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]): redux_saga_effects.ForkEffect<never>;
22
+ declare function takeLatestAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]): redux_saga_effects.ForkEffect<Generator<unknown, void, unknown>>;
23
+ declare function takeAggregateAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]): redux_saga_effects.ForkEffect<never>;
24
+ declare function putAsync(action: Action | PutEffect | AsyncThunkAction<unknown, unknown, object>): Generator<PutEffect | Promise<unknown>, unknown, unknown>;
25
+
26
+ declare const createSagaAction: <Returned, ThunkArg = void>(type: string) => SagaAction<Returned, ThunkArg>;
27
+
28
+ export { type Deferred, type Request, type SagaAction, type SagaWorker, createSagaAction, putAsync, takeAggregateAsync, takeEveryAsync, takeLatestAsync };
@@ -0,0 +1,28 @@
1
+ import { AsyncThunk, AsyncThunkAction } from '@reduxjs/toolkit';
2
+ import * as redux_saga_effects from 'redux-saga/effects';
3
+ import { ActionPattern, PutEffect } from 'redux-saga/effects';
4
+ import { Channel } from 'redux-saga';
5
+ import { Action } from 'redux';
6
+
7
+ type SagaWorker = (...args: unknown[]) => unknown;
8
+ interface Deferred<T = unknown> {
9
+ resolve: (value: T) => void;
10
+ reject: (error: unknown) => void;
11
+ promise: Promise<T>;
12
+ }
13
+ interface Request {
14
+ requestId?: string;
15
+ deferred?: Deferred;
16
+ onAdd?: (request: Request) => void;
17
+ abort?: () => void;
18
+ }
19
+ type SagaAction<Returned, ThunkArg = void> = AsyncThunk<Returned, ThunkArg, object>;
20
+
21
+ declare function takeEveryAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]): redux_saga_effects.ForkEffect<never>;
22
+ declare function takeLatestAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]): redux_saga_effects.ForkEffect<Generator<unknown, void, unknown>>;
23
+ declare function takeAggregateAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]): redux_saga_effects.ForkEffect<never>;
24
+ declare function putAsync(action: Action | PutEffect | AsyncThunkAction<unknown, unknown, object>): Generator<PutEffect | Promise<unknown>, unknown, unknown>;
25
+
26
+ declare const createSagaAction: <Returned, ThunkArg = void>(type: string) => SagaAction<Returned, ThunkArg>;
27
+
28
+ export { type Deferred, type Request, type SagaAction, type SagaWorker, createSagaAction, putAsync, takeAggregateAsync, takeEveryAsync, takeLatestAsync };
package/dist/index.js ADDED
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createSagaAction: () => createSagaAction,
24
+ putAsync: () => putAsync,
25
+ takeAggregateAsync: () => takeAggregateAsync,
26
+ takeEveryAsync: () => takeEveryAsync,
27
+ takeLatestAsync: () => takeLatestAsync
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+ var import_toolkit2 = require("@reduxjs/toolkit");
31
+
32
+ // node_modules/@redux-saga/deferred/dist/redux-saga-deferred.esm.js
33
+ function deferred() {
34
+ var def = {};
35
+ def.promise = new Promise(function(resolve, reject) {
36
+ def.resolve = resolve;
37
+ def.reject = reject;
38
+ });
39
+ return def;
40
+ }
41
+
42
+ // src/utils.ts
43
+ var requests = {};
44
+ var addRequest = (requestId) => {
45
+ const deferred2 = deferred();
46
+ const request = {
47
+ ...requests[requestId],
48
+ requestId,
49
+ deferred: deferred2
50
+ };
51
+ if (requests[requestId]) {
52
+ requests[requestId].deferred = deferred2;
53
+ if (requests[requestId].onAdd) {
54
+ requests[requestId].onAdd(request);
55
+ }
56
+ } else {
57
+ requests[requestId] = request;
58
+ }
59
+ return deferred2.promise;
60
+ };
61
+ var cleanup = (requestId) => {
62
+ delete requests[requestId];
63
+ };
64
+ var setRequestAbort = (requestId, abort) => {
65
+ if (requests[requestId]) {
66
+ requests[requestId].abort = abort;
67
+ }
68
+ };
69
+ function* getRequest(requestId) {
70
+ const request = requests[requestId];
71
+ if (!request) {
72
+ const result = yield new Promise((onAdd) => {
73
+ requests[requestId] = {
74
+ onAdd: (req) => onAdd(req)
75
+ };
76
+ });
77
+ return result;
78
+ }
79
+ return request;
80
+ }
81
+ var wrap = (saga) => function* (action, ...rest) {
82
+ const { requestId } = action.meta;
83
+ const request = yield getRequest(requestId);
84
+ if (!request.deferred) return;
85
+ const deferred2 = request.deferred;
86
+ let isFinished = false;
87
+ try {
88
+ const result = yield saga(action, ...rest);
89
+ deferred2.resolve(result);
90
+ isFinished = true;
91
+ } catch (error) {
92
+ deferred2.reject(error);
93
+ isFinished = true;
94
+ } finally {
95
+ if (!isFinished) {
96
+ deferred2.reject(new Error("Aborted"));
97
+ const currentRequest = requests[requestId];
98
+ if (currentRequest && currentRequest.abort) {
99
+ currentRequest.abort();
100
+ }
101
+ }
102
+ cleanup(requestId);
103
+ }
104
+ };
105
+
106
+ // src/effects.ts
107
+ var import_effects = require("redux-saga/effects");
108
+ var import_toolkit = require("@reduxjs/toolkit");
109
+ var takeEveryHelper = (patternOrChannel, worker, ...args) => (0, import_effects.fork)(function* () {
110
+ while (true) {
111
+ const action = yield (0, import_effects.take)(patternOrChannel);
112
+ yield (0, import_effects.fork)(worker, ...args.concat(action));
113
+ }
114
+ });
115
+ function takeEveryAsync(pattern, saga, ...args) {
116
+ return takeEveryHelper(pattern, wrap(saga), ...args);
117
+ }
118
+ function takeLatestAsync(pattern, saga, ...args) {
119
+ const tasks = {};
120
+ let deferred2;
121
+ function* wrapper(action, ...rest) {
122
+ if (deferred2) {
123
+ const lastRequestId = yield deferred2.promise;
124
+ const request = yield getRequest(lastRequestId);
125
+ if (request.abort) {
126
+ request.abort();
127
+ }
128
+ const task = yield tasks[lastRequestId].promise;
129
+ yield (0, import_effects.cancel)(task);
130
+ }
131
+ deferred2 = deferred();
132
+ const { requestId } = action.meta;
133
+ yield getRequest(requestId);
134
+ if (deferred2) {
135
+ deferred2.resolve(requestId);
136
+ }
137
+ yield wrap(saga)(action, ...rest);
138
+ deferred2 = null;
139
+ }
140
+ const customTakeEvery = (patternOrChannel, saga2, ...args2) => (0, import_effects.fork)(function* () {
141
+ while (true) {
142
+ const action = yield (0, import_effects.take)(patternOrChannel);
143
+ const { requestId } = action.meta;
144
+ tasks[requestId] = deferred();
145
+ const task = yield (0, import_effects.fork)(saga2, ...args2.concat(action));
146
+ tasks[requestId].resolve(task);
147
+ }
148
+ });
149
+ return customTakeEvery(pattern, wrapper, ...args);
150
+ }
151
+ function takeAggregateAsync(pattern, saga, ...args) {
152
+ let deferred2;
153
+ function* wrapper(action, ...rest) {
154
+ const { requestId } = action.meta;
155
+ if (deferred2) {
156
+ const request = yield getRequest(requestId);
157
+ if (request.deferred) {
158
+ const { resolve, reject } = request.deferred;
159
+ const { promise } = yield deferred2.promise;
160
+ promise.then(resolve, reject).finally(() => cleanup(requestId)).catch(() => {
161
+ });
162
+ }
163
+ } else {
164
+ deferred2 = deferred();
165
+ const request = yield getRequest(requestId);
166
+ if (request.deferred) {
167
+ const { promise } = request.deferred;
168
+ yield wrap(saga)(action, ...rest);
169
+ if (deferred2) {
170
+ deferred2.resolve({ promise });
171
+ }
172
+ deferred2 = null;
173
+ }
174
+ }
175
+ }
176
+ return takeEveryHelper(pattern, wrapper, ...args);
177
+ }
178
+ function* putAsync(action) {
179
+ const promise = yield (0, import_effects.put)(action);
180
+ const result = yield promise;
181
+ return (0, import_toolkit.unwrapResult)(result);
182
+ }
183
+
184
+ // src/index.ts
185
+ var createSagaAction = (type) => {
186
+ const thunk = (0, import_toolkit2.createAsyncThunk)(type, (_, { requestId }) => addRequest(requestId));
187
+ function actionCreator(arg) {
188
+ const originalActionCreator = thunk(arg);
189
+ return (dispatch, getState, extra) => {
190
+ const promise = originalActionCreator(dispatch, getState, extra);
191
+ if (promise.requestId) {
192
+ setRequestAbort(promise.requestId, promise.abort);
193
+ }
194
+ return promise;
195
+ };
196
+ }
197
+ Object.assign(actionCreator, {
198
+ pending: thunk.pending,
199
+ rejected: thunk.rejected,
200
+ fulfilled: thunk.fulfilled,
201
+ typePrefix: thunk.typePrefix,
202
+ type: thunk.pending.type
203
+ });
204
+ return actionCreator;
205
+ };
206
+ // Annotate the CommonJS export names for ESM import in node:
207
+ 0 && (module.exports = {
208
+ createSagaAction,
209
+ putAsync,
210
+ takeAggregateAsync,
211
+ takeEveryAsync,
212
+ takeLatestAsync
213
+ });
214
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../node_modules/@redux-saga/deferred/dist/redux-saga-deferred.esm.js","../src/utils.ts","../src/effects.ts"],"sourcesContent":["import { createAsyncThunk } from '@reduxjs/toolkit'\nimport type { ThunkDispatch } from '@reduxjs/toolkit'\nimport type { Action } from 'redux'\nimport { addRequest, setRequestAbort } from './utils'\nimport { SagaAction } from './types'\n\nexport * from './types'\nexport * from './effects'\n\nexport const createSagaAction = <Returned, ThunkArg = void>(type: string): SagaAction<Returned, ThunkArg> => {\n const thunk = createAsyncThunk<Returned, ThunkArg>(type, (_, { requestId }) => addRequest(requestId) as Promise<Returned>)\n\n function actionCreator(arg: ThunkArg) {\n const originalActionCreator = thunk(arg)\n\n return (dispatch: ThunkDispatch<unknown, unknown, Action>, getState: () => unknown, extra: unknown) => {\n const promise = originalActionCreator(dispatch, getState, extra)\n if (promise.requestId) {\n setRequestAbort(promise.requestId, promise.abort)\n }\n\n return promise\n }\n }\n\n Object.assign(actionCreator, {\n pending: thunk.pending,\n rejected: thunk.rejected,\n fulfilled: thunk.fulfilled,\n typePrefix: thunk.typePrefix,\n type: (thunk.pending as unknown as { type: string }).type,\n })\n\n return actionCreator as unknown as SagaAction<Returned, ThunkArg>\n}\n","function deferred() {\n var def = {};\n def.promise = new Promise(function (resolve, reject) {\n def.resolve = resolve;\n def.reject = reject;\n });\n return def;\n}\nfunction arrayOfDeferred(length) {\n var arr = [];\n for (var i = 0; i < length; i++) {\n arr.push(deferred());\n }\n return arr;\n}\n\nexport { arrayOfDeferred, deferred as default };\n","import createDeferred from '@redux-saga/deferred'\nimport { Request, SagaWorker } from './types'\n\nconst requests: Record<string, Request> = {}\n\nexport const addRequest = (requestId: string) => {\n const deferred = createDeferred()\n const request: Request = {\n ...requests[requestId],\n requestId,\n deferred,\n }\n\n if (requests[requestId]) {\n requests[requestId].deferred = deferred\n if (requests[requestId].onAdd) {\n requests[requestId].onAdd(request)\n }\n } else {\n requests[requestId] = request\n }\n\n return deferred.promise\n}\n\nexport const cleanup = (requestId: string) => {\n delete requests[requestId]\n}\n\nexport const setRequestAbort = (requestId: string, abort: () => void) => {\n if (requests[requestId]) {\n requests[requestId].abort = abort\n }\n}\n\nexport function* getRequest(requestId: string): Generator<unknown, Request, unknown> {\n const request = requests[requestId]\n\n if (!request) {\n const result = yield (new Promise(onAdd => {\n requests[requestId] = {\n onAdd: (req: Request) => onAdd(req)\n }\n }))\n return result as Request\n }\n\n return request\n}\n\nexport const wrap = (saga: SagaWorker) => function* (action: unknown, ...rest: unknown[]): Generator<unknown, void, unknown> {\n const { requestId } = (action as { meta: { requestId: string } }).meta\n const request = (yield getRequest(requestId)) as Request\n\n if (!request.deferred) return\n\n const deferred = request.deferred\n let isFinished = false\n\n try {\n const result = yield saga(action, ...rest)\n deferred.resolve(result)\n isFinished = true\n } catch (error) {\n deferred.reject(error)\n isFinished = true\n } finally {\n if (!isFinished) {\n deferred.reject(new Error('Aborted'))\n const currentRequest = requests[requestId]\n if (currentRequest && currentRequest.abort) {\n currentRequest.abort()\n }\n }\n cleanup(requestId)\n }\n}\n","import { put, take, fork, cancel } from 'redux-saga/effects'\nimport type { PutEffect, ActionPattern } from 'redux-saga/effects'\nimport type { Task, Channel } from 'redux-saga'\nimport type { Action } from 'redux'\nimport createDeferred from '@redux-saga/deferred'\nimport { AsyncThunkAction, unwrapResult } from '@reduxjs/toolkit'\nimport { SagaWorker, Deferred, Request } from './types'\nimport { wrap, getRequest, cleanup } from './utils'\n\n// Helper to avoid 'takeEvery' overload issues with spread arguments\nconst takeEveryHelper = (patternOrChannel: ActionPattern | Channel<Action>, worker: SagaWorker, ...args: unknown[]) => fork(function* () {\n while (true) {\n const action = (yield take(patternOrChannel as unknown as ActionPattern)) as Action\n yield fork(worker, ...args.concat(action))\n }\n})\n\nexport function takeEveryAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]) {\n return takeEveryHelper(pattern as ActionPattern | Channel<Action>, wrap(saga as unknown as SagaWorker), ...args)\n}\n\nexport function takeLatestAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]) {\n const tasks: Record<string, Deferred> = {}\n let deferred: Deferred | null\n\n function* wrapper(action: unknown, ...rest: unknown[]): Generator<unknown, void, unknown> {\n if (deferred) {\n const lastRequestId = (yield deferred.promise) as string\n const request = (yield getRequest(lastRequestId)) as Request\n\n if (request.abort) {\n request.abort()\n }\n\n const task = (yield tasks[lastRequestId].promise) as Task\n\n yield cancel(task)\n }\n\n deferred = createDeferred()\n const { requestId } = (action as { meta: { requestId: string } }).meta\n\n yield getRequest(requestId) // Ensure request is registered/ready if needed\n\n if (deferred) {\n deferred.resolve(requestId)\n }\n\n yield wrap(saga as unknown as SagaWorker)(action, ...rest)\n\n deferred = null\n }\n\n const customTakeEvery = (patternOrChannel: ActionPattern | Channel<Action>, saga: SagaWorker, ...args: unknown[]) => fork(function* (): Generator<unknown, void, unknown> {\n while (true) {\n const action = (yield take(patternOrChannel as unknown as ActionPattern)) as { meta: { requestId: string } }\n const { requestId } = action.meta\n tasks[requestId] = createDeferred()\n const task = (yield fork(saga, ...args.concat(action))) as Task\n tasks[requestId].resolve(task)\n }\n })\n\n return customTakeEvery(pattern as ActionPattern | Channel<Action>, wrapper, ...args)\n}\n\nexport function takeAggregateAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]) {\n let deferred: Deferred | null\n\n function* wrapper(action: unknown, ...rest: unknown[]): Generator<unknown, void, unknown> {\n const { requestId } = (action as { meta: { requestId: string } }).meta\n\n if (deferred) {\n const request = (yield getRequest(requestId)) as Request\n if (request.deferred) {\n const { resolve, reject } = request.deferred\n const { promise } = (yield deferred.promise) as { promise: Promise<unknown> }\n\n promise\n .then(resolve, reject)\n .finally(() => cleanup(requestId))\n .catch(() => { })\n }\n } else {\n deferred = createDeferred()\n const request = (yield getRequest(requestId)) as Request\n if (request.deferred) {\n const { promise } = request.deferred\n\n yield wrap(saga as unknown as SagaWorker)(action, ...rest)\n\n if (deferred) {\n deferred.resolve({ promise })\n }\n deferred = null\n }\n }\n }\n\n return takeEveryHelper(pattern as ActionPattern | Channel<Action>, wrapper, ...args)\n}\n\nexport function* putAsync(action: Action | PutEffect | AsyncThunkAction<unknown, unknown, object>): Generator<PutEffect | Promise<unknown>, unknown, unknown> {\n const promise = yield put(action as Action)\n const result = yield (promise as Promise<unknown>)\n return unwrapResult(result as { payload: unknown, error?: unknown, meta?: unknown })\n}\n"],"mappings":"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","names":["import_toolkit","deferred","deferred","saga","args"]}