{"versions":{"0.0.1":{"name":"@pivanov/utils","version":"0.0.1","description":"A collection of personal utilities to live a happier life","type":"module","packageManager":"pnpm@9.0.0","repository":{"type":"git","url":"git://github.com/pivanov/utils.git"},"homepage":"https://github.com/pivanov/utils#readme","bugs":{"url":"https://github.com/pivanov/utils/issues"},"scripts":{"build":"rm -rf dist && pnpm rollup -c","test":"pnpm vitest","test:coverage":"pnpm vitest --coverage","test:ui":"pnpm vitest --ui","lint":"biome lint .","format":"biome format . --write","check":"biome check . --write","prepublishOnly":"pnpm build"},"author":{"name":"Pavel Ivanov","email":"iweb.ivanov@gmail.com","url":"https://github.com/pivanov"},"keywords":["utils","typescript","react","web-components","dom","cache","event-bus","string","object","promise","r2wc","react-to-web-component"],"license":"MIT","main":"dist/cjs/index.js","module":"dist/esm/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"peerDependencies":{"react":">=18","react-dom":">=18"},"devDependencies":{"@biomejs/biome":"^1.9.4","@rollup/plugin-commonjs":"^28.0.0","@rollup/plugin-node-resolve":"^15.3.0","@rollup/plugin-typescript":"^12.1.1","@testing-library/dom":"^10.4.0","@testing-library/react":"^16.0.1","@testing-library/react-hooks":"^8.0.1","@types/node":"^22.9.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","@vercel/ncc":"0.38.2","@vitest/coverage-v8":"^2.1.4","evt":"^2.4.18","glob":"^11.0.0","husky":"^4.3.0","jsdom":"^25.0.1","react":"^18.3.1","react-dom":"^18.3.1","rollup":"^4.23.0","rollup-plugin-dts":"^6.1.1","rollup-plugin-terser":"^7.0.2","tslib":"^2.7.0","typescript":"5.7.2","vitest":"^2.1.4"},"publishConfig":{"access":"public"},"_id":"@pivanov/utils@0.0.1","gitHead":"cbc575d4da80269acb05a2a1fd03e0a69526f0e4","_nodeVersion":"18.18.2","_npmVersion":"9.8.1","dist":{"integrity":"sha512-JQ/pXeG9/Yq3UuwH2Xp4F6bSAIDGzbxT0Vrg/82tMi3Yp+Ps9AYzjSDE+zfvBRqc7J11V6MMonUrWj4+2dYgrg==","shasum":"bd9f36849e42ae307e053d30168f4a1cd9b3892a","tarball":"http://123.232.10.234:8212/nexus/content/repositories/npm-private/@pivanov/utils/-/utils-0.0.1.tgz","fileCount":6,"unpackedSize":63865,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB31K+qjRljzQ0jxkvla2zVWPuMblonaGq3gQAm0zhj6AiAhkFulBwYs0cYbVS1RsMo3LGjXiSdxTXN5Z2s42PA3FQ=="}],"size":17257},"_npmUser":{"name":"anonymous","email":"pafelka@gmail.com"},"directories":{},"maintainers":[{"name":"anonymous","email":"pafelka@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/utils_0.0.1_1736299784044_0.061382709907555455"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2025-01-08T01:29:44.218Z","publish_time":1736299784218,"_source_registry_name":"default","contributors":[]},"0.0.2":{"name":"@pivanov/utils","version":"0.0.2","description":"A collection of personal utilities to live a happier life","type":"module","packageManager":"pnpm@9.0.0","repository":{"type":"git","url":"git://github.com/pivanov/pivanov-utils.git"},"homepage":"https://github.com/pivanov/pivanov-utils#readme","bugs":{"url":"https://github.com/pivanov/pivanov-utils/issues"},"scripts":{"build":"rm -rf dist && pnpm rollup -c","test":"pnpm vitest","test:coverage":"pnpm vitest --coverage","test:ui":"pnpm vitest --ui","lint":"biome lint .","format":"biome format . --write","check":"biome check . --write","prepublishOnly":"pnpm build"},"author":{"name":"Pavel Ivanov","email":"iweb.ivanov@gmail.com","url":"https://github.com/pivanov"},"keywords":["utils","typescript","react","web-components","dom","cache","event-bus","string","object","promise","r2wc","react-to-web-component"],"license":"MIT","main":"dist/cjs/index.js","module":"dist/esm/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"peerDependencies":{"react":">=18","react-dom":">=18"},"devDependencies":{"@biomejs/biome":"^1.9.4","@rollup/plugin-commonjs":"^28.0.0","@rollup/plugin-node-resolve":"^15.3.0","@rollup/plugin-typescript":"^12.1.1","@testing-library/dom":"^10.4.0","@testing-library/react":"^16.0.1","@testing-library/react-hooks":"^8.0.1","@types/node":"^22.9.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","@vercel/ncc":"0.38.2","@vitest/coverage-v8":"^2.1.4","evt":"^2.4.18","glob":"^11.0.0","husky":"^4.3.0","jsdom":"^25.0.1","react":"^18.3.1","react-dom":"^18.3.1","rollup":"^4.23.0","rollup-plugin-dts":"^6.1.1","rollup-plugin-terser":"^7.0.2","tslib":"^2.7.0","typescript":"5.7.2","vitest":"^2.1.4"},"publishConfig":{"access":"public"},"_id":"@pivanov/utils@0.0.2","gitHead":"70eb8d9a98d9a6386e9c5268b3f9f3a75c47a576","_nodeVersion":"18.18.2","_npmVersion":"9.8.1","dist":{"integrity":"sha512-q9CN0bFWxWgMY5hVVYyBgez1jGiLBa6I+LkG37ycylPhFvEGOOeaADGtUSu46CaZasPnlY8fCdVJZmrgKb1EPA==","shasum":"32d7a381d6a837615d4ce6cd92878d4b050953b4","tarball":"http://123.232.10.234:8212/nexus/content/repositories/npm-private/@pivanov/utils/-/utils-0.0.2.tgz","fileCount":6,"unpackedSize":64419,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCIbnnwHrvngUESoNBc9Og8YzlMx9L3UxkDqFmw+6GPewIhAPw5id2mGZafH2NHSv8d33hxP//DVQsRtzw0Y+wisRNi"}],"size":17323},"_npmUser":{"name":"anonymous","email":"pafelka@gmail.com"},"directories":{},"maintainers":[{"name":"anonymous","email":"pafelka@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/utils_0.0.2_1740783612488_0.112764635374351"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2025-02-28T23:00:12.695Z","publish_time":1740783612695,"_source_registry_name":"default","contributors":[]},"0.0.3-rc.1":{"name":"@pivanov/utils","version":"0.0.3-rc.1","description":"A collection of personal utilities to live a happier life","type":"module","packageManager":"pnpm@9.0.0","repository":{"type":"git","url":"git://github.com/pivanov/pivanov-utils.git"},"homepage":"https://github.com/pivanov/pivanov-utils#readme","bugs":{"url":"https://github.com/pivanov/pivanov-utils/issues"},"scripts":{"build":"rm -rf dist && pnpm rollup -c","test":"pnpm vitest","test:coverage":"pnpm vitest --coverage","test:ui":"pnpm vitest --ui","lint":"biome lint .","format":"biome format . --write","check":"biome check . --write","prepublishOnly":"pnpm build"},"author":{"name":"Pavel Ivanov","email":"iweb.ivanov@gmail.com","url":"https://github.com/pivanov"},"keywords":["utils","typescript","react","web-components","dom","cache","event-bus","string","object","promise","r2wc","react-to-web-component"],"license":"MIT","main":"dist/cjs/index.js","module":"dist/esm/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"peerDependencies":{"react":">=18","react-dom":">=18"},"devDependencies":{"@biomejs/biome":"^1.9.4","@rollup/plugin-commonjs":"^28.0.0","@rollup/plugin-node-resolve":"^15.3.0","@rollup/plugin-typescript":"^12.1.1","@testing-library/dom":"^10.4.0","@testing-library/react":"^16.0.1","@testing-library/react-hooks":"^8.0.1","@types/node":"^22.9.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","@vercel/ncc":"0.38.2","@vitest/coverage-v8":"^2.1.4","evt":"^2.4.18","glob":"^11.0.0","husky":"^4.3.0","jsdom":"^25.0.1","react":"^18.3.1","react-dom":"^18.3.1","rollup":"^4.23.0","rollup-plugin-dts":"^6.1.1","rollup-plugin-terser":"^7.0.2","tslib":"^2.7.0","typescript":"5.7.2","vitest":"^2.1.4"},"publishConfig":{"access":"public"},"_id":"@pivanov/utils@0.0.3-rc.1","gitHead":"70eb8d9a98d9a6386e9c5268b3f9f3a75c47a576","_nodeVersion":"18.18.2","_npmVersion":"9.8.1","dist":{"integrity":"sha512-wUF/tGskvZuKveMkWJdrGk/K6xH5wPgkUWv8aHnyPH3YoO+qvUdQLbM5qKz0/ASyR33HCKQK3KFIC3Hx/YrFbg==","shasum":"48636130ab6c2981a2e81df8533ff1ddef52d32e","tarball":"http://123.232.10.234:8212/nexus/content/repositories/npm-private/@pivanov/utils/-/utils-0.0.3-rc.1.tgz","fileCount":6,"unpackedSize":66240,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGOqjhwUpoeRSiUzA4Jorsivw2KZprXFQkyycGMx06o6AiEA7Fa+/aP+ORy2enXnW7DADXEIyiKdnX7YLOgurSrxJUI="}],"size":17064},"_npmUser":{"name":"anonymous","email":"pafelka@gmail.com"},"directories":{},"maintainers":[{"name":"anonymous","email":"pafelka@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/utils_0.0.3-rc.1_1740863036001_0.8706552677499289"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2025-03-01T21:03:56.244Z","publish_time":1740863036244,"_source_registry_name":"default","deprecated":"This version has been deprecated and will be replaced with a new version. Please update to the latest version.","contributors":[]},"0.0.3":{"name":"@pivanov/utils","version":"0.0.3","description":"A collection of personal utilities to live a happier life","type":"module","repository":{"type":"git","url":"git://github.com/pivanov/pivanov-utils.git"},"homepage":"https://github.com/pivanov/pivanov-utils#readme","bugs":{"url":"https://github.com/pivanov/pivanov-utils/issues"},"author":{"name":"Pavel Ivanov","email":"iweb.ivanov@gmail.com","url":"https://github.com/pivanov"},"keywords":["utils","typescript","react","dom","cache","browser cache api","event-bus","string","object","promise"],"license":"MIT","main":"dist/cjs/index.js","module":"dist/esm/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"peerDependencies":{"react":">=18","react-dom":">=18"},"devDependencies":{"@biomejs/biome":"^1.9.4","@rollup/plugin-commonjs":"^28.0.0","@rollup/plugin-node-resolve":"^15.3.0","@rollup/plugin-typescript":"^12.1.1","@testing-library/dom":"^10.4.0","@testing-library/react":"^16.0.1","@testing-library/react-hooks":"^8.0.1","@types/node":"^22.9.0","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","@vercel/ncc":"0.38.2","@vitest/coverage-v8":"^2.1.4","evt":"^2.4.18","glob":"^11.0.0","husky":"^4.3.0","jsdom":"^25.0.1","react":"^18.3.1","react-dom":"^18.3.1","rollup":"^4.23.0","rollup-plugin-dts":"^6.1.1","rollup-plugin-terser":"^7.0.2","tslib":"^2.7.0","typescript":"5.7.2","vitest":"^2.1.4"},"publishConfig":{"access":"public"},"scripts":{"build":"rm -rf dist && pnpm rollup -c","test":"pnpm vitest","test:coverage":"pnpm vitest --coverage","test:ui":"pnpm vitest --ui","lint":"biome lint .","format":"biome format . --write","check":"biome check . --write"},"_id":"@pivanov/utils@0.0.3","_integrity":"sha512-3Tm+9/vLTg0rSGfGjmduoeR5JKUk+iWB0vhSDMaadrcbo6+hHxCVZwvvzr7Yq1V9SEwsAQj+D6ffeQXsNWYwyw==","_resolved":"/private/var/folders/33/yx94zk4x45gf12snsr9qq4zw0000gn/T/d29a117b4e79b2845669d733fa4ee269/pivanov-utils-0.0.3.tgz","_from":"file:pivanov-utils-0.0.3.tgz","_nodeVersion":"20.19.5","_npmVersion":"10.8.2","dist":{"integrity":"sha512-3Tm+9/vLTg0rSGfGjmduoeR5JKUk+iWB0vhSDMaadrcbo6+hHxCVZwvvzr7Yq1V9SEwsAQj+D6ffeQXsNWYwyw==","shasum":"26199501b4b638cc5340e7608eff60623e009ec5","tarball":"http://123.232.10.234:8212/nexus/content/repositories/npm-private/@pivanov/utils/-/utils-0.0.3.tgz","fileCount":6,"unpackedSize":53989,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQD1QX62GGktx/qDeU5yVQkcskFzxmdic0HIu0x40NsPrQIhAOVtYoRdBId7ZRXc7PYtT6p1AT0k9mT84TUpfmIR2naT"}],"size":14886},"_npmUser":{"name":"anonymous","email":"pafelka@gmail.com"},"directories":{},"maintainers":[{"name":"anonymous","email":"pafelka@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/utils_0.0.3_1766053326344_0.28572497795313767"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2025-12-18T10:22:06.503Z","publish_time":1766053326503,"_source_registry_name":"default","contributors":[]},"1.0.0":{"name":"@pivanov/utils","version":"1.0.0","description":"A focused collection of TypeScript utilities for modern web development","type":"module","repository":{"type":"git","url":"git://github.com/pivanov/pivanov-utils.git"},"homepage":"https://github.com/pivanov/pivanov-utils#readme","bugs":{"url":"https://github.com/pivanov/pivanov-utils/issues"},"scripts":{"build":"rm -rf dist && bun run scripts/build.ts && bun x tsc --emitDeclarationOnly","test":"bun test","test:coverage":"bun test --coverage","lint":"biome lint .","format":"biome format . --write","check":"biome check . --write","typecheck":"bun x tsc --noEmit","docs:dev":"bun x vitepress dev docs","docs:build":"bun x vitepress build docs","docs:preview":"bun x vitepress preview docs","prepublishOnly":"bun run build","publish":"npm publish --access public"},"author":{"name":"Pavel Ivanov","email":"iweb.ivanov@gmail.com","url":"https://github.com/pivanov"},"keywords":["utils","typescript","react","dom","cache","browser cache api","event-bus","string","object","promise"],"license":"MIT","sideEffects":false,"main":"./dist/cjs/index.js","module":"./dist/esm/index.js","types":"./dist/types/index.d.ts","exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"},"./assertion":{"types":"./dist/types/assertion/index.d.ts","import":"./dist/esm/assertion/index.js","require":"./dist/cjs/assertion/index.js"},"./object":{"types":"./dist/types/object/index.d.ts","import":"./dist/esm/object/index.js","require":"./dist/cjs/object/index.js"},"./promise":{"types":"./dist/types/promise/index.d.ts","import":"./dist/esm/promise/index.js","require":"./dist/cjs/promise/index.js"},"./string":{"types":"./dist/types/string/index.d.ts","import":"./dist/esm/string/index.js","require":"./dist/cjs/string/index.js"},"./tools":{"types":"./dist/types/tools/index.d.ts","import":"./dist/esm/tools/index.js","require":"./dist/cjs/tools/index.js"},"./types":{"types":"./dist/types/types/index.d.ts","import":"./dist/esm/types/index.js","require":"./dist/cjs/types/index.js"}},"peerDependencies":{"react":">=18","react-dom":">=18"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true}},"devDependencies":{"@biomejs/biome":"^2.4.11","@happy-dom/global-registrator":"^15.11.7","@testing-library/react":"^16.0.1","@types/bun":"^1.1.14","@types/react":"^18.3.12","@types/react-dom":"^18.3.1","react":"^18.3.1","react-dom":"^18.3.1","typescript":"^5.7.2","vitepress":"~1.5.0"},"publishConfig":{"access":"public"},"_id":"@pivanov/utils@1.0.0","_integrity":"sha512-6+8cC0wRocNVLj3WVfzIewbkGv8sqqfsEdYNz6mFHlQsZODdMHwPyR+QSmcfhyCJrpZ/Fw0cK+svevMAbHxlDQ==","_nodeVersion":"24.3.0","_npmVersion":"10.8.3","shasum":"c2a8f1bf49e557ae456b8fec988867bcd34de5ef","dist":{"integrity":"sha512-6+8cC0wRocNVLj3WVfzIewbkGv8sqqfsEdYNz6mFHlQsZODdMHwPyR+QSmcfhyCJrpZ/Fw0cK+svevMAbHxlDQ==","shasum":"c2a8f1bf49e557ae456b8fec988867bcd34de5ef","tarball":"http://123.232.10.234:8212/nexus/content/repositories/npm-private/@pivanov/utils/-/utils-1.0.0.tgz","fileCount":40,"unpackedSize":92249,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDDCguw/bXaQoB/ccI14qC5NeGVlTH5TeSHf4q5+a6sAAiAaGxWeZCIHO8h4WO02K1OmR9iJm+tbhkTm8CaEkgvF1Q=="}],"size":25111},"_npmUser":{"name":"anonymous","email":"pafelka@gmail.com"},"directories":{},"maintainers":[{"name":"anonymous","email":"pafelka@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/utils_1.0.0_1776548193415_0.47424619749811603"},"_hasShrinkwrap":false,"_cnpmcore_publish_time":"2026-04-18T21:36:33.553Z","publish_time":1776548193553,"_source_registry_name":"default","contributors":[]}},"dist-tags":{"latest":"1.0.0"},"name":"@pivanov/utils","time":{"created":"2024-11-10T02:21:00.289Z","modified":"2026-04-18T21:36:47.552Z","0.0.1":"2025-01-08T01:29:44.218Z","0.0.2":"2025-02-28T23:00:12.695Z","0.0.3-rc.1":"2025-03-01T21:03:56.244Z","0.0.3":"2025-12-18T10:22:06.503Z","1.0.0":"2026-04-18T21:36:33.553Z"},"readme":"# @pivanov/utils\n\n<p align=\"center\">\n  <i>A focused collection of TypeScript utilities for modern web development.</i>\n  <br /><br />\n  <img src=\"https://img.shields.io/npm/v/@pivanov/utils?logo=npm\" alt=\"NPM Version\" />\n  &nbsp;\n  <img src=\"https://img.shields.io/npm/dw/@pivanov/utils\" alt=\"Weekly Downloads\" />\n  &nbsp;\n  <img src=\"https://github.com/pivanov/pivanov-utils/actions/workflows/ci.yml/badge.svg?branch=main\" alt=\"CI Status\" />\n  &nbsp;\n  <img src=\"https://codecov.io/github/pivanov/pivanov-utils/graph/badge.svg?token=EPRKTP7D79\" alt=\"Coverage Status\" />\n</p>\n\n## Features\n\n- **Fully typed** - strict TypeScript across every module, literal-type preserving where it matters\n- **Tree-shakeable** - ESM + CJS + per-module subpath exports, `\"sideEffects\": false`\n- **Zero dependencies** - React is an optional peer dep only for the `useEventBus` hook\n- **Well tested** - 200+ tests, real edge cases (circular refs, typed arrays, Buffers, symbols)\n\n## Installation\n\n```bash\nbun add @pivanov/utils\n```\n\n```bash\nnpm install @pivanov/utils\n```\n\n```bash\nyarn add @pivanov/utils\n```\n\n```bash\npnpm add @pivanov/utils\n```\n\n## Quick start\n\n```ts\nimport { camelCase, snakeCase, slugify } from '@pivanov/utils/string';\nimport { pick, groupBy, deepMerge } from '@pivanov/utils/object';\nimport { isString, isNil, isDefined } from '@pivanov/utils/assertion';\nimport { sleep, timeout, retry, parallelLimit } from '@pivanov/utils/promise';\nimport { deepClone, isEqual, busDispatch, useEventBus } from '@pivanov/utils/tools';\n```\n\n## What's inside\n\n| Module | Surface |\n|---|---|\n| `assertion` | `isString`, `isNumber`, `isBoolean`, `isFunction`, `isObject`, `isRecord`, `isNull`, `isUndefined`, `isNil`, `isDefined`, `isArray`, `isDate`, `isRegExp`, `isError`, `isPromise`, `isMap`, `isSet`, `isPrimitive`, `isEmpty` |\n| `object` | `pick`, `omit`, `pickBy`, `omitBy`, `merge`, `deepMerge`, `mapValues`, `mapKeys`, `groupBy`, `invert`, `hasOwn`, `keysOf`, `entriesOf`, `fromEntries` |\n| `promise` | `sleep`, `timeout`, `retry`, `defer`, `parallelLimit` - all AbortSignal-aware where relevant |\n| `string` | `camelCase`, `pascalCase`, `kebabCase`, `snakeCase`, `titleCase`, `slugify`, `capitalize`, `uncapitalize`, `capitalizeFirstLetter`, `truncate`, `escapeHtml`, `escapeRegExp`, `words`, `lines` |\n| `tools/deepClone` | Rich deep clone (prototypes, getters/setters, symbols, Buffers, TypedArrays, circular refs) |\n| `tools/isEqual` | Deep equality with cycle detection; compares RegExp, Error, TypedArrays, ArrayBuffer |\n| `tools/dom` | `isBrowser`, `checkVisibility`, `isInViewport`, `setStyleProperties`, `calculateRenderedTextWidth` |\n| `tools/cache-api` | Typed wrapper over the browser Cache API, with TTL support |\n| `tools/eventBus` | `busDispatch`, `busSubscribe`, `busOnce`, `useEventBus` - typed topics, optional error handler |\n| `types` | `TDict`, `TObjType`, `DeepPartial`, `DeepReadonly`, `Mutable`, `Prettify` |\n\nSee the full **[API documentation](https://pivanov.github.io/pivanov-utils/)**.\n\n## Tree shaking\n\nSubpath imports give the smallest bundles:\n\n```ts\nimport { camelCase } from '@pivanov/utils/string';\nimport { deepClone } from '@pivanov/utils/tools';\n```\n\nTop-level imports tree-shake fine in modern bundlers:\n\n```ts\nimport { camelCase, deepClone } from '@pivanov/utils';\n```\n\n## A few highlights\n\n### Async that cancels\n\n```ts\nimport { sleep, timeout, retry } from '@pivanov/utils/promise';\n\nconst ctrl = new AbortController();\nawait sleep(1000, ctrl.signal);                          // cancellable\nawait timeout(fetch('/slow'), 3000);                     // race with timer\nawait retry(() => fetch('/api'), { attempts: 5, backoff: (n) => 100 * 2 ** n });\n```\n\n### Typed event bus\n\n```ts\nimport { busDispatch, useEventBus, type IEventBus } from '@pivanov/utils/tools';\n\ninterface UserLoggedIn extends IEventBus<{ id: number; name: string }> {\n  topic: 'user:logged-in';\n}\n\nuseEventBus<UserLoggedIn>('user:logged-in', (user) => console.log(user.name));\nbusDispatch<UserLoggedIn>('user:logged-in', { id: 1, name: 'Ada' });\n```\n\nSee the [Typed Events guide](https://pivanov.github.io/pivanov-utils/guides/typed-events) for an event-map pattern at scale.\n\n### Cache with TTL\n\n```ts\nimport {\n  storageSetItemWithTTL,\n  storageGetItemWithTTL,\n} from '@pivanov/utils/tools';\n\nawait storageSetItemWithTTL('app', 'token', 'abc123', 10 * 60 * 1000);\n\nconst token = await storageGetItemWithTTL<string>('app', 'token');\n// null if missing or expired; expired entries are deleted on read\n```\n\n### Deep clone that actually preserves shape\n\n```ts\nimport { deepClone } from '@pivanov/utils/tools';\n\nclass User {\n  constructor(public name: string) {}\n  greet() { return `hi ${this.name}`; }\n}\n\nconst clone = deepClone(new User('Ada'));\nclone instanceof User; // true\nclone.greet();         // 'hi Ada'\n```\n\n## Compatibility\n\n- Modern browsers (ES2022)\n- Bun, Node 18+ (ESM or CJS)\n- Cache API requires browser support (Chrome 40+, Firefox 41+, Safari 11.1+)\n- React hook requires React 18+\n\n## Development\n\n```bash\nbun install\nbun test                # run tests\nbun run test:coverage   # with coverage (lcov)\nbun run typecheck\nbun run lint\nbun run build           # ESM + CJS + .d.ts\nbun run docs:dev        # run VitePress docs site locally\n```\n\n## Sponsors\n\nSupported by [LogicStar AI](https://logicstar.ai/)\n\n## License\n\nMIT © [Pavel Ivanov](https://github.com/pivanov)","users":{}}