saga-toolkit 2.2.3 → 2.2.5

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
@@ -48,40 +48,32 @@ yarn add saga-toolkit
48
48
 
49
49
  ### 1. Create a "Saga Action"
50
50
 
51
- 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.
51
+ Instead of `createAsyncThunk` or standard action creators, use `createSagaAction`. This creates an Async Thunk that returns a promise which your Saga will resolve or reject.
52
52
 
53
- ```javascript
54
- /* slice.js */
55
- import { createSlice } from '@reduxjs/toolkit'
53
+ ```typescript
54
+ /* slice.ts */
55
+ import { createSlice, PayloadAction } from '@reduxjs/toolkit'
56
56
  import { createSagaAction } from 'saga-toolkit'
57
57
 
58
+ interface User {
59
+ id: string
60
+ name: string
61
+ }
62
+
58
63
  const name = 'users'
59
64
 
60
- // Define the action
61
- export const fetchUser = createSagaAction(`${name}/fetchUser`)
62
-
63
- const initialState = {
64
- data: null,
65
- loading: false,
66
- error: null,
67
- }
65
+ // Define the action: <Returned, ThunkArg>
66
+ export const fetchUser = createSagaAction<User, string>(`${name}/fetchUser`)
68
67
 
69
68
  const slice = createSlice({
70
69
  name,
71
- initialState,
70
+ initialState: { data: null as User | null, loading: false },
72
71
  extraReducers: (builder) => {
73
72
  builder
74
- .addCase(fetchUser.pending, (state) => {
75
- state.loading = true
76
- state.error = null
77
- })
78
- .addCase(fetchUser.fulfilled, (state, { payload }) => {
73
+ .addCase(fetchUser.pending, (state) => { state.loading = true })
74
+ .addCase(fetchUser.fulfilled, (state, action: PayloadAction<User>) => {
79
75
  state.loading = false
80
- state.data = payload
81
- })
82
- .addCase(fetchUser.rejected, (state, { error }) => {
83
- state.loading = false
84
- state.error = error
76
+ state.data = action.payload
85
77
  })
86
78
  },
87
79
  })
@@ -91,18 +83,17 @@ export default slice.reducer
91
83
 
92
84
  ### 2. Connect to a Saga
93
85
 
94
- 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.
86
+ Use `takeEveryAsync` (or `takeLatestAsync`, etc.) to listen for the action. Use the `SagaActionFromCreator` helper to type your worker sagas perfectly.
95
87
 
96
- ```javascript
97
- /* sagas.js */
88
+ ```typescript
89
+ /* sagas.ts */
98
90
  import { call } from 'redux-saga/effects'
99
- import { takeEveryAsync } from 'saga-toolkit'
91
+ import { takeEveryAsync, SagaActionFromCreator } from 'saga-toolkit'
100
92
  import { fetchUser } from './slice'
101
- import API from './api'
102
93
 
103
- function* fetchUserSaga({ meta }) {
104
- // meta.arg contains the argument passed to the dispatch
105
- const userId = meta.arg
94
+ // helper for clean typing
95
+ function* fetchUserSaga(action: SagaActionFromCreator<typeof fetchUser>) {
96
+ const userId = action.meta.arg
106
97
 
107
98
  // The return value here resolves the promise!
108
99
  const user = yield call(API.getUser, userId)
@@ -110,27 +101,32 @@ function* fetchUserSaga({ meta }) {
110
101
  }
111
102
 
112
103
  export default function* rootSaga() {
113
- yield takeEveryAsync(fetchUser.type, fetchUserSaga)
104
+ yield takeEveryAsync(fetchUser.pending.type, fetchUserSaga)
114
105
  }
115
106
  ```
116
107
 
117
108
  ### 3. Dispatch and Await in Component
118
109
 
119
- Now you can treat the Saga logic as if it were a simple async function.
110
+ #### [Pro Tip] Use `bindActionCreators`
111
+ To keep your component code clean and avoid passing `dispatch` everywhere, we recommend using `bindActionCreators`.
120
112
 
121
- ```javascript
122
- /* UserComponent.jsx */
113
+ ```tsx
114
+ /* UserComponent.tsx */
115
+ import { useMemo } from 'react'
123
116
  import { useDispatch } from 'react-redux'
124
- import { useEffect } from 'react'
117
+ import { bindActionCreators } from 'redux'
125
118
  import { fetchUser } from './slice'
126
119
 
127
- const UserComponent = ({ id }) => {
120
+ const UserComponent = ({ id }: { id: string }) => {
128
121
  const dispatch = useDispatch()
122
+
123
+ // Bind actions once
124
+ const actions = useMemo(() => bindActionCreators({ fetchUser }, dispatch), [dispatch])
129
125
 
130
126
  const handleFetch = async () => {
131
127
  try {
132
- // This waits for the Saga to finish!
133
- const user = await dispatch(fetchUser(id)).unwrap()
128
+ // Clean awaitable call!
129
+ const user = await actions.fetchUser(id).unwrap()
134
130
  console.log('Got user:', user)
135
131
  } catch (error) {
136
132
  console.error('Failed to fetch:', error)
@@ -143,27 +139,28 @@ const UserComponent = ({ id }) => {
143
139
 
144
140
  ## API Reference
145
141
 
146
- ### `createSagaAction(typePrefix)`
147
- Creates a Redux Toolkit Async Thunk that is specially designed to work with the effects below.
142
+ ### `createSagaAction<Returned, ThunkArg>(typePrefix)`
143
+ Creates a Redux Toolkit Async Thunk bridge.
148
144
  - **Returns**: An enhanced thunk action creator.
149
145
 
150
146
  ### `takeEveryAsync(pattern, saga, ...args)`
151
- Spawns a `saga` on each action dispatched to the Store that matches `pattern`.
152
- - Automatically resolves the promise associated with the action when the saga returns.
153
- - Automatically rejects the promise if the saga errors.
147
+ Spawns a `saga` on each action.
148
+ - Automatically resolves/rejects the promise associated with the action.
154
149
 
155
150
  ### `takeLatestAsync(pattern, saga, ...args)`
156
- Same as `takeEveryAsync`, but cancels any previous running task if a new matching action is dispatched.
157
- - **Note**: Cancelled tasks will reject the promise with an "Aborted" error (or similar).
151
+ Same as `takeEveryAsync`, but cancels previous running task on new actions.
152
+ - Propagates cancellation to the saga and rejets the promise with "Aborted".
158
153
 
159
154
  ### `takeAggregateAsync(pattern, saga, ...args)`
160
- 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.
161
- - Useful for de-duplicating identical requests (e.g., multiple components requesting "load config" simultaneously).
155
+ Wait for the saga to finish. Subsequent identical actions dispatched while it's running will all share the **same promise result**.
156
+ - Perfect for de-duplicating rapid "Refresh" calls.
162
157
 
163
158
  ### `putAsync(action)`
164
- Dispatches an action to the store and waits for the result.
165
- - Useful when you want to call another saga-action from within a saga and wait for it (composition).
166
- - **Example**: `const result = yield putAsync(otherAction())`
159
+ Dispatches an action and waits for its Saga to finish.
160
+ - `const result = yield putAsync(otherAction())`
161
+
162
+ ### `SagaActionFromCreator<typeof actionCreator>`
163
+ TypeScript helper to extract the correct action type for your Saga worker.
167
164
 
168
165
  ## License
169
166
 
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { AsyncThunk, AsyncThunkAction } from '@reduxjs/toolkit';
2
2
  import * as redux_saga_effects from 'redux-saga/effects';
3
3
  import { ActionPattern, PutEffect } from 'redux-saga/effects';
4
- import { Channel } from 'redux-saga';
4
+ import { Channel, Task } from 'redux-saga';
5
5
  import { Action } from 'redux';
6
6
 
7
7
  type SagaWorker = (...args: unknown[]) => unknown;
@@ -13,16 +13,23 @@ interface Deferred<T = unknown> {
13
13
  interface Request {
14
14
  requestId?: string;
15
15
  deferred?: Deferred;
16
- onAdd?: (request: Request) => void;
17
16
  abort?: () => void;
17
+ task?: any;
18
+ onAdd?: (request: Request) => void;
19
+ handled?: boolean;
18
20
  }
19
21
  type SagaAction<Returned, ThunkArg = void> = AsyncThunk<Returned, ThunkArg, object>;
22
+ type SagaActionFromCreator<T extends (...args: any[]) => any> = ReturnType<ReturnType<T>['pending']>;
20
23
 
21
24
  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>>;
25
+ 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<Generator<unknown, Request, unknown> | redux_saga_effects.TakeEffect | redux_saga_effects.ForkEffect<unknown> | redux_saga_effects.CancelEffect, never, {
26
+ meta: {
27
+ requestId: string;
28
+ };
29
+ } & Request & Task<any>>>;
23
30
  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
31
  declare function putAsync(action: Action | PutEffect | AsyncThunkAction<unknown, unknown, object>): Generator<PutEffect | Promise<unknown>, unknown, unknown>;
25
32
 
26
33
  declare const createSagaAction: <Returned, ThunkArg = void>(type: string) => SagaAction<Returned, ThunkArg>;
27
34
 
28
- export { type Deferred, type Request, type SagaAction, type SagaWorker, createSagaAction, putAsync, takeAggregateAsync, takeEveryAsync, takeLatestAsync };
35
+ export { type Deferred, type Request, type SagaAction, type SagaActionFromCreator, type SagaWorker, createSagaAction, putAsync, takeAggregateAsync, takeEveryAsync, takeLatestAsync };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { AsyncThunk, AsyncThunkAction } from '@reduxjs/toolkit';
2
2
  import * as redux_saga_effects from 'redux-saga/effects';
3
3
  import { ActionPattern, PutEffect } from 'redux-saga/effects';
4
- import { Channel } from 'redux-saga';
4
+ import { Channel, Task } from 'redux-saga';
5
5
  import { Action } from 'redux';
6
6
 
7
7
  type SagaWorker = (...args: unknown[]) => unknown;
@@ -13,16 +13,23 @@ interface Deferred<T = unknown> {
13
13
  interface Request {
14
14
  requestId?: string;
15
15
  deferred?: Deferred;
16
- onAdd?: (request: Request) => void;
17
16
  abort?: () => void;
17
+ task?: any;
18
+ onAdd?: (request: Request) => void;
19
+ handled?: boolean;
18
20
  }
19
21
  type SagaAction<Returned, ThunkArg = void> = AsyncThunk<Returned, ThunkArg, object>;
22
+ type SagaActionFromCreator<T extends (...args: any[]) => any> = ReturnType<ReturnType<T>['pending']>;
20
23
 
21
24
  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>>;
25
+ 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<Generator<unknown, Request, unknown> | redux_saga_effects.TakeEffect | redux_saga_effects.ForkEffect<unknown> | redux_saga_effects.CancelEffect, never, {
26
+ meta: {
27
+ requestId: string;
28
+ };
29
+ } & Request & Task<any>>>;
23
30
  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
31
  declare function putAsync(action: Action | PutEffect | AsyncThunkAction<unknown, unknown, object>): Generator<PutEffect | Promise<unknown>, unknown, unknown>;
25
32
 
26
33
  declare const createSagaAction: <Returned, ThunkArg = void>(type: string) => SagaAction<Returned, ThunkArg>;
27
34
 
28
- export { type Deferred, type Request, type SagaAction, type SagaWorker, createSagaAction, putAsync, takeAggregateAsync, takeEveryAsync, takeLatestAsync };
35
+ export { type Deferred, type Request, type SagaAction, type SagaActionFromCreator, type SagaWorker, createSagaAction, putAsync, takeAggregateAsync, takeEveryAsync, takeLatestAsync };
package/dist/index.js CHANGED
@@ -46,7 +46,8 @@ var addRequest = (requestId) => {
46
46
  const request = {
47
47
  ...requests[requestId],
48
48
  requestId,
49
- deferred: deferred2
49
+ deferred: deferred2,
50
+ handled: false
50
51
  };
51
52
  if (requests[requestId]) {
52
53
  requests[requestId].deferred = deferred2;
@@ -56,6 +57,11 @@ var addRequest = (requestId) => {
56
57
  } else {
57
58
  requests[requestId] = request;
58
59
  }
60
+ setTimeout(() => {
61
+ if (requests[requestId] && !requests[requestId].handled) {
62
+ delete requests[requestId];
63
+ }
64
+ }, 3e4);
59
65
  return deferred2.promise;
60
66
  };
61
67
  var cleanup = (requestId) => {
@@ -68,10 +74,14 @@ var setRequestAbort = (requestId, abort) => {
68
74
  };
69
75
  function* getRequest(requestId) {
70
76
  const request = requests[requestId];
77
+ if (request) {
78
+ request.handled = true;
79
+ }
71
80
  if (!request) {
72
81
  const result = yield new Promise((onAdd) => {
73
82
  requests[requestId] = {
74
- onAdd: (req) => onAdd(req)
83
+ onAdd: (req) => onAdd(req),
84
+ handled: true
75
85
  };
76
86
  });
77
87
  return result;
@@ -116,37 +126,26 @@ function takeEveryAsync(pattern, saga, ...args) {
116
126
  return takeEveryHelper(pattern, wrap(saga), ...args);
117
127
  }
118
128
  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* () {
129
+ return (0, import_effects.fork)(function* () {
130
+ let lastTask = null;
131
+ let lastRequestId = null;
141
132
  while (true) {
142
- const action = yield (0, import_effects.take)(patternOrChannel);
133
+ const action = yield (0, import_effects.take)(pattern);
143
134
  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);
135
+ if (lastTask) {
136
+ if (lastRequestId) {
137
+ const lastRequest = yield getRequest(lastRequestId);
138
+ if (lastRequest.abort) {
139
+ lastRequest.abort();
140
+ }
141
+ }
142
+ yield (0, import_effects.cancel)(lastTask);
143
+ }
144
+ lastRequestId = requestId;
145
+ const worker = wrap(saga);
146
+ lastTask = yield (0, import_effects.fork)(worker, ...args.concat(action));
147
147
  }
148
148
  });
149
- return customTakeEvery(pattern, wrapper, ...args);
150
149
  }
151
150
  function takeAggregateAsync(pattern, saga, ...args) {
152
151
  let deferred2;
package/dist/index.js.map CHANGED
@@ -1 +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"]}
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 handled: false\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 // Auto-cleanup if not picked up by a saga within 30 seconds\n setTimeout(() => {\n if (requests[requestId] && !requests[requestId].handled) {\n delete requests[requestId]\n }\n }, 30000)\n\n return deferred.promise\n}\n\nexport const cleanup = (requestId: string) => {\n delete requests[requestId]\n}\n\n/** @internal */\nexport const _clearInternalState = () => {\n for (const key in requests) {\n delete requests[key]\n }\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 request.handled = true\n }\n\n if (!request) {\n const result = yield (new Promise(onAdd => {\n requests[requestId] = {\n onAdd: (req: Request) => onAdd(req),\n handled: true\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\n/** @internal */\nexport const _getInternalState = () => ({\n requests,\n size: Object.keys(requests).length\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 return fork(function* () {\n let lastTask: Task | null = null\n let lastRequestId: string | null = null\n\n while (true) {\n const action = (yield take(pattern as unknown as ActionPattern)) as { meta: { requestId: string } }\n const { requestId } = action.meta\n\n if (lastTask) {\n if (lastRequestId) {\n const lastRequest = (yield getRequest(lastRequestId)) as Request\n if (lastRequest.abort) {\n lastRequest.abort()\n }\n }\n yield cancel(lastTask)\n }\n\n lastRequestId = requestId\n const worker = wrap(saga as unknown as SagaWorker)\n lastTask = (yield fork(worker as any, ...args.concat(action))) as Task\n }\n })\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","deferred"]}
package/dist/index.mjs CHANGED
@@ -18,7 +18,8 @@ var addRequest = (requestId) => {
18
18
  const request = {
19
19
  ...requests[requestId],
20
20
  requestId,
21
- deferred: deferred2
21
+ deferred: deferred2,
22
+ handled: false
22
23
  };
23
24
  if (requests[requestId]) {
24
25
  requests[requestId].deferred = deferred2;
@@ -28,6 +29,11 @@ var addRequest = (requestId) => {
28
29
  } else {
29
30
  requests[requestId] = request;
30
31
  }
32
+ setTimeout(() => {
33
+ if (requests[requestId] && !requests[requestId].handled) {
34
+ delete requests[requestId];
35
+ }
36
+ }, 3e4);
31
37
  return deferred2.promise;
32
38
  };
33
39
  var cleanup = (requestId) => {
@@ -40,10 +46,14 @@ var setRequestAbort = (requestId, abort) => {
40
46
  };
41
47
  function* getRequest(requestId) {
42
48
  const request = requests[requestId];
49
+ if (request) {
50
+ request.handled = true;
51
+ }
43
52
  if (!request) {
44
53
  const result = yield new Promise((onAdd) => {
45
54
  requests[requestId] = {
46
- onAdd: (req) => onAdd(req)
55
+ onAdd: (req) => onAdd(req),
56
+ handled: true
47
57
  };
48
58
  });
49
59
  return result;
@@ -88,37 +98,26 @@ function takeEveryAsync(pattern, saga, ...args) {
88
98
  return takeEveryHelper(pattern, wrap(saga), ...args);
89
99
  }
90
100
  function takeLatestAsync(pattern, saga, ...args) {
91
- const tasks = {};
92
- let deferred2;
93
- function* wrapper(action, ...rest) {
94
- if (deferred2) {
95
- const lastRequestId = yield deferred2.promise;
96
- const request = yield getRequest(lastRequestId);
97
- if (request.abort) {
98
- request.abort();
99
- }
100
- const task = yield tasks[lastRequestId].promise;
101
- yield cancel(task);
102
- }
103
- deferred2 = deferred();
104
- const { requestId } = action.meta;
105
- yield getRequest(requestId);
106
- if (deferred2) {
107
- deferred2.resolve(requestId);
108
- }
109
- yield wrap(saga)(action, ...rest);
110
- deferred2 = null;
111
- }
112
- const customTakeEvery = (patternOrChannel, saga2, ...args2) => fork(function* () {
101
+ return fork(function* () {
102
+ let lastTask = null;
103
+ let lastRequestId = null;
113
104
  while (true) {
114
- const action = yield take(patternOrChannel);
105
+ const action = yield take(pattern);
115
106
  const { requestId } = action.meta;
116
- tasks[requestId] = deferred();
117
- const task = yield fork(saga2, ...args2.concat(action));
118
- tasks[requestId].resolve(task);
107
+ if (lastTask) {
108
+ if (lastRequestId) {
109
+ const lastRequest = yield getRequest(lastRequestId);
110
+ if (lastRequest.abort) {
111
+ lastRequest.abort();
112
+ }
113
+ }
114
+ yield cancel(lastTask);
115
+ }
116
+ lastRequestId = requestId;
117
+ const worker = wrap(saga);
118
+ lastTask = yield fork(worker, ...args.concat(action));
119
119
  }
120
120
  });
121
- return customTakeEvery(pattern, wrapper, ...args);
122
121
  }
123
122
  function takeAggregateAsync(pattern, saga, ...args) {
124
123
  let deferred2;
@@ -1 +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":["deferred","deferred","saga","args"]}
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 handled: false\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 // Auto-cleanup if not picked up by a saga within 30 seconds\n setTimeout(() => {\n if (requests[requestId] && !requests[requestId].handled) {\n delete requests[requestId]\n }\n }, 30000)\n\n return deferred.promise\n}\n\nexport const cleanup = (requestId: string) => {\n delete requests[requestId]\n}\n\n/** @internal */\nexport const _clearInternalState = () => {\n for (const key in requests) {\n delete requests[key]\n }\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 request.handled = true\n }\n\n if (!request) {\n const result = yield (new Promise(onAdd => {\n requests[requestId] = {\n onAdd: (req: Request) => onAdd(req),\n handled: true\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\n/** @internal */\nexport const _getInternalState = () => ({\n requests,\n size: Object.keys(requests).length\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 return fork(function* () {\n let lastTask: Task | null = null\n let lastRequestId: string | null = null\n\n while (true) {\n const action = (yield take(pattern as unknown as ActionPattern)) as { meta: { requestId: string } }\n const { requestId } = action.meta\n\n if (lastTask) {\n if (lastRequestId) {\n const lastRequest = (yield getRequest(lastRequestId)) as Request\n if (lastRequest.abort) {\n lastRequest.abort()\n }\n }\n yield cancel(lastTask)\n }\n\n lastRequestId = requestId\n const worker = wrap(saga as unknown as SagaWorker)\n lastTask = (yield fork(worker as any, ...args.concat(action))) as Task\n }\n })\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":["deferred","deferred","deferred"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saga-toolkit",
3
- "version": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "description": "An extension for redux-toolkit that allows sagas to resolve async thunk actions.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
package/src/effects.ts CHANGED
@@ -20,48 +20,29 @@ export function takeEveryAsync<A extends Action = Action>(pattern: ActionPattern
20
20
  }
21
21
 
22
22
  export function takeLatestAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]) {
23
- const tasks: Record<string, Deferred> = {}
24
- let deferred: Deferred | null
23
+ return fork(function* () {
24
+ let lastTask: Task | null = null
25
+ let lastRequestId: string | null = null
25
26
 
26
- function* wrapper(action: unknown, ...rest: unknown[]): Generator<unknown, void, unknown> {
27
- if (deferred) {
28
- const lastRequestId = (yield deferred.promise) as string
29
- const request = (yield getRequest(lastRequestId)) as Request
27
+ while (true) {
28
+ const action = (yield take(pattern as unknown as ActionPattern)) as { meta: { requestId: string } }
29
+ const { requestId } = action.meta
30
30
 
31
- if (request.abort) {
32
- request.abort()
31
+ if (lastTask) {
32
+ if (lastRequestId) {
33
+ const lastRequest = (yield getRequest(lastRequestId)) as Request
34
+ if (lastRequest.abort) {
35
+ lastRequest.abort()
36
+ }
37
+ }
38
+ yield cancel(lastTask)
33
39
  }
34
40
 
35
- const task = (yield tasks[lastRequestId].promise) as Task
36
-
37
- yield cancel(task)
38
- }
39
-
40
- deferred = createDeferred()
41
- const { requestId } = (action as { meta: { requestId: string } }).meta
42
-
43
- yield getRequest(requestId) // Ensure request is registered/ready if needed
44
-
45
- if (deferred) {
46
- deferred.resolve(requestId)
47
- }
48
-
49
- yield wrap(saga as unknown as SagaWorker)(action, ...rest)
50
-
51
- deferred = null
52
- }
53
-
54
- const customTakeEvery = (patternOrChannel: ActionPattern | Channel<Action>, saga: SagaWorker, ...args: unknown[]) => fork(function* (): Generator<unknown, void, unknown> {
55
- while (true) {
56
- const action = (yield take(patternOrChannel as unknown as ActionPattern)) as { meta: { requestId: string } }
57
- const { requestId } = action.meta
58
- tasks[requestId] = createDeferred()
59
- const task = (yield fork(saga, ...args.concat(action))) as Task
60
- tasks[requestId].resolve(task)
41
+ lastRequestId = requestId
42
+ const worker = wrap(saga as unknown as SagaWorker)
43
+ lastTask = (yield fork(worker as any, ...args.concat(action))) as Task
61
44
  }
62
45
  })
63
-
64
- return customTakeEvery(pattern as ActionPattern | Channel<Action>, wrapper, ...args)
65
46
  }
66
47
 
67
48
  export function takeAggregateAsync<A extends Action = Action>(pattern: ActionPattern<A> | Channel<A>, saga: (action: A, ...args: unknown[]) => unknown, ...args: unknown[]) {
package/src/types.ts CHANGED
@@ -11,8 +11,12 @@ export interface Deferred<T = unknown> {
11
11
  export interface Request {
12
12
  requestId?: string
13
13
  deferred?: Deferred
14
- onAdd?: (request: Request) => void
15
14
  abort?: () => void
15
+ task?: any // Using any to avoid circular dependency with Task from redux-saga
16
+ onAdd?: (request: Request) => void
17
+ handled?: boolean
16
18
  }
17
19
 
18
20
  export type SagaAction<Returned, ThunkArg = void> = AsyncThunk<Returned, ThunkArg, object>
21
+
22
+ export type SagaActionFromCreator<T extends (...args: any[]) => any> = ReturnType<ReturnType<T>['pending']>
package/src/utils.ts CHANGED
@@ -9,6 +9,7 @@ export const addRequest = (requestId: string) => {
9
9
  ...requests[requestId],
10
10
  requestId,
11
11
  deferred,
12
+ handled: false
12
13
  }
13
14
 
14
15
  if (requests[requestId]) {
@@ -20,6 +21,13 @@ export const addRequest = (requestId: string) => {
20
21
  requests[requestId] = request
21
22
  }
22
23
 
24
+ // Auto-cleanup if not picked up by a saga within 30 seconds
25
+ setTimeout(() => {
26
+ if (requests[requestId] && !requests[requestId].handled) {
27
+ delete requests[requestId]
28
+ }
29
+ }, 30000)
30
+
23
31
  return deferred.promise
24
32
  }
25
33
 
@@ -27,6 +35,13 @@ export const cleanup = (requestId: string) => {
27
35
  delete requests[requestId]
28
36
  }
29
37
 
38
+ /** @internal */
39
+ export const _clearInternalState = () => {
40
+ for (const key in requests) {
41
+ delete requests[key]
42
+ }
43
+ }
44
+
30
45
  export const setRequestAbort = (requestId: string, abort: () => void) => {
31
46
  if (requests[requestId]) {
32
47
  requests[requestId].abort = abort
@@ -36,10 +51,15 @@ export const setRequestAbort = (requestId: string, abort: () => void) => {
36
51
  export function* getRequest(requestId: string): Generator<unknown, Request, unknown> {
37
52
  const request = requests[requestId]
38
53
 
54
+ if (request) {
55
+ request.handled = true
56
+ }
57
+
39
58
  if (!request) {
40
59
  const result = yield (new Promise(onAdd => {
41
60
  requests[requestId] = {
42
- onAdd: (req: Request) => onAdd(req)
61
+ onAdd: (req: Request) => onAdd(req),
62
+ handled: true
43
63
  }
44
64
  }))
45
65
  return result as Request
@@ -75,3 +95,9 @@ export const wrap = (saga: SagaWorker) => function* (action: unknown, ...rest: u
75
95
  cleanup(requestId)
76
96
  }
77
97
  }
98
+
99
+ /** @internal */
100
+ export const _getInternalState = () => ({
101
+ requests,
102
+ size: Object.keys(requests).length
103
+ })