preact-missing-hooks 2.0.0 → 2.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
@@ -15,7 +15,7 @@
15
15
 
16
16
  If this package helps you, please consider dropping a star on the [GitHub repo](https://github.com/prakhardubey2002/Preact-Missing-Hooks).
17
17
 
18
- A lightweight, extendable collection of React-like hooks for Preact, including utilities for transitions, DOM mutation observation, global event buses, theme detection, network status, clipboard access, rage-click detection (e.g. for Sentry), a priority task queue (sequential or parallel), and a production-ready **IndexedDB** hook with tables, transactions, and a full CRUD API.
18
+ A lightweight, extendable collection of React-like hooks for Preact, including utilities for transitions, DOM mutation observation, global event buses, theme detection, network status, clipboard access, rage-click detection (e.g. for Sentry), a priority task queue (sequential or parallel), a production-ready **IndexedDB** hook with tables, transactions, and a full CRUD API, and **WebRTC-based IP detection** (`useWebRTCIP`) for frontend-only IP hints.
19
19
 
20
20
  ---
21
21
 
@@ -31,6 +31,7 @@ A lightweight, extendable collection of React-like hooks for Preact, including u
31
31
  - **`useRageClick`** — Detects rage clicks (repeated rapid clicks in the same spot). Use with Sentry or similar to detect and fix rage-click issues and lower rage-click-related support.
32
32
  - **`useThreadedWorker`** — Run async work in a queue with **sequential** (single worker, priority-ordered) or **parallel** (worker pool) mode. Optional priority (1 = highest); FIFO within same priority.
33
33
  - **`useIndexedDB`** — IndexedDB abstraction with database/table init, insert, update, delete, exists, query (cursor + filter), upsert, bulk insert, clear, count, and full transaction support. Singleton connection, Promise-based API, optional `onSuccess`/`onError` callbacks.
34
+ - **`useWebRTCIP`** — Detects client IP addresses using WebRTC ICE candidates and a STUN server (frontend-only). **Not highly reliable**; use as a first-priority hint and fall back to a public IP API (e.g. [ipapi.co](https://ipapi.co), [ipify](https://www.ipify.org), [ip-api.com](https://ip-api.com)) when it fails or returns empty.
34
35
  - Fully TypeScript compatible
35
36
  - Bundled with Microbundle
36
37
  - Zero dependencies (except `preact`)
@@ -55,8 +56,9 @@ npm install preact-missing-hooks
55
56
  ```ts
56
57
  import { useThreadedWorker } from 'preact-missing-hooks/useThreadedWorker'
57
58
  import { useClipboard } from 'preact-missing-hooks/useClipboard'
59
+ import { useWebRTCIP } from 'preact-missing-hooks/useWebRTCIP'
58
60
  ```
59
- All hooks are available: `useTransition`, `useMutationObserver`, `useEventBus`, `useWrappedChildren`, `usePreferredTheme`, `useNetworkState`, `useClipboard`, `useRageClick`, `useThreadedWorker`, `useIndexedDB`.
61
+ All hooks are available: `useTransition`, `useMutationObserver`, `useEventBus`, `useWrappedChildren`, `usePreferredTheme`, `useNetworkState`, `useClipboard`, `useRageClick`, `useThreadedWorker`, `useIndexedDB`, `useWebRTCIP`.
60
62
 
61
63
  ---
62
64
 
@@ -373,6 +375,46 @@ function App() {
373
375
 
374
376
  ---
375
377
 
378
+ ### `useWebRTCIP`
379
+
380
+ Detects client IP addresses using WebRTC ICE candidates and a STUN server (**frontend-only**, no backend). **Not highly reliable** — use as a **first-priority** hint; if it fails or returns empty, fall back to a public IP API (e.g. [ipapi.co](https://ipapi.co), [ipify](https://www.ipify.org), [ip-api.com](https://ip-api.com)).
381
+
382
+ Returns `{ ips: string[], loading: boolean, error: string | null }`. Options: `stunServers`, `timeout` (ms), `onDetect(ip)`.
383
+
384
+ ```tsx
385
+ import { useWebRTCIP } from 'preact-missing-hooks'
386
+ import { useState, useEffect } from 'preact/hooks'
387
+
388
+ function ClientIP() {
389
+ const { ips, loading, error } = useWebRTCIP({
390
+ timeout: 4000,
391
+ onDetect: (ip) => {
392
+ /* optional: e.g. analytics */
393
+ },
394
+ })
395
+ const [fallbackIP, setFallbackIP] = useState<string | null>(null)
396
+
397
+ // Fallback to public IP API when WebRTC fails or returns empty
398
+ useEffect(() => {
399
+ if (loading || ips.length > 0) return
400
+ if (error) {
401
+ fetch('https://api.ipify.org?format=json')
402
+ .then((r) => r.json())
403
+ .then((d) => setFallbackIP(d.ip))
404
+ .catch(() => {})
405
+ }
406
+ }, [loading, ips.length, error])
407
+
408
+ if (loading) return <p>Detecting IP…</p>
409
+ if (ips.length > 0) return <p>IPs (WebRTC): {ips.join(', ')}</p>
410
+ if (fallbackIP) return <p>IP (fallback API): {fallbackIP}</p>
411
+ if (error) return <p>WebRTC failed. Try fallback API.</p>
412
+ return null
413
+ }
414
+ ```
415
+
416
+ ---
417
+
376
418
  ## Built With
377
419
 
378
420
  - [Preact](https://preactjs.com)
package/dist/index.d.ts CHANGED
@@ -8,3 +8,4 @@ export * from './useClipboard';
8
8
  export * from './useRageClick';
9
9
  export * from './useThreadedWorker';
10
10
  export * from './useIndexedDB';
11
+ export * from './useWebRTCIP';
package/dist/index.js CHANGED
@@ -1,2 +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 c(e,n){try{var r=e()}catch(e){return n(e)}return r&&r.then?r.then(void 0,n):r}var a=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}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],a=e.useState(null),s=a[0],f=a[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(c(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(c(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],c=e.useState(null),l=c[0],v=c[1],d=e.useState(!1),h=d[0],p=d[1],m=e.useRef(n);return m.current=n,e.useEffect(function(){var e=!1;v(null),p(!1),i(null);var n=m.current;return function(e){var n=e.name+"_v"+e.version,r=a.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 c=i[u],a=t[c];if(!r.objectStoreNames.contains(c)){var s,f=r.createObjectStore(c,{keyPath:a.keyPath,autoIncrement:null!=(s=a.autoIncrement)&&s});if(a.indexes)for(var l,v=o(a.indexes);!(l=v()).done;){var d=l.value;f.createIndex(d,d,{unique:!1})}}}}})}(e),a.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,c){var a=t.add(i);a.onsuccess=function(){o[c]=a.result,++u===e.length&&n(o)},a.onerror=function(){var e;return r(null!=(e=a.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),c={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,c){var a=t.add(i);a.onsuccess=function(){o[c]=a.result,++u===e.length&&n(o)},a.onerror=function(){var e;return r(null!=(e=a.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)}},a=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(c);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 a}),o)}}}(n);i(r),p(!0)}}).catch(function(n){e||v(n)}),function(){e=!0}},[n.name,n.version]),{db:t,isReady:h,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,c=void 0===i?1e3:i,a=r.distanceThreshold,s=void 0===a?30:a,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-c,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,c,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],c=u[1],a=e.useState(void 0),s=a[0],f=a[1],l=e.useState(void 0),v=l[0],d=l[1],h=e.useState(0),p=h[0],m=h[1],w=e.useRef([]),y=e.useRef(0),g=e.useRef(0),b=e.useRef(!1),E=e.useRef(n);E.current=n;var k=e.useCallback(function(){m(w.current.length+g.current)},[]),x=e.useCallback(function(){if(!(b.current||g.current>=o)){if(0===w.current.length)return 0===g.current&&c(!1),void k();w.current.sort(function(e,n){return e.priority!==n.priority?e.priority-n.priority:e.sequence-n.sequence});var e=w.current.shift();g.current+=1,c(!0),k(),(0,E.current)(e.data).then(function(n){f(n),d(void 0),e.resolve(n)}).catch(function(n){d(n),e.reject(n)}).finally(function(){g.current-=1,k(),x()}),w.current.length>0&&g.current<o&&x()}},[o,k]),S=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=++y.current,u=new Promise(function(n,r){w.current.push({data:e,priority:t,sequence:o,resolve:n,reject:r})});return k(),c(!0),queueMicrotask(x),u},[x,k]),P=e.useCallback(function(){var e=w.current;w.current=[],e.forEach(function(e){return e.reject(new Error("Task cleared from queue"))}),k(),0===g.current&&c(!1)},[k]),M=e.useCallback(function(){b.current=!0,P()},[P]);return e.useEffect(function(){return function(){b.current=!0}},[]),{run:S,loading:i,result:s,error:v,queueSize:p,clearQueue:P,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.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 c=null==i?void 0:i.style,a=null==t?void 0:t.style;return c&&a&&"object"==typeof c&&"object"==typeof a&&(r.style="override"===o?u({},c,a):u({},a,c)),n.cloneElement(e,r)};return Array.isArray(r)?r.map(e):e(r)},[r,t,o])};
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 c(e,n){try{var r=e()}catch(e){return n(e)}return r&&r.then?r.then(void 0,n):r}var a=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],a=e.useState(null),s=a[0],f=a[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(c(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(c(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],c=e.useState(null),l=c[0],d=c[1],v=e.useState(!1),h=v[0],p=v[1],m=e.useRef(n);return m.current=n,e.useEffect(function(){var e=!1;d(null),p(!1),i(null);var n=m.current;return function(e){var n=e.name+"_v"+e.version,r=a.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 c=i[u],a=t[c];if(!r.objectStoreNames.contains(c)){var s,f=r.createObjectStore(c,{keyPath:a.keyPath,autoIncrement:null!=(s=a.autoIncrement)&&s});if(a.indexes)for(var l,d=o(a.indexes);!(l=d()).done;){var v=l.value;f.createIndex(v,v,{unique:!1})}}}}})}(e),a.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,c){var a=t.add(i);a.onsuccess=function(){o[c]=a.result,++u===e.length&&n(o)},a.onerror=function(){var e;return r(null!=(e=a.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),c={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,c){var a=t.add(i);a.onsuccess=function(){o[c]=a.result,++u===e.length&&n(o)},a.onerror=function(){var e;return r(null!=(e=a.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)}},a=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(c);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 a}),o)}}}(n);i(r),p(!0)}}).catch(function(n){e||d(n)}),function(){e=!0}},[n.name,n.version]),{db:t,isReady:h,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,c=void 0===i?1e3:i,a=r.distanceThreshold,s=void 0===a?30:a,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-c,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,c,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],c=u[1],a=e.useState(void 0),s=a[0],f=a[1],l=e.useState(void 0),d=l[0],v=l[1],h=e.useState(0),p=h[0],m=h[1],w=e.useRef([]),y=e.useRef(0),g=e.useRef(0),b=e.useRef(!1),E=e.useRef(n);E.current=n;var k=e.useCallback(function(){m(w.current.length+g.current)},[]),S=e.useCallback(function(){if(!(b.current||g.current>=o)){if(0===w.current.length)return 0===g.current&&c(!1),void k();w.current.sort(function(e,n){return e.priority!==n.priority?e.priority-n.priority:e.sequence-n.sequence});var e=w.current.shift();g.current+=1,c(!0),k(),(0,E.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,k(),S()}),w.current.length>0&&g.current<o&&S()}},[o,k]),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=++y.current,u=new Promise(function(n,r){w.current.push({data:e,priority:t,sequence:o,resolve:n,reject:r})});return k(),c(!0),queueMicrotask(S),u},[S,k]),P=e.useCallback(function(){var e=w.current;w.current=[],e.forEach(function(e){return e.reject(new Error("Task cleared from queue"))}),k(),0===g.current&&c(!1)},[k]),C=e.useCallback(function(){b.current=!0,P()},[P]);return e.useEffect(function(){return function(){b.current=!0}},[]),{run:x,loading:i,result:s,error:d,queueSize:p,clearQueue:P,terminate:C}},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.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,c=e.useState([]),a=c[0],s=c[1],f=e.useState(!0),v=f[0],h=f[1],p=e.useState(null),m=p[0],w=p[1],y=e.useRef(null),g=e.useRef(null),b=e.useRef(new Set),E=e.useRef(i);return E.current=i,e.useEffect(function(){if("undefined"==typeof window)return h(!1),void w("WebRTC IP detection is not available during SSR");if("undefined"==typeof RTCPeerConnection)return h(!1),void w("RTCPeerConnection is not available");var e=new Set;b.current=e;var n=function(){g.current&&(clearTimeout(g.current),g.current=null),y.current&&(y.current.close(),y.current=null),h(!1)},r=function(n){e.has(n)||(e.add(n),s(function(e){return[].concat(e,[n])}),null==E.current||E.current(n))};try{var o=new RTCPeerConnection({iceServers:[{urls:t}]});y.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){w(e instanceof Error?e.message:"Failed to create offer"),n()}),g.current=setTimeout(function(){return n()},u)}catch(e){w(e instanceof Error?e.message:"WebRTC setup failed"),n()}return function(){n()}},[t.join(","),u]),{ips:a,loading:v,error:m}},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 c=null==i?void 0:i.style,a=null==t?void 0:t.style;return c&&a&&"object"==typeof c&&"object"==typeof a&&(r.style="override"===o?u({},c,a):u({},a,c)),n.cloneElement(e,r)};return Array.isArray(r)?r.map(e):e(r)},[r,t,o])};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/useEventBus.ts","../src/useNetworkState.ts","../src/indexedDB/openDB.ts","../src/indexedDB/requestToPromise.ts","../src/indexedDB/tableController.ts","../src/useClipboard.ts","../src/useIndexedDB.ts","../src/indexedDB/dbController.ts","../src/useMutationObserver.ts","../src/usePreferredTheme.ts","../src/useRageClick.ts","../src/useThreadedWorker.ts","../src/useTransition.ts","../src/useWrappedChildren.ts"],"sourcesContent":["import { useCallback, useEffect } from 'preact/hooks';\r\n\r\ntype EventMap = Record<string, (...args: any[]) => void>;\r\n\r\nconst listeners = new Map<string, Set<(...args: any[]) => void>>();\r\n\r\n/**\r\n * A Preact hook to publish and subscribe to custom events across components.\r\n * @returns An object with `emit` and `on` methods.\r\n */\r\nexport function useEventBus<T extends EventMap>() {\r\n const emit = useCallback(<K extends keyof T>(event: K, ...args: Parameters<T[K]>) => {\r\n const handlers = listeners.get(event as string);\r\n if (handlers) {\r\n handlers.forEach((handler) => handler(...args));\r\n }\r\n }, []);\r\n\r\n const on = useCallback(<K extends keyof T>(event: K, handler: T[K]) => {\r\n let handlers = listeners.get(event as string);\r\n if (!handlers) {\r\n handlers = new Set();\r\n listeners.set(event as string, handlers);\r\n }\r\n handlers.add(handler);\r\n\r\n return () => {\r\n handlers!.delete(handler);\r\n if (handlers!.size === 0) {\r\n listeners.delete(event as string);\r\n }\r\n };\r\n }, []);\r\n\r\n return { emit, on };\r\n}\r\n","import { useEffect, useState } from 'preact/hooks';\r\n\r\n/** Network Information API (not in all browsers) */\r\ninterface NetworkInformation extends EventTarget {\r\n effectiveType?: string;\r\n downlink?: number;\r\n rtt?: number;\r\n saveData?: boolean;\r\n type?: string;\r\n}\r\n\r\n/** Effective connection type from Network Information API */\r\nexport type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g';\r\n\r\n/** Network connection type (e.g., wifi, cellular) */\r\nexport type ConnectionType =\r\n | 'bluetooth'\r\n | 'cellular'\r\n | 'ethernet'\r\n | 'mixed'\r\n | 'none'\r\n | 'other'\r\n | 'unknown'\r\n | 'wifi';\r\n\r\nexport interface NetworkState {\r\n /** Whether the browser is online */\r\n online: boolean;\r\n /** Effective connection type (when supported) */\r\n effectiveType?: EffectiveConnectionType;\r\n /** Estimated downlink speed in Mbps (when supported) */\r\n downlink?: number;\r\n /** Estimated round-trip time in ms (when supported) */\r\n rtt?: number;\r\n /** Whether the user has requested reduced data usage (when supported) */\r\n saveData?: boolean;\r\n /** Connection type (when supported) */\r\n connectionType?: ConnectionType;\r\n}\r\n\r\nfunction getNetworkState(): NetworkState {\r\n if (typeof navigator === 'undefined') {\r\n return { online: true };\r\n }\r\n\r\n const state: NetworkState = {\r\n online: navigator.onLine,\r\n };\r\n\r\n const connection =\r\n (navigator as Navigator & { connection?: NetworkInformation }).connection;\r\n\r\n if (connection) {\r\n if (connection.effectiveType !== undefined) {\r\n state.effectiveType = connection.effectiveType as EffectiveConnectionType;\r\n }\r\n if (connection.downlink !== undefined) {\r\n state.downlink = connection.downlink;\r\n }\r\n if (connection.rtt !== undefined) {\r\n state.rtt = connection.rtt;\r\n }\r\n if (connection.saveData !== undefined) {\r\n state.saveData = connection.saveData;\r\n }\r\n if (connection.type !== undefined) {\r\n state.connectionType = connection.type as ConnectionType;\r\n }\r\n }\r\n\r\n return state;\r\n}\r\n\r\n/**\r\n * A Preact hook that returns the current network state, including online/offline\r\n * status and (when supported) connection type, downlink, RTT, and save-data preference.\r\n * Updates reactively when the network state changes.\r\n *\r\n * @returns The current network state object\r\n *\r\n * @example\r\n * ```tsx\r\n * function NetworkStatus() {\r\n * const { online, effectiveType, saveData } = useNetworkState();\r\n * return (\r\n * <div>\r\n * Status: {online ? 'Online' : 'Offline'}\r\n * {effectiveType && ` (${effectiveType})`}\r\n * {saveData && ' - Reduced data mode'}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useNetworkState(): NetworkState {\r\n const [state, setState] = useState<NetworkState>(getNetworkState);\r\n\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return;\r\n\r\n const updateState = () => setState(getNetworkState());\r\n\r\n window.addEventListener('online', updateState);\r\n window.addEventListener('offline', updateState);\r\n\r\n const connection = (navigator as Navigator & { connection?: NetworkInformation })\r\n .connection;\r\n if (connection?.addEventListener) {\r\n connection.addEventListener('change', updateState);\r\n }\r\n\r\n return () => {\r\n window.removeEventListener('online', updateState);\r\n window.removeEventListener('offline', updateState);\r\n if (connection?.removeEventListener) {\r\n connection.removeEventListener('change', updateState);\r\n }\r\n };\r\n }, []);\r\n\r\n return state;\r\n}\r\n","/**\r\n * Opens IndexedDB and runs onupgradeneeded to create stores and indexes.\r\n * Singleton per (name, version).\r\n * @module indexedDB/openDB\r\n */\r\n\r\nimport type { IndexedDBConfig, TableSchema } from './types';\r\nimport { requestToPromise } from './requestToPromise';\r\n\r\nconst connectionCache = new Map<string, Promise<IDBDatabase>>();\r\n\r\n/**\r\n * Opens the database and creates/upgrades object stores and indexes from config.\r\n * Uses a singleton cache per (name, version); repeated calls with the same config reuse the same connection.\r\n */\r\nexport function openDB(config: IndexedDBConfig): Promise<IDBDatabase> {\r\n const key = `${config.name}_v${config.version}`;\r\n let promise = connectionCache.get(key);\r\n if (promise) return promise;\r\n promise = _openDB(config);\r\n connectionCache.set(key, promise);\r\n return promise;\r\n}\r\n\r\nfunction _openDB(config: IndexedDBConfig): Promise<IDBDatabase> {\r\n return new Promise<IDBDatabase>((resolve, reject) => {\r\n const request = indexedDB.open(config.name, config.version);\r\n request.onerror = () => reject(request.error ?? new DOMException('Failed to open database'));\r\n request.onsuccess = () => resolve(request.result);\r\n request.onupgradeneeded = (event: IDBVersionChangeEvent) => {\r\n const db = (event.target as IDBOpenDBRequest).result;\r\n const tables = config.tables;\r\n for (const tableName of Object.keys(tables)) {\r\n const schema = tables[tableName] as TableSchema;\r\n if (!db.objectStoreNames.contains(tableName)) {\r\n const store = db.createObjectStore(tableName, {\r\n keyPath: schema.keyPath,\r\n autoIncrement: schema.autoIncrement ?? false,\r\n });\r\n if (schema.indexes) {\r\n for (const indexName of schema.indexes) {\r\n store.createIndex(indexName, indexName, { unique: false });\r\n }\r\n }\r\n }\r\n }\r\n };\r\n });\r\n}\r\n","/**\r\n * Wraps an IDBRequest in a Promise.\r\n * @module indexedDB/requestToPromise\r\n */\r\n\r\n/**\r\n * Converts an IDBRequest to a Promise. Rejects with the request's error on failure.\r\n * @param request - Native IndexedDB request.\r\n * @returns Promise that resolves with the request result or rejects with DOMException.\r\n */\r\nexport function requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\r\n return new Promise<T>((resolve, reject) => {\r\n request.onsuccess = () => resolve(request.result);\r\n request.onerror = () => reject(request.error ?? new DOMException('Unknown IndexedDB error'));\r\n });\r\n}\r\n","/**\r\n * Table controller: insert, update, delete, exists, query, upsert, bulkInsert, clear, count.\r\n * Works in standalone mode (opens its own transaction per op) or bound to a transaction.\r\n * @module indexedDB/tableController\r\n */\r\n\r\nimport type { OperationCallbacks } from './types';\r\nimport { requestToPromise } from './requestToPromise';\r\n\r\n/** Runs optional callbacks and returns the result. */\r\nfunction withCallbacks<T>(\r\n promise: Promise<T>,\r\n options?: OperationCallbacks<T>\r\n): Promise<T> {\r\n if (!options) return promise;\r\n return promise\r\n .then((result) => {\r\n options.onSuccess?.(result);\r\n return result;\r\n })\r\n .catch((err: DOMException) => {\r\n options.onError?.(err);\r\n throw err;\r\n });\r\n}\r\n\r\n/**\r\n * Standalone table controller: opens a new transaction for each operation.\r\n */\r\nfunction createStandaloneController(db: IDBDatabase, tableName: string): ITableController {\r\n function getStore(mode: IDBTransactionMode): IDBObjectStore {\r\n const tx = db.transaction([tableName], mode);\r\n return tx.objectStore(tableName);\r\n }\r\n\r\n return {\r\n insert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey> {\r\n const store = getStore('readwrite');\r\n return withCallbacks(requestToPromise(store.add(data)), options);\r\n },\r\n\r\n update<T>(key: IDBValidKey, updates: Partial<T>, options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore('readwrite');\r\n const getReq = store.get(key);\r\n return withCallbacks(\r\n requestToPromise(getReq).then((existing) => {\r\n if (existing === undefined) {\r\n throw new DOMException('Key not found', 'NotFoundError');\r\n }\r\n const merged = { ...existing, ...updates } as T;\r\n return requestToPromise(store.put(merged));\r\n }).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n delete(key: IDBValidKey, options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore('readwrite');\r\n return withCallbacks(\r\n requestToPromise(store.delete(key)).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n exists(key: IDBValidKey): Promise<boolean> {\r\n const store = getStore('readonly');\r\n return requestToPromise(store.getKey(key)).then((k) => k !== undefined);\r\n },\r\n\r\n query<T>(filterFn: (item: T) => boolean, options?: OperationCallbacks<T[]>): Promise<T[]> {\r\n const store = getStore('readonly');\r\n const request = store.openCursor();\r\n const results: T[] = [];\r\n return withCallbacks(\r\n new Promise<T[]>((resolve, reject) => {\r\n request.onsuccess = () => {\r\n const cursor = request.result;\r\n if (cursor) {\r\n if (filterFn(cursor.value as T)) results.push(cursor.value as T);\r\n cursor.continue();\r\n } else {\r\n resolve(results);\r\n }\r\n };\r\n request.onerror = () => reject(request.error ?? new DOMException('Unknown error'));\r\n }),\r\n options\r\n );\r\n },\r\n\r\n upsert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey> {\r\n const store = getStore('readwrite');\r\n return withCallbacks(requestToPromise(store.put(data)), options);\r\n },\r\n\r\n bulkInsert<T>(items: T[], options?: OperationCallbacks<IDBValidKey[]>): Promise<IDBValidKey[]> {\r\n const store = getStore('readwrite');\r\n const keys: IDBValidKey[] = [];\r\n if (items.length === 0) {\r\n return withCallbacks(Promise.resolve(keys), options);\r\n }\r\n let completed = 0;\r\n const promise = new Promise<IDBValidKey[]>((resolve, reject) => {\r\n const onDone = () => {\r\n completed++;\r\n if (completed === items.length) resolve(keys);\r\n };\r\n items.forEach((item, i) => {\r\n const req = store.add(item);\r\n req.onsuccess = () => {\r\n keys[i] = req.result;\r\n onDone();\r\n };\r\n req.onerror = () => reject(req.error ?? new DOMException('Unknown error'));\r\n });\r\n });\r\n return withCallbacks(promise, options);\r\n },\r\n\r\n clear(options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore('readwrite');\r\n return withCallbacks(\r\n requestToPromise(store.clear()).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n count(options?: OperationCallbacks<number>): Promise<number> {\r\n const store = getStore('readonly');\r\n return withCallbacks(requestToPromise(store.count()), options ?? {});\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Transaction-scoped table controller: uses the given transaction (no new transaction).\r\n */\r\nfunction createTransactionController(tx: IDBTransaction, tableName: string): ITableController {\r\n function getStore(): IDBObjectStore {\r\n return tx.objectStore(tableName);\r\n }\r\n\r\n return {\r\n insert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey> {\r\n const store = getStore();\r\n return withCallbacks(requestToPromise(store.add(data)), options);\r\n },\r\n\r\n update<T>(key: IDBValidKey, updates: Partial<T>, options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore();\r\n return withCallbacks(\r\n requestToPromise(store.get(key)).then((existing) => {\r\n if (existing === undefined) {\r\n throw new DOMException('Key not found', 'NotFoundError');\r\n }\r\n const merged = { ...existing, ...updates } as T;\r\n return requestToPromise(store.put(merged));\r\n }).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n delete(key: IDBValidKey, options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore();\r\n return withCallbacks(\r\n requestToPromise(store.delete(key)).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n exists(key: IDBValidKey): Promise<boolean> {\r\n const store = getStore();\r\n return requestToPromise(store.getKey(key)).then((k) => k !== undefined);\r\n },\r\n\r\n query<T>(filterFn: (item: T) => boolean, options?: OperationCallbacks<T[]>): Promise<T[]> {\r\n const store = getStore();\r\n const request = store.openCursor();\r\n const results: T[] = [];\r\n return withCallbacks(\r\n new Promise<T[]>((resolve, reject) => {\r\n request.onsuccess = () => {\r\n const cursor = request.result;\r\n if (cursor) {\r\n if (filterFn(cursor.value as T)) results.push(cursor.value as T);\r\n cursor.continue();\r\n } else {\r\n resolve(results);\r\n }\r\n };\r\n request.onerror = () => reject(request.error ?? new DOMException('Unknown error'));\r\n }),\r\n options\r\n );\r\n },\r\n\r\n upsert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey> {\r\n const store = getStore();\r\n return withCallbacks(requestToPromise(store.put(data)), options);\r\n },\r\n\r\n bulkInsert<T>(items: T[], options?: OperationCallbacks<IDBValidKey[]>): Promise<IDBValidKey[]> {\r\n const store = getStore();\r\n const keys: IDBValidKey[] = [];\r\n if (items.length === 0) {\r\n return withCallbacks(Promise.resolve(keys), options);\r\n }\r\n let completed = 0;\r\n const promise = new Promise<IDBValidKey[]>((resolve, reject) => {\r\n items.forEach((item, i) => {\r\n const req = store.add(item);\r\n req.onsuccess = () => {\r\n keys[i] = req.result;\r\n completed++;\r\n if (completed === items.length) resolve(keys);\r\n };\r\n req.onerror = () => reject(req.error ?? new DOMException('Unknown error'));\r\n });\r\n });\r\n return withCallbacks(promise, options);\r\n },\r\n\r\n clear(options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore();\r\n return withCallbacks(\r\n requestToPromise(store.clear()).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n count(options?: OperationCallbacks<number>): Promise<number> {\r\n const store = getStore();\r\n return withCallbacks(requestToPromise(store.count()), options ?? {});\r\n },\r\n };\r\n}\r\n\r\n/** Public interface for a table controller (standalone or transaction-scoped). */\r\nexport interface ITableController {\r\n insert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey>;\r\n update<T>(key: IDBValidKey, updates: Partial<T>, options?: OperationCallbacks<void>): Promise<void>;\r\n delete(key: IDBValidKey, options?: OperationCallbacks<void>): Promise<void>;\r\n exists(key: IDBValidKey): Promise<boolean>;\r\n query<T>(filterFn: (item: T) => boolean, options?: OperationCallbacks<T[]>): Promise<T[]>;\r\n upsert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey>;\r\n bulkInsert<T>(items: T[], options?: OperationCallbacks<IDBValidKey[]>): Promise<IDBValidKey[]>;\r\n clear(options?: OperationCallbacks<void>): Promise<void>;\r\n count(options?: OperationCallbacks<number>): Promise<number>;\r\n}\r\n\r\nexport function createTableController(db: IDBDatabase, tableName: string): ITableController {\r\n return createStandaloneController(db, tableName);\r\n}\r\n\r\nexport function createTransactionTableController(tx: IDBTransaction, tableName: string): ITableController {\r\n return createTransactionController(tx, tableName);\r\n}\r\n","import { useCallback, useState } from 'preact/hooks';\r\n\r\nexport interface UseClipboardOptions {\r\n /** Duration in ms to keep `copied` true before resetting. Default: 2000 */\r\n resetDelay?: number;\r\n}\r\n\r\nexport interface UseClipboardReturn {\r\n /** Copy text to the clipboard. Returns true on success. */\r\n copy: (text: string) => Promise<boolean>;\r\n /** Read text from the clipboard. Returns empty string if denied or unavailable. */\r\n paste: () => Promise<string>;\r\n /** Whether the last copy operation succeeded (resets after resetDelay) */\r\n copied: boolean;\r\n /** Error from the last failed operation, or null */\r\n error: Error | null;\r\n /** Manually reset copied and error state */\r\n reset: () => void;\r\n}\r\n\r\n/**\r\n * A Preact hook for reading and writing the clipboard. Uses the async\r\n * Clipboard API when available (requires secure context and user gesture).\r\n *\r\n * @param options - Optional configuration (e.g., resetDelay for copied state)\r\n * @returns Object with copy, paste, copied, error, and reset\r\n *\r\n * @example\r\n * ```tsx\r\n * function CopyButton() {\r\n * const { copy, copied, error } = useClipboard();\r\n * return (\r\n * <button onClick={() => copy('Hello!')}>\r\n * {copied ? 'Copied!' : 'Copy'}\r\n * </button>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useClipboard(options: UseClipboardOptions = {}): UseClipboardReturn {\r\n const { resetDelay = 2000 } = options;\r\n\r\n const [copied, setCopied] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const reset = useCallback(() => {\r\n setCopied(false);\r\n setError(null);\r\n }, []);\r\n\r\n const copy = useCallback(\r\n async (text: string): Promise<boolean> => {\r\n setError(null);\r\n\r\n if (typeof navigator === 'undefined' || !navigator.clipboard) {\r\n const err = new Error('Clipboard API is not available');\r\n setError(err);\r\n return false;\r\n }\r\n\r\n try {\r\n await navigator.clipboard.writeText(text);\r\n setCopied(true);\r\n if (resetDelay > 0) {\r\n setTimeout(() => setCopied(false), resetDelay);\r\n }\r\n return true;\r\n } catch (e) {\r\n const err = e instanceof Error ? e : new Error(String(e));\r\n setError(err);\r\n return false;\r\n }\r\n },\r\n [resetDelay]\r\n );\r\n\r\n const paste = useCallback(async (): Promise<string> => {\r\n setError(null);\r\n\r\n if (typeof navigator === 'undefined' || !navigator.clipboard) {\r\n const err = new Error('Clipboard API is not available');\r\n setError(err);\r\n return '';\r\n }\r\n\r\n try {\r\n const text = await navigator.clipboard.readText();\r\n return text;\r\n } catch (e) {\r\n const err = e instanceof Error ? e : new Error(String(e));\r\n setError(err);\r\n return '';\r\n }\r\n }, []);\r\n\r\n return { copy, paste, copied, error, reset };\r\n}\r\n","/**\r\n * Preact hook for IndexedDB: open database, create stores/indexes, return a database controller.\r\n * Uses a singleton connection per (name, version).\r\n * @module useIndexedDB\r\n */\r\n\r\nimport { useState, useEffect, useRef } from 'preact/hooks';\r\nimport type { IndexedDBConfig } from './indexedDB/types';\r\nimport { openDB } from './indexedDB/openDB';\r\nimport { createDBController } from './indexedDB/dbController';\r\nimport type { IDBController } from './indexedDB/dbController';\r\n\r\nexport type { IndexedDBConfig, IDBController } from './indexedDB';\r\n\r\nexport interface UseIndexedDBReturn {\r\n /** Database controller (table, transaction). Null until the database is open. */\r\n db: IDBController | null;\r\n /** True once the database is open and ready. */\r\n isReady: boolean;\r\n /** Error from opening the database, if any. */\r\n error: DOMException | null;\r\n}\r\n\r\n/**\r\n * Opens an IndexedDB database and returns a controller for tables and transactions.\r\n * Handles onupgradeneeded: creates object stores and indexes from config.\r\n * Connection is a singleton per (config.name, config.version).\r\n *\r\n * @param config - Database name, version, and table schemas (keyPath, autoIncrement, indexes).\r\n * @returns { db, isReady, error }. Use db.table(name) and db.transaction(...) when isReady is true.\r\n *\r\n * @example\r\n * const { db, isReady, error } = useIndexedDB({\r\n * name: 'my-db',\r\n * version: 1,\r\n * tables: {\r\n * users: { keyPath: 'id', autoIncrement: true, indexes: ['email'] },\r\n * },\r\n * })\r\n * if (isReady && db) {\r\n * const users = db.table('users')\r\n * await users.insert({ email: 'a@b.com' })\r\n * await db.transaction(['users'], 'readwrite', (tx) => tx.table('users').insert({ email: 'b@b.com' }))\r\n * }\r\n */\r\nexport function useIndexedDB(config: IndexedDBConfig): UseIndexedDBReturn {\r\n const [db, setDb] = useState<IDBController | null>(null);\r\n const [error, setError] = useState<DOMException | null>(null);\r\n const [isReady, setIsReady] = useState(false);\r\n const configRef = useRef(config);\r\n configRef.current = config;\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n setError(null);\r\n setIsReady(false);\r\n setDb(null);\r\n\r\n const { name, version, tables } = configRef.current;\r\n openDB({ name, version, tables })\r\n .then((database) => {\r\n if (cancelled) {\r\n database.close();\r\n return;\r\n }\r\n const controller = createDBController(database, configRef.current);\r\n setDb(controller);\r\n setIsReady(true);\r\n })\r\n .catch((err: DOMException) => {\r\n if (!cancelled) setError(err);\r\n });\r\n\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [config.name, config.version]);\r\n\r\n return { db, isReady, error };\r\n}\r\n\r\n/*\r\n * Usage example:\r\n *\r\n * const { db, isReady, error } = useIndexedDB({\r\n * name: 'my-app-db',\r\n * version: 1,\r\n * tables: {\r\n * users: { keyPath: 'id', autoIncrement: true, indexes: ['email'] },\r\n * settings: { keyPath: 'key' },\r\n * },\r\n * })\r\n *\r\n * if (error) return <div>Failed to open database</div>\r\n * if (!isReady || !db) return <div>Loading...</div>\r\n *\r\n * const users = db.table('users')\r\n * await users.insert({ email: 'a@b.com', name: 'Alice' })\r\n * await users.update(1, { name: 'Alice Smith' })\r\n * const found = await users.query((u) => u.email.startsWith('a@'))\r\n * const n = await users.count()\r\n * await users.delete(1)\r\n * await users.upsert({ id: 2, email: 'b@b.com' })\r\n * await users.bulkInsert([{ email: 'c@b.com' }, { email: 'd@b.com' }])\r\n * await users.clear({ onSuccess: () => console.log('cleared') })\r\n *\r\n * await db.transaction(['users', 'settings'], 'readwrite', async (tx) => {\r\n * await tx.table('users').insert({ email: 'e@b.com' })\r\n * await tx.table('settings').upsert({ key: 'theme', value: 'dark' })\r\n * }, { onSuccess: () => console.log('transaction done') })\r\n */\r\n","/**\r\n * Database controller: table(name), transaction(storeNames, mode, callback, options).\r\n * @module indexedDB/dbController\r\n */\r\n\r\nimport type { IndexedDBConfig, TransactionOptions } from './types';\r\nimport type { ITableController } from './tableController';\r\nimport { createTableController, createTransactionTableController } from './tableController';\r\n\r\n/** Transaction context passed to the callback: provides table(name) bound to this transaction. */\r\nexport interface TransactionContext {\r\n /** Returns a table controller bound to this transaction. Use for all ops inside the callback. */\r\n table: (name: string) => ITableController;\r\n}\r\n\r\n/**\r\n * Database controller built from an open IDBDatabase.\r\n * Exposes table(name) and transaction(...).\r\n */\r\nexport interface IDBController {\r\n /** Underlying IDBDatabase (read-only). */\r\n readonly db: IDBDatabase;\r\n /** Returns true if an object store with the given name exists. */\r\n hasTable: (name: string) => boolean;\r\n /** Returns a table controller for the given store (each op opens its own transaction). */\r\n table: (name: string) => ITableController;\r\n /**\r\n * Runs a callback inside a single transaction. All operations in the callback use the same transaction.\r\n * @param storeNames - Object store names to include in the transaction.\r\n * @param mode - 'readonly' | 'readwrite'.\r\n * @param callback - Async or sync function receiving { table(name) }. Return value is ignored; await all ops inside.\r\n * @param options - Optional onSuccess/onError callbacks.\r\n * @returns Promise that resolves when the transaction completes (after all requests and the callback).\r\n */\r\n transaction: <T = void>(\r\n storeNames: string[],\r\n mode: IDBTransactionMode,\r\n callback: (tx: TransactionContext) => T | Promise<T>,\r\n options?: TransactionOptions\r\n ) => Promise<void>;\r\n}\r\n\r\nfunction withTransactionCallbacks(\r\n promise: Promise<void>,\r\n options?: TransactionOptions\r\n): Promise<void> {\r\n if (!options) return promise;\r\n return promise\r\n .then(() => options.onSuccess?.())\r\n .catch((err: DOMException) => {\r\n options.onError?.(err);\r\n throw err;\r\n });\r\n}\r\n\r\n/**\r\n * Creates a database controller from an open IDBDatabase instance.\r\n */\r\nexport function createDBController(db: IDBDatabase, _config: IndexedDBConfig): IDBController {\r\n return {\r\n get db(): IDBDatabase {\r\n return db;\r\n },\r\n\r\n hasTable(name: string): boolean {\r\n return db.objectStoreNames.contains(name);\r\n },\r\n\r\n table(name: string): ITableController {\r\n return createTableController(db, name);\r\n },\r\n\r\n transaction<T = void>(\r\n storeNames: string[],\r\n mode: IDBTransactionMode,\r\n callback: (tx: TransactionContext) => T | Promise<T>,\r\n options?: TransactionOptions\r\n ): Promise<void> {\r\n const tx = db.transaction(storeNames, mode);\r\n const txContext: TransactionContext = {\r\n table: (tableName: string) => createTransactionTableController(tx, tableName),\r\n };\r\n const txPromise = new Promise<void>((resolve, reject) => {\r\n tx.oncomplete = () => resolve();\r\n tx.onerror = () => reject(tx.error ?? new DOMException('Transaction failed'));\r\n });\r\n const callbackResult = callback(txContext);\r\n const promise = Promise.resolve(callbackResult).then(() => txPromise);\r\n return withTransactionCallbacks(promise, options);\r\n },\r\n };\r\n}\r\n","import { RefObject } from 'preact'\r\nimport { useEffect } from 'preact/hooks'\r\n\r\nexport type UseMutationObserverOptions = MutationObserverInit\r\n\r\n/**\r\n * A Preact hook to observe DOM mutations using MutationObserver.\r\n * @param target - The element to observe.\r\n * @param callback - Function to call on mutation.\r\n * @param options - MutationObserver options.\r\n */\r\nexport function useMutationObserver(\r\n targetRef: RefObject<HTMLElement | null>,\r\n callback: MutationCallback,\r\n options: MutationObserverInit\r\n) {\r\n useEffect(() => {\r\n const node = targetRef.current\r\n if (!node) return\r\n\r\n const observer = new MutationObserver(callback)\r\n observer.observe(node, options)\r\n\r\n return () => observer.disconnect()\r\n }, [targetRef, callback, options])\r\n}\r\n","import { useEffect, useState } from 'preact/hooks';\r\n\r\nexport type PreferredTheme = 'light' | 'dark' | 'no-preference';\r\n\r\n/**\r\n * A Preact hook that returns the user's preferred color scheme based on the\r\n * `prefers-color-scheme` media query. Updates reactively when the user changes\r\n * their system or browser theme preference.\r\n *\r\n * @returns The preferred theme: 'light', 'dark', or 'no-preference'\r\n *\r\n * @example\r\n * ```tsx\r\n * function ThemeAwareComponent() {\r\n * const theme = usePreferredTheme();\r\n * return (\r\n * <div data-theme={theme}>\r\n * Current preference: {theme}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function usePreferredTheme(): PreferredTheme {\r\n const [theme, setTheme] = useState<PreferredTheme>(() => {\r\n if (typeof window === 'undefined') return 'no-preference';\r\n\r\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n const lightQuery = window.matchMedia('(prefers-color-scheme: light)');\r\n\r\n if (darkQuery.matches) return 'dark';\r\n if (lightQuery.matches) return 'light';\r\n return 'no-preference';\r\n });\r\n\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return;\r\n\r\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n\r\n const handleChange = (e: MediaQueryListEvent) => {\r\n setTheme(e.matches ? 'dark' : 'light');\r\n };\r\n\r\n // Re-check in case of no-preference (some browsers don't support light query)\r\n const updateTheme = () => {\r\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n const lightQuery = window.matchMedia('(prefers-color-scheme: light)');\r\n\r\n if (darkQuery.matches) setTheme('dark');\r\n else if (lightQuery.matches) setTheme('light');\r\n else setTheme('no-preference');\r\n };\r\n\r\n mediaQuery.addEventListener('change', handleChange);\r\n\r\n // Fallback: some environments may not fire change, so we also listen for light\r\n const lightQuery = window.matchMedia('(prefers-color-scheme: light)');\r\n lightQuery.addEventListener('change', updateTheme);\r\n\r\n return () => {\r\n mediaQuery.removeEventListener('change', handleChange);\r\n lightQuery.removeEventListener('change', updateTheme);\r\n };\r\n }, []);\r\n\r\n return theme;\r\n}\r\n","import type { RefObject } from 'preact'\r\nimport { useEffect, useRef } from 'preact/hooks'\r\n\r\nexport interface RageClickPayload {\r\n /** Number of clicks that triggered the rage click */\r\n count: number\r\n /** Last click event (e.g. for Sentry context) */\r\n event: MouseEvent\r\n}\r\n\r\nexport interface UseRageClickOptions {\r\n /** Called when a rage click is detected. Use this to report to Sentry or your error tracker. */\r\n onRageClick: (payload: RageClickPayload) => void\r\n /** Minimum number of clicks in the time window to count as rage click. Default: 5 (Sentry-style). */\r\n threshold?: number\r\n /** Time window in ms. Default: 1000. */\r\n timeWindow?: number\r\n /** Max distance in px between clicks to count as same spot. Default: 30. Set to Infinity to ignore distance. */\r\n distanceThreshold?: number\r\n}\r\n\r\ninterface ClickRecord {\r\n time: number\r\n x: number\r\n y: number\r\n}\r\n\r\nfunction distance(a: ClickRecord, b: ClickRecord): number {\r\n return Math.hypot(b.x - a.x, b.y - a.y)\r\n}\r\n\r\n/**\r\n * Detects \"rage clicks\" (repeated rapid clicks in the same area), e.g. when the UI\r\n * is unresponsive. Use the callback to report to Sentry or similar tools to surface\r\n * rage click issues and lower rage-click-related support.\r\n *\r\n * @param targetRef - Ref of the element to monitor (e.g. a button or card).\r\n * @param options - onRageClick callback and optional threshold, timeWindow, distanceThreshold.\r\n *\r\n * @example\r\n * ```tsx\r\n * const ref = useRef<HTMLButtonElement>(null)\r\n * useRageClick(ref, {\r\n * onRageClick: ({ count, event }) => {\r\n * Sentry.captureMessage('Rage click detected', { extra: { count, target: event.target } })\r\n * },\r\n * })\r\n * return <button ref={ref}>Submit</button>\r\n * ```\r\n */\r\nexport function useRageClick(\r\n targetRef: RefObject<HTMLElement | null>,\r\n options: UseRageClickOptions\r\n) {\r\n const {\r\n onRageClick,\r\n threshold = 5,\r\n timeWindow = 1000,\r\n distanceThreshold = 30,\r\n } = options\r\n\r\n const onRageClickRef = useRef(onRageClick)\r\n onRageClickRef.current = onRageClick\r\n\r\n const clicksRef = useRef<ClickRecord[]>([])\r\n\r\n useEffect(() => {\r\n const node = targetRef.current\r\n if (!node) return\r\n\r\n const handleClick = (e: MouseEvent) => {\r\n const now = Date.now()\r\n const record: ClickRecord = { time: now, x: e.clientX, y: e.clientY }\r\n\r\n const clicks = clicksRef.current\r\n const cutoff = now - timeWindow\r\n const recent = clicks.filter((c) => c.time >= cutoff)\r\n recent.push(record)\r\n\r\n if (distanceThreshold !== Infinity) {\r\n const inRange = recent.filter(\r\n (c) => distance(c, record) <= distanceThreshold\r\n )\r\n if (inRange.length >= threshold) {\r\n onRageClickRef.current({ count: inRange.length, event: e })\r\n clicksRef.current = []\r\n return\r\n }\r\n } else {\r\n if (recent.length >= threshold) {\r\n onRageClickRef.current({ count: recent.length, event: e })\r\n clicksRef.current = []\r\n return\r\n }\r\n }\r\n\r\n clicksRef.current = recent\r\n }\r\n\r\n node.addEventListener('click', handleClick)\r\n return () => node.removeEventListener('click', handleClick)\r\n }, [targetRef, threshold, timeWindow, distanceThreshold])\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'preact/hooks';\r\n\r\n/** Lower number = higher priority. Default priority when not specified. */\r\nconst DEFAULT_PRIORITY = 1;\r\n\r\nexport type ThreadedWorkerMode = 'sequential' | 'parallel';\r\n\r\nexport interface UseThreadedWorkerOptions {\r\n /** Sequential: single worker, priority-ordered. Parallel: worker pool. */\r\n mode: ThreadedWorkerMode;\r\n /** Max concurrent workers. Only used when mode is \"parallel\". Default 4. */\r\n concurrency?: number;\r\n}\r\n\r\nexport interface RunOptions {\r\n /** 1 = highest priority. Lower number runs first. FIFO within same priority. */\r\n priority?: number;\r\n}\r\n\r\ninterface QueuedTask<TData, TResult> {\r\n data: TData;\r\n priority: number;\r\n sequence: number;\r\n resolve: (value: TResult) => void;\r\n reject: (reason: unknown) => void;\r\n}\r\n\r\nexport interface UseThreadedWorkerReturn<TData, TResult> {\r\n /** Enqueue work. Returns a Promise that resolves with the worker result. */\r\n run: (data: TData, options?: RunOptions) => Promise<TResult>;\r\n /** True while any task is queued or running. */\r\n loading: boolean;\r\n /** Result of the most recently completed successful task. */\r\n result: TResult | undefined;\r\n /** Error from the most recently failed task. */\r\n error: unknown;\r\n /** Number of tasks currently queued + running. */\r\n queueSize: number;\r\n /** Clear all pending (not yet started) tasks. Running tasks continue. */\r\n clearQueue: () => void;\r\n /** Stop accepting new work and clear pending queue. Running tasks finish. */\r\n terminate: () => void;\r\n}\r\n\r\n/**\r\n * Production-grade hook to run async work in a queue with optional priority\r\n * and either sequential or parallel execution.\r\n *\r\n * @param workerFn - Async function to run for each task (e.g. API call, heavy compute).\r\n * @param options - mode: \"sequential\" | \"parallel\", concurrency (parallel only).\r\n * @returns run, loading, result, error, queueSize, clearQueue, terminate.\r\n */\r\nexport function useThreadedWorker<TData, TResult>(\r\n workerFn: (data: TData) => Promise<TResult>,\r\n options: UseThreadedWorkerOptions\r\n): UseThreadedWorkerReturn<TData, TResult> {\r\n const { mode, concurrency = 4 } = options;\r\n const maxConcurrent = mode === 'sequential' ? 1 : Math.max(1, concurrency);\r\n\r\n const [loading, setLoading] = useState(false);\r\n const [result, setResult] = useState<TResult | undefined>(undefined);\r\n const [error, setError] = useState<unknown>(undefined);\r\n const [queueSize, setQueueSize] = useState(0);\r\n\r\n const queueRef = useRef<QueuedTask<TData, TResult>[]>([]);\r\n const sequenceRef = useRef(0);\r\n const activeCountRef = useRef(0);\r\n const terminatedRef = useRef(false);\r\n const workerFnRef = useRef(workerFn);\r\n workerFnRef.current = workerFn;\r\n\r\n const updateQueueSize = useCallback(() => {\r\n setQueueSize(queueRef.current.length + activeCountRef.current);\r\n }, []);\r\n\r\n const processNext = useCallback(() => {\r\n if (terminatedRef.current) return;\r\n if (activeCountRef.current >= maxConcurrent) return;\r\n if (queueRef.current.length === 0) {\r\n if (activeCountRef.current === 0) setLoading(false);\r\n updateQueueSize();\r\n return;\r\n }\r\n\r\n // Sort by priority (asc), then by sequence (FIFO within same priority).\r\n queueRef.current.sort((a, b) => {\r\n if (a.priority !== b.priority) return a.priority - b.priority;\r\n return a.sequence - b.sequence;\r\n });\r\n const task = queueRef.current.shift()!;\r\n activeCountRef.current += 1;\r\n setLoading(true);\r\n updateQueueSize();\r\n\r\n const fn = workerFnRef.current;\r\n fn(task.data)\r\n .then((value) => {\r\n setResult(value);\r\n setError(undefined);\r\n task.resolve(value);\r\n })\r\n .catch((err) => {\r\n setError(err);\r\n task.reject(err);\r\n })\r\n .finally(() => {\r\n activeCountRef.current -= 1;\r\n updateQueueSize();\r\n processNext();\r\n });\r\n\r\n // Fill remaining slots (parallel mode).\r\n if (queueRef.current.length > 0 && activeCountRef.current < maxConcurrent) {\r\n processNext();\r\n }\r\n }, [maxConcurrent, updateQueueSize]);\r\n\r\n const run = useCallback(\r\n (data: TData, runOptions?: RunOptions): Promise<TResult> => {\r\n if (terminatedRef.current) {\r\n return Promise.reject(new Error('Worker is terminated'));\r\n }\r\n const priority = runOptions?.priority ?? DEFAULT_PRIORITY;\r\n const sequence = ++sequenceRef.current;\r\n const promise = new Promise<TResult>((resolve, reject) => {\r\n queueRef.current.push({ data, priority, sequence, resolve, reject });\r\n });\r\n updateQueueSize();\r\n setLoading(true);\r\n queueMicrotask(processNext);\r\n return promise;\r\n },\r\n [processNext, updateQueueSize]\r\n );\r\n\r\n const clearQueue = useCallback(() => {\r\n const pending = queueRef.current;\r\n queueRef.current = [];\r\n pending.forEach((t) => t.reject(new Error('Task cleared from queue')));\r\n updateQueueSize();\r\n if (activeCountRef.current === 0) setLoading(false);\r\n }, [updateQueueSize]);\r\n\r\n const terminate = useCallback(() => {\r\n terminatedRef.current = true;\r\n clearQueue();\r\n }, [clearQueue]);\r\n\r\n // Reset terminated on unmount so the same hook instance can't be \"revived\" without options change.\r\n useEffect(() => {\r\n return () => {\r\n terminatedRef.current = true;\r\n };\r\n }, []);\r\n\r\n return {\r\n run,\r\n loading,\r\n result,\r\n error,\r\n queueSize,\r\n clearQueue,\r\n terminate,\r\n };\r\n}\r\n","import { useState, useCallback } from 'preact/hooks';\r\n\r\n/**\r\n * Mimics React's useTransition hook in Preact.\r\n * @returns [startTransition, isPending]\r\n */\r\nexport function useTransition(): [startTransition: (callback: () => void) => void, isPending: boolean] {\r\n const [isPending, setIsPending] = useState(false);\r\n\r\n const startTransition = useCallback((callback: () => void) => {\r\n setIsPending(true);\r\n Promise.resolve().then(() => {\r\n callback();\r\n setIsPending(false);\r\n });\r\n }, []);\r\n\r\n return [startTransition, isPending];\r\n}\r\n","import { ComponentChildren, cloneElement, isValidElement } from 'preact'\r\nimport { useMemo } from 'preact/hooks'\r\n\r\nexport type InjectableProps = Record<string, any>\r\n\r\n/**\r\n * A Preact hook to wrap children components and inject additional props into them.\r\n * @param children - The children to wrap and enhance with props.\r\n * @param injectProps - The props to inject into each child component.\r\n * @param mergeStrategy - How to handle prop conflicts ('override' | 'preserve'). Defaults to 'preserve'.\r\n * @returns Enhanced children with injected props.\r\n */\r\nexport function useWrappedChildren(\r\n children: ComponentChildren,\r\n injectProps: InjectableProps,\r\n mergeStrategy: 'override' | 'preserve' = 'preserve'\r\n): ComponentChildren {\r\n return useMemo(() => {\r\n if (!children) return children\r\n\r\n const enhanceChild = (child: any): any => {\r\n if (!isValidElement(child)) return child\r\n\r\n const existingProps = child.props || {}\r\n \r\n let mergedProps: InjectableProps\r\n \r\n if (mergeStrategy === 'override') {\r\n // Injected props override existing ones\r\n mergedProps = { ...existingProps, ...injectProps }\r\n } else {\r\n // Existing props are preserved, injected props are added only if not present\r\n mergedProps = { ...injectProps, ...existingProps }\r\n }\r\n\r\n // Special handling for style prop to merge style objects properly\r\n const existingStyle = (existingProps as any)?.style\r\n const injectStyle = (injectProps as any)?.style\r\n \r\n if (existingStyle && injectStyle && \r\n typeof existingStyle === 'object' && typeof injectStyle === 'object') {\r\n if (mergeStrategy === 'override') {\r\n (mergedProps as any).style = { ...existingStyle, ...injectStyle }\r\n } else {\r\n (mergedProps as any).style = { ...injectStyle, ...existingStyle }\r\n }\r\n }\r\n\r\n return cloneElement(child, mergedProps)\r\n }\r\n\r\n if (Array.isArray(children)) {\r\n return children.map(enhanceChild)\r\n }\r\n\r\n return enhanceChild(children)\r\n }, [children, injectProps, mergeStrategy])\r\n}"],"names":["listeners","Map","getNetworkState","navigator","online","state","onLine","connection","undefined","effectiveType","downlink","rtt","saveData","type","connectionType","connectionCache","requestToPromise","request","Promise","resolve","reject","onsuccess","result","onerror","_request$error","error","DOMException","withCallbacks","promise","options","then","onSuccess","err","onError","_options$resetDelay","resetDelay","_useState","useState","copied","setCopied","_useState2","setError","reset","useCallback","copy","text","clipboard","Error","_catch","writeText","setTimeout","e","String","paste","readText","emit","event","_arguments","arguments","handlers","get","forEach","handler","apply","slice","call","on","Set","set","add","size","config","db","setDb","_useState3","isReady","setIsReady","configRef","useRef","current","useEffect","cancelled","_configRef$current","key","name","version","indexedDB","open","onupgradeneeded","target","tables","_i","_Object$keys","Object","keys","length","tableName","schema","objectStoreNames","contains","_schema$autoIncrement","store","createObjectStore","keyPath","autoIncrement","indexes","_step","_iterator","_createForOfIteratorHelperLoose","done","indexName","value","createIndex","unique","_openDB","openDB","database","close","controller","hasTable","table","getStore","mode","transaction","objectStore","insert","data","update","updates","existing","merged","_extends","put","delete","exists","getKey","k","query","filterFn","openCursor","results","cursor","push","upsert","bulkInsert","items","completed","item","i","req","_req$error","clear","count","createStandaloneController","createTableController","storeNames","callback","tx","txContext","_request$error2","_req$error2","createTransactionController","createTransactionTableController","txPromise","oncomplete","_tx$error","callbackResult","withTransactionCallbacks","createDBController","targetRef","node","observer","MutationObserver","observe","disconnect","setState","window","updateState","addEventListener","removeEventListener","darkQuery","matchMedia","lightQuery","matches","theme","setTheme","mediaQuery","handleChange","updateTheme","onRageClick","_options$threshold","threshold","_options$timeWindow","timeWindow","_options$distanceThre","distanceThreshold","onRageClickRef","clicksRef","handleClick","now","Date","record","time","x","clientX","y","clientY","cutoff","recent","filter","c","Infinity","inRange","a","b","Math","hypot","workerFn","_options$concurrency","concurrency","maxConcurrent","max","loading","setLoading","setResult","_useState4","queueSize","setQueueSize","queueRef","sequenceRef","activeCountRef","terminatedRef","workerFnRef","updateQueueSize","processNext","sort","priority","sequence","task","shift","fn","run","runOptions","_runOptions$priority","queueMicrotask","clearQueue","pending","t","terminate","isPending","setIsPending","children","injectProps","mergeStrategy","useMemo","enhanceChild","child","isValidElement","mergedProps","existingProps","props","existingStyle","style","injectStyle","cloneElement","Array","isArray","map"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/useEventBus.ts","../src/useNetworkState.ts","../src/indexedDB/openDB.ts","../src/indexedDB/requestToPromise.ts","../src/indexedDB/tableController.ts","../src/useWebRTCIP.ts","../src/useClipboard.ts","../src/useIndexedDB.ts","../src/indexedDB/dbController.ts","../src/useMutationObserver.ts","../src/usePreferredTheme.ts","../src/useRageClick.ts","../src/useThreadedWorker.ts","../src/useTransition.ts","../src/useWrappedChildren.ts"],"sourcesContent":["import { useCallback, useEffect } from 'preact/hooks';\r\n\r\ntype EventMap = Record<string, (...args: any[]) => void>;\r\n\r\nconst listeners = new Map<string, Set<(...args: any[]) => void>>();\r\n\r\n/**\r\n * A Preact hook to publish and subscribe to custom events across components.\r\n * @returns An object with `emit` and `on` methods.\r\n */\r\nexport function useEventBus<T extends EventMap>() {\r\n const emit = useCallback(<K extends keyof T>(event: K, ...args: Parameters<T[K]>) => {\r\n const handlers = listeners.get(event as string);\r\n if (handlers) {\r\n handlers.forEach((handler) => handler(...args));\r\n }\r\n }, []);\r\n\r\n const on = useCallback(<K extends keyof T>(event: K, handler: T[K]) => {\r\n let handlers = listeners.get(event as string);\r\n if (!handlers) {\r\n handlers = new Set();\r\n listeners.set(event as string, handlers);\r\n }\r\n handlers.add(handler);\r\n\r\n return () => {\r\n handlers!.delete(handler);\r\n if (handlers!.size === 0) {\r\n listeners.delete(event as string);\r\n }\r\n };\r\n }, []);\r\n\r\n return { emit, on };\r\n}\r\n","import { useEffect, useState } from 'preact/hooks';\r\n\r\n/** Network Information API (not in all browsers) */\r\ninterface NetworkInformation extends EventTarget {\r\n effectiveType?: string;\r\n downlink?: number;\r\n rtt?: number;\r\n saveData?: boolean;\r\n type?: string;\r\n}\r\n\r\n/** Effective connection type from Network Information API */\r\nexport type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g';\r\n\r\n/** Network connection type (e.g., wifi, cellular) */\r\nexport type ConnectionType =\r\n | 'bluetooth'\r\n | 'cellular'\r\n | 'ethernet'\r\n | 'mixed'\r\n | 'none'\r\n | 'other'\r\n | 'unknown'\r\n | 'wifi';\r\n\r\nexport interface NetworkState {\r\n /** Whether the browser is online */\r\n online: boolean;\r\n /** Effective connection type (when supported) */\r\n effectiveType?: EffectiveConnectionType;\r\n /** Estimated downlink speed in Mbps (when supported) */\r\n downlink?: number;\r\n /** Estimated round-trip time in ms (when supported) */\r\n rtt?: number;\r\n /** Whether the user has requested reduced data usage (when supported) */\r\n saveData?: boolean;\r\n /** Connection type (when supported) */\r\n connectionType?: ConnectionType;\r\n}\r\n\r\nfunction getNetworkState(): NetworkState {\r\n if (typeof navigator === 'undefined') {\r\n return { online: true };\r\n }\r\n\r\n const state: NetworkState = {\r\n online: navigator.onLine,\r\n };\r\n\r\n const connection =\r\n (navigator as Navigator & { connection?: NetworkInformation }).connection;\r\n\r\n if (connection) {\r\n if (connection.effectiveType !== undefined) {\r\n state.effectiveType = connection.effectiveType as EffectiveConnectionType;\r\n }\r\n if (connection.downlink !== undefined) {\r\n state.downlink = connection.downlink;\r\n }\r\n if (connection.rtt !== undefined) {\r\n state.rtt = connection.rtt;\r\n }\r\n if (connection.saveData !== undefined) {\r\n state.saveData = connection.saveData;\r\n }\r\n if (connection.type !== undefined) {\r\n state.connectionType = connection.type as ConnectionType;\r\n }\r\n }\r\n\r\n return state;\r\n}\r\n\r\n/**\r\n * A Preact hook that returns the current network state, including online/offline\r\n * status and (when supported) connection type, downlink, RTT, and save-data preference.\r\n * Updates reactively when the network state changes.\r\n *\r\n * @returns The current network state object\r\n *\r\n * @example\r\n * ```tsx\r\n * function NetworkStatus() {\r\n * const { online, effectiveType, saveData } = useNetworkState();\r\n * return (\r\n * <div>\r\n * Status: {online ? 'Online' : 'Offline'}\r\n * {effectiveType && ` (${effectiveType})`}\r\n * {saveData && ' - Reduced data mode'}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useNetworkState(): NetworkState {\r\n const [state, setState] = useState<NetworkState>(getNetworkState);\r\n\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return;\r\n\r\n const updateState = () => setState(getNetworkState());\r\n\r\n window.addEventListener('online', updateState);\r\n window.addEventListener('offline', updateState);\r\n\r\n const connection = (navigator as Navigator & { connection?: NetworkInformation })\r\n .connection;\r\n if (connection?.addEventListener) {\r\n connection.addEventListener('change', updateState);\r\n }\r\n\r\n return () => {\r\n window.removeEventListener('online', updateState);\r\n window.removeEventListener('offline', updateState);\r\n if (connection?.removeEventListener) {\r\n connection.removeEventListener('change', updateState);\r\n }\r\n };\r\n }, []);\r\n\r\n return state;\r\n}\r\n","/**\r\n * Opens IndexedDB and runs onupgradeneeded to create stores and indexes.\r\n * Singleton per (name, version).\r\n * @module indexedDB/openDB\r\n */\r\n\r\nimport type { IndexedDBConfig, TableSchema } from './types';\r\nimport { requestToPromise } from './requestToPromise';\r\n\r\nconst connectionCache = new Map<string, Promise<IDBDatabase>>();\r\n\r\n/**\r\n * Opens the database and creates/upgrades object stores and indexes from config.\r\n * Uses a singleton cache per (name, version); repeated calls with the same config reuse the same connection.\r\n */\r\nexport function openDB(config: IndexedDBConfig): Promise<IDBDatabase> {\r\n const key = `${config.name}_v${config.version}`;\r\n let promise = connectionCache.get(key);\r\n if (promise) return promise;\r\n promise = _openDB(config);\r\n connectionCache.set(key, promise);\r\n return promise;\r\n}\r\n\r\nfunction _openDB(config: IndexedDBConfig): Promise<IDBDatabase> {\r\n return new Promise<IDBDatabase>((resolve, reject) => {\r\n const request = indexedDB.open(config.name, config.version);\r\n request.onerror = () => reject(request.error ?? new DOMException('Failed to open database'));\r\n request.onsuccess = () => resolve(request.result);\r\n request.onupgradeneeded = (event: IDBVersionChangeEvent) => {\r\n const db = (event.target as IDBOpenDBRequest).result;\r\n const tables = config.tables;\r\n for (const tableName of Object.keys(tables)) {\r\n const schema = tables[tableName] as TableSchema;\r\n if (!db.objectStoreNames.contains(tableName)) {\r\n const store = db.createObjectStore(tableName, {\r\n keyPath: schema.keyPath,\r\n autoIncrement: schema.autoIncrement ?? false,\r\n });\r\n if (schema.indexes) {\r\n for (const indexName of schema.indexes) {\r\n store.createIndex(indexName, indexName, { unique: false });\r\n }\r\n }\r\n }\r\n }\r\n };\r\n });\r\n}\r\n","/**\r\n * Wraps an IDBRequest in a Promise.\r\n * @module indexedDB/requestToPromise\r\n */\r\n\r\n/**\r\n * Converts an IDBRequest to a Promise. Rejects with the request's error on failure.\r\n * @param request - Native IndexedDB request.\r\n * @returns Promise that resolves with the request result or rejects with DOMException.\r\n */\r\nexport function requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\r\n return new Promise<T>((resolve, reject) => {\r\n request.onsuccess = () => resolve(request.result);\r\n request.onerror = () => reject(request.error ?? new DOMException('Unknown IndexedDB error'));\r\n });\r\n}\r\n","/**\r\n * Table controller: insert, update, delete, exists, query, upsert, bulkInsert, clear, count.\r\n * Works in standalone mode (opens its own transaction per op) or bound to a transaction.\r\n * @module indexedDB/tableController\r\n */\r\n\r\nimport type { OperationCallbacks } from './types';\r\nimport { requestToPromise } from './requestToPromise';\r\n\r\n/** Runs optional callbacks and returns the result. */\r\nfunction withCallbacks<T>(\r\n promise: Promise<T>,\r\n options?: OperationCallbacks<T>\r\n): Promise<T> {\r\n if (!options) return promise;\r\n return promise\r\n .then((result) => {\r\n options.onSuccess?.(result);\r\n return result;\r\n })\r\n .catch((err: DOMException) => {\r\n options.onError?.(err);\r\n throw err;\r\n });\r\n}\r\n\r\n/**\r\n * Standalone table controller: opens a new transaction for each operation.\r\n */\r\nfunction createStandaloneController(db: IDBDatabase, tableName: string): ITableController {\r\n function getStore(mode: IDBTransactionMode): IDBObjectStore {\r\n const tx = db.transaction([tableName], mode);\r\n return tx.objectStore(tableName);\r\n }\r\n\r\n return {\r\n insert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey> {\r\n const store = getStore('readwrite');\r\n return withCallbacks(requestToPromise(store.add(data)), options);\r\n },\r\n\r\n update<T>(key: IDBValidKey, updates: Partial<T>, options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore('readwrite');\r\n const getReq = store.get(key);\r\n return withCallbacks(\r\n requestToPromise(getReq).then((existing) => {\r\n if (existing === undefined) {\r\n throw new DOMException('Key not found', 'NotFoundError');\r\n }\r\n const merged = { ...existing, ...updates } as T;\r\n return requestToPromise(store.put(merged));\r\n }).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n delete(key: IDBValidKey, options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore('readwrite');\r\n return withCallbacks(\r\n requestToPromise(store.delete(key)).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n exists(key: IDBValidKey): Promise<boolean> {\r\n const store = getStore('readonly');\r\n return requestToPromise(store.getKey(key)).then((k) => k !== undefined);\r\n },\r\n\r\n query<T>(filterFn: (item: T) => boolean, options?: OperationCallbacks<T[]>): Promise<T[]> {\r\n const store = getStore('readonly');\r\n const request = store.openCursor();\r\n const results: T[] = [];\r\n return withCallbacks(\r\n new Promise<T[]>((resolve, reject) => {\r\n request.onsuccess = () => {\r\n const cursor = request.result;\r\n if (cursor) {\r\n if (filterFn(cursor.value as T)) results.push(cursor.value as T);\r\n cursor.continue();\r\n } else {\r\n resolve(results);\r\n }\r\n };\r\n request.onerror = () => reject(request.error ?? new DOMException('Unknown error'));\r\n }),\r\n options\r\n );\r\n },\r\n\r\n upsert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey> {\r\n const store = getStore('readwrite');\r\n return withCallbacks(requestToPromise(store.put(data)), options);\r\n },\r\n\r\n bulkInsert<T>(items: T[], options?: OperationCallbacks<IDBValidKey[]>): Promise<IDBValidKey[]> {\r\n const store = getStore('readwrite');\r\n const keys: IDBValidKey[] = [];\r\n if (items.length === 0) {\r\n return withCallbacks(Promise.resolve(keys), options);\r\n }\r\n let completed = 0;\r\n const promise = new Promise<IDBValidKey[]>((resolve, reject) => {\r\n const onDone = () => {\r\n completed++;\r\n if (completed === items.length) resolve(keys);\r\n };\r\n items.forEach((item, i) => {\r\n const req = store.add(item);\r\n req.onsuccess = () => {\r\n keys[i] = req.result;\r\n onDone();\r\n };\r\n req.onerror = () => reject(req.error ?? new DOMException('Unknown error'));\r\n });\r\n });\r\n return withCallbacks(promise, options);\r\n },\r\n\r\n clear(options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore('readwrite');\r\n return withCallbacks(\r\n requestToPromise(store.clear()).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n count(options?: OperationCallbacks<number>): Promise<number> {\r\n const store = getStore('readonly');\r\n return withCallbacks(requestToPromise(store.count()), options ?? {});\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Transaction-scoped table controller: uses the given transaction (no new transaction).\r\n */\r\nfunction createTransactionController(tx: IDBTransaction, tableName: string): ITableController {\r\n function getStore(): IDBObjectStore {\r\n return tx.objectStore(tableName);\r\n }\r\n\r\n return {\r\n insert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey> {\r\n const store = getStore();\r\n return withCallbacks(requestToPromise(store.add(data)), options);\r\n },\r\n\r\n update<T>(key: IDBValidKey, updates: Partial<T>, options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore();\r\n return withCallbacks(\r\n requestToPromise(store.get(key)).then((existing) => {\r\n if (existing === undefined) {\r\n throw new DOMException('Key not found', 'NotFoundError');\r\n }\r\n const merged = { ...existing, ...updates } as T;\r\n return requestToPromise(store.put(merged));\r\n }).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n delete(key: IDBValidKey, options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore();\r\n return withCallbacks(\r\n requestToPromise(store.delete(key)).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n exists(key: IDBValidKey): Promise<boolean> {\r\n const store = getStore();\r\n return requestToPromise(store.getKey(key)).then((k) => k !== undefined);\r\n },\r\n\r\n query<T>(filterFn: (item: T) => boolean, options?: OperationCallbacks<T[]>): Promise<T[]> {\r\n const store = getStore();\r\n const request = store.openCursor();\r\n const results: T[] = [];\r\n return withCallbacks(\r\n new Promise<T[]>((resolve, reject) => {\r\n request.onsuccess = () => {\r\n const cursor = request.result;\r\n if (cursor) {\r\n if (filterFn(cursor.value as T)) results.push(cursor.value as T);\r\n cursor.continue();\r\n } else {\r\n resolve(results);\r\n }\r\n };\r\n request.onerror = () => reject(request.error ?? new DOMException('Unknown error'));\r\n }),\r\n options\r\n );\r\n },\r\n\r\n upsert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey> {\r\n const store = getStore();\r\n return withCallbacks(requestToPromise(store.put(data)), options);\r\n },\r\n\r\n bulkInsert<T>(items: T[], options?: OperationCallbacks<IDBValidKey[]>): Promise<IDBValidKey[]> {\r\n const store = getStore();\r\n const keys: IDBValidKey[] = [];\r\n if (items.length === 0) {\r\n return withCallbacks(Promise.resolve(keys), options);\r\n }\r\n let completed = 0;\r\n const promise = new Promise<IDBValidKey[]>((resolve, reject) => {\r\n items.forEach((item, i) => {\r\n const req = store.add(item);\r\n req.onsuccess = () => {\r\n keys[i] = req.result;\r\n completed++;\r\n if (completed === items.length) resolve(keys);\r\n };\r\n req.onerror = () => reject(req.error ?? new DOMException('Unknown error'));\r\n });\r\n });\r\n return withCallbacks(promise, options);\r\n },\r\n\r\n clear(options?: OperationCallbacks<void>): Promise<void> {\r\n const store = getStore();\r\n return withCallbacks(\r\n requestToPromise(store.clear()).then(() => undefined),\r\n options\r\n );\r\n },\r\n\r\n count(options?: OperationCallbacks<number>): Promise<number> {\r\n const store = getStore();\r\n return withCallbacks(requestToPromise(store.count()), options ?? {});\r\n },\r\n };\r\n}\r\n\r\n/** Public interface for a table controller (standalone or transaction-scoped). */\r\nexport interface ITableController {\r\n insert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey>;\r\n update<T>(key: IDBValidKey, updates: Partial<T>, options?: OperationCallbacks<void>): Promise<void>;\r\n delete(key: IDBValidKey, options?: OperationCallbacks<void>): Promise<void>;\r\n exists(key: IDBValidKey): Promise<boolean>;\r\n query<T>(filterFn: (item: T) => boolean, options?: OperationCallbacks<T[]>): Promise<T[]>;\r\n upsert<T>(data: T, options?: OperationCallbacks<IDBValidKey>): Promise<IDBValidKey>;\r\n bulkInsert<T>(items: T[], options?: OperationCallbacks<IDBValidKey[]>): Promise<IDBValidKey[]>;\r\n clear(options?: OperationCallbacks<void>): Promise<void>;\r\n count(options?: OperationCallbacks<number>): Promise<number>;\r\n}\r\n\r\nexport function createTableController(db: IDBDatabase, tableName: string): ITableController {\r\n return createStandaloneController(db, tableName);\r\n}\r\n\r\nexport function createTransactionTableController(tx: IDBTransaction, tableName: string): ITableController {\r\n return createTransactionController(tx, tableName);\r\n}\r\n","/**\r\n * useWebRTCIP – detect local/public IPs via WebRTC ICE candidates and STUN.\r\n * Not highly reliable; use as first-priority hint and fall back to a public IP API (e.g. ipapi.co) if needed.\r\n * @module useWebRTCIP\r\n */\r\n\r\nimport { useEffect, useState, useRef } from 'preact/hooks';\r\n\r\n/** IPv4 regex for ICE candidate strings (captures dotted-decimal). */\r\nconst IPV4_REGEX = /\\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;\r\n\r\nconst DEFAULT_STUN_SERVERS: string[] = ['stun:stun.l.google.com:19302'];\r\nconst DEFAULT_TIMEOUT_MS = 3000;\r\n\r\nexport interface UseWebRTCIPOptions {\r\n /** STUN server URLs (default: Google STUN). */\r\n stunServers?: string[];\r\n /** Stop gathering after this many ms (default: 3000). */\r\n timeout?: number;\r\n /** Called once per newly detected IP (no duplicates). */\r\n onDetect?: (ip: string) => void;\r\n}\r\n\r\nexport interface UseWebRTCIPReturn {\r\n /** Unique IPv4 addresses found from ICE candidates. */\r\n ips: string[];\r\n /** True while ICE gathering is in progress. */\r\n loading: boolean;\r\n /** Error message if WebRTC is unavailable or detection fails. */\r\n error: string | null;\r\n}\r\n\r\nfunction isSSR(): boolean {\r\n return typeof window === 'undefined';\r\n}\r\n\r\nfunction isWebRTCAvailable(): boolean {\r\n return typeof RTCPeerConnection !== 'undefined';\r\n}\r\n\r\n/**\r\n * Extracts IPv4 addresses from an ICE candidate string.\r\n * Filters out common non-public/local patterns (e.g. 0.0.0.0) if desired; currently returns all matches.\r\n */\r\nfunction extractIPv4FromCandidate(candidate: string): string[] {\r\n const matches = candidate.match(IPV4_REGEX);\r\n return matches ? [...matches] : [];\r\n}\r\n\r\n/**\r\n * Attempts to detect client IP addresses using WebRTC ICE candidates and a STUN server.\r\n * Works frontend-only (no backend). Not guaranteed to return a public IP; use as a hint and\r\n * fall back to a public IP API (e.g. ipapi.co, ip-api.com) if you need reliability.\r\n *\r\n * @param options - Optional: stunServers, timeout (ms), onDetect(ip) callback.\r\n * @returns { ips, loading, error } – unique IPv4s, loading flag, and error message.\r\n *\r\n * @example\r\n * const { ips, loading, error } = useWebRTCIP({\r\n * timeout: 5000,\r\n * onDetect: (ip) => console.log('Detected:', ip),\r\n * })\r\n * // If ips is empty and error is set, fall back to: fetch('https://api.ipify.org?format=json')\r\n */\r\nexport function useWebRTCIP(options: UseWebRTCIPOptions = {}): UseWebRTCIPReturn {\r\n const {\r\n stunServers = DEFAULT_STUN_SERVERS,\r\n timeout: timeoutMs = DEFAULT_TIMEOUT_MS,\r\n onDetect,\r\n } = options;\r\n\r\n const [ips, setIps] = useState<string[]>([]);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const pcRef = useRef<RTCPeerConnection | null>(null);\r\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const reportedRef = useRef<Set<string>>(new Set());\r\n const onDetectRef = useRef(onDetect);\r\n onDetectRef.current = onDetect;\r\n\r\n useEffect(() => {\r\n if (isSSR()) {\r\n setLoading(false);\r\n setError('WebRTC IP detection is not available during SSR');\r\n return;\r\n }\r\n\r\n if (!isWebRTCAvailable()) {\r\n setLoading(false);\r\n setError('RTCPeerConnection is not available');\r\n return;\r\n }\r\n\r\n const reported = new Set<string>();\r\n reportedRef.current = reported;\r\n\r\n const finish = () => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n timeoutRef.current = null;\r\n }\r\n if (pcRef.current) {\r\n pcRef.current.close();\r\n pcRef.current = null;\r\n }\r\n setLoading(false);\r\n };\r\n\r\n const addIP = (ip: string) => {\r\n if (reported.has(ip)) return;\r\n reported.add(ip);\r\n setIps((prev) => {\r\n const next = [...prev, ip];\r\n return next;\r\n });\r\n onDetectRef.current?.(ip);\r\n };\r\n\r\n try {\r\n const pc = new RTCPeerConnection({\r\n iceServers: [{ urls: stunServers }],\r\n });\r\n pcRef.current = pc;\r\n\r\n pc.onicecandidate = (event) => {\r\n const c = event.candidate;\r\n if (!c || !c.candidate) return;\r\n const found = extractIPv4FromCandidate(c.candidate);\r\n found.forEach(addIP);\r\n };\r\n\r\n pc.createDataChannel('');\r\n\r\n pc.createOffer()\r\n .then((offer) => pc.setLocalDescription(offer))\r\n .catch((err) => {\r\n setError(err instanceof Error ? err.message : 'Failed to create offer');\r\n finish();\r\n });\r\n\r\n timeoutRef.current = setTimeout(() => finish(), timeoutMs);\r\n } catch (err) {\r\n setError(err instanceof Error ? err.message : 'WebRTC setup failed');\r\n finish();\r\n }\r\n\r\n return () => {\r\n finish();\r\n };\r\n }, [stunServers.join(','), timeoutMs]);\r\n\r\n return { ips, loading, error };\r\n}\r\n\r\n/*\r\n * Example usage (Preact component):\r\n *\r\n * function MyIPDisplay() {\r\n * const { ips, loading, error } = useWebRTCIP({\r\n * timeout: 4000,\r\n * onDetect: (ip) => { / * optional: e.g. send to analytics * / },\r\n * })\r\n *\r\n * if (loading) return <p>Detecting IP…</p>\r\n * if (error) return <p>WebRTC failed: {error}. Try fallback API.</p>\r\n * return <p>IPs (WebRTC): {ips.join(', ') || 'None'}</p>\r\n * }\r\n *\r\n * Fallback to public IP API when WebRTC fails or returns empty:\r\n * const [apiIP, setApiIP] = useState<string | null>(null)\r\n * useEffect(() => {\r\n * if (!loading && ips.length === 0 && error)\r\n * fetch('https://api.ipify.org?format=json').then(r => r.json()).then(d => setApiIP(d.ip))\r\n * }, [loading, ips.length, error])\r\n */\r\n","import { useCallback, useState } from 'preact/hooks';\r\n\r\nexport interface UseClipboardOptions {\r\n /** Duration in ms to keep `copied` true before resetting. Default: 2000 */\r\n resetDelay?: number;\r\n}\r\n\r\nexport interface UseClipboardReturn {\r\n /** Copy text to the clipboard. Returns true on success. */\r\n copy: (text: string) => Promise<boolean>;\r\n /** Read text from the clipboard. Returns empty string if denied or unavailable. */\r\n paste: () => Promise<string>;\r\n /** Whether the last copy operation succeeded (resets after resetDelay) */\r\n copied: boolean;\r\n /** Error from the last failed operation, or null */\r\n error: Error | null;\r\n /** Manually reset copied and error state */\r\n reset: () => void;\r\n}\r\n\r\n/**\r\n * A Preact hook for reading and writing the clipboard. Uses the async\r\n * Clipboard API when available (requires secure context and user gesture).\r\n *\r\n * @param options - Optional configuration (e.g., resetDelay for copied state)\r\n * @returns Object with copy, paste, copied, error, and reset\r\n *\r\n * @example\r\n * ```tsx\r\n * function CopyButton() {\r\n * const { copy, copied, error } = useClipboard();\r\n * return (\r\n * <button onClick={() => copy('Hello!')}>\r\n * {copied ? 'Copied!' : 'Copy'}\r\n * </button>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useClipboard(options: UseClipboardOptions = {}): UseClipboardReturn {\r\n const { resetDelay = 2000 } = options;\r\n\r\n const [copied, setCopied] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const reset = useCallback(() => {\r\n setCopied(false);\r\n setError(null);\r\n }, []);\r\n\r\n const copy = useCallback(\r\n async (text: string): Promise<boolean> => {\r\n setError(null);\r\n\r\n if (typeof navigator === 'undefined' || !navigator.clipboard) {\r\n const err = new Error('Clipboard API is not available');\r\n setError(err);\r\n return false;\r\n }\r\n\r\n try {\r\n await navigator.clipboard.writeText(text);\r\n setCopied(true);\r\n if (resetDelay > 0) {\r\n setTimeout(() => setCopied(false), resetDelay);\r\n }\r\n return true;\r\n } catch (e) {\r\n const err = e instanceof Error ? e : new Error(String(e));\r\n setError(err);\r\n return false;\r\n }\r\n },\r\n [resetDelay]\r\n );\r\n\r\n const paste = useCallback(async (): Promise<string> => {\r\n setError(null);\r\n\r\n if (typeof navigator === 'undefined' || !navigator.clipboard) {\r\n const err = new Error('Clipboard API is not available');\r\n setError(err);\r\n return '';\r\n }\r\n\r\n try {\r\n const text = await navigator.clipboard.readText();\r\n return text;\r\n } catch (e) {\r\n const err = e instanceof Error ? e : new Error(String(e));\r\n setError(err);\r\n return '';\r\n }\r\n }, []);\r\n\r\n return { copy, paste, copied, error, reset };\r\n}\r\n","/**\r\n * Preact hook for IndexedDB: open database, create stores/indexes, return a database controller.\r\n * Uses a singleton connection per (name, version).\r\n * @module useIndexedDB\r\n */\r\n\r\nimport { useState, useEffect, useRef } from 'preact/hooks';\r\nimport type { IndexedDBConfig } from './indexedDB/types';\r\nimport { openDB } from './indexedDB/openDB';\r\nimport { createDBController } from './indexedDB/dbController';\r\nimport type { IDBController } from './indexedDB/dbController';\r\n\r\nexport type { IndexedDBConfig, IDBController } from './indexedDB';\r\n\r\nexport interface UseIndexedDBReturn {\r\n /** Database controller (table, transaction). Null until the database is open. */\r\n db: IDBController | null;\r\n /** True once the database is open and ready. */\r\n isReady: boolean;\r\n /** Error from opening the database, if any. */\r\n error: DOMException | null;\r\n}\r\n\r\n/**\r\n * Opens an IndexedDB database and returns a controller for tables and transactions.\r\n * Handles onupgradeneeded: creates object stores and indexes from config.\r\n * Connection is a singleton per (config.name, config.version).\r\n *\r\n * @param config - Database name, version, and table schemas (keyPath, autoIncrement, indexes).\r\n * @returns { db, isReady, error }. Use db.table(name) and db.transaction(...) when isReady is true.\r\n *\r\n * @example\r\n * const { db, isReady, error } = useIndexedDB({\r\n * name: 'my-db',\r\n * version: 1,\r\n * tables: {\r\n * users: { keyPath: 'id', autoIncrement: true, indexes: ['email'] },\r\n * },\r\n * })\r\n * if (isReady && db) {\r\n * const users = db.table('users')\r\n * await users.insert({ email: 'a@b.com' })\r\n * await db.transaction(['users'], 'readwrite', (tx) => tx.table('users').insert({ email: 'b@b.com' }))\r\n * }\r\n */\r\nexport function useIndexedDB(config: IndexedDBConfig): UseIndexedDBReturn {\r\n const [db, setDb] = useState<IDBController | null>(null);\r\n const [error, setError] = useState<DOMException | null>(null);\r\n const [isReady, setIsReady] = useState(false);\r\n const configRef = useRef(config);\r\n configRef.current = config;\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n setError(null);\r\n setIsReady(false);\r\n setDb(null);\r\n\r\n const { name, version, tables } = configRef.current;\r\n openDB({ name, version, tables })\r\n .then((database) => {\r\n if (cancelled) {\r\n database.close();\r\n return;\r\n }\r\n const controller = createDBController(database, configRef.current);\r\n setDb(controller);\r\n setIsReady(true);\r\n })\r\n .catch((err: DOMException) => {\r\n if (!cancelled) setError(err);\r\n });\r\n\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [config.name, config.version]);\r\n\r\n return { db, isReady, error };\r\n}\r\n\r\n/*\r\n * Usage example:\r\n *\r\n * const { db, isReady, error } = useIndexedDB({\r\n * name: 'my-app-db',\r\n * version: 1,\r\n * tables: {\r\n * users: { keyPath: 'id', autoIncrement: true, indexes: ['email'] },\r\n * settings: { keyPath: 'key' },\r\n * },\r\n * })\r\n *\r\n * if (error) return <div>Failed to open database</div>\r\n * if (!isReady || !db) return <div>Loading...</div>\r\n *\r\n * const users = db.table('users')\r\n * await users.insert({ email: 'a@b.com', name: 'Alice' })\r\n * await users.update(1, { name: 'Alice Smith' })\r\n * const found = await users.query((u) => u.email.startsWith('a@'))\r\n * const n = await users.count()\r\n * await users.delete(1)\r\n * await users.upsert({ id: 2, email: 'b@b.com' })\r\n * await users.bulkInsert([{ email: 'c@b.com' }, { email: 'd@b.com' }])\r\n * await users.clear({ onSuccess: () => console.log('cleared') })\r\n *\r\n * await db.transaction(['users', 'settings'], 'readwrite', async (tx) => {\r\n * await tx.table('users').insert({ email: 'e@b.com' })\r\n * await tx.table('settings').upsert({ key: 'theme', value: 'dark' })\r\n * }, { onSuccess: () => console.log('transaction done') })\r\n */\r\n","/**\r\n * Database controller: table(name), transaction(storeNames, mode, callback, options).\r\n * @module indexedDB/dbController\r\n */\r\n\r\nimport type { IndexedDBConfig, TransactionOptions } from './types';\r\nimport type { ITableController } from './tableController';\r\nimport { createTableController, createTransactionTableController } from './tableController';\r\n\r\n/** Transaction context passed to the callback: provides table(name) bound to this transaction. */\r\nexport interface TransactionContext {\r\n /** Returns a table controller bound to this transaction. Use for all ops inside the callback. */\r\n table: (name: string) => ITableController;\r\n}\r\n\r\n/**\r\n * Database controller built from an open IDBDatabase.\r\n * Exposes table(name) and transaction(...).\r\n */\r\nexport interface IDBController {\r\n /** Underlying IDBDatabase (read-only). */\r\n readonly db: IDBDatabase;\r\n /** Returns true if an object store with the given name exists. */\r\n hasTable: (name: string) => boolean;\r\n /** Returns a table controller for the given store (each op opens its own transaction). */\r\n table: (name: string) => ITableController;\r\n /**\r\n * Runs a callback inside a single transaction. All operations in the callback use the same transaction.\r\n * @param storeNames - Object store names to include in the transaction.\r\n * @param mode - 'readonly' | 'readwrite'.\r\n * @param callback - Async or sync function receiving { table(name) }. Return value is ignored; await all ops inside.\r\n * @param options - Optional onSuccess/onError callbacks.\r\n * @returns Promise that resolves when the transaction completes (after all requests and the callback).\r\n */\r\n transaction: <T = void>(\r\n storeNames: string[],\r\n mode: IDBTransactionMode,\r\n callback: (tx: TransactionContext) => T | Promise<T>,\r\n options?: TransactionOptions\r\n ) => Promise<void>;\r\n}\r\n\r\nfunction withTransactionCallbacks(\r\n promise: Promise<void>,\r\n options?: TransactionOptions\r\n): Promise<void> {\r\n if (!options) return promise;\r\n return promise\r\n .then(() => options.onSuccess?.())\r\n .catch((err: DOMException) => {\r\n options.onError?.(err);\r\n throw err;\r\n });\r\n}\r\n\r\n/**\r\n * Creates a database controller from an open IDBDatabase instance.\r\n */\r\nexport function createDBController(db: IDBDatabase, _config: IndexedDBConfig): IDBController {\r\n return {\r\n get db(): IDBDatabase {\r\n return db;\r\n },\r\n\r\n hasTable(name: string): boolean {\r\n return db.objectStoreNames.contains(name);\r\n },\r\n\r\n table(name: string): ITableController {\r\n return createTableController(db, name);\r\n },\r\n\r\n transaction<T = void>(\r\n storeNames: string[],\r\n mode: IDBTransactionMode,\r\n callback: (tx: TransactionContext) => T | Promise<T>,\r\n options?: TransactionOptions\r\n ): Promise<void> {\r\n const tx = db.transaction(storeNames, mode);\r\n const txContext: TransactionContext = {\r\n table: (tableName: string) => createTransactionTableController(tx, tableName),\r\n };\r\n const txPromise = new Promise<void>((resolve, reject) => {\r\n tx.oncomplete = () => resolve();\r\n tx.onerror = () => reject(tx.error ?? new DOMException('Transaction failed'));\r\n });\r\n const callbackResult = callback(txContext);\r\n const promise = Promise.resolve(callbackResult).then(() => txPromise);\r\n return withTransactionCallbacks(promise, options);\r\n },\r\n };\r\n}\r\n","import { RefObject } from 'preact'\r\nimport { useEffect } from 'preact/hooks'\r\n\r\nexport type UseMutationObserverOptions = MutationObserverInit\r\n\r\n/**\r\n * A Preact hook to observe DOM mutations using MutationObserver.\r\n * @param target - The element to observe.\r\n * @param callback - Function to call on mutation.\r\n * @param options - MutationObserver options.\r\n */\r\nexport function useMutationObserver(\r\n targetRef: RefObject<HTMLElement | null>,\r\n callback: MutationCallback,\r\n options: MutationObserverInit\r\n) {\r\n useEffect(() => {\r\n const node = targetRef.current\r\n if (!node) return\r\n\r\n const observer = new MutationObserver(callback)\r\n observer.observe(node, options)\r\n\r\n return () => observer.disconnect()\r\n }, [targetRef, callback, options])\r\n}\r\n","import { useEffect, useState } from 'preact/hooks';\r\n\r\nexport type PreferredTheme = 'light' | 'dark' | 'no-preference';\r\n\r\n/**\r\n * A Preact hook that returns the user's preferred color scheme based on the\r\n * `prefers-color-scheme` media query. Updates reactively when the user changes\r\n * their system or browser theme preference.\r\n *\r\n * @returns The preferred theme: 'light', 'dark', or 'no-preference'\r\n *\r\n * @example\r\n * ```tsx\r\n * function ThemeAwareComponent() {\r\n * const theme = usePreferredTheme();\r\n * return (\r\n * <div data-theme={theme}>\r\n * Current preference: {theme}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function usePreferredTheme(): PreferredTheme {\r\n const [theme, setTheme] = useState<PreferredTheme>(() => {\r\n if (typeof window === 'undefined') return 'no-preference';\r\n\r\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n const lightQuery = window.matchMedia('(prefers-color-scheme: light)');\r\n\r\n if (darkQuery.matches) return 'dark';\r\n if (lightQuery.matches) return 'light';\r\n return 'no-preference';\r\n });\r\n\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return;\r\n\r\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n\r\n const handleChange = (e: MediaQueryListEvent) => {\r\n setTheme(e.matches ? 'dark' : 'light');\r\n };\r\n\r\n // Re-check in case of no-preference (some browsers don't support light query)\r\n const updateTheme = () => {\r\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n const lightQuery = window.matchMedia('(prefers-color-scheme: light)');\r\n\r\n if (darkQuery.matches) setTheme('dark');\r\n else if (lightQuery.matches) setTheme('light');\r\n else setTheme('no-preference');\r\n };\r\n\r\n mediaQuery.addEventListener('change', handleChange);\r\n\r\n // Fallback: some environments may not fire change, so we also listen for light\r\n const lightQuery = window.matchMedia('(prefers-color-scheme: light)');\r\n lightQuery.addEventListener('change', updateTheme);\r\n\r\n return () => {\r\n mediaQuery.removeEventListener('change', handleChange);\r\n lightQuery.removeEventListener('change', updateTheme);\r\n };\r\n }, []);\r\n\r\n return theme;\r\n}\r\n","import type { RefObject } from 'preact'\r\nimport { useEffect, useRef } from 'preact/hooks'\r\n\r\nexport interface RageClickPayload {\r\n /** Number of clicks that triggered the rage click */\r\n count: number\r\n /** Last click event (e.g. for Sentry context) */\r\n event: MouseEvent\r\n}\r\n\r\nexport interface UseRageClickOptions {\r\n /** Called when a rage click is detected. Use this to report to Sentry or your error tracker. */\r\n onRageClick: (payload: RageClickPayload) => void\r\n /** Minimum number of clicks in the time window to count as rage click. Default: 5 (Sentry-style). */\r\n threshold?: number\r\n /** Time window in ms. Default: 1000. */\r\n timeWindow?: number\r\n /** Max distance in px between clicks to count as same spot. Default: 30. Set to Infinity to ignore distance. */\r\n distanceThreshold?: number\r\n}\r\n\r\ninterface ClickRecord {\r\n time: number\r\n x: number\r\n y: number\r\n}\r\n\r\nfunction distance(a: ClickRecord, b: ClickRecord): number {\r\n return Math.hypot(b.x - a.x, b.y - a.y)\r\n}\r\n\r\n/**\r\n * Detects \"rage clicks\" (repeated rapid clicks in the same area), e.g. when the UI\r\n * is unresponsive. Use the callback to report to Sentry or similar tools to surface\r\n * rage click issues and lower rage-click-related support.\r\n *\r\n * @param targetRef - Ref of the element to monitor (e.g. a button or card).\r\n * @param options - onRageClick callback and optional threshold, timeWindow, distanceThreshold.\r\n *\r\n * @example\r\n * ```tsx\r\n * const ref = useRef<HTMLButtonElement>(null)\r\n * useRageClick(ref, {\r\n * onRageClick: ({ count, event }) => {\r\n * Sentry.captureMessage('Rage click detected', { extra: { count, target: event.target } })\r\n * },\r\n * })\r\n * return <button ref={ref}>Submit</button>\r\n * ```\r\n */\r\nexport function useRageClick(\r\n targetRef: RefObject<HTMLElement | null>,\r\n options: UseRageClickOptions\r\n) {\r\n const {\r\n onRageClick,\r\n threshold = 5,\r\n timeWindow = 1000,\r\n distanceThreshold = 30,\r\n } = options\r\n\r\n const onRageClickRef = useRef(onRageClick)\r\n onRageClickRef.current = onRageClick\r\n\r\n const clicksRef = useRef<ClickRecord[]>([])\r\n\r\n useEffect(() => {\r\n const node = targetRef.current\r\n if (!node) return\r\n\r\n const handleClick = (e: MouseEvent) => {\r\n const now = Date.now()\r\n const record: ClickRecord = { time: now, x: e.clientX, y: e.clientY }\r\n\r\n const clicks = clicksRef.current\r\n const cutoff = now - timeWindow\r\n const recent = clicks.filter((c) => c.time >= cutoff)\r\n recent.push(record)\r\n\r\n if (distanceThreshold !== Infinity) {\r\n const inRange = recent.filter(\r\n (c) => distance(c, record) <= distanceThreshold\r\n )\r\n if (inRange.length >= threshold) {\r\n onRageClickRef.current({ count: inRange.length, event: e })\r\n clicksRef.current = []\r\n return\r\n }\r\n } else {\r\n if (recent.length >= threshold) {\r\n onRageClickRef.current({ count: recent.length, event: e })\r\n clicksRef.current = []\r\n return\r\n }\r\n }\r\n\r\n clicksRef.current = recent\r\n }\r\n\r\n node.addEventListener('click', handleClick)\r\n return () => node.removeEventListener('click', handleClick)\r\n }, [targetRef, threshold, timeWindow, distanceThreshold])\r\n}\r\n","import { useState, useCallback, useRef, useEffect } from 'preact/hooks';\r\n\r\n/** Lower number = higher priority. Default priority when not specified. */\r\nconst DEFAULT_PRIORITY = 1;\r\n\r\nexport type ThreadedWorkerMode = 'sequential' | 'parallel';\r\n\r\nexport interface UseThreadedWorkerOptions {\r\n /** Sequential: single worker, priority-ordered. Parallel: worker pool. */\r\n mode: ThreadedWorkerMode;\r\n /** Max concurrent workers. Only used when mode is \"parallel\". Default 4. */\r\n concurrency?: number;\r\n}\r\n\r\nexport interface RunOptions {\r\n /** 1 = highest priority. Lower number runs first. FIFO within same priority. */\r\n priority?: number;\r\n}\r\n\r\ninterface QueuedTask<TData, TResult> {\r\n data: TData;\r\n priority: number;\r\n sequence: number;\r\n resolve: (value: TResult) => void;\r\n reject: (reason: unknown) => void;\r\n}\r\n\r\nexport interface UseThreadedWorkerReturn<TData, TResult> {\r\n /** Enqueue work. Returns a Promise that resolves with the worker result. */\r\n run: (data: TData, options?: RunOptions) => Promise<TResult>;\r\n /** True while any task is queued or running. */\r\n loading: boolean;\r\n /** Result of the most recently completed successful task. */\r\n result: TResult | undefined;\r\n /** Error from the most recently failed task. */\r\n error: unknown;\r\n /** Number of tasks currently queued + running. */\r\n queueSize: number;\r\n /** Clear all pending (not yet started) tasks. Running tasks continue. */\r\n clearQueue: () => void;\r\n /** Stop accepting new work and clear pending queue. Running tasks finish. */\r\n terminate: () => void;\r\n}\r\n\r\n/**\r\n * Production-grade hook to run async work in a queue with optional priority\r\n * and either sequential or parallel execution.\r\n *\r\n * @param workerFn - Async function to run for each task (e.g. API call, heavy compute).\r\n * @param options - mode: \"sequential\" | \"parallel\", concurrency (parallel only).\r\n * @returns run, loading, result, error, queueSize, clearQueue, terminate.\r\n */\r\nexport function useThreadedWorker<TData, TResult>(\r\n workerFn: (data: TData) => Promise<TResult>,\r\n options: UseThreadedWorkerOptions\r\n): UseThreadedWorkerReturn<TData, TResult> {\r\n const { mode, concurrency = 4 } = options;\r\n const maxConcurrent = mode === 'sequential' ? 1 : Math.max(1, concurrency);\r\n\r\n const [loading, setLoading] = useState(false);\r\n const [result, setResult] = useState<TResult | undefined>(undefined);\r\n const [error, setError] = useState<unknown>(undefined);\r\n const [queueSize, setQueueSize] = useState(0);\r\n\r\n const queueRef = useRef<QueuedTask<TData, TResult>[]>([]);\r\n const sequenceRef = useRef(0);\r\n const activeCountRef = useRef(0);\r\n const terminatedRef = useRef(false);\r\n const workerFnRef = useRef(workerFn);\r\n workerFnRef.current = workerFn;\r\n\r\n const updateQueueSize = useCallback(() => {\r\n setQueueSize(queueRef.current.length + activeCountRef.current);\r\n }, []);\r\n\r\n const processNext = useCallback(() => {\r\n if (terminatedRef.current) return;\r\n if (activeCountRef.current >= maxConcurrent) return;\r\n if (queueRef.current.length === 0) {\r\n if (activeCountRef.current === 0) setLoading(false);\r\n updateQueueSize();\r\n return;\r\n }\r\n\r\n // Sort by priority (asc), then by sequence (FIFO within same priority).\r\n queueRef.current.sort((a, b) => {\r\n if (a.priority !== b.priority) return a.priority - b.priority;\r\n return a.sequence - b.sequence;\r\n });\r\n const task = queueRef.current.shift()!;\r\n activeCountRef.current += 1;\r\n setLoading(true);\r\n updateQueueSize();\r\n\r\n const fn = workerFnRef.current;\r\n fn(task.data)\r\n .then((value) => {\r\n setResult(value);\r\n setError(undefined);\r\n task.resolve(value);\r\n })\r\n .catch((err) => {\r\n setError(err);\r\n task.reject(err);\r\n })\r\n .finally(() => {\r\n activeCountRef.current -= 1;\r\n updateQueueSize();\r\n processNext();\r\n });\r\n\r\n // Fill remaining slots (parallel mode).\r\n if (queueRef.current.length > 0 && activeCountRef.current < maxConcurrent) {\r\n processNext();\r\n }\r\n }, [maxConcurrent, updateQueueSize]);\r\n\r\n const run = useCallback(\r\n (data: TData, runOptions?: RunOptions): Promise<TResult> => {\r\n if (terminatedRef.current) {\r\n return Promise.reject(new Error('Worker is terminated'));\r\n }\r\n const priority = runOptions?.priority ?? DEFAULT_PRIORITY;\r\n const sequence = ++sequenceRef.current;\r\n const promise = new Promise<TResult>((resolve, reject) => {\r\n queueRef.current.push({ data, priority, sequence, resolve, reject });\r\n });\r\n updateQueueSize();\r\n setLoading(true);\r\n queueMicrotask(processNext);\r\n return promise;\r\n },\r\n [processNext, updateQueueSize]\r\n );\r\n\r\n const clearQueue = useCallback(() => {\r\n const pending = queueRef.current;\r\n queueRef.current = [];\r\n pending.forEach((t) => t.reject(new Error('Task cleared from queue')));\r\n updateQueueSize();\r\n if (activeCountRef.current === 0) setLoading(false);\r\n }, [updateQueueSize]);\r\n\r\n const terminate = useCallback(() => {\r\n terminatedRef.current = true;\r\n clearQueue();\r\n }, [clearQueue]);\r\n\r\n // Reset terminated on unmount so the same hook instance can't be \"revived\" without options change.\r\n useEffect(() => {\r\n return () => {\r\n terminatedRef.current = true;\r\n };\r\n }, []);\r\n\r\n return {\r\n run,\r\n loading,\r\n result,\r\n error,\r\n queueSize,\r\n clearQueue,\r\n terminate,\r\n };\r\n}\r\n","import { useState, useCallback } from 'preact/hooks';\r\n\r\n/**\r\n * Mimics React's useTransition hook in Preact.\r\n * @returns [startTransition, isPending]\r\n */\r\nexport function useTransition(): [startTransition: (callback: () => void) => void, isPending: boolean] {\r\n const [isPending, setIsPending] = useState(false);\r\n\r\n const startTransition = useCallback((callback: () => void) => {\r\n setIsPending(true);\r\n Promise.resolve().then(() => {\r\n callback();\r\n setIsPending(false);\r\n });\r\n }, []);\r\n\r\n return [startTransition, isPending];\r\n}\r\n","import { ComponentChildren, cloneElement, isValidElement } from 'preact'\r\nimport { useMemo } from 'preact/hooks'\r\n\r\nexport type InjectableProps = Record<string, any>\r\n\r\n/**\r\n * A Preact hook to wrap children components and inject additional props into them.\r\n * @param children - The children to wrap and enhance with props.\r\n * @param injectProps - The props to inject into each child component.\r\n * @param mergeStrategy - How to handle prop conflicts ('override' | 'preserve'). Defaults to 'preserve'.\r\n * @returns Enhanced children with injected props.\r\n */\r\nexport function useWrappedChildren(\r\n children: ComponentChildren,\r\n injectProps: InjectableProps,\r\n mergeStrategy: 'override' | 'preserve' = 'preserve'\r\n): ComponentChildren {\r\n return useMemo(() => {\r\n if (!children) return children\r\n\r\n const enhanceChild = (child: any): any => {\r\n if (!isValidElement(child)) return child\r\n\r\n const existingProps = child.props || {}\r\n \r\n let mergedProps: InjectableProps\r\n \r\n if (mergeStrategy === 'override') {\r\n // Injected props override existing ones\r\n mergedProps = { ...existingProps, ...injectProps }\r\n } else {\r\n // Existing props are preserved, injected props are added only if not present\r\n mergedProps = { ...injectProps, ...existingProps }\r\n }\r\n\r\n // Special handling for style prop to merge style objects properly\r\n const existingStyle = (existingProps as any)?.style\r\n const injectStyle = (injectProps as any)?.style\r\n \r\n if (existingStyle && injectStyle && \r\n typeof existingStyle === 'object' && typeof injectStyle === 'object') {\r\n if (mergeStrategy === 'override') {\r\n (mergedProps as any).style = { ...existingStyle, ...injectStyle }\r\n } else {\r\n (mergedProps as any).style = { ...injectStyle, ...existingStyle }\r\n }\r\n }\r\n\r\n return cloneElement(child, mergedProps)\r\n }\r\n\r\n if (Array.isArray(children)) {\r\n return children.map(enhanceChild)\r\n }\r\n\r\n return enhanceChild(children)\r\n }, [children, injectProps, mergeStrategy])\r\n}"],"names":["listeners","Map","getNetworkState","navigator","online","state","onLine","connection","undefined","effectiveType","downlink","rtt","saveData","type","connectionType","connectionCache","requestToPromise","request","Promise","resolve","reject","onsuccess","result","onerror","_request$error","error","DOMException","withCallbacks","promise","options","then","onSuccess","err","onError","IPV4_REGEX","DEFAULT_STUN_SERVERS","_options$resetDelay","resetDelay","_useState","useState","copied","setCopied","_useState2","setError","reset","useCallback","copy","text","clipboard","Error","_catch","writeText","setTimeout","e","String","paste","readText","emit","event","_arguments","arguments","handlers","get","forEach","handler","apply","slice","call","on","Set","set","add","size","config","db","setDb","_useState3","isReady","setIsReady","configRef","useRef","current","useEffect","cancelled","_configRef$current","key","name","version","indexedDB","open","onupgradeneeded","target","tables","_i","_Object$keys","Object","keys","length","tableName","schema","objectStoreNames","contains","_schema$autoIncrement","store","createObjectStore","keyPath","autoIncrement","indexes","_step","_iterator","_createForOfIteratorHelperLoose","done","indexName","value","createIndex","unique","_openDB","openDB","database","close","controller","hasTable","table","getStore","mode","transaction","objectStore","insert","data","update","updates","existing","merged","_extends","put","delete","exists","getKey","k","query","filterFn","openCursor","results","cursor","push","upsert","bulkInsert","items","completed","item","i","req","_req$error","clear","count","createStandaloneController","createTableController","storeNames","callback","tx","txContext","_request$error2","_req$error2","createTransactionController","createTransactionTableController","txPromise","oncomplete","_tx$error","callbackResult","withTransactionCallbacks","createDBController","targetRef","node","observer","MutationObserver","observe","disconnect","setState","window","updateState","addEventListener","removeEventListener","darkQuery","matchMedia","lightQuery","matches","theme","setTheme","mediaQuery","handleChange","updateTheme","onRageClick","_options$threshold","threshold","_options$timeWindow","timeWindow","_options$distanceThre","distanceThreshold","onRageClickRef","clicksRef","handleClick","now","Date","record","time","x","clientX","y","clientY","cutoff","recent","filter","c","Infinity","inRange","a","b","Math","hypot","workerFn","_options$concurrency","concurrency","maxConcurrent","max","loading","setLoading","setResult","_useState4","queueSize","setQueueSize","queueRef","sequenceRef","activeCountRef","terminatedRef","workerFnRef","updateQueueSize","processNext","sort","priority","sequence","task","shift","fn","run","runOptions","_runOptions$priority","queueMicrotask","clearQueue","pending","t","terminate","isPending","setIsPending","_options$stunServers","stunServers","_options$timeout","timeout","timeoutMs","onDetect","ips","setIps","pcRef","timeoutRef","reportedRef","onDetectRef","RTCPeerConnection","reported","finish","clearTimeout","addIP","ip","has","prev","concat","pc","iceServers","urls","onicecandidate","candidate","match","createDataChannel","createOffer","offer","setLocalDescription","message","join","children","injectProps","mergeStrategy","useMemo","enhanceChild","child","isValidElement","mergedProps","existingProps","props","existingStyle","style","injectStyle","cloneElement","Array","isArray","map"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{useState as n,useCallback as r,useEffect as e,useMemo as t,useRef as o}from"preact/hooks";import{isValidElement as i,cloneElement as u}from"preact";function c(){var e=n(!1),t=e[0],o=e[1];return[r(function(n){o(!0),Promise.resolve().then(function(){n(),o(!1)})},[]),t]}function a(n,r,t){e(function(){var e=n.current;if(e){var o=new MutationObserver(r);return o.observe(e,t),function(){return o.disconnect()}}},[n,r,t])}var f=new Map;function l(){var n=r(function(n){var r=arguments,e=f.get(n);e&&e.forEach(function(n){return n.apply(void 0,[].slice.call(r,1))})},[]);return{emit:n,on:r(function(n,r){var e=f.get(n);return e||(e=new Set,f.set(n,e)),e.add(r),function(){e.delete(r),0===e.size&&f.delete(n)}},[])}}function s(n,r){(null==r||r>n.length)&&(r=n.length);for(var e=0,t=Array(r);e<r;e++)t[e]=n[e];return t}function v(n,r){var e="undefined"!=typeof Symbol&&n[Symbol.iterator]||n["@@iterator"];if(e)return(e=e.call(n)).next.bind(e);if(Array.isArray(n)||(e=function(n,r){if(n){if("string"==typeof n)return s(n,r);var e={}.toString.call(n).slice(8,-1);return"Object"===e&&n.constructor&&(e=n.constructor.name),"Map"===e||"Set"===e?Array.from(n):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?s(n,r):void 0}}(n))||r&&n&&"number"==typeof n.length){e&&(n=e);var t=0;return function(){return t>=n.length?{done:!0}:{done:!1,value:n[t++]}}}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 d(){return d=Object.assign?Object.assign.bind():function(n){for(var r=1;r<arguments.length;r++){var e=arguments[r];for(var t in e)({}).hasOwnProperty.call(e,t)&&(n[t]=e[t])}return n},d.apply(null,arguments)}function h(n,r,e){return void 0===e&&(e="preserve"),t(function(){if(!n)return n;var t=function(n){if(!i(n))return n;var t,o=n.props||{};t="override"===e?d({},o,r):d({},r,o);var c=null==o?void 0:o.style,a=null==r?void 0:r.style;return c&&a&&"object"==typeof c&&"object"==typeof a&&(t.style="override"===e?d({},c,a):d({},a,c)),u(n,t)};return Array.isArray(n)?n.map(t):t(n)},[n,r,e])}function p(){var r=n(function(){if("undefined"==typeof window)return"no-preference";var n=window.matchMedia("(prefers-color-scheme: dark)"),r=window.matchMedia("(prefers-color-scheme: light)");return n.matches?"dark":r.matches?"light":"no-preference"}),t=r[0],o=r[1];return e(function(){if("undefined"!=typeof window){var n=window.matchMedia("(prefers-color-scheme: dark)"),r=function(n){o(n.matches?"dark":"light")},e=function(){var n=window.matchMedia("(prefers-color-scheme: dark)"),r=window.matchMedia("(prefers-color-scheme: light)");o(n.matches?"dark":r.matches?"light":"no-preference")};n.addEventListener("change",r);var t=window.matchMedia("(prefers-color-scheme: light)");return t.addEventListener("change",e),function(){n.removeEventListener("change",r),t.removeEventListener("change",e)}}},[]),t}function m(){if("undefined"==typeof navigator)return{online:!0};var n={online:navigator.onLine},r=navigator.connection;return r&&(void 0!==r.effectiveType&&(n.effectiveType=r.effectiveType),void 0!==r.downlink&&(n.downlink=r.downlink),void 0!==r.rtt&&(n.rtt=r.rtt),void 0!==r.saveData&&(n.saveData=r.saveData),void 0!==r.type&&(n.connectionType=r.type)),n}function w(){var r=n(m),t=r[0],o=r[1];return e(function(){if("undefined"!=typeof window){var n=function(){return o(m())};window.addEventListener("online",n),window.addEventListener("offline",n);var r=navigator.connection;return null!=r&&r.addEventListener&&r.addEventListener("change",n),function(){window.removeEventListener("online",n),window.removeEventListener("offline",n),null!=r&&r.removeEventListener&&r.removeEventListener("change",n)}}},[]),t}function y(n,r){try{var e=n()}catch(n){return r(n)}return e&&e.then?e.then(void 0,r):e}function g(e){void 0===e&&(e={});var t=e.resetDelay,o=void 0===t?2e3:t,i=n(!1),u=i[0],c=i[1],a=n(null),f=a[0],l=a[1],s=r(function(){c(!1),l(null)},[]);return{copy:r(function(n){try{if(l(null),"undefined"==typeof navigator||!navigator.clipboard){var r=new Error("Clipboard API is not available");return l(r),Promise.resolve(!1)}return Promise.resolve(y(function(){return Promise.resolve(navigator.clipboard.writeText(n)).then(function(){return c(!0),o>0&&setTimeout(function(){return c(!1)},o),!0})},function(n){var r=n instanceof Error?n:new Error(String(n));return l(r),!1}))}catch(n){return Promise.reject(n)}},[o]),paste:r(function(){try{if(l(null),"undefined"==typeof navigator||!navigator.clipboard){var n=new Error("Clipboard API is not available");return l(n),Promise.resolve("")}return Promise.resolve(y(function(){return Promise.resolve(navigator.clipboard.readText())},function(n){var r=n instanceof Error?n:new Error(String(n));return l(r),""}))}catch(n){return Promise.reject(n)}},[]),copied:u,error:f,reset:s}}function b(n,r){var t=r.onRageClick,i=r.threshold,u=void 0===i?5:i,c=r.timeWindow,a=void 0===c?1e3:c,f=r.distanceThreshold,l=void 0===f?30:f,s=o(t);s.current=t;var v=o([]);e(function(){var r=n.current;if(r){var e=function(n){var r=Date.now(),e={time:r,x:n.clientX,y:n.clientY},t=r-a,o=v.current.filter(function(n){return n.time>=t});if(o.push(e),Infinity!==l){var i=o.filter(function(n){return r=n,t=e,Math.hypot(t.x-r.x,t.y-r.y)<=l;var r,t});if(i.length>=u)return s.current({count:i.length,event:n}),void(v.current=[])}else if(o.length>=u)return s.current({count:o.length,event:n}),void(v.current=[]);v.current=o};return r.addEventListener("click",e),function(){return r.removeEventListener("click",e)}}},[n,u,a,l])}function E(t,i){var u=i.concurrency,c="sequential"===i.mode?1:Math.max(1,void 0===u?4:u),a=n(!1),f=a[0],l=a[1],s=n(void 0),v=s[0],d=s[1],h=n(void 0),p=h[0],m=h[1],w=n(0),y=w[0],g=w[1],b=o([]),E=o(0),k=o(0),P=o(!1),x=o(t);x.current=t;var M=r(function(){g(b.current.length+k.current)},[]),j=r(function(){if(!(P.current||k.current>=c)){if(0===b.current.length)return 0===k.current&&l(!1),void M();b.current.sort(function(n,r){return n.priority!==r.priority?n.priority-r.priority:n.sequence-r.sequence});var n=b.current.shift();k.current+=1,l(!0),M(),(0,x.current)(n.data).then(function(r){d(r),m(void 0),n.resolve(r)}).catch(function(r){m(r),n.reject(r)}).finally(function(){k.current-=1,M(),j()}),b.current.length>0&&k.current<c&&j()}},[c,M]),S=r(function(n,r){var e;if(P.current)return Promise.reject(new Error("Worker is terminated"));var t=null!=(e=null==r?void 0:r.priority)?e:1,o=++E.current,i=new Promise(function(r,e){b.current.push({data:n,priority:t,sequence:o,resolve:r,reject:e})});return M(),l(!0),queueMicrotask(j),i},[j,M]),D=r(function(){var n=b.current;b.current=[],n.forEach(function(n){return n.reject(new Error("Task cleared from queue"))}),M(),0===k.current&&l(!1)},[M]),O=r(function(){P.current=!0,D()},[D]);return e(function(){return function(){P.current=!0}},[]),{run:S,loading:f,result:v,error:p,queueSize:y,clearQueue:D,terminate:O}}var k=new Map;function P(n){return new Promise(function(r,e){n.onsuccess=function(){return r(n.result)},n.onerror=function(){var r;return e(null!=(r=n.error)?r:new DOMException("Unknown IndexedDB error"))}})}function x(n,r){return r?n.then(function(n){return null==r.onSuccess||r.onSuccess(n),n}).catch(function(n){throw null==r.onError||r.onError(n),n}):n}function M(r){var t=n(null),i=t[0],u=t[1],c=n(null),a=c[0],f=c[1],l=n(!1),s=l[0],h=l[1],p=o(r);return p.current=r,e(function(){var n=!1;f(null),h(!1),u(null);var r=p.current;return function(n){var r=n.name+"_v"+n.version,e=k.get(r);return e||(e=function(n){return new Promise(function(r,e){var t=indexedDB.open(n.name,n.version);t.onerror=function(){var n;return e(null!=(n=t.error)?n:new DOMException("Failed to open database"))},t.onsuccess=function(){return r(t.result)},t.onupgradeneeded=function(r){for(var e=r.target.result,t=n.tables,o=0,i=Object.keys(t);o<i.length;o++){var u=i[o],c=t[u];if(!e.objectStoreNames.contains(u)){var a,f=e.createObjectStore(u,{keyPath:c.keyPath,autoIncrement:null!=(a=c.autoIncrement)&&a});if(c.indexes)for(var l,s=v(c.indexes);!(l=s()).done;){var d=l.value;f.createIndex(d,d,{unique:!1})}}}}})}(n),k.set(r,e),e)}({name:r.name,version:r.version,tables:r.tables}).then(function(r){if(n)r.close();else{var e=function(n){return{get db(){return n},hasTable:function(r){return n.objectStoreNames.contains(r)},table:function(r){return function(n,r){return function(n,r){function e(e){return n.transaction([r],e).objectStore(r)}return{insert:function(n,r){return x(P(e("readwrite").add(n)),r)},update:function(n,r,t){var o=e("readwrite");return x(P(o.get(n)).then(function(n){if(void 0===n)throw new DOMException("Key not found","NotFoundError");var e=d({},n,r);return P(o.put(e))}).then(function(){}),t)},delete:function(n,r){return x(P(e("readwrite").delete(n)).then(function(){}),r)},exists:function(n){return P(e("readonly").getKey(n)).then(function(n){return void 0!==n})},query:function(n,r){var t=e("readonly").openCursor(),o=[];return x(new Promise(function(r,e){t.onsuccess=function(){var e=t.result;e?(n(e.value)&&o.push(e.value),e.continue()):r(o)},t.onerror=function(){var n;return e(null!=(n=t.error)?n:new DOMException("Unknown error"))}}),r)},upsert:function(n,r){return x(P(e("readwrite").put(n)),r)},bulkInsert:function(n,r){var t=e("readwrite"),o=[];if(0===n.length)return x(Promise.resolve(o),r);var i=0;return x(new Promise(function(r,e){n.forEach(function(u,c){var a=t.add(u);a.onsuccess=function(){o[c]=a.result,++i===n.length&&r(o)},a.onerror=function(){var n;return e(null!=(n=a.error)?n:new DOMException("Unknown error"))}})}),r)},clear:function(n){return x(P(e("readwrite").clear()).then(function(){}),n)},count:function(n){return x(P(e("readonly").count()),null!=n?n:{})}}}(n,r)}(n,r)},transaction:function(r,e,t,o){var i=n.transaction(r,e),u={table:function(n){return function(n,r){return function(n,r){function e(){return n.objectStore(r)}return{insert:function(n,r){return x(P(e().add(n)),r)},update:function(n,r,t){var o=e();return x(P(o.get(n)).then(function(n){if(void 0===n)throw new DOMException("Key not found","NotFoundError");var e=d({},n,r);return P(o.put(e))}).then(function(){}),t)},delete:function(n,r){return x(P(e().delete(n)).then(function(){}),r)},exists:function(n){return P(e().getKey(n)).then(function(n){return void 0!==n})},query:function(n,r){var t=e().openCursor(),o=[];return x(new Promise(function(r,e){t.onsuccess=function(){var e=t.result;e?(n(e.value)&&o.push(e.value),e.continue()):r(o)},t.onerror=function(){var n;return e(null!=(n=t.error)?n:new DOMException("Unknown error"))}}),r)},upsert:function(n,r){return x(P(e().put(n)),r)},bulkInsert:function(n,r){var t=e(),o=[];if(0===n.length)return x(Promise.resolve(o),r);var i=0;return x(new Promise(function(r,e){n.forEach(function(u,c){var a=t.add(u);a.onsuccess=function(){o[c]=a.result,++i===n.length&&r(o)},a.onerror=function(){var n;return e(null!=(n=a.error)?n:new DOMException("Unknown error"))}})}),r)},clear:function(n){return x(P(e().clear()).then(function(){}),n)},count:function(n){return x(P(e().count()),null!=n?n:{})}}}(n,r)}(i,n)}},c=new Promise(function(n,r){i.oncomplete=function(){return n()},i.onerror=function(){var n;return r(null!=(n=i.error)?n:new DOMException("Transaction failed"))}}),a=t(u);return function(n,r){return r?n.then(function(){return null==r.onSuccess?void 0:r.onSuccess()}).catch(function(n){throw null==r.onError||r.onError(n),n}):n}(Promise.resolve(a).then(function(){return c}),o)}}}(r);u(e),h(!0)}}).catch(function(r){n||f(r)}),function(){n=!0}},[r.name,r.version]),{db:i,isReady:s,error:a}}export{g as useClipboard,l as useEventBus,M as useIndexedDB,a as useMutationObserver,w as useNetworkState,p as usePreferredTheme,b as useRageClick,E as useThreadedWorker,c as useTransition,h as useWrappedChildren};
1
+ import{useState as n,useCallback as e,useEffect as r,useMemo as t,useRef as o}from"preact/hooks";import{isValidElement as i,cloneElement as u}from"preact";function c(){var r=n(!1),t=r[0],o=r[1];return[e(function(n){o(!0),Promise.resolve().then(function(){n(),o(!1)})},[]),t]}function a(n,e,t){r(function(){var r=n.current;if(r){var o=new MutationObserver(e);return o.observe(r,t),function(){return o.disconnect()}}},[n,e,t])}var f=new Map;function l(){var n=e(function(n){var e=arguments,r=f.get(n);r&&r.forEach(function(n){return n.apply(void 0,[].slice.call(e,1))})},[]);return{emit:n,on:e(function(n,e){var r=f.get(n);return r||(r=new Set,f.set(n,r)),r.add(e),function(){r.delete(e),0===r.size&&f.delete(n)}},[])}}function s(n,e){(null==e||e>n.length)&&(e=n.length);for(var r=0,t=Array(e);r<e;r++)t[r]=n[r];return t}function v(n,e){var r="undefined"!=typeof Symbol&&n[Symbol.iterator]||n["@@iterator"];if(r)return(r=r.call(n)).next.bind(r);if(Array.isArray(n)||(r=function(n,e){if(n){if("string"==typeof n)return s(n,e);var r={}.toString.call(n).slice(8,-1);return"Object"===r&&n.constructor&&(r=n.constructor.name),"Map"===r||"Set"===r?Array.from(n):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?s(n,e):void 0}}(n))||e&&n&&"number"==typeof n.length){r&&(n=r);var t=0;return function(){return t>=n.length?{done:!0}:{done:!1,value:n[t++]}}}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 d(){return d=Object.assign?Object.assign.bind():function(n){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var t in r)({}).hasOwnProperty.call(r,t)&&(n[t]=r[t])}return n},d.apply(null,arguments)}function h(n,e,r){return void 0===r&&(r="preserve"),t(function(){if(!n)return n;var t=function(n){if(!i(n))return n;var t,o=n.props||{};t="override"===r?d({},o,e):d({},e,o);var c=null==o?void 0:o.style,a=null==e?void 0:e.style;return c&&a&&"object"==typeof c&&"object"==typeof a&&(t.style="override"===r?d({},c,a):d({},a,c)),u(n,t)};return Array.isArray(n)?n.map(t):t(n)},[n,e,r])}function p(){var e=n(function(){if("undefined"==typeof window)return"no-preference";var n=window.matchMedia("(prefers-color-scheme: dark)"),e=window.matchMedia("(prefers-color-scheme: light)");return n.matches?"dark":e.matches?"light":"no-preference"}),t=e[0],o=e[1];return r(function(){if("undefined"!=typeof window){var n=window.matchMedia("(prefers-color-scheme: dark)"),e=function(n){o(n.matches?"dark":"light")},r=function(){var n=window.matchMedia("(prefers-color-scheme: dark)"),e=window.matchMedia("(prefers-color-scheme: light)");o(n.matches?"dark":e.matches?"light":"no-preference")};n.addEventListener("change",e);var t=window.matchMedia("(prefers-color-scheme: light)");return t.addEventListener("change",r),function(){n.removeEventListener("change",e),t.removeEventListener("change",r)}}},[]),t}function m(){if("undefined"==typeof navigator)return{online:!0};var n={online:navigator.onLine},e=navigator.connection;return e&&(void 0!==e.effectiveType&&(n.effectiveType=e.effectiveType),void 0!==e.downlink&&(n.downlink=e.downlink),void 0!==e.rtt&&(n.rtt=e.rtt),void 0!==e.saveData&&(n.saveData=e.saveData),void 0!==e.type&&(n.connectionType=e.type)),n}function w(){var e=n(m),t=e[0],o=e[1];return r(function(){if("undefined"!=typeof window){var n=function(){return o(m())};window.addEventListener("online",n),window.addEventListener("offline",n);var e=navigator.connection;return null!=e&&e.addEventListener&&e.addEventListener("change",n),function(){window.removeEventListener("online",n),window.removeEventListener("offline",n),null!=e&&e.removeEventListener&&e.removeEventListener("change",n)}}},[]),t}function y(n,e){try{var r=n()}catch(n){return e(n)}return r&&r.then?r.then(void 0,e):r}function g(r){void 0===r&&(r={});var t=r.resetDelay,o=void 0===t?2e3:t,i=n(!1),u=i[0],c=i[1],a=n(null),f=a[0],l=a[1],s=e(function(){c(!1),l(null)},[]);return{copy:e(function(n){try{if(l(null),"undefined"==typeof navigator||!navigator.clipboard){var e=new Error("Clipboard API is not available");return l(e),Promise.resolve(!1)}return Promise.resolve(y(function(){return Promise.resolve(navigator.clipboard.writeText(n)).then(function(){return c(!0),o>0&&setTimeout(function(){return c(!1)},o),!0})},function(n){var e=n instanceof Error?n:new Error(String(n));return l(e),!1}))}catch(n){return Promise.reject(n)}},[o]),paste:e(function(){try{if(l(null),"undefined"==typeof navigator||!navigator.clipboard){var n=new Error("Clipboard API is not available");return l(n),Promise.resolve("")}return Promise.resolve(y(function(){return Promise.resolve(navigator.clipboard.readText())},function(n){var e=n instanceof Error?n:new Error(String(n));return l(e),""}))}catch(n){return Promise.reject(n)}},[]),copied:u,error:f,reset:s}}function b(n,e){var t=e.onRageClick,i=e.threshold,u=void 0===i?5:i,c=e.timeWindow,a=void 0===c?1e3:c,f=e.distanceThreshold,l=void 0===f?30:f,s=o(t);s.current=t;var v=o([]);r(function(){var e=n.current;if(e){var r=function(n){var e=Date.now(),r={time:e,x:n.clientX,y:n.clientY},t=e-a,o=v.current.filter(function(n){return n.time>=t});if(o.push(r),Infinity!==l){var i=o.filter(function(n){return e=n,t=r,Math.hypot(t.x-e.x,t.y-e.y)<=l;var e,t});if(i.length>=u)return s.current({count:i.length,event:n}),void(v.current=[])}else if(o.length>=u)return s.current({count:o.length,event:n}),void(v.current=[]);v.current=o};return e.addEventListener("click",r),function(){return e.removeEventListener("click",r)}}},[n,u,a,l])}function E(t,i){var u=i.concurrency,c="sequential"===i.mode?1:Math.max(1,void 0===u?4:u),a=n(!1),f=a[0],l=a[1],s=n(void 0),v=s[0],d=s[1],h=n(void 0),p=h[0],m=h[1],w=n(0),y=w[0],g=w[1],b=o([]),E=o(0),P=o(0),k=o(!1),x=o(t);x.current=t;var S=e(function(){g(b.current.length+P.current)},[]),M=e(function(){if(!(k.current||P.current>=c)){if(0===b.current.length)return 0===P.current&&l(!1),void S();b.current.sort(function(n,e){return n.priority!==e.priority?n.priority-e.priority:n.sequence-e.sequence});var n=b.current.shift();P.current+=1,l(!0),S(),(0,x.current)(n.data).then(function(e){d(e),m(void 0),n.resolve(e)}).catch(function(e){m(e),n.reject(e)}).finally(function(){P.current-=1,S(),M()}),b.current.length>0&&P.current<c&&M()}},[c,S]),j=e(function(n,e){var r;if(k.current)return Promise.reject(new Error("Worker is terminated"));var t=null!=(r=null==e?void 0:e.priority)?r:1,o=++E.current,i=new Promise(function(e,r){b.current.push({data:n,priority:t,sequence:o,resolve:e,reject:r})});return S(),l(!0),queueMicrotask(M),i},[M,S]),D=e(function(){var n=b.current;b.current=[],n.forEach(function(n){return n.reject(new Error("Task cleared from queue"))}),S(),0===P.current&&l(!1)},[S]),T=e(function(){k.current=!0,D()},[D]);return r(function(){return function(){k.current=!0}},[]),{run:j,loading:f,result:v,error:p,queueSize:y,clearQueue:D,terminate:T}}var P=new Map;function k(n){return new Promise(function(e,r){n.onsuccess=function(){return e(n.result)},n.onerror=function(){var e;return r(null!=(e=n.error)?e:new DOMException("Unknown IndexedDB error"))}})}function x(n,e){return e?n.then(function(n){return null==e.onSuccess||e.onSuccess(n),n}).catch(function(n){throw null==e.onError||e.onError(n),n}):n}function S(e){var t=n(null),i=t[0],u=t[1],c=n(null),a=c[0],f=c[1],l=n(!1),s=l[0],h=l[1],p=o(e);return p.current=e,r(function(){var n=!1;f(null),h(!1),u(null);var e=p.current;return function(n){var e=n.name+"_v"+n.version,r=P.get(e);return r||(r=function(n){return new Promise(function(e,r){var t=indexedDB.open(n.name,n.version);t.onerror=function(){var n;return r(null!=(n=t.error)?n:new DOMException("Failed to open database"))},t.onsuccess=function(){return e(t.result)},t.onupgradeneeded=function(e){for(var r=e.target.result,t=n.tables,o=0,i=Object.keys(t);o<i.length;o++){var u=i[o],c=t[u];if(!r.objectStoreNames.contains(u)){var a,f=r.createObjectStore(u,{keyPath:c.keyPath,autoIncrement:null!=(a=c.autoIncrement)&&a});if(c.indexes)for(var l,s=v(c.indexes);!(l=s()).done;){var d=l.value;f.createIndex(d,d,{unique:!1})}}}}})}(n),P.set(e,r),r)}({name:e.name,version:e.version,tables:e.tables}).then(function(e){if(n)e.close();else{var r=function(n){return{get db(){return n},hasTable:function(e){return n.objectStoreNames.contains(e)},table:function(e){return function(n,e){return function(n,e){function r(r){return n.transaction([e],r).objectStore(e)}return{insert:function(n,e){return x(k(r("readwrite").add(n)),e)},update:function(n,e,t){var o=r("readwrite");return x(k(o.get(n)).then(function(n){if(void 0===n)throw new DOMException("Key not found","NotFoundError");var r=d({},n,e);return k(o.put(r))}).then(function(){}),t)},delete:function(n,e){return x(k(r("readwrite").delete(n)).then(function(){}),e)},exists:function(n){return k(r("readonly").getKey(n)).then(function(n){return void 0!==n})},query:function(n,e){var t=r("readonly").openCursor(),o=[];return x(new Promise(function(e,r){t.onsuccess=function(){var r=t.result;r?(n(r.value)&&o.push(r.value),r.continue()):e(o)},t.onerror=function(){var n;return r(null!=(n=t.error)?n:new DOMException("Unknown error"))}}),e)},upsert:function(n,e){return x(k(r("readwrite").put(n)),e)},bulkInsert:function(n,e){var t=r("readwrite"),o=[];if(0===n.length)return x(Promise.resolve(o),e);var i=0;return x(new Promise(function(e,r){n.forEach(function(u,c){var a=t.add(u);a.onsuccess=function(){o[c]=a.result,++i===n.length&&e(o)},a.onerror=function(){var n;return r(null!=(n=a.error)?n:new DOMException("Unknown error"))}})}),e)},clear:function(n){return x(k(r("readwrite").clear()).then(function(){}),n)},count:function(n){return x(k(r("readonly").count()),null!=n?n:{})}}}(n,e)}(n,e)},transaction:function(e,r,t,o){var i=n.transaction(e,r),u={table:function(n){return function(n,e){return function(n,e){function r(){return n.objectStore(e)}return{insert:function(n,e){return x(k(r().add(n)),e)},update:function(n,e,t){var o=r();return x(k(o.get(n)).then(function(n){if(void 0===n)throw new DOMException("Key not found","NotFoundError");var r=d({},n,e);return k(o.put(r))}).then(function(){}),t)},delete:function(n,e){return x(k(r().delete(n)).then(function(){}),e)},exists:function(n){return k(r().getKey(n)).then(function(n){return void 0!==n})},query:function(n,e){var t=r().openCursor(),o=[];return x(new Promise(function(e,r){t.onsuccess=function(){var r=t.result;r?(n(r.value)&&o.push(r.value),r.continue()):e(o)},t.onerror=function(){var n;return r(null!=(n=t.error)?n:new DOMException("Unknown error"))}}),e)},upsert:function(n,e){return x(k(r().put(n)),e)},bulkInsert:function(n,e){var t=r(),o=[];if(0===n.length)return x(Promise.resolve(o),e);var i=0;return x(new Promise(function(e,r){n.forEach(function(u,c){var a=t.add(u);a.onsuccess=function(){o[c]=a.result,++i===n.length&&e(o)},a.onerror=function(){var n;return r(null!=(n=a.error)?n:new DOMException("Unknown error"))}})}),e)},clear:function(n){return x(k(r().clear()).then(function(){}),n)},count:function(n){return x(k(r().count()),null!=n?n:{})}}}(n,e)}(i,n)}},c=new Promise(function(n,e){i.oncomplete=function(){return n()},i.onerror=function(){var n;return e(null!=(n=i.error)?n:new DOMException("Transaction failed"))}}),a=t(u);return function(n,e){return e?n.then(function(){return null==e.onSuccess?void 0:e.onSuccess()}).catch(function(n){throw null==e.onError||e.onError(n),n}):n}(Promise.resolve(a).then(function(){return c}),o)}}}(e);u(r),h(!0)}}).catch(function(e){n||f(e)}),function(){n=!0}},[e.name,e.version]),{db:i,isReady:s,error:a}}var M=/\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,j=["stun:stun.l.google.com:19302"];function D(e){void 0===e&&(e={});var t=e.stunServers,i=void 0===t?j:t,u=e.timeout,c=void 0===u?3e3:u,a=e.onDetect,f=n([]),l=f[0],s=f[1],v=n(!0),d=v[0],h=v[1],p=n(null),m=p[0],w=p[1],y=o(null),g=o(null),b=o(new Set),E=o(a);return E.current=a,r(function(){if("undefined"==typeof window)return h(!1),void w("WebRTC IP detection is not available during SSR");if("undefined"==typeof RTCPeerConnection)return h(!1),void w("RTCPeerConnection is not available");var n=new Set;b.current=n;var e=function(){g.current&&(clearTimeout(g.current),g.current=null),y.current&&(y.current.close(),y.current=null),h(!1)},r=function(e){n.has(e)||(n.add(e),s(function(n){return[].concat(n,[e])}),null==E.current||E.current(e))};try{var t=new RTCPeerConnection({iceServers:[{urls:i}]});y.current=t,t.onicecandidate=function(n){var e,t=n.candidate;t&&t.candidate&&((e=t.candidate.match(M))?[].concat(e):[]).forEach(r)},t.createDataChannel(""),t.createOffer().then(function(n){return t.setLocalDescription(n)}).catch(function(n){w(n instanceof Error?n.message:"Failed to create offer"),e()}),g.current=setTimeout(function(){return e()},c)}catch(n){w(n instanceof Error?n.message:"WebRTC setup failed"),e()}return function(){e()}},[i.join(","),c]),{ips:l,loading:d,error:m}}export{g as useClipboard,l as useEventBus,S as useIndexedDB,a as useMutationObserver,w as useNetworkState,p as usePreferredTheme,b as useRageClick,E as useThreadedWorker,c as useTransition,D as useWebRTCIP,h as useWrappedChildren};
2
2
  //# sourceMappingURL=index.module.js.map