From 8e8fa594978e1f8215879ebc425a10f1defd020c Mon Sep 17 00:00:00 2001 From: TENSIILE Date: Tue, 3 Mar 2026 20:17:08 +0300 Subject: [PATCH 1/3] chore(ci): adds ci startup when creating a PR (#5) --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..70c6dff --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + pull_request: + branches: + - main + - develop + - 'release/v*.*.*' + push: + branches: + - main + - develop + - 'release/v*.*.*' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci + + - name: Run checks + run: npm run verify + + - name: Build project + run: npm run build From b559683d8584b62e50982e7e93e4e275902f6983 Mon Sep 17 00:00:00 2001 From: TENSIILE Date: Tue, 3 Mar 2026 20:35:27 +0300 Subject: [PATCH 2/3] feat(use-reusable-aborter): adds the useReusableAborter hook (#5) --- readme.md | 92 ++++++++++++++++++- src/hooks/index.ts | 1 + src/hooks/use-reusable-aborter/index.ts | 1 + .../use-reusable-aborter.ts | 9 ++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/hooks/use-reusable-aborter/index.ts create mode 100644 src/hooks/use-reusable-aborter/use-reusable-aborter.ts diff --git a/readme.md b/readme.md index 556f845..9eea17b 100644 --- a/readme.md +++ b/readme.md @@ -135,6 +135,63 @@ const { requestState } = useAborter(); console.log(requestState); // 'cancelled' / 'pending' / 'fulfilled' / 'rejected' / 'aborted' ``` +### `useReusableAborter` + +#### Props + +```typescript +// The type can be found in `saborter/types` +const reusableAborter = new useReusableAborter(props?: ReusableAborterProps); +``` + +#### Props Parameters + +| Parameter | Type | Description | Required | +| --------- | ---------------------- | ------------------------------------- | -------- | +| `props` | `ReusableAborterProps` | ReusableAborter configuration options | No | + +**ReusableAborterProps:** + +```typescript +{ + /** + * Determines which listeners are carried over when the abort signal is reset. + * - If `true`, all listeners (both `onabort` and event listeners) are preserved. + * - If `false`, no listeners are preserved. + * - If an object, specific listener types can be enabled/disabled individually. + */ + attractListeners?: boolean | AttractListeners; +} +``` + +#### Properties + +`signal: AbortSignal` + +Returns the `AbortSignal` associated with the current controller. + +```javascript +const reusableAborter = useReusableAborter(); + +// Using signal in the request +fetch('/api/data', { + signal: reusableAborter.signal +}); +``` + +#### Methods + +`abort(reason?): void` + +**Parameters:** + +- `reason?: any` - the reason for aborting the request. + +Immediately cancels the currently executing request. + +> [!NOTE] +> Can be called multiple times. Each call will restore the `signal`, and the `aborted` property will always be `false`. + ## 🎯 Usage Examples ### Basic Usage @@ -172,7 +229,7 @@ const Component = () => { }; ``` -### The `AbortError` `initiator` changed while unmounting the component. +### The `AbortError` `initiator` changed while unmounting the component ```javascript import { AbortError } from 'saborter'; @@ -193,6 +250,39 @@ const Component = () => { }; ``` +### Using `useReusableAborter` + +```typescript +const aborter = new useReusableAborter(); + +// Get the current signal +const signal = aborter.signal; + +// Attach listeners +signal.addEventListener('abort', () => console.log('Listener 1')); +signal.addEventListener('abort', () => console.log('Listener 2'), { once: true }); // won't be recovered + +// Set onabort handler +signal.onabort = () => console.log('Onabort handler'); + +// First abort +aborter.abort('First reason'); +// Output: +// Listener 1 +// Listener 2 (once) +// Onabort handler + +// The signal is now a fresh one, but the non‑once listeners and onabort are reattached +signal.addEventListener('abort', () => console.log('Listener 3')); // new listener, will survive next abort + +// Second abort +aborter.abort('Second reason'); +// Output: +// Listener 1 +// Onabort handler +// Listener 3 +``` + ## 📋 License MIT License - see [LICENSE](./LICENSE) for details. diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 135fb70..8ca50fb 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1 +1,2 @@ export * from './use-aborter'; +export * from './use-reusable-aborter'; diff --git a/src/hooks/use-reusable-aborter/index.ts b/src/hooks/use-reusable-aborter/index.ts new file mode 100644 index 0000000..915a57f --- /dev/null +++ b/src/hooks/use-reusable-aborter/index.ts @@ -0,0 +1 @@ +export * from './use-reusable-aborter'; diff --git a/src/hooks/use-reusable-aborter/use-reusable-aborter.ts b/src/hooks/use-reusable-aborter/use-reusable-aborter.ts new file mode 100644 index 0000000..6678684 --- /dev/null +++ b/src/hooks/use-reusable-aborter/use-reusable-aborter.ts @@ -0,0 +1,9 @@ +import { useRef } from 'react'; +import { ReusableAborter } from 'saborter'; +import { ReusableAborterProps } from 'saborter/types'; + +export const useReusableAborter = (props: ReusableAborterProps = {}) => { + const reusableAborter = useRef(new ReusableAborter(props)); + + return reusableAborter.current; +}; From cd3348b4ab28bf22265974ae5299cbee4fdbb2c2 Mon Sep 17 00:00:00 2001 From: TENSIILE Date: Tue, 3 Mar 2026 20:47:17 +0300 Subject: [PATCH 3/3] chore: updating the version of the saborter in package-lock (#5) --- package-lock.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 37ed0dc..4cdce54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "prettier": "^3.7.4", "react": "^17.0.2", "rimraf": "^6.1.2", - "saborter": "^1.4.2", + "saborter": "^2.0.1", "ts-node": "^10.9.2", "typescript": "^5.9.3", "vite": "^7.3.0", @@ -36,7 +36,7 @@ }, "peerDependencies": { "react": ">=17.0.0 <=20.0.0", - "saborter": "^1.4.2" + "saborter": "^2.0.1" } }, "node_modules/@babel/code-frame": { @@ -6883,9 +6883,9 @@ } }, "node_modules/saborter": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/saborter/-/saborter-1.4.2.tgz", - "integrity": "sha512-+z+ixvBB8cHfmh19hjMVsHu+fKzhYjrFw/RwNkXUtwLkWIm2r7vxlNetzWQlDUUZPakG6lZOKsHjbeosmT9pIA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/saborter/-/saborter-2.0.1.tgz", + "integrity": "sha512-S8n1PcieQP+yPDyIl/PnMUOOF7pM5x+VhO/JDEoGsVmq8YHsCSMsY8dIWuD6uH22n4NxTg0RfUhyn5+N5LxwkA==", "dev": true, "license": "MIT" },