@hardlydifficult/poller 1.0.3 → 1.0.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.
Files changed (2) hide show
  1. package/README.md +77 -47
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @hardlydifficult/poller
2
2
 
3
- Polls an async function at a configurable interval and triggers callbacks when the result changes.
3
+ A generic polling utility that periodically fetches data and invokes a callback only when the result changes, using JSON-based deep equality comparison.
4
4
 
5
- ## Install
5
+ ## Installation
6
6
 
7
7
  ```bash
8
8
  npm install @hardlydifficult/poller
@@ -13,6 +13,31 @@ npm install @hardlydifficult/poller
13
13
  ```typescript
14
14
  import { Poller } from "@hardlydifficult/poller";
15
15
 
16
+ // Create a poller that fetches mock API data every 5 seconds
17
+ const poller = new Poller(
18
+ async () => {
19
+ const response = await fetch("https://api.example.com/status");
20
+ return await response.json();
21
+ },
22
+ (current, previous) => {
23
+ console.log("Status changed:", current);
24
+ },
25
+ 5000,
26
+ (error) => {
27
+ console.error("Polling error:", error);
28
+ }
29
+ );
30
+
31
+ await poller.start(); // Begins polling immediately
32
+ // ... later
33
+ poller.stop(); // Stops polling
34
+ ```
35
+
36
+ ### Basic Usage
37
+
38
+ ```typescript
39
+ import { Poller } from "@hardlydifficult/poller";
40
+
16
41
  const poller = new Poller(
17
42
  async () => await fetchCurrentState(),
18
43
  (current, previous) => console.log("State changed!", current),
@@ -30,7 +55,7 @@ poller.stop();
30
55
 
31
56
  ## Polling
32
57
 
33
- The `Poller` class periodically fetches data and invokes a callback when the result changes. It fetches immediately on `start()`, then at the configured interval.
58
+ The `Poller` class fetches data at a configurable interval and invokes a callback when the result changes. It fetches immediately on `start()`, then at the configured interval.
34
59
 
35
60
  ```typescript
36
61
  import { Poller } from "@hardlydifficult/poller";
@@ -51,9 +76,30 @@ await poller.start();
51
76
  // Fetches immediately, then every 10 seconds
52
77
  ```
53
78
 
79
+ ### `start()`
80
+
81
+ Starts polling. Fetches immediately, then at the configured interval. Calling `start()` multiple times is safe (idempotent).
82
+
83
+ ```typescript
84
+ await poller.start(); // Fetches immediately
85
+ await poller.start(); // No-op, already running
86
+ ```
87
+
88
+ ### `stop()`
89
+
90
+ Stops polling and cleans up all timers.
91
+
92
+ ```typescript
93
+ poller.stop();
94
+ // No more polls will fire
95
+ ```
96
+
54
97
  ## Change Detection
55
98
 
56
- Changes are detected using JSON serialization for deep equality. Structurally identical objects are considered unchanged, even if they are different references.
99
+ Changes are detected using JSON serialization for deep equality. Structurally identical objects are considered unchangedeven if they are different references.
100
+
101
+ - On first poll, `onChange(current, undefined)` is invoked.
102
+ - On subsequent polls, `onChange(current, previous)` is invoked only if the new value differs structurally.
57
103
 
58
104
  ```typescript
59
105
  const poller = new Poller(
@@ -72,6 +118,10 @@ await poller.start();
72
118
 
73
119
  Call `trigger()` to manually poll immediately, with optional debouncing. Multiple rapid triggers are coalesced into a single poll.
74
120
 
121
+ - Accepts a `debounceMs` parameter (default: `1000`).
122
+ - Multiple rapid calls reset the debounce timer — only the last trigger fires.
123
+ - No-op if the poller is not running.
124
+
75
125
  ```typescript
76
126
  const poller = new Poller(
77
127
  async () => await fetchData(),
@@ -95,7 +145,8 @@ poller.trigger(500); // Only one poll fires after 500ms
95
145
 
96
146
  ## Error Handling
97
147
 
98
- Errors during fetch are passed to the optional `onError` callback. Polling continues regardless of errors.
148
+ - Errors during fetch are caught and passed to the optional `onError` callback.
149
+ - Polling continues after errors — no automatic retry logic is applied.
99
150
 
100
151
  ```typescript
101
152
  const poller = new Poller(
@@ -111,63 +162,42 @@ const poller = new Poller(
111
162
  await poller.start();
112
163
  ```
113
164
 
114
- ## Lifecycle
165
+ ## Core Features
115
166
 
116
- ### `start()`
117
-
118
- Starts polling. Fetches immediately, then at the configured interval. Calling `start()` multiple times is safe (idempotent).
119
-
120
- ```typescript
121
- const poller = new Poller(
122
- async () => await fetchData(),
123
- (current) => console.log("Updated:", current),
124
- 5000
125
- );
167
+ ### Polling Lifecycle
126
168
 
127
- await poller.start(); // Fetches immediately
128
- await poller.start(); // No-op, already running
129
- ```
169
+ The `Poller` manages its lifecycle with `start()` and `stop()` methods.
130
170
 
131
- ### `stop()`
171
+ - `start()`: Begins polling immediately and then at the configured interval. Idempotent — calling it multiple times has no effect after the first call.
172
+ - `stop()`: Clears the polling timer and any pending debounced triggers. No-op if already stopped.
132
173
 
133
- Stops polling and cleans up all timers.
174
+ ### Concurrent Fetch Handling
134
175
 
135
- ```typescript
136
- poller.stop();
137
- // No more polls will fire
138
- ```
176
+ Overlapping fetches (e.g., due to slow network) are automatically skipped:
177
+ - If `poll()` is already running when the interval fires, the next poll is skipped until the current one completes.
139
178
 
140
179
  ## API Reference
141
180
 
142
- ### Constructor
143
-
144
- ```typescript
145
- new Poller<T>(
146
- fetchFn: () => Promise<T>,
147
- onChange: (current: T, previous: T | undefined) => void,
148
- intervalMs: number,
149
- onError?: (error: unknown) => void
150
- )
151
- ```
181
+ ### `Poller<T>` Constructor
152
182
 
153
- | Parameter | Description |
154
- |-----------|-------------|
155
- | `fetchFn` | Async function that returns the current state |
156
- | `onChange` | Called with `(current, previous)` when state changes |
157
- | `intervalMs` | Polling interval in milliseconds |
158
- | `onError` | Optional error handler; polling continues on errors |
183
+ | Parameter | Type | Description |
184
+ |-------------|---------------------------------------|------------------------------------------|
185
+ | `fetchFn` | `() => Promise<T>` | Async function returning the current state |
186
+ | `onChange` | `(current: T, previous: T | undefined) => void` | Callback invoked on state changes |
187
+ | `intervalMs`| `number` | Polling interval in milliseconds |
188
+ | `onError?` | `(error: unknown) => void` | Optional callback for fetch errors |
159
189
 
160
190
  ### Methods
161
191
 
162
- | Method | Description |
163
- |--------|-------------|
164
- | `start()` | Start polling (fetches immediately, then at interval) |
165
- | `stop()` | Stop polling and clean up timers |
166
- | `trigger(debounceMs?)` | Manually trigger a poll with debounce (default 1000ms) |
192
+ | Method | Signature | Description |
193
+ |--------------|----------------------------------------|----------------------------------------------|
194
+ | `start()` | `(): Promise<void>` | Begins polling (immediate + interval-based) |
195
+ | `stop()` | `(): void` | Stops polling and clears timers |
196
+ | `trigger()` | `(debounceMs?: number) => void` | Triggers a debounced manual poll |
167
197
 
168
198
  ### Behavior
169
199
 
170
- - **Deep equality** — uses JSON serialization to detect changes
200
+ - **Deep equality** — uses JSON serialization to detect structural changes
171
201
  - **Overlap prevention** — skips a poll if the previous fetch is still running
172
202
  - **Error resilience** — continues polling after fetch errors
173
203
  - **Idempotent start** — calling `start()` multiple times is safe
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardlydifficult/poller",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [