preact-missing-hooks 3.1.0 → 4.0.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.
Files changed (72) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/.husky/pre-push +1 -0
  3. package/.prettierignore +3 -0
  4. package/.prettierrc +6 -0
  5. package/Readme.md +149 -136
  6. package/dist/entry.cjs +21 -0
  7. package/dist/entry.js +2 -0
  8. package/dist/entry.js.map +1 -0
  9. package/dist/entry.modern.mjs +2 -0
  10. package/dist/entry.modern.mjs.map +1 -0
  11. package/dist/entry.module.js +2 -0
  12. package/dist/entry.module.js.map +1 -0
  13. package/dist/entry.umd.js +2 -0
  14. package/dist/entry.umd.js.map +1 -0
  15. package/dist/index.d.ts +13 -13
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.modern.mjs +2 -0
  18. package/dist/index.modern.mjs.map +1 -0
  19. package/dist/index.module.js.map +1 -1
  20. package/dist/index.umd.js.map +1 -1
  21. package/dist/indexedDB/dbController.d.ts +2 -2
  22. package/dist/indexedDB/index.d.ts +6 -6
  23. package/dist/indexedDB/openDB.d.ts +1 -1
  24. package/dist/indexedDB/tableController.d.ts +1 -1
  25. package/dist/indexedDB/types.d.ts +1 -2
  26. package/dist/react.js +1 -0
  27. package/dist/react.modern.mjs +1 -0
  28. package/dist/react.module.js +1 -0
  29. package/dist/react.umd.js +1 -0
  30. package/dist/useEventBus.d.ts +1 -1
  31. package/dist/useIndexedDB.d.ts +3 -3
  32. package/dist/useMutationObserver.d.ts +1 -1
  33. package/dist/useNetworkState.d.ts +3 -3
  34. package/dist/usePreferredTheme.d.ts +1 -1
  35. package/dist/useRageClick.d.ts +1 -1
  36. package/dist/useThreadedWorker.d.ts +1 -1
  37. package/dist/useTransition.d.ts +4 -1
  38. package/dist/useWorkerNotifications.d.ts +1 -1
  39. package/dist/useWrappedChildren.d.ts +3 -3
  40. package/eslint.config.mjs +10 -0
  41. package/package.json +60 -6
  42. package/scripts/generate-entry.cjs +34 -0
  43. package/src/index.ts +13 -13
  44. package/src/indexedDB/dbController.ts +101 -92
  45. package/src/indexedDB/index.ts +16 -11
  46. package/src/indexedDB/openDB.ts +49 -49
  47. package/src/indexedDB/requestToPromise.ts +17 -16
  48. package/src/indexedDB/tableController.ts +331 -257
  49. package/src/indexedDB/types.ts +35 -35
  50. package/src/useClipboard.ts +99 -97
  51. package/src/useEventBus.ts +39 -36
  52. package/src/useIndexedDB.ts +111 -111
  53. package/src/useMutationObserver.ts +26 -26
  54. package/src/useNetworkState.ts +124 -122
  55. package/src/usePreferredTheme.ts +68 -68
  56. package/src/useRageClick.ts +103 -103
  57. package/src/useThreadedWorker.ts +165 -165
  58. package/src/useTransition.ts +22 -19
  59. package/src/useWasmCompute.ts +209 -204
  60. package/src/useWebRTCIP.ts +181 -176
  61. package/src/useWorkerNotifications.ts +28 -20
  62. package/src/useWrappedChildren.ts +72 -58
  63. package/tests/react-adapter.tsx +12 -0
  64. package/tests/setup-react.ts +4 -0
  65. package/tests/useClipboard.test.tsx +4 -2
  66. package/tests/useThreadedWorker.test.tsx +3 -1
  67. package/tests/useWasmCompute.test.tsx +1 -1
  68. package/tests/useWebRTCIP.test.tsx +3 -1
  69. package/vite.config.ts +11 -4
  70. package/vitest.config.preact.ts +20 -0
  71. package/vitest.config.react.ts +36 -0
  72. package/vitest.workspace.ts +6 -0
@@ -0,0 +1 @@
1
+ npm run lint && npm run format
@@ -0,0 +1 @@
1
+ npm run test && npm run size
@@ -0,0 +1,3 @@
1
+ dist
2
+ node_modules
3
+ *.min.js
package/.prettierrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": false,
4
+ "tabWidth": 2,
5
+ "trailingComma": "es5"
6
+ }
package/Readme.md CHANGED
@@ -36,7 +36,7 @@ A lightweight, extendable collection of React-like hooks for Preact, including u
36
36
  - **`useWorkerNotifications`** — Listens to a Worker's messages and maintains state: running tasks, completed/failed counts, event history, average task duration, throughput per second, and queue size. Worker posts `task_start` / `task_end` / `task_fail` / `queue_size`; returns `progress` (default view of all active worker data) plus individual stats.
37
37
  - Fully TypeScript compatible
38
38
  - Bundled with Microbundle
39
- - Zero dependencies (except `preact`)
39
+ - Zero dependencies (peer: `preact` or `react` — use `/react` for React)
40
40
 
41
41
  ---
42
42
 
@@ -46,22 +46,35 @@ A lightweight, extendable collection of React-like hooks for Preact, including u
46
46
  npm install preact-missing-hooks
47
47
  ```
48
48
 
49
+ Ensure your app has either **preact** or **react** installed (the package uses whichever is present).
50
+
49
51
  ---
50
52
 
51
53
  ## Import options
52
54
 
53
- - **Main package** Import from the package root:
54
- ```ts
55
- import { useThreadedWorker, useClipboard } from 'preact-missing-hooks'
56
- ```
55
+ Use the same import in Preact and React projects:
56
+
57
+ ```ts
58
+ import { useThreadedWorker, useClipboard } from "preact-missing-hooks";
59
+ ```
60
+
61
+ - **How it picks Preact vs React**
62
+ - **CommonJS / Node:** The package detects which of `preact` or `react` is installed and uses that build automatically.
63
+ - **ESM (Vite, Webpack, etc.):** Default is the Preact build. In a **React** app, add the `react` condition so the package resolves to the React build:
64
+ - **Vite:** `vite.config.ts` → `resolve: { conditions: ['react'] }`
65
+ - **Webpack:** `resolve.conditionNames` (or similar) to include `'react'`
66
+ - **Or** in React projects you can always import from the explicit entry: `preact-missing-hooks/react`.
67
+
57
68
  - **Subpath exports (tree-shakeable)** — Import a single hook:
69
+
58
70
  ```ts
59
- import { useThreadedWorker } from 'preact-missing-hooks/useThreadedWorker'
60
- import { useClipboard } from 'preact-missing-hooks/useClipboard'
61
- import { useWebRTCIP } from 'preact-missing-hooks/useWebRTCIP'
62
- import { useWasmCompute } from 'preact-missing-hooks/useWasmCompute'
63
- import { useWorkerNotifications } from 'preact-missing-hooks/useWorkerNotifications'
71
+ import { useThreadedWorker } from "preact-missing-hooks/useThreadedWorker";
72
+ import { useClipboard } from "preact-missing-hooks/useClipboard";
73
+ import { useWebRTCIP } from "preact-missing-hooks/useWebRTCIP";
74
+ import { useWasmCompute } from "preact-missing-hooks/useWasmCompute";
75
+ import { useWorkerNotifications } from "preact-missing-hooks/useWorkerNotifications";
64
76
  ```
77
+
65
78
  All hooks are available: `useTransition`, `useMutationObserver`, `useEventBus`, `useWrappedChildren`, `usePreferredTheme`, `useNetworkState`, `useClipboard`, `useRageClick`, `useThreadedWorker`, `useIndexedDB`, `useWebRTCIP`, `useWasmCompute`, `useWorkerNotifications`.
66
79
 
67
80
  ---
@@ -71,22 +84,22 @@ npm install preact-missing-hooks
71
84
  ### `useTransition`
72
85
 
73
86
  ```tsx
74
- import { useTransition } from 'preact-missing-hooks'
87
+ import { useTransition } from "preact-missing-hooks";
75
88
 
76
89
  function ExampleTransition() {
77
- const [startTransition, isPending] = useTransition()
90
+ const [startTransition, isPending] = useTransition();
78
91
 
79
92
  const handleClick = () => {
80
93
  startTransition(() => {
81
94
  // perform an expensive update here
82
- })
83
- }
95
+ });
96
+ };
84
97
 
85
98
  return (
86
99
  <button onClick={handleClick} disabled={isPending}>
87
- {isPending ? 'Loading...' : 'Click Me'}
100
+ {isPending ? "Loading..." : "Click Me"}
88
101
  </button>
89
- )
102
+ );
90
103
  }
91
104
  ```
92
105
 
@@ -95,21 +108,21 @@ function ExampleTransition() {
95
108
  ### `useMutationObserver`
96
109
 
97
110
  ```tsx
98
- import { useRef } from 'preact/hooks'
99
- import { useMutationObserver } from 'preact-missing-hooks'
111
+ import { useRef } from "preact/hooks";
112
+ import { useMutationObserver } from "preact-missing-hooks";
100
113
 
101
114
  function ExampleMutation() {
102
- const ref = useRef<HTMLDivElement>(null)
115
+ const ref = useRef<HTMLDivElement>(null);
103
116
 
104
117
  useMutationObserver(
105
118
  ref,
106
119
  (mutations) => {
107
- console.log('Detected mutations:', mutations)
120
+ console.log("Detected mutations:", mutations);
108
121
  },
109
- { childList: true, subtree: true },
110
- )
122
+ { childList: true, subtree: true }
123
+ );
111
124
 
112
- return <div ref={ref}>Observe this content</div>
125
+ return <div ref={ref}>Observe this content</div>;
113
126
  }
114
127
  ```
115
128
 
@@ -120,33 +133,33 @@ function ExampleMutation() {
120
133
  ```tsx
121
134
  // types.ts
122
135
  export type Events = {
123
- notify: (message: string) => void
124
- }
136
+ notify: (message: string) => void;
137
+ };
125
138
 
126
139
  // Sender.tsx
127
- import { useEventBus } from 'preact-missing-hooks'
128
- import type { Events } from './types'
140
+ import { useEventBus } from "preact-missing-hooks";
141
+ import type { Events } from "./types";
129
142
 
130
143
  function Sender() {
131
- const { emit } = useEventBus<Events>()
132
- return <button onClick={() => emit('notify', 'Hello World!')}>Send</button>
144
+ const { emit } = useEventBus<Events>();
145
+ return <button onClick={() => emit("notify", "Hello World!")}>Send</button>;
133
146
  }
134
147
 
135
148
  // Receiver.tsx
136
- import { useEventBus } from 'preact-missing-hooks'
137
- import { useState, useEffect } from 'preact/hooks'
138
- import type { Events } from './types'
149
+ import { useEventBus } from "preact-missing-hooks";
150
+ import { useState, useEffect } from "preact/hooks";
151
+ import type { Events } from "./types";
139
152
 
140
153
  function Receiver() {
141
- const [msg, setMsg] = useState<string>('')
142
- const { on } = useEventBus<Events>()
154
+ const [msg, setMsg] = useState<string>("");
155
+ const { on } = useEventBus<Events>();
143
156
 
144
157
  useEffect(() => {
145
- const unsubscribe = on('notify', setMsg)
146
- return unsubscribe
147
- }, [])
158
+ const unsubscribe = on("notify", setMsg);
159
+ return unsubscribe;
160
+ }, []);
148
161
 
149
- return <div>Message: {msg}</div>
162
+ return <div>Message: {msg}</div>;
150
163
  }
151
164
  ```
152
165
 
@@ -155,19 +168,19 @@ function Receiver() {
155
168
  ### `useWrappedChildren`
156
169
 
157
170
  ```tsx
158
- import { useWrappedChildren } from 'preact-missing-hooks'
171
+ import { useWrappedChildren } from "preact-missing-hooks";
159
172
 
160
173
  function ParentComponent({ children }) {
161
174
  // Inject common props into all children
162
175
  const injectProps = {
163
- className: 'enhanced-child',
164
- onClick: () => console.log('Child clicked!'),
165
- style: { border: '1px solid #ccc' },
166
- }
176
+ className: "enhanced-child",
177
+ onClick: () => console.log("Child clicked!"),
178
+ style: { border: "1px solid #ccc" },
179
+ };
167
180
 
168
- const wrappedChildren = useWrappedChildren(children, injectProps)
181
+ const wrappedChildren = useWrappedChildren(children, injectProps);
169
182
 
170
- return <div className="parent">{wrappedChildren}</div>
183
+ return <div className="parent">{wrappedChildren}</div>;
171
184
  }
172
185
 
173
186
  // Usage with preserve strategy (default - existing props are preserved)
@@ -175,21 +188,21 @@ function PreserveExample() {
175
188
  return (
176
189
  <ParentComponent>
177
190
  <button className="btn">Existing class preserved</button>
178
- <span style={{ color: 'red' }}>Both styles applied</span>
191
+ <span style={{ color: "red" }}>Both styles applied</span>
179
192
  </ParentComponent>
180
- )
193
+ );
181
194
  }
182
195
 
183
196
  // Usage with override strategy (injected props override existing ones)
184
197
  function OverrideExample() {
185
- const injectProps = { className: 'new-class' }
198
+ const injectProps = { className: "new-class" };
186
199
  const children = (
187
200
  <button className="old-class">Class will be overridden</button>
188
- )
201
+ );
189
202
 
190
- const wrappedChildren = useWrappedChildren(children, injectProps, 'override')
203
+ const wrappedChildren = useWrappedChildren(children, injectProps, "override");
191
204
 
192
- return <div>{wrappedChildren}</div>
205
+ return <div>{wrappedChildren}</div>;
193
206
  }
194
207
  ```
195
208
 
@@ -198,12 +211,12 @@ function OverrideExample() {
198
211
  ### `usePreferredTheme`
199
212
 
200
213
  ```tsx
201
- import { usePreferredTheme } from 'preact-missing-hooks'
214
+ import { usePreferredTheme } from "preact-missing-hooks";
202
215
 
203
216
  function ThemeAwareComponent() {
204
- const theme = usePreferredTheme() // 'light' | 'dark' | 'no-preference'
217
+ const theme = usePreferredTheme(); // 'light' | 'dark' | 'no-preference'
205
218
 
206
- return <div data-theme={theme}>Your system prefers: {theme}</div>
219
+ return <div data-theme={theme}>Your system prefers: {theme}</div>;
207
220
  }
208
221
  ```
209
222
 
@@ -212,18 +225,18 @@ function ThemeAwareComponent() {
212
225
  ### `useNetworkState`
213
226
 
214
227
  ```tsx
215
- import { useNetworkState } from 'preact-missing-hooks'
228
+ import { useNetworkState } from "preact-missing-hooks";
216
229
 
217
230
  function NetworkStatus() {
218
- const { online, effectiveType, saveData } = useNetworkState()
231
+ const { online, effectiveType, saveData } = useNetworkState();
219
232
 
220
233
  return (
221
234
  <div>
222
- Status: {online ? 'Online' : 'Offline'}
235
+ Status: {online ? "Online" : "Offline"}
223
236
  {effectiveType && ` (${effectiveType})`}
224
- {saveData && ' — Reduced data mode enabled'}
237
+ {saveData && " — Reduced data mode enabled"}
225
238
  </div>
226
- )
239
+ );
227
240
  }
228
241
  ```
229
242
 
@@ -232,34 +245,34 @@ function NetworkStatus() {
232
245
  ### `useClipboard`
233
246
 
234
247
  ```tsx
235
- import { useState } from 'preact/hooks'
236
- import { useClipboard } from 'preact-missing-hooks'
248
+ import { useState } from "preact/hooks";
249
+ import { useClipboard } from "preact-missing-hooks";
237
250
 
238
251
  function CopyButton() {
239
- const { copy, copied, error } = useClipboard({ resetDelay: 2000 })
252
+ const { copy, copied, error } = useClipboard({ resetDelay: 2000 });
240
253
 
241
254
  return (
242
- <button onClick={() => copy('Hello, World!')}>
243
- {copied ? 'Copied!' : 'Copy'}
255
+ <button onClick={() => copy("Hello, World!")}>
256
+ {copied ? "Copied!" : "Copy"}
244
257
  </button>
245
- )
258
+ );
246
259
  }
247
260
 
248
261
  function PasteInput() {
249
- const [text, setText] = useState('')
250
- const { paste } = useClipboard()
262
+ const [text, setText] = useState("");
263
+ const { paste } = useClipboard();
251
264
 
252
265
  const handlePaste = async () => {
253
- const content = await paste()
254
- setText(content)
255
- }
266
+ const content = await paste();
267
+ setText(content);
268
+ };
256
269
 
257
270
  return (
258
271
  <div>
259
272
  <input value={text} onChange={(e) => setText(e.target.value)} />
260
273
  <button onClick={handlePaste}>Paste</button>
261
274
  </div>
262
- )
275
+ );
263
276
  }
264
277
  ```
265
278
 
@@ -270,26 +283,26 @@ function PasteInput() {
270
283
  Detects rage clicks (multiple rapid clicks in the same area), e.g. when the UI is unresponsive. Report them to [Sentry](https://docs.sentry.io/product/issues/issue-details/replay-issues/rage-clicks/) or your error tracker to surface rage-click issues and lower rage-click-related support.
271
284
 
272
285
  ```tsx
273
- import { useRef } from 'preact/hooks'
274
- import { useRageClick } from 'preact-missing-hooks'
286
+ import { useRef } from "preact/hooks";
287
+ import { useRageClick } from "preact-missing-hooks";
275
288
 
276
289
  function SubmitButton() {
277
- const ref = useRef<HTMLButtonElement>(null)
290
+ const ref = useRef<HTMLButtonElement>(null);
278
291
 
279
292
  useRageClick(ref, {
280
293
  onRageClick: ({ count, event }) => {
281
294
  // Report to Sentry (or your error tracker) to create rage-click issues
282
- Sentry.captureMessage('Rage click detected', {
283
- level: 'warning',
284
- extra: { count, target: event.target, tag: 'rage_click' },
285
- })
295
+ Sentry.captureMessage("Rage click detected", {
296
+ level: "warning",
297
+ extra: { count, target: event.target, tag: "rage_click" },
298
+ });
286
299
  },
287
300
  threshold: 5, // min clicks (default 5, Sentry-style)
288
301
  timeWindow: 1000, // ms (default 1000)
289
302
  distanceThreshold: 30, // px (default 30)
290
- })
303
+ });
291
304
 
292
- return <button ref={ref}>Submit</button>
305
+ return <button ref={ref}>Submit</button>;
293
306
  }
294
307
  ```
295
308
 
@@ -300,16 +313,16 @@ function SubmitButton() {
300
313
  Runs async work in a queue with **sequential** (one task at a time, by priority) or **parallel** (worker pool) execution. Lower priority number = higher priority; same priority is FIFO.
301
314
 
302
315
  ```tsx
303
- import { useThreadedWorker } from 'preact-missing-hooks'
316
+ import { useThreadedWorker } from "preact-missing-hooks";
304
317
 
305
318
  // Sequential: one task at a time, sorted by priority
306
- const sequential = useThreadedWorker(fetchUser, { mode: 'sequential' })
319
+ const sequential = useThreadedWorker(fetchUser, { mode: "sequential" });
307
320
 
308
321
  // Parallel: up to N tasks at once
309
322
  const parallel = useThreadedWorker(processItem, {
310
- mode: 'parallel',
323
+ mode: "parallel",
311
324
  concurrency: 4,
312
- })
325
+ });
313
326
 
314
327
  // API (same for both modes)
315
328
  const {
@@ -320,11 +333,11 @@ const {
320
333
  queueSize, // tasks queued + running
321
334
  clearQueue, // clear pending tasks (running continue)
322
335
  terminate, // clear queue and reject new run()
323
- } = sequential
336
+ } = sequential;
324
337
 
325
338
  // Run with priority (1 = highest)
326
- await run({ userId: 1 }, { priority: 1 })
327
- await run({ userId: 2 }, { priority: 3 })
339
+ await run({ userId: 1 }, { priority: 1 });
340
+ await run({ userId: 2 }, { priority: 3 });
328
341
  ```
329
342
 
330
343
  ---
@@ -340,40 +353,40 @@ Production-ready IndexedDB hook: database initialization, table creation (with k
340
353
  **Database API:** `db.table(name)`, `db.hasTable(name)`, `db.transaction(storeNames, mode, callback, options?)`.
341
354
 
342
355
  ```tsx
343
- import { useIndexedDB } from 'preact-missing-hooks'
356
+ import { useIndexedDB } from "preact-missing-hooks";
344
357
 
345
358
  function App() {
346
359
  const { db, isReady, error } = useIndexedDB({
347
- name: 'my-app-db',
360
+ name: "my-app-db",
348
361
  version: 1,
349
362
  tables: {
350
- users: { keyPath: 'id', autoIncrement: true, indexes: ['email'] },
351
- settings: { keyPath: 'key' },
363
+ users: { keyPath: "id", autoIncrement: true, indexes: ["email"] },
364
+ settings: { keyPath: "key" },
352
365
  },
353
- })
366
+ });
354
367
 
355
- if (error) return <div>Failed to open database</div>
356
- if (!isReady || !db) return <div>Loading...</div>
368
+ if (error) return <div>Failed to open database</div>;
369
+ if (!isReady || !db) return <div>Loading...</div>;
357
370
 
358
- const users = db.table('users')
371
+ const users = db.table("users");
359
372
 
360
373
  // All operations return Promises and accept optional { onSuccess, onError }
361
- await users.insert({ email: 'a@b.com', name: 'Alice' })
362
- await users.update(1, { name: 'Alice Smith' })
363
- const found = await users.query((u) => u.email.startsWith('a@'))
364
- const n = await users.count()
365
- await users.delete(1)
366
- await users.upsert({ id: 2, email: 'b@b.com' })
367
- await users.bulkInsert([{ email: 'c@b.com' }, { email: 'd@b.com' }])
368
- await users.clear()
374
+ await users.insert({ email: "a@b.com", name: "Alice" });
375
+ await users.update(1, { name: "Alice Smith" });
376
+ const found = await users.query((u) => u.email.startsWith("a@"));
377
+ const n = await users.count();
378
+ await users.delete(1);
379
+ await users.upsert({ id: 2, email: "b@b.com" });
380
+ await users.bulkInsert([{ email: "c@b.com" }, { email: "d@b.com" }]);
381
+ await users.clear();
369
382
 
370
383
  // Full transaction support
371
- await db.transaction(['users', 'settings'], 'readwrite', async (tx) => {
372
- await tx.table('users').insert({ email: 'e@b.com' })
373
- await tx.table('settings').upsert({ key: 'theme', value: 'dark' })
374
- })
384
+ await db.transaction(["users", "settings"], "readwrite", async (tx) => {
385
+ await tx.table("users").insert({ email: "e@b.com" });
386
+ await tx.table("settings").upsert({ key: "theme", value: "dark" });
387
+ });
375
388
 
376
- return <div>DB ready. Tables: {db.hasTable('users') ? 'users' : ''}</div>
389
+ return <div>DB ready. Tables: {db.hasTable("users") ? "users" : ""}</div>;
377
390
  }
378
391
  ```
379
392
 
@@ -386,8 +399,8 @@ Detects client IP addresses using WebRTC ICE candidates and a STUN server (**fro
386
399
  Returns `{ ips: string[], loading: boolean, error: string | null }`. Options: `stunServers`, `timeout` (ms), `onDetect(ip)`.
387
400
 
388
401
  ```tsx
389
- import { useWebRTCIP } from 'preact-missing-hooks'
390
- import { useState, useEffect } from 'preact/hooks'
402
+ import { useWebRTCIP } from "preact-missing-hooks";
403
+ import { useState, useEffect } from "preact/hooks";
391
404
 
392
405
  function ClientIP() {
393
406
  const { ips, loading, error } = useWebRTCIP({
@@ -395,25 +408,25 @@ function ClientIP() {
395
408
  onDetect: (ip) => {
396
409
  /* optional: e.g. analytics */
397
410
  },
398
- })
399
- const [fallbackIP, setFallbackIP] = useState<string | null>(null)
411
+ });
412
+ const [fallbackIP, setFallbackIP] = useState<string | null>(null);
400
413
 
401
414
  // Fallback to public IP API when WebRTC fails or returns empty
402
415
  useEffect(() => {
403
- if (loading || ips.length > 0) return
416
+ if (loading || ips.length > 0) return;
404
417
  if (error) {
405
- fetch('https://api.ipify.org?format=json')
418
+ fetch("https://api.ipify.org?format=json")
406
419
  .then((r) => r.json())
407
420
  .then((d) => setFallbackIP(d.ip))
408
- .catch(() => {})
421
+ .catch(() => {});
409
422
  }
410
- }, [loading, ips.length, error])
423
+ }, [loading, ips.length, error]);
411
424
 
412
- if (loading) return <p>Detecting IP…</p>
413
- if (ips.length > 0) return <p>IPs (WebRTC): {ips.join(', ')}</p>
414
- if (fallbackIP) return <p>IP (fallback API): {fallbackIP}</p>
415
- if (error) return <p>WebRTC failed. Try fallback API.</p>
416
- return null
425
+ if (loading) return <p>Detecting IP…</p>;
426
+ if (ips.length > 0) return <p>IPs (WebRTC): {ips.join(", ")}</p>;
427
+ if (fallbackIP) return <p>IP (fallback API): {fallbackIP}</p>;
428
+ if (error) return <p>WebRTC failed. Try fallback API.</p>;
429
+ return null;
417
430
  }
418
431
  ```
419
432
 
@@ -426,23 +439,23 @@ Runs WebAssembly computation in a Web Worker so the main thread stays responsive
426
439
  Returns `{ compute, result, loading, error, ready }`. Options: `wasmUrl` (required), `exportName` (default `'compute'`), optional `workerUrl` (custom worker script), optional `importObject` (must be serializable for the default worker).
427
440
 
428
441
  ```tsx
429
- import { useWasmCompute } from 'preact-missing-hooks'
442
+ import { useWasmCompute } from "preact-missing-hooks";
430
443
 
431
444
  function AddWithWasm() {
432
445
  const { compute, result, loading, error, ready } = useWasmCompute<
433
446
  number,
434
447
  number
435
448
  >({
436
- wasmUrl: '/add.wasm',
437
- exportName: 'add',
438
- })
449
+ wasmUrl: "/add.wasm",
450
+ exportName: "add",
451
+ });
439
452
 
440
453
  const handleClick = () => {
441
- if (ready) compute(2).then(() => {})
442
- }
454
+ if (ready) compute(2).then(() => {});
455
+ };
443
456
 
444
- if (error) return <p>WASM unavailable: {error}</p>
445
- if (!ready) return <p>Loading WASM…</p>
457
+ if (error) return <p>WASM unavailable: {error}</p>;
458
+ if (!ready) return <p>Loading WASM…</p>;
446
459
  return (
447
460
  <div>
448
461
  <button onClick={handleClick} disabled={loading}>
@@ -450,7 +463,7 @@ function AddWithWasm() {
450
463
  </button>
451
464
  {result != null && <p>Result: {result}</p>}
452
465
  </div>
453
- )
466
+ );
454
467
  }
455
468
  ```
456
469
 
@@ -463,27 +476,27 @@ Listens to a Worker's `message` events and maintains state and derived stats. Yo
463
476
  Returns `runningTasks`, `completedCount`, `failedCount`, `eventHistory`, `averageDurationMs`, `throughputPerSecond`, `currentQueueSize`, and **`progress`** — a single object with all active worker data (running, completed, failed, totalProcessed, avg duration, throughput/s, queue). Options: `maxHistory` (default 100), `throughputWindowMs` (default 1000).
464
477
 
465
478
  ```tsx
466
- import { useWorkerNotifications } from 'preact-missing-hooks'
479
+ import { useWorkerNotifications } from "preact-missing-hooks";
467
480
 
468
481
  function WorkerDashboard({ worker }) {
469
482
  const { progress, eventHistory } = useWorkerNotifications(worker, {
470
483
  maxHistory: 50,
471
- })
484
+ });
472
485
 
473
486
  return (
474
487
  <div>
475
488
  <p>
476
- Running: {progress.runningTasks.length} | Done:{' '}
489
+ Running: {progress.runningTasks.length} | Done:{" "}
477
490
  {progress.completedCount} | Failed: {progress.failedCount}
478
491
  </p>
479
492
  <p>
480
- Avg: {progress.averageDurationMs.toFixed(0)}ms | Throughput:{' '}
481
- {progress.throughputPerSecond.toFixed(2)}/s | Queue:{' '}
493
+ Avg: {progress.averageDurationMs.toFixed(0)}ms | Throughput:{" "}
494
+ {progress.throughputPerSecond.toFixed(2)}/s | Queue:{" "}
482
495
  {progress.currentQueueSize}
483
496
  </p>
484
497
  <small>Events: {eventHistory.length}</small>
485
498
  </div>
486
- )
499
+ );
487
500
  }
488
501
  ```
489
502
 
package/dist/entry.cjs ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * Auto-detect Preact vs React and re-export the matching build.
4
+ * Used for require('preact-missing-hooks') in Node / CJS bundlers.
5
+ */
6
+ function detect() {
7
+ try {
8
+ require.resolve("preact");
9
+ return require("./index.js");
10
+ } catch (_) {
11
+ try {
12
+ require.resolve("react");
13
+ return require("./react.js");
14
+ } catch (_) {
15
+ throw new Error(
16
+ "preact-missing-hooks: Install either \"preact\" or \"react\" in your project."
17
+ );
18
+ }
19
+ }
20
+ }
21
+ module.exports = detect();
package/dist/entry.js ADDED
@@ -0,0 +1,2 @@
1
+ var e=require("preact/hooks"),n=require("preact"),r=new Map;function t(e,n){(null==n||n>e.length)&&(n=e.length);for(var r=0,t=Array(n);r<n;r++)t[r]=e[r];return t}function o(e,n){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(r)return(r=r.call(e)).next.bind(r);if(Array.isArray(e)||(r=function(e,n){if(e){if("string"==typeof e)return t(e,n);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?t(e,n):void 0}}(e))||n&&e&&"number"==typeof e.length){r&&(e=r);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function u(){return u=Object.assign?Object.assign.bind():function(e){for(var n=1;n<arguments.length;n++){var r=arguments[n];for(var t in r)({}).hasOwnProperty.call(r,t)&&(e[t]=r[t])}return e},u.apply(null,arguments)}function i(){if("undefined"==typeof navigator)return{online:!0};var e={online:navigator.onLine},n=navigator.connection;return n&&(void 0!==n.effectiveType&&(e.effectiveType=n.effectiveType),void 0!==n.downlink&&(e.downlink=n.downlink),void 0!==n.rtt&&(e.rtt=n.rtt),void 0!==n.saveData&&(e.saveData=n.saveData),void 0!==n.type&&(e.connectionType=n.type)),e}function a(e,n){try{var r=e()}catch(e){return n(e)}return r&&r.then?r.then(void 0,n):r}var c=new Map;function s(e){return new Promise(function(n,r){e.onsuccess=function(){return n(e.result)},e.onerror=function(){var n;return r(null!=(n=e.error)?n:new DOMException("Unknown IndexedDB error"))}})}function f(e,n){return n?e.then(function(e){return null==n.onSuccess||n.onSuccess(e),e}).catch(function(e){throw null==n.onError||n.onError(e),e}):e}var l=/\b(?:25[0-5]|2[0-4]\d|1?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|1?\d{1,2})){3}\b/g,d=["stun:stun.l.google.com:19302"];exports.useClipboard=function(n){void 0===n&&(n={});var r=n.resetDelay,t=void 0===r?2e3:r,o=e.useState(!1),u=o[0],i=o[1],c=e.useState(null),s=c[0],f=c[1],l=e.useCallback(function(){i(!1),f(null)},[]);return{copy:e.useCallback(function(e){try{if(f(null),"undefined"==typeof navigator||!navigator.clipboard){var n=new Error("Clipboard API is not available");return f(n),Promise.resolve(!1)}return Promise.resolve(a(function(){return Promise.resolve(navigator.clipboard.writeText(e)).then(function(){return i(!0),t>0&&setTimeout(function(){return i(!1)},t),!0})},function(e){var n=e instanceof Error?e:new Error(String(e));return f(n),!1}))}catch(e){return Promise.reject(e)}},[t]),paste:e.useCallback(function(){try{if(f(null),"undefined"==typeof navigator||!navigator.clipboard){var e=new Error("Clipboard API is not available");return f(e),Promise.resolve("")}return Promise.resolve(a(function(){return Promise.resolve(navigator.clipboard.readText())},function(e){var n=e instanceof Error?e:new Error(String(e));return f(n),""}))}catch(e){return Promise.reject(e)}},[]),copied:u,error:s,reset:l}},exports.useEventBus=function(){return{emit:e.useCallback(function(e){var n=arguments,t=r.get(e);t&&t.forEach(function(e){return e.apply(void 0,[].slice.call(n,1))})},[]),on:e.useCallback(function(e,n){var t=r.get(e);return t||(t=new Set,r.set(e,t)),t.add(n),function(){t.delete(n),0===t.size&&r.delete(e)}},[])}},exports.useIndexedDB=function(n){var r=e.useState(null),t=r[0],i=r[1],a=e.useState(null),l=a[0],d=a[1],v=e.useState(!1),p=v[0],m=v[1],h=e.useRef(n);return h.current=n,e.useEffect(function(){var e=!1;d(null),m(!1),i(null);var n=h.current;return function(e){var n=e.name+"_v"+e.version,r=c.get(n);return r||(r=function(e){return new Promise(function(n,r){var t=indexedDB.open(e.name,e.version);t.onerror=function(){var e;return r(null!=(e=t.error)?e:new DOMException("Failed to open database"))},t.onsuccess=function(){return n(t.result)},t.onupgradeneeded=function(n){for(var r=n.target.result,t=e.tables,u=0,i=Object.keys(t);u<i.length;u++){var a=i[u],c=t[a];if(!r.objectStoreNames.contains(a)){var s,f=r.createObjectStore(a,{keyPath:c.keyPath,autoIncrement:null!=(s=c.autoIncrement)&&s});if(c.indexes)for(var l,d=o(c.indexes);!(l=d()).done;){var v=l.value;f.createIndex(v,v,{unique:!1})}}}}})}(e),c.set(n,r),r)}({name:n.name,version:n.version,tables:n.tables}).then(function(n){if(e)n.close();else{var r=function(e){return{get db(){return e},hasTable:function(n){return e.objectStoreNames.contains(n)},table:function(n){return function(e,n){return function(e,n){function r(r){return e.transaction([n],r).objectStore(n)}return{insert:function(e,n){return f(s(r("readwrite").add(e)),n)},update:function(e,n,t){var o=r("readwrite");return f(s(o.get(e)).then(function(e){if(void 0===e)throw new DOMException("Key not found","NotFoundError");var r=u({},e,n);return s(o.put(r))}).then(function(){}),t)},delete:function(e,n){return f(s(r("readwrite").delete(e)).then(function(){}),n)},exists:function(e){return s(r("readonly").getKey(e)).then(function(e){return void 0!==e})},query:function(e,n){var t=r("readonly").openCursor(),o=[];return f(new Promise(function(n,r){t.onsuccess=function(){var r=t.result;r?(e(r.value)&&o.push(r.value),r.continue()):n(o)},t.onerror=function(){var e;return r(null!=(e=t.error)?e:new DOMException("Unknown error"))}}),n)},upsert:function(e,n){return f(s(r("readwrite").put(e)),n)},bulkInsert:function(e,n){var t=r("readwrite"),o=[];if(0===e.length)return f(Promise.resolve(o),n);var u=0;return f(new Promise(function(n,r){e.forEach(function(i,a){var c=t.add(i);c.onsuccess=function(){o[a]=c.result,++u===e.length&&n(o)},c.onerror=function(){var e;return r(null!=(e=c.error)?e:new DOMException("Unknown error"))}})}),n)},clear:function(e){return f(s(r("readwrite").clear()).then(function(){}),e)},count:function(e){return f(s(r("readonly").count()),null!=e?e:{})}}}(e,n)}(e,n)},transaction:function(n,r,t,o){var i=e.transaction(n,r),a={table:function(e){return function(e,n){return function(e,n){function r(){return e.objectStore(n)}return{insert:function(e,n){return f(s(r().add(e)),n)},update:function(e,n,t){var o=r();return f(s(o.get(e)).then(function(e){if(void 0===e)throw new DOMException("Key not found","NotFoundError");var r=u({},e,n);return s(o.put(r))}).then(function(){}),t)},delete:function(e,n){return f(s(r().delete(e)).then(function(){}),n)},exists:function(e){return s(r().getKey(e)).then(function(e){return void 0!==e})},query:function(e,n){var t=r().openCursor(),o=[];return f(new Promise(function(n,r){t.onsuccess=function(){var r=t.result;r?(e(r.value)&&o.push(r.value),r.continue()):n(o)},t.onerror=function(){var e;return r(null!=(e=t.error)?e:new DOMException("Unknown error"))}}),n)},upsert:function(e,n){return f(s(r().put(e)),n)},bulkInsert:function(e,n){var t=r(),o=[];if(0===e.length)return f(Promise.resolve(o),n);var u=0;return f(new Promise(function(n,r){e.forEach(function(i,a){var c=t.add(i);c.onsuccess=function(){o[a]=c.result,++u===e.length&&n(o)},c.onerror=function(){var e;return r(null!=(e=c.error)?e:new DOMException("Unknown error"))}})}),n)},clear:function(e){return f(s(r().clear()).then(function(){}),e)},count:function(e){return f(s(r().count()),null!=e?e:{})}}}(e,n)}(i,e)}},c=new Promise(function(e,n){i.oncomplete=function(){return e()},i.onerror=function(){var e;return n(null!=(e=i.error)?e:new DOMException("Transaction failed"))}}),l=t(a);return function(e,n){return n?e.then(function(){return null==n.onSuccess?void 0:n.onSuccess()}).catch(function(e){throw null==n.onError||n.onError(e),e}):e}(Promise.resolve(l).then(function(){return c}),o)}}}(n);i(r),m(!0)}}).catch(function(n){e||d(n)}),function(){e=!0}},[n.name,n.version]),{db:t,isReady:p,error:l}},exports.useMutationObserver=function(n,r,t){e.useEffect(function(){var e=n.current;if(e){var o=new MutationObserver(r);return o.observe(e,t),function(){return o.disconnect()}}},[n,r,t])},exports.useNetworkState=function(){var n=e.useState(i),r=n[0],t=n[1];return e.useEffect(function(){if("undefined"!=typeof window){var e=function(){return t(i())};window.addEventListener("online",e),window.addEventListener("offline",e);var n=navigator.connection;return null!=n&&n.addEventListener&&n.addEventListener("change",e),function(){window.removeEventListener("online",e),window.removeEventListener("offline",e),null!=n&&n.removeEventListener&&n.removeEventListener("change",e)}}},[]),r},exports.usePreferredTheme=function(){var n=e.useState(function(){if("undefined"==typeof window)return"no-preference";var e=window.matchMedia("(prefers-color-scheme: dark)"),n=window.matchMedia("(prefers-color-scheme: light)");return e.matches?"dark":n.matches?"light":"no-preference"}),r=n[0],t=n[1];return e.useEffect(function(){if("undefined"!=typeof window){var e=window.matchMedia("(prefers-color-scheme: dark)"),n=function(e){t(e.matches?"dark":"light")},r=function(){var e=window.matchMedia("(prefers-color-scheme: dark)"),n=window.matchMedia("(prefers-color-scheme: light)");t(e.matches?"dark":n.matches?"light":"no-preference")};e.addEventListener("change",n);var o=window.matchMedia("(prefers-color-scheme: light)");return o.addEventListener("change",r),function(){e.removeEventListener("change",n),o.removeEventListener("change",r)}}},[]),r},exports.useRageClick=function(n,r){var t=r.onRageClick,o=r.threshold,u=void 0===o?5:o,i=r.timeWindow,a=void 0===i?1e3:i,c=r.distanceThreshold,s=void 0===c?30:c,f=e.useRef(t);f.current=t;var l=e.useRef([]);e.useEffect(function(){var e=n.current;if(e){var r=function(e){var n=Date.now(),r={time:n,x:e.clientX,y:e.clientY},t=n-a,o=l.current.filter(function(e){return e.time>=t});if(o.push(r),Infinity!==s){var i=o.filter(function(e){return n=e,t=r,Math.hypot(t.x-n.x,t.y-n.y)<=s;var n,t});if(i.length>=u)return f.current({count:i.length,event:e}),void(l.current=[])}else if(o.length>=u)return f.current({count:o.length,event:e}),void(l.current=[]);l.current=o};return e.addEventListener("click",r),function(){return e.removeEventListener("click",r)}}},[n,u,a,s])},exports.useThreadedWorker=function(n,r){var t=r.concurrency,o="sequential"===r.mode?1:Math.max(1,void 0===t?4:t),u=e.useState(!1),i=u[0],a=u[1],c=e.useState(void 0),s=c[0],f=c[1],l=e.useState(void 0),d=l[0],v=l[1],p=e.useState(0),m=p[0],h=p[1],y=e.useRef([]),w=e.useRef(0),g=e.useRef(0),b=e.useRef(!1),k=e.useRef(n);k.current=n;var E=e.useCallback(function(){h(y.current.length+g.current)},[]),S=e.useCallback(function(){if(!(b.current||g.current>=o)){if(0===y.current.length)return 0===g.current&&a(!1),void E();y.current.sort(function(e,n){return e.priority!==n.priority?e.priority-n.priority:e.sequence-n.sequence});var e=y.current.shift();g.current+=1,a(!0),E(),(0,k.current)(e.data).then(function(n){f(n),v(void 0),e.resolve(n)}).catch(function(n){v(n),e.reject(n)}).finally(function(){g.current-=1,E(),S()}),y.current.length>0&&g.current<o&&S()}},[o,E]),x=e.useCallback(function(e,n){var r;if(b.current)return Promise.reject(new Error("Worker is terminated"));var t=null!=(r=null==n?void 0:n.priority)?r:1,o=++w.current,u=new Promise(function(n,r){y.current.push({data:e,priority:t,sequence:o,resolve:n,reject:r})});return E(),a(!0),queueMicrotask(S),u},[S,E]),C=e.useCallback(function(){var e=y.current;y.current=[],e.forEach(function(e){return e.reject(new Error("Task cleared from queue"))}),E(),0===g.current&&a(!1)},[E]),M=e.useCallback(function(){b.current=!0,C()},[C]);return e.useEffect(function(){return function(){b.current=!0}},[]),{run:x,loading:i,result:s,error:d,queueSize:m,clearQueue:C,terminate:M}},exports.useTransition=function(){var n=e.useState(!1),r=n[0],t=n[1];return[e.useCallback(function(e){t(!0),Promise.resolve().then(function(){e(),t(!1)})},[]),r]},exports.useWasmCompute=function(n){var r=n.wasmUrl,t=n.exportName,o=void 0===t?"compute":t,u=n.workerUrl,i=n.importObject,a=e.useState(void 0),c=a[0],s=a[1],f=e.useState(!0),l=f[0],d=f[1],v=e.useState(null),p=v[0],m=v[1],h=e.useState(!1),y=h[0],w=h[1],g=e.useRef(null),b=e.useRef(null),k=e.useRef(null);return e.useEffect(function(){if("undefined"==typeof window)return m("useWasmCompute is not available during SSR"),void d(!1);if("undefined"==typeof Worker)return m("Worker is not supported in this environment"),void d(!1);if("undefined"==typeof WebAssembly||"function"!=typeof WebAssembly.instantiate)return m("WebAssembly is not supported in this environment"),void d(!1);m(null),w(!1);var e=function(e){if(e)return new Worker(e);var n=new Blob(["\nself.onmessage = async (e) => {\n const d = e.data;\n if (d.type === 'init') {\n try {\n const res = await fetch(d.wasmUrl);\n const buf = await res.arrayBuffer();\n const mod = await WebAssembly.instantiate(buf, d.importObject || {});\n self.wasmInstance = mod.instance;\n self.exportName = d.exportName || 'compute';\n self.postMessage({ type: 'ready' });\n } catch (err) {\n self.postMessage({ type: 'error', error: (err && err.message) || String(err) });\n }\n return;\n }\n if (d.type === 'compute') {\n try {\n const fn = self.wasmInstance.exports[self.exportName];\n if (typeof fn !== 'function') {\n self.postMessage({ type: 'error', error: 'Export \"' + self.exportName + '\" is not a function' });\n return;\n }\n const result = fn(d.input);\n self.postMessage({ type: 'result', result: result });\n } catch (err) {\n self.postMessage({ type: 'error', error: (err && err.message) || String(err) });\n }\n }\n};\n"],{type:"application/javascript"}),r=URL.createObjectURL(n),t=new Worker(r);return URL.revokeObjectURL(r),t}(u);g.current=e;var n=function(e){var n,r=null!=(n=e.data)?n:{},t=r.type,o=r.result,u=r.error;return"ready"===t?(w(!0),void d(!1)):"error"===t?(m(null!=u?u:"Unknown error"),d(!1),void(k.current&&(k.current(new Error(u)),b.current=null,k.current=null))):void("result"===t&&(s(o),d(!1),b.current&&(b.current(o),b.current=null,k.current=null)))};return e.addEventListener("message",n),e.postMessage({type:"init",wasmUrl:r,exportName:o,importObject:null!=i?i:{}}),function(){e.removeEventListener("message",n),e.terminate(),g.current=null,k.current&&(k.current(new Error("Worker terminated")),b.current=null,k.current=null)}},[r,o,u,i]),{compute:e.useCallback(function(e){return new Promise(function(n,r){g.current&&y?p?r(new Error(p)):(b.current=n,k.current=r,d(!0),g.current.postMessage({type:"compute",input:e})):r(new Error("WASM not ready"))})},[y,p]),result:c,loading:l,error:p,ready:y}},exports.useWebRTCIP=function(n){void 0===n&&(n={});var r=n.stunServers,t=void 0===r?d:r,o=n.timeout,u=void 0===o?3e3:o,i=n.onDetect,a=e.useState([]),c=a[0],s=a[1],f=e.useState(!0),v=f[0],p=f[1],m=e.useState(null),h=m[0],y=m[1],w=e.useRef(null),g=e.useRef(null),b=e.useRef(new Set),k=e.useRef(i);return k.current=i,e.useEffect(function(){if("undefined"==typeof window)return p(!1),void y("WebRTC IP detection is not available during SSR");if("undefined"==typeof RTCPeerConnection)return p(!1),void y("RTCPeerConnection is not available");var e=new Set;b.current=e;var n=function(){g.current&&(clearTimeout(g.current),g.current=null),w.current&&(w.current.close(),w.current=null),p(!1)},r=function(n){e.has(n)||(e.add(n),s(function(e){return[].concat(e,[n])}),null==k.current||k.current(n))};try{var o=new RTCPeerConnection({iceServers:[{urls:t}]});w.current=o,o.onicecandidate=function(e){var n,t=e.candidate;t&&t.candidate&&((n=t.candidate.match(l))?[].concat(n):[]).forEach(r)},o.createDataChannel(""),o.createOffer().then(function(e){return o.setLocalDescription(e)}).catch(function(e){y(e instanceof Error?e.message:"Failed to create offer"),n()}),g.current=setTimeout(function(){return n()},u)}catch(e){y(e instanceof Error?e.message:"WebRTC setup failed"),n()}return function(){n()}},[t.join(","),u]),{ips:c,loading:v,error:h}},exports.useWorkerNotifications=function(n,r){void 0===r&&(r={});var t=r.maxHistory,o=void 0===t?100:t,u=r.throughputWindowMs,i=void 0===u?1e3:u,a=e.useState([]),c=a[0],s=a[1],f=e.useState(0),l=f[0],d=f[1],v=e.useState(0),p=v[0],m=v[1],h=e.useState([]),y=h[0],w=h[1],g=e.useState(0),b=g[0],k=g[1],E=e.useRef([]),S=e.useRef(0),x=e.useRef(0);e.useEffect(function(){if(n){var e=function(e){var n=function(e){if(null==e||"object"!=typeof e)return null;var n=e.type;return"task_start"!==n&&"task_end"!==n&&"task_fail"!==n&&"queue_size"!==n?null:{type:n,taskId:"string"==typeof e.taskId?e.taskId:void 0,duration:"number"==typeof e.duration?e.duration:void 0,error:"string"==typeof e.error?e.error:void 0,size:"number"==typeof e.size?e.size:void 0,timestamp:Date.now()}}(e.data);if(n)if(w(function(e){return[].concat(e,[n]).slice(-o)}),"task_start"===n.type&&n.taskId)s(function(e){return e.includes(n.taskId)?e:[].concat(e,[n.taskId])});else if("task_end"===n.type){n.taskId&&s(function(e){return e.filter(function(e){return e!==n.taskId})}),d(function(e){return e+1});var r=Date.now()-i;E.current=[].concat(E.current.filter(function(e){return e>=r}),[n.timestamp]),"number"==typeof n.duration&&(S.current+=n.duration,x.current+=1)}else"task_fail"===n.type?(n.taskId&&s(function(e){return e.filter(function(e){return e!==n.taskId})}),m(function(e){return e+1})):"queue_size"===n.type&&"number"==typeof n.size&&k(n.size)};return n.addEventListener("message",e),function(){return n.removeEventListener("message",e)}}},[n,o]);var C=e.useMemo(function(){var e=x.current;return e>0?S.current/e:0},[y]),M=e.useMemo(function(){var e=Date.now()-i;return E.current.filter(function(n){return n>=e}).length/(i/1e3)},[y,i]),P=e.useMemo(function(){return{runningTasks:c,completedCount:l,failedCount:p,averageDurationMs:C,throughputPerSecond:M,currentQueueSize:b,totalProcessed:l+p,recentEventCount:y.length}},[c,l,p,C,M,b,y.length]);return{runningTasks:c,completedCount:l,failedCount:p,eventHistory:y,averageDurationMs:C,throughputPerSecond:M,currentQueueSize:b,progress:P}},exports.useWrappedChildren=function(r,t,o){return void 0===o&&(o="preserve"),e.useMemo(function(){if(!r)return r;var e=function(e){if(!n.isValidElement(e))return e;var r,i=e.props||{};r="override"===o?u({},i,t):u({},t,i);var a=null==i?void 0:i.style,c=null==t?void 0:t.style;return a&&c&&"object"==typeof a&&"object"==typeof c&&(r.style="override"===o?u({},a,c):u({},c,a)),n.cloneElement(e,r)};return Array.isArray(r)?r.map(e):e(r)},[r,t,o])};
2
+ //# sourceMappingURL=entry.js.map