jrx 0.0.1 → 0.1.0

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
@@ -17,6 +17,19 @@ npm install jrx
17
17
  - Composable reactive utilities
18
18
  - Browser and Node.js compatible
19
19
 
20
+ ## API Overview
21
+
22
+ - [`makeRenderLoop()`](#makerenderloop) - Render loops with automatic cleanup
23
+ - [`addInterval(cb, ms)`](#addintervalcb-ms) - Repeating intervals with cleanup
24
+ - [`addIntervalAsync(cb, ms)`](#addintervalasynccb-ms) - Async intervals with cancellation
25
+ - [`addRequestAnimationFrame(cb)`](#addrequestanimationframecb) - Single animation frame with cleanup
26
+ - [`addRequestAnimationFrameLoop(cb)`](#addrequestanimationframeloopcb) - Animation frame loops
27
+ - [`addSubs(subs, cb, options?)`](#addsubssubs-cb-options) - Multiple subscription management
28
+ - [`addTimeout(cb, ms)`](#addtimeoutcb-ms) - Timeouts with cleanup
29
+ - [`addTransition(cb, durationMs)`](#addtransitioncb-durationms) - Progress-based animations
30
+ - [`computed(fn, getDeps?)`](#computedfn-getdeps) - Memoized computed values
31
+ - [`retry(cb, options?)`](#retrycb-options) - Async retry with exponential backoff
32
+
20
33
  ## API
21
34
 
22
35
  ### `makeRenderLoop()`
@@ -49,11 +62,13 @@ dispose()
49
62
 
50
63
  Creates a repeating interval with cleanup. The callback can optionally return a cleanup function that runs before the next invocation.
51
64
 
65
+ **Note:** The callback fires **immediately** on first call, then waits `ms` milliseconds **after** the previous callback completes. This is not a fixed-rate timer.
66
+
52
67
  ```typescript
53
68
  import { addInterval } from 'jrx'
54
69
 
55
70
  const dispose = addInterval(() => {
56
- console.log('Tick')
71
+ console.log('Tick') // Called immediately, then every 1000ms after completion
57
72
 
58
73
  // Optional: return cleanup function
59
74
  return () => {
@@ -69,10 +84,13 @@ dispose()
69
84
 
70
85
  Async version of `addInterval`. Waits for the callback to complete before scheduling the next invocation.
71
86
 
87
+ **Note:** The callback fires **immediately** on first call, then waits `ms` milliseconds **after** the previous async callback completes.
88
+
72
89
  ```typescript
73
90
  import { addIntervalAsync } from 'jrx'
74
91
 
75
92
  const dispose = addIntervalAsync(async (disposer) => {
93
+ // Called immediately, then 5000ms after each completion
76
94
  await fetchData()
77
95
 
78
96
  // Check if disposed during async operation
@@ -86,7 +104,7 @@ dispose()
86
104
 
87
105
  ### `addRequestAnimationFrame(cb)`
88
106
 
89
- Creates a `requestAnimationFrame` loop with cleanup.
107
+ Executes a callback on the next animation frame with cleanup.
90
108
 
91
109
  ```typescript
92
110
  import { addRequestAnimationFrame } from 'jrx'
@@ -100,6 +118,27 @@ const dispose = addRequestAnimationFrame((now) => {
100
118
  }
101
119
  })
102
120
 
121
+ // Cancel if needed before the frame fires
122
+ dispose()
123
+ ```
124
+
125
+ ### `addRequestAnimationFrameLoop(cb)`
126
+
127
+ Creates a continuous `requestAnimationFrame` loop with cleanup.
128
+
129
+ ```typescript
130
+ import { addRequestAnimationFrameLoop } from 'jrx'
131
+
132
+ const dispose = addRequestAnimationFrameLoop((now) => {
133
+ updateAnimation(now)
134
+
135
+ // Optional: return cleanup function
136
+ return () => {
137
+ cleanupAnimation()
138
+ }
139
+ })
140
+
141
+ // Stop the loop
103
142
  dispose()
104
143
  ```
105
144
 
@@ -196,6 +235,8 @@ console.log(value2.value) // Recomputed: 12
196
235
 
197
236
  Retries an async operation with exponential backoff on failure.
198
237
 
238
+ **Default backoff:** `[5, 5, 10, 10, 20, 20, 40, 40, 60, -1]` seconds (where `-1` means retry forever with 60s delay)
239
+
199
240
  ```typescript
200
241
  import retry from 'jrx/retry'
201
242
 
@@ -249,7 +290,8 @@ console.log(data) // T | undefined
249
290
  ```
250
291
 
251
292
  **Options:**
252
- - `backoffSec`: Array of retry delays in seconds. Use `-1` for infinite retries with the last delay. Default: `[5, 5, 10, 10, 20, 20, 40, 40, 60, -1]`
293
+ - `backoffSec`: Array of retry delays in seconds. Use `-1` for infinite retries with the last delay.
294
+ - Default: `[5, 5, 10, 10, 20, 20, 40, 40, 60, -1]`
253
295
  - `disposer`: Optional disposer for cancellation. When provided, the return type is `T | undefined`. Otherwise, the return type is `T`.
254
296
 
255
297
  **Callback parameters:**
@@ -269,7 +311,7 @@ const disposer = makeDisposer()
269
311
  // Collect disposers
270
312
  disposer.add(addInterval(() => console.log('tick'), 1000))
271
313
  disposer.add(addTimeout(() => console.log('timeout'), 5000))
272
- disposer.add(addRequestAnimationFrame((now) => render(now)))
314
+ disposer.add(addRequestAnimationFrameLoop((now) => render(now)))
273
315
 
274
316
  // Cleanup all at once
275
317
  disposer.dispose()
package/computed.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export default function computed<T, Deps extends any[] = unknown[]>(fn: () => T, getDeps?: () => Deps): {
2
+ readonly value: T;
3
+ };
package/computed.js ADDED
@@ -0,0 +1,17 @@
1
+ export default function computed(fn, getDeps) {
2
+ let cached;
3
+ let lastDeps;
4
+ let first = true;
5
+ return {
6
+ get value() {
7
+ if (!getDeps)
8
+ return fn();
9
+ const deps = getDeps();
10
+ if (!first && deps.length === lastDeps.length && deps.every((dep, idx) => Object.is(dep, lastDeps[idx])))
11
+ return cached;
12
+ first = false;
13
+ lastDeps = deps;
14
+ return (cached = fn());
15
+ },
16
+ };
17
+ }
package/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export declare function makeRenderLoop(): {
6
6
  export declare function addInterval(cb: () => void | (() => any), ms: number): () => void;
7
7
  export declare function addIntervalAsync(cb: (disposer: Disposer) => void | (() => any) | Promise<void> | Promise<() => any>, ms: number): () => void;
8
8
  export declare function addRequestAnimationFrame(cb: (now: DOMHighResTimeStamp) => void | (() => any)): () => void;
9
+ export declare function addRequestAnimationFrameLoop(cb: (now: DOMHighResTimeStamp) => void | (() => any)): () => void;
9
10
  export declare function addSubs<Subs extends any[]>(subs: Subs, cb: () => void | (() => void), { now }?: {
10
11
  now?: boolean;
11
12
  }): (this: void) => void;
package/index.js CHANGED
@@ -44,6 +44,14 @@ export function addIntervalAsync(cb, ms) {
44
44
  }
45
45
  }
46
46
  export function addRequestAnimationFrame(cb) {
47
+ const disposer = makeDisposer();
48
+ const raf = requestAnimationFrame(now => disposer.add(cb(now)));
49
+ return () => {
50
+ disposer.dispose();
51
+ cancelAnimationFrame(raf);
52
+ };
53
+ }
54
+ export function addRequestAnimationFrameLoop(cb) {
47
55
  const reset = makeReset();
48
56
  let raf = requestAnimationFrame(wrapper);
49
57
  return () => {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "jrx",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
- "prepublishOnly": "npx tsc index.ts retry.ts --target esnext --declaration --moduleResolution node",
6
+ "prepublishOnly": "npx tsc index.ts retry.ts computed.ts --target esnext --declaration --moduleResolution node",
7
7
  "test": "node --test tests/*.test.ts",
8
8
  "test:coverage": "node --test --experimental-test-coverage tests/*.test.ts"
9
9
  },