{"_id":"@alsoftworks/temporize","_rev":"5-22e9e40354cdf3598bf5a2e65ab2ecc5","name":"@alsoftworks/temporize","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@alsoftworks/temporize","version":"0.1.0","keywords":["debounce","throttle","promise","typescript","abortsignal","react","react-hooks","vue","vue-composables"],"license":"MIT","_id":"@alsoftworks/temporize@0.1.0","maintainers":[{"name":"alsoftworks","email":"maxhyde192@gmail.com"}],"dist":{"shasum":"b8adb48ec675c9191425931f96e3a7001389ffe3","tarball":"https://registry.npmjs.org/@alsoftworks/temporize/-/temporize-0.1.0.tgz","fileCount":18,"integrity":"sha512-NT7M9D6OwuQKzS2q+MDiAKFD3J6mmXnzL1ig4fhNIAFHqn85ZtDJsTMsRozNFGvteM8+rHrR8Exodq+KwE119w==","signatures":[{"sig":"MEQCIA0/L/EY8cYAIrvKqgWAnEUnyeyt1nGxGR976pvqPmcbAiB1Is7GV58RZdDrIxDx61t47ucf4MPhkOfNPhHF3ZiRXQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":56807},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=16"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./vue":{"types":"./dist/vue/index.d.ts","import":"./dist/vue/index.js","require":"./dist/vue/index.cjs"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js","require":"./dist/react/index.cjs"}},"scripts":{"test":"vitest run","bench":"tsx bench/index.ts","build":"tsup src/index.ts src/react/index.ts src/vue/index.ts --format esm,cjs --dts --clean --minify --treeshake","test:core":"vitest run test/debounce.test.ts test/debounceAsync.test.ts test/throttle.test.ts","typecheck":"tsc --noEmit","build:core":"tsup src/index.ts --format esm,cjs --dts --clean --minify --treeshake","test:watch":"vitest","prepublishOnly":"npm test && npm run typecheck && npm run build"},"_npmUser":{"name":"alsoftworks","email":"maxhyde192@gmail.com"},"_npmVersion":"11.6.2","description":"Typed, promise-aware debounce and throttle utilities with cancellation.","directories":{},"sideEffects":false,"_nodeVersion":"24.13.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vue":"^3.5.13","tsup":"^8.3.5","jsdom":"^22.1.0","react":"^18.3.1","vitest":"^4.1.10","react-dom":"^18.3.1","tinybench":"^3.1.1","typescript":"^5.7.2","@types/node":"^22.10.2","@types/react":"^18.3.18","@vue/test-utils":"^2.4.6","lodash.debounce":"^4.0.8","lodash.throttle":"^4.1.1","@types/react-dom":"^18.3.5","@testing-library/react":"^14.3.1","@types/lodash.debounce":"^4.0.9","@types/lodash.throttle":"^4.1.9"},"peerDependencies":{"vue":"^3.0.0","react":"^17.0.0 || ^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/temporize_0.1.0_1785601047008_0.25526301673524965","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alsoftworks/temporize","version":"0.2.0","keywords":["debounce","throttle","batch","retry","requestidlecallback","promise","typescript","abortsignal","react","react-hooks","vue","vue-composables"],"license":"MIT","_id":"@alsoftworks/temporize@0.2.0","maintainers":[{"name":"alsoftworks","email":"maxhyde192@gmail.com"}],"dist":{"shasum":"eeff1ed5c3a960b2cf1f07786767d52ab6050eff","tarball":"https://registry.npmjs.org/@alsoftworks/temporize/-/temporize-0.2.0.tgz","fileCount":18,"integrity":"sha512-JY3GUzRSdMDjUMfJa1FJf9+ZH7MZZSydbpeL0bf23+GGkoRnwU0rQgbBje9aSxv0U/Ooo0vSd6aOXI2CBGXfxg==","signatures":[{"sig":"MEQCIFdfgmJ6Isxu/p/Jao1OfIb6ACCSn1cVqfv81bQ+i//FAiBDr0ZxA0hD+soZEpTsPYb0O2ztTk5sBuKjLbZiLv0PLw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75482},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=16"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./vue":{"types":"./dist/vue/index.d.ts","import":"./dist/vue/index.js","require":"./dist/vue/index.cjs"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js","require":"./dist/react/index.cjs"}},"gitHead":"d26bb43087b531a82de104d99dbef893c2f31230","scripts":{"test":"vitest run","bench":"tsx bench/index.ts","build":"tsup src/index.ts src/react/index.ts src/vue/index.ts --format esm,cjs --dts --clean --minify --treeshake","test:core":"vitest run test/debounce.test.ts test/debounceAsync.test.ts test/throttle.test.ts test/batch.test.ts test/retry.test.ts test/idle.test.ts","typecheck":"tsc --noEmit","build:core":"tsup src/index.ts --format esm,cjs --dts --clean --minify --treeshake","test:watch":"vitest","prepublishOnly":"npm test && npm run typecheck && npm run build"},"_npmUser":{"name":"alsoftworks","email":"maxhyde192@gmail.com"},"_npmVersion":"11.6.2","description":"Typed, promise-aware debounce and throttle utilities with cancellation.","directories":{},"sideEffects":false,"_nodeVersion":"24.13.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vue":"^3.5.13","tsup":"^8.3.5","jsdom":"^22.1.0","react":"^18.3.1","vitest":"^4.1.10","react-dom":"^18.3.1","tinybench":"^3.1.1","typescript":"^5.7.2","@types/node":"^22.10.2","@types/react":"^18.3.18","@vue/test-utils":"^2.4.6","lodash.debounce":"^4.0.8","lodash.throttle":"^4.1.1","@types/react-dom":"^18.3.5","@testing-library/react":"^14.3.1","@types/lodash.debounce":"^4.0.9","@types/lodash.throttle":"^4.1.9"},"peerDependencies":{"vue":"^3.0.0","react":"^17.0.0 || ^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/temporize_0.2.0_1785606318838_0.4840440908534356","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@alsoftworks/temporize","version":"0.3.0","keywords":["debounce","throttle","concurrency","batch","retry","requestidlecallback","promise","typescript","abortsignal","react","react-hooks","vue","vue-composables"],"license":"MIT","_id":"@alsoftworks/temporize@0.3.0","maintainers":[{"name":"alsoftworks","email":"maxhyde192@gmail.com"}],"homepage":"https://github.com/nyvexis1/temporize#readme","bugs":{"url":"https://github.com/nyvexis1/temporize/issues"},"dist":{"shasum":"a47dd3e702a556505e71d5d3b5dd09f30af5b055","tarball":"https://registry.npmjs.org/@alsoftworks/temporize/-/temporize-0.3.0.tgz","fileCount":20,"integrity":"sha512-yCkFg6qk2kqjQ33z06EfUvRfSGtlTAcLWW9uutW2gRkTr9yn8SpTLXHeNrJgOPPxaahKdsVoF/2+Miz6mfgN8Q==","signatures":[{"sig":"MEUCIQDurP/9um+1qkQ++EqsK3CWvaAMiz3J+dZ0egylC2T4IAIgXgvGUmFxlpxTgcsOr7fcOj6xyeSoM9hEHUhZF5Ll70k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":108730},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=16"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./vue":{"types":"./dist/vue/index.d.ts","import":"./dist/vue/index.js","require":"./dist/vue/index.cjs"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js","require":"./dist/react/index.cjs"}},"gitHead":"a29844aaeb5db2616832dc3c5bd806d889abb80b","scripts":{"lint":"eslint src test bench scripts && prettier --check .","size":"node scripts/check-size.mjs","test":"vitest run","bench":"tsx bench/index.ts","build":"tsup src/index.ts src/react/index.ts src/vue/index.ts --format esm,cjs --dts --clean --minify --treeshake","format":"prettier --write .","test:core":"vitest run test/debounce.test.ts test/debounceAsync.test.ts test/throttle.test.ts test/batch.test.ts test/retry.test.ts test/idle.test.ts test/concurrencyLimit.test.ts test/errors.test.ts","typecheck":"tsc --noEmit","build:core":"tsup src/index.ts --format esm,cjs --dts --clean --minify --treeshake","test:watch":"vitest","prepublishOnly":"npm run lint && npm run typecheck && npm test && npm run build && npm run size"},"_npmUser":{"name":"alsoftworks","email":"maxhyde192@gmail.com"},"repository":{"url":"git+https://github.com/nyvexis1/temporize.git","type":"git"},"_npmVersion":"11.6.2","description":"Typed, promise-aware timing and concurrency utilities with cancellation.","directories":{},"sideEffects":false,"_nodeVersion":"24.13.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vue":"^3.5.13","tsup":"^8.3.5","jsdom":"^22.1.0","react":"^18.3.1","eslint":"^9.39.5","vitest":"^3.2.7","esbuild":"^0.25.0","globals":"^16.4.0","prettier":"^3.9.0","react-dom":"^18.3.1","tinybench":"^3.1.1","@eslint/js":"^9.39.5","typescript":"^5.7.2","@types/node":"^22.10.2","@types/react":"^18.3.18","@vue/test-utils":"^2.4.6","lodash.debounce":"^4.0.8","lodash.throttle":"^4.1.1","@types/react-dom":"^18.3.5","typescript-eslint":"^8.65.0","@testing-library/react":"^14.3.1","@types/lodash.debounce":"^4.0.9","@types/lodash.throttle":"^4.1.9","eslint-config-prettier":"^10.1.8"},"peerDependencies":{"vue":"^3.0.0","react":"^17.0.0 || ^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/temporize_0.3.0_1785615077379_0.15425025459592656","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@alsoftworks/temporize","version":"0.3.1","keywords":["debounce","throttle","concurrency","batch","retry","requestidlecallback","promise","typescript","abortsignal","react","react-hooks","vue","vue-composables"],"license":"MIT","_id":"@alsoftworks/temporize@0.3.1","maintainers":[{"name":"alsoftworks","email":"maxhyde192@gmail.com"}],"homepage":"https://github.com/nyvexis1/temporize#readme","bugs":{"url":"https://github.com/nyvexis1/temporize/issues"},"dist":{"shasum":"38e040c8fa7ea698d2383a06e05536f90a86f59d","tarball":"https://registry.npmjs.org/@alsoftworks/temporize/-/temporize-0.3.1.tgz","fileCount":21,"integrity":"sha512-e4ZK3WX415TbBWJ4SJBGELKiuC62Dcn9Jvjn9c2Q5uHmrRmfDisbgMQwKsSYeN2Jy1/vgMA2ROov0N0Jq/hLJg==","signatures":[{"sig":"MEUCIQDAFtVANmgU12b0T33y0Zy96Geyz9DJ2r0i1NNWhp7stQIgFN+1Lcc4DH1bMsBTL8i9RWVVXqnw1DEgiyPqD0tjy2s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":111779},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=16"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./vue":{"types":"./dist/vue/index.d.ts","import":"./dist/vue/index.js","require":"./dist/vue/index.cjs"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js","require":"./dist/react/index.cjs"}},"gitHead":"6d415a387e1260a53a6ca2c3f6a96fb6a2f3c327","scripts":{"lint":"eslint src test bench scripts && prettier --check .","size":"node scripts/check-size.mjs","test":"vitest run","bench":"tsx bench/index.ts","build":"tsup src/index.ts src/react/index.ts src/vue/index.ts --format esm,cjs --dts --clean --minify --treeshake","format":"prettier --write .","test:core":"vitest run test/debounce.test.ts test/debounceAsync.test.ts test/throttle.test.ts test/batch.test.ts test/retry.test.ts test/idle.test.ts test/concurrencyLimit.test.ts test/errors.test.ts","typecheck":"tsc --noEmit","build:core":"tsup src/index.ts --format esm,cjs --dts --clean --minify --treeshake","test:watch":"vitest","prepublishOnly":"npm run lint && npm run typecheck && npm test && npm run build && npm run size"},"_npmUser":{"name":"alsoftworks","email":"maxhyde192@gmail.com"},"repository":{"url":"git+https://github.com/nyvexis1/temporize.git","type":"git"},"_npmVersion":"11.6.2","description":"Typed, promise-aware timing and concurrency utilities with cancellation.","directories":{},"sideEffects":false,"_nodeVersion":"24.13.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vue":"^3.5.13","tsup":"^8.3.5","jsdom":"^22.1.0","react":"^18.3.1","eslint":"^9.39.5","vitest":"^3.2.7","esbuild":"^0.25.0","globals":"^16.4.0","prettier":"^3.9.0","react-dom":"^18.3.1","tinybench":"^3.1.1","@eslint/js":"^9.39.5","typescript":"^5.7.2","@types/node":"^22.10.2","@types/react":"^18.3.18","@vue/test-utils":"^2.4.6","lodash.debounce":"^4.0.8","lodash.throttle":"^4.1.1","@types/react-dom":"^18.3.5","typescript-eslint":"^8.65.0","@testing-library/react":"^14.3.1","@types/lodash.debounce":"^4.0.9","@types/lodash.throttle":"^4.1.9","eslint-config-prettier":"^10.1.8"},"peerDependencies":{"vue":"^3.0.0","react":"^17.0.0 || ^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/temporize_0.3.1_1785619982151_0.6817546438700526","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@alsoftworks/temporize","version":"0.4.0","description":"Typed, promise-aware timing and concurrency utilities with cancellation.","type":"module","sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js","require":"./dist/react/index.cjs"},"./vue":{"types":"./dist/vue/index.d.ts","import":"./dist/vue/index.js","require":"./dist/vue/index.cjs"}},"scripts":{"build":"tsup src/index.ts src/react/index.ts src/vue/index.ts --format esm,cjs --dts --clean --minify --treeshake","build:core":"tsup src/index.ts --format esm,cjs --dts --clean --minify --treeshake","test":"vitest run","test:core":"vitest run test/debounce.test.ts test/debounceAsync.test.ts test/throttle.test.ts test/batch.test.ts test/retry.test.ts test/idle.test.ts test/concurrencyLimit.test.ts test/sample.test.ts test/once.test.ts test/timeout.test.ts test/poll.test.ts test/errors.test.ts","test:watch":"vitest","bench":"tsx bench/index.ts","typecheck":"tsc --noEmit","lint":"eslint src test bench scripts && prettier --check .","format":"prettier --write .","size":"node scripts/check-size.mjs","prepublishOnly":"npm run lint && npm run typecheck && npm test && npm run build && npm run size"},"keywords":["debounce","throttle","concurrency","poll","timeout","once","sample","batch","retry","requestidlecallback","promise","typescript","abortsignal","react","react-hooks","vue","vue-composables"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/nyvexis1/temporize.git"},"homepage":"https://github.com/nyvexis1/temporize#readme","bugs":{"url":"https://github.com/nyvexis1/temporize/issues"},"publishConfig":{"access":"public"},"engines":{"node":">=16"},"peerDependencies":{"react":"^17.0.0 || ^18.0.0 || ^19.0.0","vue":"^3.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"vue":{"optional":true}},"devDependencies":{"@testing-library/react":"^14.3.1","@eslint/js":"^9.39.5","@types/node":"^22.10.2","@types/lodash.debounce":"^4.0.9","@types/lodash.throttle":"^4.1.9","@types/react":"^18.3.18","@types/react-dom":"^18.3.5","@vue/test-utils":"^2.4.6","eslint":"^9.39.5","eslint-config-prettier":"^10.1.8","esbuild":"^0.25.0","globals":"^16.4.0","jsdom":"^22.1.0","lodash.debounce":"^4.0.8","lodash.throttle":"^4.1.1","react":"^18.3.1","react-dom":"^18.3.1","prettier":"^3.9.0","tinybench":"^3.1.1","tsup":"^8.3.5","tsx":"^4.19.2","typescript":"^5.7.2","typescript-eslint":"^8.65.0","vitest":"^3.2.7","vue":"^3.5.13"},"gitHead":"8a22cb10041bc201423e89e1904c72ae3f698e9b","_id":"@alsoftworks/temporize@0.4.0","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-Odi6RZyWUrbDnmr0GD0VAuDUn3rxqmCeAOLshvdqy7VP4QqzJ/EWEH123ZJd6SjoJFumyKM+PPAmjArTj8iQRw==","shasum":"137ac4094adfa1970079a5548f5985e3ea2d29c4","tarball":"https://registry.npmjs.org/@alsoftworks/temporize/-/temporize-0.4.0.tgz","fileCount":21,"unpackedSize":141856,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFD+NEUAkd3/lpn1zk4+K/0JDS8WS/J6suGTBIVS7F/GAiEAofoBm92WyUpFz4hm1fmOkkwL3klKRaZAsv7J29sX5FM="}]},"_npmUser":{"name":"alsoftworks","email":"maxhyde192@gmail.com"},"directories":{},"maintainers":[{"name":"alsoftworks","email":"maxhyde192@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/temporize_0.4.0_1785756456480_0.38464466056978286"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-01T16:17:26.889Z","modified":"2026-08-03T11:27:36.756Z","0.1.0":"2026-08-01T16:17:27.180Z","0.2.0":"2026-08-01T17:45:18.973Z","0.3.0":"2026-08-01T20:11:17.516Z","0.3.1":"2026-08-01T21:33:02.311Z","0.4.0":"2026-08-03T11:27:36.621Z"},"bugs":{"url":"https://github.com/nyvexis1/temporize/issues"},"license":"MIT","homepage":"https://github.com/nyvexis1/temporize#readme","keywords":["debounce","throttle","concurrency","poll","timeout","once","sample","batch","retry","requestidlecallback","promise","typescript","abortsignal","react","react-hooks","vue","vue-composables"],"repository":{"type":"git","url":"git+https://github.com/nyvexis1/temporize.git"},"description":"Typed, promise-aware timing and concurrency utilities with cancellation.","maintainers":[{"name":"alsoftworks","email":"maxhyde192@gmail.com"}],"readme":"# temporize\r\n\r\n[![npm version](https://img.shields.io/npm/v/%40alsoftworks%2Ftemporize)](https://www.npmjs.com/package/@alsoftworks/temporize)\r\n[![npm downloads](https://img.shields.io/npm/dm/%40alsoftworks%2Ftemporize)](https://www.npmjs.com/package/@alsoftworks/temporize)\r\n[![CI](https://github.com/nyvexis1/temporize/actions/workflows/ci.yml/badge.svg)](https://github.com/nyvexis1/temporize/actions/workflows/ci.yml)\r\n[![bundle size](https://img.shields.io/bundlephobia/minzip/%40alsoftworks%2Ftemporize)](https://bundlephobia.com/package/@alsoftworks/temporize)\r\n\r\nSmall, strongly typed timing and concurrency utilities for modern TypeScript.\r\nPromise-returning primitives preserve the wrapped function's actual result,\r\nwhile intentionally lossy schedulers use explicit void call signatures. There\r\nare no runtime dependencies, and the package ships as ESM and CommonJS.\r\n\r\n## Install\r\n\r\n```sh\r\nnpm install @alsoftworks/temporize\r\n```\r\n\r\n## Why temporize?\r\n\r\n| Capability                                   | temporize                          | lodash.debounce / lodash.throttle      |\r\n| -------------------------------------------- | ---------------------------------- | -------------------------------------- |\r\n| Inferred arguments and resolved return value | Full generic inference             | Types available separately             |\r\n| Per-call return                              | `Promise<Awaited<R>>`              | Last synchronous result or `undefined` |\r\n| Async error propagation                      | Native promise rejection           | Caller manages returned value          |\r\n| Cancellation                                 | `.cancel()` and `AbortSignal`      | `.cancel()`                            |\r\n| Maximum debounce wait                        | Yes                                | Yes                                    |\r\n| Promise queue rate limiter                   | `throttlePromise`                  | No                                     |\r\n| Async overlap policy                         | `debounceAsync`                    | No                                     |\r\n| Animation-frame throttling                   | Browser API plus Node fallback     | No                                     |\r\n| Multi-call argument batching                 | `batch`                            | No                                     |\r\n| Exponential-backoff retries                  | `retry`                            | No                                     |\r\n| Idle-period scheduling                       | `idle` with Safari/Node fallback   | No                                     |\r\n| Concurrent promise limiting                  | `concurrencyLimit` with FIFO queue | No                                     |\r\n| Fixed-clock latest-value sampling            | `sample`                           | No                                     |\r\n| Shared one-time initialization               | `once`                             | No                                     |\r\n| Hard async waiting budget                    | `timeout`                          | No                                     |\r\n| Condition-based async polling                | `poll`                             | No                                     |\r\n| Runtime dependencies                         | Zero                               | Zero for per-method packages           |\r\n| Modules                                      | ESM and CommonJS                   | CommonJS per-method packages           |\r\n\r\n### Scope\r\n\r\ntemporize deliberately focuses on controlling when, how often, and for how long\r\nwork runs. Debouncing, throttling, sampling, batching, one-time execution,\r\ntimeouts, polling, retries, concurrency limits, frame scheduling, and idle\r\nscheduling form that timing-control family. Object, array, string, and other\r\ngeneral utility helpers are intentionally out of scope; that boundary is a\r\ndesign decision, not an omission.\r\n\r\n## Usage\r\n\r\n### `debounce`\r\n\r\n```ts\r\nimport { debounce } from \"@alsoftworks/temporize\";\r\n\r\nconst search = debounce(\r\n  async (query: string) => {\r\n    const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);\r\n    return response.json() as Promise<{ total: number }>;\r\n  },\r\n  250,\r\n  { maxWait: 1_000 },\r\n);\r\n\r\nconst result = await search(\"type inference\");\r\nconsole.log(result.total);\r\n\r\nsearch.pending();\r\nawait search.flush();\r\nsearch.cancel();\r\n```\r\n\r\nSeveral calls coalesced into one invocation each receive a separate promise\r\nsettled with that invocation's result. A leading-only debounce resolves calls\r\nsuppressed within the window with the leading invocation's result.\r\n\r\n### `throttle`\r\n\r\n```ts\r\nimport { throttle } from \"@alsoftworks/temporize\";\r\n\r\nconst savePosition = throttle(\r\n  (x: number, y: number) => ({ x, y, savedAt: Date.now() }),\r\n  100,\r\n  { leading: true, trailing: true },\r\n);\r\n\r\nconst saved = await savePosition(120, 80);\r\n```\r\n\r\nRegular throttling coalesces excess calls and uses the latest arguments for a\r\ntrailing invocation.\r\n\r\n### `rafThrottle`\r\n\r\n```ts\r\nimport { rafThrottle } from \"@alsoftworks/temporize\";\r\n\r\nconst updateLayout = rafThrottle((width: number) => {\r\n  document.documentElement.style.setProperty(\"--viewport-width\", `${width}px`);\r\n});\r\n\r\nwindow.addEventListener(\"resize\", () => updateLayout(window.innerWidth));\r\n\r\n// Discard a frame that has been requested but has not run.\r\nupdateLayout.cancel();\r\n```\r\n\r\nIn browsers, `rafThrottle` uses `requestAnimationFrame`. During SSR and in Node,\r\nit falls back to a 16 ms `setTimeout`, so importing and calling it is safe when\r\nanimation-frame globals do not exist.\r\n\r\n### `debounceAsync`\r\n\r\n```ts\r\nimport { debounceAsync } from \"@alsoftworks/temporize\";\r\n\r\nconst loadUser = debounceAsync(\r\n  async (id: string, signal: AbortSignal) => {\r\n    const response = await fetch(`/api/users/${id}`, { signal });\r\n    return response.json() as Promise<{ id: string; name: string }>;\r\n  },\r\n  200,\r\n  { overlap: \"cancel-previous\" },\r\n);\r\n\r\n// The AbortSignal parameter is supplied internally and omitted from this call.\r\nconst user = await loadUser(\"user_123\");\r\n```\r\n\r\nThe overlap policies are:\r\n\r\n- `\"queue\"` (default): keep each fired invocation and start it after active\r\n  work settles.\r\n- `\"drop\"`: do not start the overlapping invocation; its callers adopt the\r\n  active invocation's promise.\r\n- `\"cancel-previous\"`: abort the active invocation's internally supplied\r\n  signal and immediately start the new invocation. JavaScript functions that\r\n  do not declare a signal safely ignore the extra argument.\r\n\r\nFor typed signal injection, declare a required `AbortSignal` as the wrapped\r\nfunction's final parameter. `debounceAsync` removes that parameter from the\r\nreturned function's call signature. Cancellation is cooperative: an async\r\noperation must observe the signal to stop its in-flight work.\r\n\r\n### `throttlePromise`\r\n\r\n```ts\r\nimport { throttlePromise } from \"@alsoftworks/temporize\";\r\n\r\nconst sendRequest = throttlePromise(\r\n  async (path: string) => fetch(path).then((response) => response.status),\r\n  100,\r\n);\r\n\r\nconst requests = [\r\n  sendRequest(\"/api/one\"),\r\n  sendRequest(\"/api/two\"),\r\n  sendRequest(\"/api/three\"),\r\n];\r\n\r\nconsole.log(sendRequest.queued());\r\nconsole.log(await Promise.all(requests));\r\n```\r\n\r\nEvery call enters a FIFO queue and starts at least one window after the prior\r\nstart. The queue limits dispatch rate, not concurrency: a slow promise may\r\nstill be active when the next window begins.\r\n\r\n### `batch`\r\n\r\n```ts\r\nimport { batch } from \"@alsoftworks/temporize\";\r\n\r\nconst markNotificationsRead = batch(\r\n  async (calls: Array<[notificationId: string]>) => {\r\n    const ids = calls.map(([notificationId]) => notificationId);\r\n    const response = await fetch(\"/api/notifications/read\", {\r\n      method: \"POST\",\r\n      headers: { \"content-type\": \"application/json\" },\r\n      body: JSON.stringify({ ids }),\r\n    });\r\n    if (!response.ok) throw new Error(\"Could not mark notifications as read\");\r\n    return ids.length;\r\n  },\r\n  50,\r\n  { maxSize: 100 },\r\n);\r\n\r\nconst first = markNotificationsRead(\"notification_1\");\r\nconst second = markNotificationsRead(\"notification_2\");\r\n\r\n// Both promises resolve to 2 because both calls shared one batch invocation.\r\nconsole.log(await first, await second);\r\n```\r\n\r\nUnlike `debounce`, `batch` retains every argument tuple. The wrapped function\r\nruns once with the complete array, and every caller in that batch receives the\r\nsame result or error.\r\n\r\n### `retry`\r\n\r\n```ts\r\nimport { retry } from \"@alsoftworks/temporize\";\r\n\r\nconst controller = new AbortController();\r\nconst fetchJson = retry(\r\n  async (url: string) => {\r\n    const response = await fetch(url, { signal: controller.signal });\r\n    if (!response.ok) throw new Error(`Request failed: ${response.status}`);\r\n    return response.json() as Promise<unknown>;\r\n  },\r\n  {\r\n    attempts: 4,\r\n    baseDelay: 250,\r\n    maxDelay: 2_000,\r\n    signal: controller.signal,\r\n  },\r\n);\r\n\r\nconst data = await fetchJson(\"/api/flaky-report\");\r\n```\r\n\r\nRetries use exponential backoff and randomized jitter by default. Supply\r\n`shouldRetry` when permanent errors should stop immediately.\r\n\r\n### `idle`\r\n\r\n```ts\r\nimport { idle } from \"@alsoftworks/temporize\";\r\n\r\nconst recordAnalytics = idle(\r\n  (eventName: string, properties: object) => {\r\n    navigator.sendBeacon(\"/analytics\", JSON.stringify({ eventName, properties }));\r\n  },\r\n  { timeout: 2_000 },\r\n);\r\n\r\nrecordAnalytics(\"dashboard_viewed\", { source: \"navigation\" });\r\n```\r\n\r\nRapid calls are coalesced using the latest arguments and deferred until the\r\nbrowser is idle. Safari, Node, and other environments without\r\n`requestIdleCallback` use a 1 ms timer fallback.\r\n\r\n### `concurrencyLimit`\r\n\r\n```ts\r\nimport { concurrencyLimit } from \"@alsoftworks/temporize\";\r\n\r\nconst upload = concurrencyLimit(async (file: File) => {\r\n  const body = new FormData();\r\n  body.append(\"file\", file);\r\n  const response = await fetch(\"/api/uploads\", { method: \"POST\", body });\r\n  if (!response.ok) throw new Error(`Upload failed: ${file.name}`);\r\n  return response.json();\r\n}, 3);\r\n\r\nconst uploaded = await Promise.all(files.map(upload));\r\nconsole.log(upload.pending(), upload.queued());\r\n```\r\n\r\nAt most three uploads start together. Excess calls wait in FIFO order.\r\n`upload.cancel()` rejects queued calls, but deliberately lets uploads already in\r\nflight finish because the wrapped function may not be cancellable. Later calls\r\ncan be queued normally; aborting the configured signal also rejects future calls.\r\n\r\n### `sample`\r\n\r\n```ts\r\nimport { sample } from \"@alsoftworks/temporize\";\r\n\r\nconst displayPrice = sample((price: number) => {\r\n  document.querySelector(\"#price\")!.textContent = price.toFixed(2);\r\n}, 250);\r\n\r\nsocket.addEventListener(\"message\", (event) => {\r\n  const update = JSON.parse(event.data) as { price: number };\r\n  displayPrice(update.price);\r\n});\r\n\r\nsocket.addEventListener(\"close\", displayPrice.cancel);\r\n```\r\n\r\nThe first update starts a fixed sampling clock. Each tick renders only the\r\nlatest price received since the previous tick. A tick with no new update does\r\nnothing, avoiding duplicate renders with stale data.\r\n\r\n### `once`\r\n\r\n```ts\r\nimport { once } from \"@alsoftworks/temporize\";\r\n\r\nconst initializeApp = once(async () => {\r\n  const response = await fetch(\"/api/config\");\r\n  if (!response.ok) throw new Error(\"Configuration failed\");\r\n  return response.json() as Promise<{ region: string }>;\r\n});\r\n\r\n// Both callers share one initialization and receive the same configuration.\r\nconst [fromRouter, fromDashboard] = await Promise.all([\r\n  initializeApp(),\r\n  initializeApp(),\r\n]);\r\n```\r\n\r\nA rejected initialization resets automatically, allowing the next caller to\r\ntry again. Use `reset()` for an explicit new lifecycle or `resetAfter` for a\r\ntime-limited shared result.\r\n\r\n### `timeout`\r\n\r\n```ts\r\nimport { timeout } from \"@alsoftworks/temporize\";\r\n\r\nconst fetchWithinBudget = timeout(\r\n  (url: string, signal?: AbortSignal) => fetch(url, { signal }),\r\n  2_000,\r\n);\r\n\r\nconst response = await fetchWithinBudget(\"/api/report\");\r\n```\r\n\r\n`timeout` gives up on one slow invocation; `retry` starts new invocations after\r\nfailures. Timing out only stops the underlying fetch here because it observes\r\nthe internally supplied signal. Functions that ignore the signal continue in\r\nthe background while the caller receives `TemporizeTimeoutError`.\r\n\r\n### `poll`\r\n\r\n```ts\r\nimport { poll } from \"@alsoftworks/temporize\";\r\n\r\ntype Job = { id: string; status: \"queued\" | \"running\" | \"done\" };\r\n\r\nconst waitForJob = poll(\r\n  async (id: string) => {\r\n    const response = await fetch(`/api/jobs/${id}`);\r\n    if (!response.ok) throw new Error(`Status failed: ${response.status}`);\r\n    return response.json() as Promise<Job>;\r\n  },\r\n  500,\r\n  {\r\n    until: (job) => job.status === \"done\",\r\n    attempts: 60,\r\n    timeout: 30_000,\r\n  },\r\n);\r\n\r\nconst completed = await waitForJob(\"job_123\");\r\n```\r\n\r\nA failed status request counts as an attempt and polling continues after the\r\ninterval. Exhausting the attempt count or overall deadline rejects with\r\n`TemporizeTimeoutError`.\r\n\r\n## Framework Adapters\r\n\r\nReact and Vue adapters are optional subpath exports. Installing\r\n`@alsoftworks/temporize`\r\ndoes not install either framework, and importing the core package does not load\r\nadapter code. Add only the peer your application already uses.\r\n\r\n### React\r\n\r\n```sh\r\nnpm install @alsoftworks/temporize react\r\n```\r\n\r\nImport hooks from `@alsoftworks/temporize/react`:\r\n\r\n```tsx\r\nimport { useEffect, useState } from \"react\";\r\nimport { useDebouncedValue } from \"@alsoftworks/temporize/react\";\r\n\r\nexport function Search(): JSX.Element {\r\n  const [query, setQuery] = useState(\"\");\r\n  const debouncedQuery = useDebouncedValue(query, 300);\r\n\r\n  useEffect(() => {\r\n    if (!debouncedQuery) return;\r\n    void fetch(`/api/search?q=${encodeURIComponent(debouncedQuery)}`);\r\n  }, [debouncedQuery]);\r\n\r\n  return (\r\n    <input\r\n      value={query}\r\n      onChange={(event) => setQuery(event.target.value)}\r\n      placeholder=\"Search\"\r\n    />\r\n  );\r\n}\r\n```\r\n\r\nUse `useThrottle` for high-frequency browser events. The hook keeps a stable\r\nfunction identity, invokes the latest callback closure, and cancels pending\r\ntrailing work when the component unmounts.\r\n\r\n```tsx\r\nimport { useEffect, useState } from \"react\";\r\nimport { TemporizeAbortError } from \"@alsoftworks/temporize\";\r\nimport { useThrottle } from \"@alsoftworks/temporize/react\";\r\n\r\nexport function ScrollPosition(): JSX.Element {\r\n  const [position, setPosition] = useState(0);\r\n  const updatePosition = useThrottle((next: number) => {\r\n    setPosition(next);\r\n  }, 100);\r\n\r\n  useEffect(() => {\r\n    const handleScroll = () => {\r\n      void updatePosition(window.scrollY).catch((error: unknown) => {\r\n        if (!(error instanceof TemporizeAbortError)) throw error;\r\n      });\r\n    };\r\n    window.addEventListener(\"scroll\", handleScroll, { passive: true });\r\n    return () => window.removeEventListener(\"scroll\", handleScroll);\r\n  }, [updatePosition]);\r\n\r\n  return <output>{position}</output>;\r\n}\r\n```\r\n\r\nThe React entry point exports `useDebounce`, `useThrottle`,\r\n`useDebouncedValue`, and `useRafThrottle`. Every function wrapper exposes the\r\nsame `cancel`, `flush`, and `pending` methods as its core equivalent;\r\n`useRafThrottle` exposes `cancel`.\r\n\r\n### Vue\r\n\r\n```sh\r\nnpm install @alsoftworks/temporize vue\r\n```\r\n\r\nImport composables from `@alsoftworks/temporize/vue`. `useDebouncedRef` accepts\r\na raw value, ref, computed ref, or getter and returns a normal writable ref.\r\n\r\n```vue\r\n<script setup lang=\"ts\">\r\nimport { ref, watch } from \"vue\";\r\nimport { useDebouncedRef } from \"@alsoftworks/temporize/vue\";\r\n\r\nconst query = ref(\"\");\r\nconst debouncedQuery = useDebouncedRef(query, 300);\r\n\r\nwatch(debouncedQuery, (value) => {\r\n  if (!value) return;\r\n  void fetch(`/api/search?q=${encodeURIComponent(value)}`);\r\n});\r\n</script>\r\n\r\n<template>\r\n  <input v-model=\"query\" placeholder=\"Search\" />\r\n</template>\r\n```\r\n\r\nUse the Vue throttle composable for scroll handling. Pending work is cancelled\r\nthrough Vue's `onUnmounted` lifecycle automatically.\r\n\r\n```vue\r\n<script setup lang=\"ts\">\r\nimport { onMounted, onUnmounted, ref } from \"vue\";\r\nimport { TemporizeAbortError } from \"@alsoftworks/temporize\";\r\nimport { useThrottle } from \"@alsoftworks/temporize/vue\";\r\n\r\nconst position = ref(0);\r\nconst updatePosition = useThrottle((next: number) => {\r\n  position.value = next;\r\n}, 100);\r\n\r\nconst handleScroll = () => {\r\n  void updatePosition(window.scrollY).catch((error: unknown) => {\r\n    if (!(error instanceof TemporizeAbortError)) throw error;\r\n  });\r\n};\r\n\r\nonMounted(() => window.addEventListener(\"scroll\", handleScroll, { passive: true }));\r\nonUnmounted(() => window.removeEventListener(\"scroll\", handleScroll));\r\n</script>\r\n\r\n<template>\r\n  <output>{{ position }}</output>\r\n</template>\r\n```\r\n\r\nThe Vue entry point exports `useDebounce`, `useThrottle`, `useDebouncedRef`,\r\nand `useRafThrottle`. React and Vue are optional peer dependencies, so the core\r\nlibrary retains zero framework and runtime dependencies.\r\n\r\n## API reference\r\n\r\n### `debounce(fn, wait, options?)`\r\n\r\nReturns `(...args) => Promise<Awaited<R>>` with:\r\n\r\n- `cancel(): void` clears the timer and rejects calls waiting to run with a\r\n  `TemporizeAbortError`. It does not stop work already invoked.\r\n- `flush(): Promise<Awaited<R>> | undefined` immediately runs pending trailing\r\n  work and returns the shared result promise. It returns the most recent result\r\n  when no work is pending, or `undefined` before any invocation exists.\r\n- `pending(): boolean` reports whether trailing work is waiting to run.\r\n\r\n`DebounceOptions`:\r\n\r\n| Option     | Default | Meaning                                                                                         |\r\n| ---------- | ------- | ----------------------------------------------------------------------------------------------- |\r\n| `leading`  | `false` | Invoke at the beginning of the quiet window.                                                    |\r\n| `trailing` | `true`  | Invoke with the latest arguments at its end.                                                    |\r\n| `maxWait`  | none    | Force an invocation during a continuous call stream. Values below `wait` are clamped to `wait`. |\r\n| `signal`   | none    | Cancel pending work on abort and reject calls made after abort.                                 |\r\n\r\nNegative and non-finitely falsy waits are treated as zero. A zero-wait trailing\r\ncall still runs in a later timer task.\r\n\r\n### `throttle(fn, wait, options?)`\r\n\r\nReturns the same promise function and lifecycle methods as `debounce`.\r\n`ThrottleOptions`:\r\n\r\n| Option     | Default | Meaning                                                         |\r\n| ---------- | ------- | --------------------------------------------------------------- |\r\n| `leading`  | `true`  | Invoke at the beginning of the first window.                    |\r\n| `trailing` | `true`  | Invoke at the end with the latest suppressed call.              |\r\n| `signal`   | none    | Cancel pending work on abort and reject calls made after abort. |\r\n\r\n### `rafThrottle(fn)`\r\n\r\nReturns a void function with `cancel(): void`. Repeated calls before a frame are\r\ncoalesced and the latest arguments and `this` value are used.\r\n\r\n### `debounceAsync(fn, wait, options?)`\r\n\r\nReturns the same lifecycle shape as `debounce`. `DebounceAsyncOptions` includes\r\nall `DebounceOptions` plus:\r\n\r\n| Option    | Default   | Meaning                                                                             |\r\n| --------- | --------- | ----------------------------------------------------------------------------------- |\r\n| `overlap` | `\"queue\"` | Use `\"queue\"`, `\"drop\"`, or `\"cancel-previous\"` when a firing overlaps active work. |\r\n\r\nCalling `.cancel()` rejects scheduled debounce work and overlap jobs that are\r\nqueued but not started. It does not implicitly abort active work; the\r\n`\"cancel-previous\"` policy aborts active work when its replacement starts.\r\n\r\n### `throttlePromise(fn, wait, options?)`\r\n\r\nReturns `(...args) => Promise<Awaited<R>>` with:\r\n\r\n- `cancel(): void` rejects every queued call and clears the drain timer.\r\n- `flush(): Promise<Awaited<R>> | undefined` immediately starts the next item.\r\n- `pending(): boolean` reports a queued item or scheduled drain.\r\n- `queued(): number` returns the number of calls not yet started.\r\n\r\n`ThrottlePromiseOptions`:\r\n\r\n| Option    | Default | Meaning                                                               |\r\n| --------- | ------- | --------------------------------------------------------------------- |\r\n| `leading` | `true`  | Start the first queued call immediately; when false, wait one window. |\r\n| `signal`  | none    | Cancel queued work on abort and reject calls made after abort.        |\r\n\r\n### `batch(fn, wait, options?)`\r\n\r\nReturns `(...args) => Promise<R>` with:\r\n\r\n- `cancel(): void` rejects all calls still queued in the current batch.\r\n- `flush(): Promise<R> | undefined` immediately invokes queued work and returns\r\n  the shared result promise.\r\n- `pending(): boolean` reports whether the current batch contains calls.\r\n- `size(): number` returns the number of queued argument tuples.\r\n\r\n`BatchOptions`:\r\n\r\n| Option    | Default   | Meaning                                                                         |\r\n| --------- | --------- | ------------------------------------------------------------------------------- |\r\n| `maxSize` | unlimited | Fire immediately when the queue reaches this size. Values below `1` become `1`. |\r\n| `signal`  | none      | Cancel queued work on abort and reject calls made after abort.                  |\r\n\r\nThe wrapped function receives `Args[]`, preserving every call's complete\r\nargument tuple. It runs only once per batch, so all callers in that batch settle\r\nwith the same result or error.\r\n\r\n### `retry(fn, options?)`\r\n\r\nReturns an async function with the same inferred arguments and resolved result.\r\nThe first successful attempt resolves the call. Exhaustion rejects with a\r\n`TemporizeTimeoutError` whose `attempts` and `cause` expose the attempt count\r\nand last failure. Aborting rejects immediately with `TemporizeAbortError`.\r\n\r\n`RetryOptions`:\r\n\r\n| Option        | Default           | Meaning                                                                  |\r\n| ------------- | ----------------- | ------------------------------------------------------------------------ |\r\n| `attempts`    | `3`               | Maximum total invocations, including the first.                          |\r\n| `baseDelay`   | `200`             | Delay in milliseconds after the first failure.                           |\r\n| `maxDelay`    | `5000`            | Maximum delay between attempts.                                          |\r\n| `factor`      | `2`               | Exponential multiplier for each subsequent delay.                        |\r\n| `jitter`      | `true`            | Randomize each delay between zero and its calculated value.              |\r\n| `shouldRetry` | retry every error | Decide whether a failure and its one-based attempt number are retryable. |\r\n| `signal`      | none              | Abort an active wait and prevent future attempts.                        |\r\n\r\n### `idle(fn, options?)`\r\n\r\nReturns a void function with `cancel(): void`. Calls before the idle callback\r\nruns are coalesced using the latest arguments and `this` value.\r\n\r\n`IdleOptions`:\r\n\r\n| Option    | Default         | Meaning                                               |\r\n| --------- | --------------- | ----------------------------------------------------- |\r\n| `timeout` | browser default | Native `requestIdleCallback` timeout in milliseconds. |\r\n\r\nWhen idle callbacks are unavailable, scheduling falls back to `setTimeout(fn,\r\n1)`.\r\n\r\n### `concurrencyLimit(fn, max, options?)`\r\n\r\nReturns `(...args) => Promise<R>` with:\r\n\r\n- `cancel(): void` rejects queued calls without interrupting work already in\r\n  flight; later calls remain usable.\r\n- `pending(): number` returns the number of running calls.\r\n- `queued(): number` returns the number of calls waiting for a slot.\r\n\r\nCalls start in FIFO order as slots become available. `max` must be a positive\r\ninteger and invalid values throw a synchronous `TypeError`. Passing\r\n`{ signal }` applies the same queued-only cancellation rule and preserves the\r\nsignal's abort reason on `TemporizeAbortError.reason`.\r\n\r\n### `sample(fn, interval, options?)`\r\n\r\nReturns a void function with:\r\n\r\n- `cancel(): void` stops the clock and discards arguments waiting for a tick.\r\n- `flush(): Promise<Awaited<R>> | undefined` immediately invokes pending work.\r\n- `pending(): boolean` reports whether new arguments await the next tick.\r\n\r\nThe clock starts lazily with the first call and stays fixed until cancellation.\r\nOnly the latest arguments and `this` value received between ticks are retained.\r\nTicks without new data do not invoke `fn`, preventing duplicate stale work.\r\nBecause clock-triggered calls have no per-call promise, scheduled async\r\nfunctions should handle their own errors; errors from `flush()` are observable\r\nthrough its returned promise. `{ signal }` cancels the clock and makes later\r\ncalls no-ops.\r\n\r\n### `once(fn, options?)`\r\n\r\nReturns `(...args) => Promise<Awaited<R>>` with:\r\n\r\n- `reset(): void` forgets the shared invocation and permits another one.\r\n- `invoked(): boolean` reports whether an invocation exists and has not reset.\r\n\r\nConcurrent and subsequent calls share the first invocation's result. Rejection\r\nautomatically resets the wrapper so the next call retries. `resetAfter` starts\r\nwhen an invocation begins; after that duration, a new call may invoke `fn`\r\nagain. `{ signal }` rejects callers waiting when it aborts and all later calls,\r\nwithout interrupting underlying work.\r\n\r\n### `timeout(fn, ms, options?)`\r\n\r\nReturns an async function with the wrapped function's inferred public arguments\r\nand resolved result. Expiration rejects with `TemporizeTimeoutError`, whose\r\n`attempts` is undefined because only one invocation occurred. An optional or\r\nrequired final `AbortSignal` declared by `fn` is supplied internally and omitted\r\nfrom the returned function's arguments.\r\n\r\nTiming out rejects the caller but cannot forcibly stop arbitrary promises. The\r\ninternal signal lets cooperative operations such as `fetch` stop themselves.\r\nAn external `{ signal }` rejects with `TemporizeAbortError` and also aborts that\r\ninternal signal. Negative budgets become zero; `Infinity` disables the timeout\r\nwhile retaining external cancellation.\r\n\r\n### `poll(fn, interval, options?)`\r\n\r\nReturns an async function that repeatedly calls `fn` with the original\r\narguments. `PollOptions<R>`:\r\n\r\n| Option     | Default          | Meaning                                                          |\r\n| ---------- | ---------------- | ---------------------------------------------------------------- |\r\n| `until`    | result is truthy | Accept a successful result and stop polling.                     |\r\n| `attempts` | unlimited        | Maximum calls, including rejected calls; values below `1` use 1. |\r\n| `timeout`  | unlimited        | Overall wall-clock budget across calls and interval waits.       |\r\n| `signal`   | none             | Abort active waiting and all future attempts.                    |\r\n\r\nA rejected invocation counts as an attempt and polling continues after the\r\ninterval. Predicate errors propagate immediately. Attempts or time exhaustion\r\nrejects with `TemporizeTimeoutError`; `.attempts` contains calls already made\r\nand `.cause` contains the most recent invocation failure when available.\r\n\r\n### Errors\r\n\r\n`TemporizeAbortError` identifies cancellation by `.cancel()` or an\r\n`AbortSignal`. Its `reason` and `cause` preserve a supplied signal reason.\r\n`TemporizeTimeoutError` identifies expired timeouts, polls, and retries. Its\r\noptional `attempts` and `cause` describe repeated operations when relevant.\r\n\r\n## Size and builds\r\n\r\n`npm run build` emits minified ESM, CommonJS, and declarations in `dist/`.\r\nThe implementation is tree-shakeable (`sideEffects: false`) and has no runtime\r\ndependencies. The core exports are designed to remain below 1 kB each after\r\nminification and gzip when consumed individually. Current tree-shaken ESM\r\nmeasurements with esbuild are: `debounce` 722 B, `throttle` 759 B,\r\n`rafThrottle` 236 B, `throttlePromise` 559 B, `debounceAsync` 997 B, `batch`\r\n511 B, `retry` 662 B, `idle` 242 B, `concurrencyLimit` 525 B, `sample` 303 B,\r\n`once` 462 B, `timeout` 501 B, and `poll` 745 B.\r\n`debounceAsync` is closest to the budget because its concurrency state machine\r\nand internal AbortController must cover all three overlap policies.\r\n\r\nUse a bundle analyzer in the final application because shared helpers, module\r\nformat wrappers, and bundler chunking affect attributed per-export sizes.\r\n\r\nThe optional adapters are measured as independent, minified ESM bundles with\r\ntheir framework peer externalized. The React entry is 1,169 B gzipped and the\r\nVue entry is 1,089 B gzipped. Both include the core scheduling code they use\r\nand remain below the separate 1.5 kB adapter budget.\r\n\r\n## Development\r\n\r\n```sh\r\nnpm test\r\nnpm run typecheck\r\nnpm run lint\r\nnpm run build\r\nnpm run size\r\nnpm run bench\r\n```\r\n\r\nUse `npm run build:core` or `npm run test:core` when working only on the\r\nframework-free entry point. `npm publish` runs the complete tests, strict type\r\ncheck, and all three builds automatically through `prepublishOnly`.\r\n\r\nReact, Vue, jsdom, and their testing utilities are repository-only development\r\ndependencies. npm does not support installing only a named subset of one\r\npackage's `devDependencies`, so contributors running a normal `npm install`\r\nreceive the adapter test stack. Package consumers do not: React and Vue are\r\noptional peers, stay external in adapter builds, and never enter the core\r\nbundle.\r\n\r\nSee [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution requirements and\r\n[CHANGELOG.md](./CHANGELOG.md) for release history.\r\n\r\nThe tinybench benchmark measures raw wrapper dispatch overhead against the\r\nstandalone `lodash.debounce` and `lodash.throttle` packages. It intentionally\r\ndoes not claim behavioral equivalence: temporize allocates a real promise for\r\nevery call, which is part of its contract and part of the measured cost.\r\n\r\n## License\r\n\r\nMIT. See [LICENSE](LICENSE). Third-party development and benchmark references\r\nare documented in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md); they are not\r\nincluded in Temporize's runtime bundles.\r\n","readmeFilename":"README.md"}