jrx 0.1.0 → 0.2.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.
- package/README.md +57 -2
- package/addEvtListener.d.ts +15 -0
- package/addEvtListener.js +6 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/package.json +5 -11
- package/retry.d.ts +5 -0
- package/retry.js +6 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A lightweight TypeScript library for managing side effects, subscriptions, and a
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm
|
|
8
|
+
npm i jrx
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Features
|
|
@@ -20,6 +20,7 @@ npm install jrx
|
|
|
20
20
|
## API Overview
|
|
21
21
|
|
|
22
22
|
- [`makeRenderLoop()`](#makerenderloop) - Render loops with automatic cleanup
|
|
23
|
+
- [`addEvtListener(target, event, handler, option?)`](#addevtlistenertarget-event-handler-option) - Event listeners with cleanup
|
|
23
24
|
- [`addInterval(cb, ms)`](#addintervalcb-ms) - Repeating intervals with cleanup
|
|
24
25
|
- [`addIntervalAsync(cb, ms)`](#addintervalasynccb-ms) - Async intervals with cancellation
|
|
25
26
|
- [`addRequestAnimationFrame(cb)`](#addrequestanimationframecb) - Single animation frame with cleanup
|
|
@@ -29,6 +30,7 @@ npm install jrx
|
|
|
29
30
|
- [`addTransition(cb, durationMs)`](#addtransitioncb-durationms) - Progress-based animations
|
|
30
31
|
- [`computed(fn, getDeps?)`](#computedfn-getdeps) - Memoized computed values
|
|
31
32
|
- [`retry(cb, options?)`](#retrycb-options) - Async retry with exponential backoff
|
|
33
|
+
- [`addRetry(cb, options?)`](#addretrycb-options) - Fire-and-forget retry with disposal
|
|
32
34
|
|
|
33
35
|
## API
|
|
34
36
|
|
|
@@ -58,6 +60,28 @@ requestAnimationFrame(loop)
|
|
|
58
60
|
dispose()
|
|
59
61
|
```
|
|
60
62
|
|
|
63
|
+
### `addEvtListener(target, event, handler, option?)`
|
|
64
|
+
|
|
65
|
+
Adds an event listener to a target and returns a disposer that removes it. Works with any object that implements `addEventListener`/`removeEventListener` (DOM elements, `window`, `document`, etc.).
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { addEvtListener } from 'jrx'
|
|
69
|
+
|
|
70
|
+
// Basic usage
|
|
71
|
+
const dispose = addEvtListener(window, 'resize', (e) => {
|
|
72
|
+
console.log('Window resized', e)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// With options
|
|
76
|
+
const dispose2 = addEvtListener(element, 'click', (e) => {
|
|
77
|
+
console.log('Clicked', e)
|
|
78
|
+
}, { capture: true })
|
|
79
|
+
|
|
80
|
+
// Cleanup
|
|
81
|
+
dispose()
|
|
82
|
+
dispose2()
|
|
83
|
+
```
|
|
84
|
+
|
|
61
85
|
### `addInterval(cb, ms)`
|
|
62
86
|
|
|
63
87
|
Creates a repeating interval with cleanup. The callback can optionally return a cleanup function that runs before the next invocation.
|
|
@@ -238,7 +262,7 @@ Retries an async operation with exponential backoff on failure.
|
|
|
238
262
|
**Default backoff:** `[5, 5, 10, 10, 20, 20, 40, 40, 60, -1]` seconds (where `-1` means retry forever with 60s delay)
|
|
239
263
|
|
|
240
264
|
```typescript
|
|
241
|
-
import retry from 'jrx
|
|
265
|
+
import {retry} from 'jrx'
|
|
242
266
|
|
|
243
267
|
// Basic usage - retries with default backoff
|
|
244
268
|
const result = await retry(async (disposer, { resetBackoff }) => {
|
|
@@ -298,6 +322,37 @@ console.log(data) // T | undefined
|
|
|
298
322
|
- `disposer`: A disposer for the current retry attempt. Check `disposer.signal.aborted` to handle cancellation
|
|
299
323
|
- `info.resetBackoff()`: Call this to reset the backoff counter to the beginning (useful when making partial progress)
|
|
300
324
|
|
|
325
|
+
### `addRetry(cb, options?)`
|
|
326
|
+
|
|
327
|
+
Fire-and-forget version of `retry`. Starts the retry loop in the background and returns a dispose function to cancel it.
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
import {addRetry} from 'jrx'
|
|
331
|
+
|
|
332
|
+
// Start a retry loop in the background
|
|
333
|
+
const dispose = addRetry(async (disposer, { resetBackoff }) => {
|
|
334
|
+
const response = await fetch('/api/data')
|
|
335
|
+
if (!response.ok) throw new Error('Failed')
|
|
336
|
+
processData(await response.json())
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
// Cancel the retry loop
|
|
340
|
+
dispose()
|
|
341
|
+
|
|
342
|
+
// With custom backoff
|
|
343
|
+
const dispose2 = addRetry(
|
|
344
|
+
async (disposer) => {
|
|
345
|
+
await connectWebSocket()
|
|
346
|
+
},
|
|
347
|
+
{ backoffSec: [1, 2, 5, -1] }
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
dispose2()
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Options:**
|
|
354
|
+
- `backoffSec`: Array of retry delays in seconds (same as `retry`)
|
|
355
|
+
|
|
301
356
|
## Cleanup Pattern
|
|
302
357
|
|
|
303
358
|
All functions return disposer functions that clean up resources:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type IEventHandler<Target extends {
|
|
2
|
+
addEventListener(event: string, handler: any, option?: any): any;
|
|
3
|
+
}, EventName> = Target['addEventListener'] extends (event: EventName, handler: infer Handler, ...args: any[]) => any ? Handler extends (...params: any[]) => any ? Handler : never : never;
|
|
4
|
+
type IEventOption<Target extends {
|
|
5
|
+
addEventListener(event: string, handler: any, option?: any): any;
|
|
6
|
+
}, EventName, Handler> = Target['addEventListener'] extends (event: EventName, handler: Handler, option: infer Option) => any ? Option : never;
|
|
7
|
+
export default function addEvtListener<Target extends {
|
|
8
|
+
addEventListener(event: string, handler: any, option?: any): any;
|
|
9
|
+
removeEventListener(event: string, handler: any, option?: any): any;
|
|
10
|
+
}, EventName extends Parameters<Target['addEventListener']>[0], Handler extends IEventHandler<Target, EventName>>(target: Target, event: EventName, handler: Handler, option?: IEventOption<Target, EventName, Handler>): () => void;
|
|
11
|
+
export default function addEvtListener<Target extends {
|
|
12
|
+
addEventListener(event: string, handler: any, option?: any): any;
|
|
13
|
+
removeEventListener(event: string, handler: any, option?: any): any;
|
|
14
|
+
}, EventName extends Parameters<Target['addEventListener']>[0]>(target: Target, event: EventName, handler: (...args: any[]) => any, option?: IEventOption<Target, EventName, IEventHandler<Target, EventName>>): () => void;
|
|
15
|
+
export {};
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { type Disposer } from 'jdisposer';
|
|
2
|
+
import retry, { addRetry } from './retry.js';
|
|
3
|
+
import computed from './computed.js';
|
|
4
|
+
import addEvtListener from './addEvtListener.js';
|
|
5
|
+
export { retry, computed, addEvtListener, addRetry };
|
|
2
6
|
export declare function makeRenderLoop(): {
|
|
3
7
|
loop(this: void, time: DOMHighResTimeStamp): void;
|
|
4
8
|
setLoop(this: void, loop: (time: DOMHighResTimeStamp) => void | (() => void)): () => void;
|
package/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { makeDisposer, makeReset } from 'jdisposer';
|
|
2
|
+
import retry, { addRetry } from './retry.js';
|
|
3
|
+
import computed from './computed.js';
|
|
4
|
+
import addEvtListener from './addEvtListener.js';
|
|
5
|
+
export { retry, computed, addEvtListener, addRetry };
|
|
2
6
|
export function makeRenderLoop() {
|
|
3
7
|
let loop_;
|
|
4
8
|
const reset = makeReset();
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jrx",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"prepublishOnly": "npx tsc
|
|
6
|
+
"prepublishOnly": "npx tsc",
|
|
7
7
|
"test": "node --test tests/*.test.ts",
|
|
8
8
|
"test:coverage": "node --test --experimental-test-coverage tests/*.test.ts"
|
|
9
9
|
},
|
|
@@ -11,14 +11,6 @@
|
|
|
11
11
|
".": {
|
|
12
12
|
"types": "./index.d.ts",
|
|
13
13
|
"import": "./index.js"
|
|
14
|
-
},
|
|
15
|
-
"./retry": {
|
|
16
|
-
"types": "./retry.d.ts",
|
|
17
|
-
"import": "./retry.js"
|
|
18
|
-
},
|
|
19
|
-
"./computed": {
|
|
20
|
-
"types": "./computed.d.ts",
|
|
21
|
-
"import": "./computed.js"
|
|
22
14
|
}
|
|
23
15
|
},
|
|
24
16
|
"repository": {
|
|
@@ -31,7 +23,9 @@
|
|
|
31
23
|
"retry.js",
|
|
32
24
|
"retry.d.ts",
|
|
33
25
|
"computed.js",
|
|
34
|
-
"computed.d.ts"
|
|
26
|
+
"computed.d.ts",
|
|
27
|
+
"addEvtListener.js",
|
|
28
|
+
"addEvtListener.d.ts"
|
|
35
29
|
],
|
|
36
30
|
"devDependencies": {
|
|
37
31
|
"@types/node": "^25.2.1",
|
package/retry.d.ts
CHANGED
|
@@ -10,3 +10,8 @@ export default function retry<T>(cb: (disposer: Disposer, info: {
|
|
|
10
10
|
backoffSec?: number[];
|
|
11
11
|
disposer: Disposer;
|
|
12
12
|
}): Promise<T | undefined>;
|
|
13
|
+
export declare function addRetry<T>(cb: (disposer: Disposer, info: {
|
|
14
|
+
resetBackoff(): void;
|
|
15
|
+
}) => T | Promise<T>, options?: {
|
|
16
|
+
backoffSec?: number[];
|
|
17
|
+
}): (this: void) => void;
|
package/retry.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { makeReset } from 'jdisposer';
|
|
1
|
+
import { makeDisposer, makeReset } from 'jdisposer';
|
|
2
2
|
export default async function retry(cb, { backoffSec = [5, 5, 10, 10, 20, 20, 40, 40, 60, -1], // -1: retry forever with the last backoff . first element must not be -1
|
|
3
3
|
disposer, } = {}) {
|
|
4
4
|
const reset = makeReset();
|
|
@@ -30,3 +30,8 @@ disposer, } = {}) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
export function addRetry(cb, options) {
|
|
34
|
+
const disposer = makeDisposer();
|
|
35
|
+
void retry(cb, { ...options, disposer });
|
|
36
|
+
return disposer.dispose;
|
|
37
|
+
}
|