{"_id":"@dariushstony/smart-storage","_rev":"11-dbc2928573870a69e3061d6cf4d4c3f9","name":"@dariushstony/smart-storage","dist-tags":{"latest":"1.1.2"},"versions":{"0.1.0":{"name":"@dariushstony/smart-storage","version":"0.1.0","keywords":["storage","localStorage","sessionStorage","web-storage","ttl","cache","browser-storage","ssr-safe","typescript","transform","debounce","expiration"],"author":{"name":"Dariush Hadipour"},"license":"MIT","_id":"@dariushstony/smart-storage@0.1.0","maintainers":[{"name":"dariushstony","email":"dariushhadi87@gmail.com"}],"homepage":"https://github.com/DariushStony/smart-storage#readme","bugs":{"url":"https://github.com/DariushStony/smart-storage/issues"},"dist":{"shasum":"3cda01c4bbb23c8962aac65e161011ba0c000459","tarball":"https://registry.npmjs.org/@dariushstony/smart-storage/-/smart-storage-0.1.0.tgz","fileCount":9,"integrity":"sha512-VFXlZiLFX6A7oidIxieRa4UqPtzYpO4OrQyf2DpkgmOHdFhUH4tiQDUYjtuOp5Iu/ynELqW5wrf2CdColSrDCA==","signatures":[{"sig":"MEQCIA3gHqYUWHNWAgK4dQehR3qALWQsg/ZV7Ju3FdAjDzYCAiB5NuBI9761kavv7/4sSyxNAaT1NuO3lmoSBmNg74djuw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":173310},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"3b1f6148b626145de1500257ab6691125b697e78","private":false,"scripts":{"lint":"eslint \"src/**/*.ts\"","size":"size-limit","build":"tsup","check":"pnpm format:check && pnpm lint && pnpm typecheck","clean":"rm -rf dist","format":"prettier --write \"src/**/*.{ts,tsx,json}\"","analyze":"size-limit --why","prepare":"husky","release":"semantic-release","lint:fix":"eslint \"src/**/*.ts\" --fix","typecheck":"tsc --noEmit","size:check":"pnpm build && size-limit","format:check":"prettier --check \"src/**/*.{ts,tsx,json}\""},"_npmUser":{"name":"dariushstony","email":"dariushhadi87@gmail.com"},"repository":{"url":"git+https://github.com/DariushStony/smart-storage.git","type":"git"},"_npmVersion":"11.6.1","description":"A robust, SSR-safe, production-ready wrapper around Web Storage (localStorage/sessionStorage) with TTL, transforms, debouncing, and automatic cleanup","directories":{},"lint-staged":{"*.{json,md}":["prettier --write"],"src/**/*.{ts,tsx}":["prettier --write","eslint --fix"]},"sideEffects":false,"_nodeVersion":"24.11.0","_hasShrinkwrap":false,"packageManager":"pnpm@9.0.0","devDependencies":{"tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.2","prettier":"^3.8.1","@eslint/js":"^9.39.2","size-limit":"^11.2.0","typescript":"^5.9.3","lint-staged":"^15.5.2","@commitlint/cli":"^19.8.1","semantic-release":"^24.2.9","typescript-eslint":"^8.54.0","@semantic-release/git":"^10.0.1","@semantic-release/npm":"^13.1.3","@semantic-release/github":"^11.0.1","@semantic-release/changelog":"^6.0.3","@size-limit/preset-small-lib":"^11.2.0","@commitlint/config-conventional":"^19.8.1","@semantic-release/commit-analyzer":"^13.0.1","@semantic-release/release-notes-generator":"^14.1.0"},"_npmOperationalInternal":{"tmp":"tmp/smart-storage_0.1.0_1770398936897_0.8598297024744206","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@dariushstony/smart-storage","version":"1.0.0","keywords":["storage","localStorage","sessionStorage","web-storage","ttl","cache","browser-storage","ssr-safe","typescript","transform","debounce","expiration"],"author":{"name":"Dariush Hadipour"},"license":"MIT","_id":"@dariushstony/smart-storage@1.0.0","maintainers":[{"name":"dariushstony","email":"dariushhadi87@gmail.com"}],"homepage":"https://github.com/DariushStony/smart-storage#readme","bugs":{"url":"https://github.com/DariushStony/smart-storage/issues"},"dist":{"shasum":"74d8b64b3e4d04357332f1c4f7005607cff6037c","tarball":"https://registry.npmjs.org/@dariushstony/smart-storage/-/smart-storage-1.0.0.tgz","fileCount":9,"integrity":"sha512-D9hrnYu20TM8Sudi2z+mLr3dvN1WjdWEwXZa2zVKtlPkDqtSnJ0dkD8WB8nghskCokjikm7fe+RrWBOt0T660A==","signatures":[{"sig":"MEYCIQCJ+BlvJcPMx6ZEmCGU6N2PuFxLK2zsPbkyXrwccoDRoQIhANvWLcMN5hZPsOU70I2KfgZTF/ufug6eDQG24Th8Ik3s","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":173220},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"753eb1626f7387ebe6eecb8a11417140e8a06789","private":false,"scripts":{"lint":"eslint \"src/**/*.ts\"","size":"size-limit","build":"tsup","check":"pnpm format:check && pnpm lint && pnpm typecheck","clean":"rm -rf dist","format":"prettier --write \"src/**/*.{ts,tsx,json}\"","analyze":"size-limit --why","prepare":"husky","release":"semantic-release","lint:fix":"eslint \"src/**/*.ts\" --fix","typecheck":"tsc --noEmit","size:check":"pnpm build && size-limit","format:check":"prettier --check \"src/**/*.{ts,tsx,json}\""},"_npmUser":{"name":"dariushstony","email":"dariushhadi87@gmail.com"},"repository":{"url":"git+https://github.com/DariushStony/smart-storage.git","type":"git"},"_npmVersion":"10.8.2","description":"A robust, SSR-safe, production-ready wrapper around Web Storage (localStorage/sessionStorage) with TTL, transforms, debouncing, and automatic cleanup","directories":{},"lint-staged":{"*.{json,md}":["prettier --write"],"src/**/*.{ts,tsx}":["prettier --write","eslint --fix"]},"sideEffects":false,"_nodeVersion":"20.20.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.2","prettier":"^3.8.1","@eslint/js":"^9.39.2","size-limit":"^11.2.0","typescript":"^5.9.3","lint-staged":"^15.5.2","@commitlint/cli":"^19.8.1","semantic-release":"^24.2.9","typescript-eslint":"^8.54.0","@semantic-release/git":"^10.0.1","@semantic-release/npm":"^13.1.3","@semantic-release/github":"^11.0.1","@semantic-release/changelog":"^6.0.3","@size-limit/preset-small-lib":"^11.2.0","@commitlint/config-conventional":"^19.8.1","@semantic-release/commit-analyzer":"^13.0.1","@semantic-release/release-notes-generator":"^14.1.0"},"_npmOperationalInternal":{"tmp":"tmp/smart-storage_1.0.0_1770454642181_0.4635202714716997","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@dariushstony/smart-storage","version":"1.0.1","keywords":["storage","localStorage","sessionStorage","web-storage","ttl","cache","browser-storage","ssr-safe","typescript","transform","debounce","expiration"],"author":{"name":"Dariush Hadipour"},"license":"MIT","_id":"@dariushstony/smart-storage@1.0.1","maintainers":[{"name":"dariushstony","email":"dariushhadi87@gmail.com"}],"homepage":"https://github.com/DariushStony/smart-storage#readme","bugs":{"url":"https://github.com/DariushStony/smart-storage/issues"},"dist":{"shasum":"37162c8021b0c2fd3603f27a27077c4b71aebd2c","tarball":"https://registry.npmjs.org/@dariushstony/smart-storage/-/smart-storage-1.0.1.tgz","fileCount":9,"integrity":"sha512-+NxDCoVTwIIZ13BUfuruvQi+xC3yk/8ypzq3LtVAxEYFFQfVDxy1ENvWl0ol8oJ460tgnzu8mQeyXMbzQhKDGg==","signatures":[{"sig":"MEUCIQDDoRrh586nZ4GzWt4tE5+3pY+dDvrg6O68nFjpJe4g+QIgCBnuefhjYMJcprpzGZ/Nf9pjFCC4sy63Lr1BNI9r0MA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":173338},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"9c653247d50d6754e39d0d04cc3ae8c973e83dea","private":false,"scripts":{"lint":"eslint \"src/**/*.ts\"","size":"size-limit","build":"tsup","check":"pnpm format:check && pnpm lint && pnpm typecheck","clean":"rm -rf dist","format":"prettier --write \"src/**/*.{ts,tsx,json}\"","analyze":"size-limit --why","prepare":"husky","release":"semantic-release","lint:fix":"eslint \"src/**/*.ts\" --fix","typecheck":"tsc --noEmit","size:check":"pnpm build && size-limit","format:check":"prettier --check \"src/**/*.{ts,tsx,json}\""},"_npmUser":{"name":"dariushstony","email":"dariushhadi87@gmail.com"},"repository":{"url":"git+https://github.com/DariushStony/smart-storage.git","type":"git"},"_npmVersion":"10.8.2","description":"A robust, SSR-safe, production-ready wrapper around Web Storage (localStorage/sessionStorage) with TTL, transforms, debouncing, and automatic cleanup","directories":{},"lint-staged":{"*.{json,md}":["prettier --write"],"src/**/*.{ts,tsx}":["prettier --write","eslint --fix"]},"sideEffects":false,"_nodeVersion":"20.20.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","husky":"^9.1.7","eslint":"^9.39.2","prettier":"^3.8.1","@eslint/js":"^9.39.2","size-limit":"^11.2.0","typescript":"^5.9.3","lint-staged":"^15.5.2","@commitlint/cli":"^19.8.1","semantic-release":"^24.2.9","typescript-eslint":"^8.54.0","@semantic-release/git":"^10.0.1","@semantic-release/npm":"^13.1.3","@semantic-release/github":"^11.0.1","@semantic-release/changelog":"^6.0.3","@size-limit/preset-small-lib":"^11.2.0","@commitlint/config-conventional":"^19.8.1","@semantic-release/commit-analyzer":"^13.0.1","@semantic-release/release-notes-generator":"^14.1.0"},"_npmOperationalInternal":{"tmp":"tmp/smart-storage_1.0.1_1770462231514_0.5933431672677236","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"@dariushstony/smart-storage","repository":{"type":"git","url":"git+https://github.com/DariushStony/smart-storage.git"},"bugs":{"url":"https://github.com/DariushStony/smart-storage/issues"},"homepage":"https://github.com/DariushStony/smart-storage#readme","version":"1.1.2","private":false,"publishConfig":{"access":"public"},"description":"A robust, SSR-safe, production-ready wrapper around Web Storage (localStorage/sessionStorage) with TTL, transforms, debouncing, and automatic cleanup","keywords":["storage","localStorage","sessionStorage","web-storage","ttl","cache","browser-storage","ssr-safe","typescript","transform","debounce","expiration"],"author":{"name":"Dariush Hadipour"},"license":"MIT","type":"module","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"}},"sideEffects":false,"packageManager":"pnpm@11.18.0","scripts":{"prepare":"husky","lint":"oxlint --type-aware","lint:fix":"oxlint --type-aware --fix","format":"prettier --write \"{src,tests}/**/*.{ts,tsx,json}\"","format:check":"prettier --check \"{src,tests}/**/*.{ts,tsx,json}\"","size":"size-limit","size:check":"pnpm build && size-limit","analyze":"size-limit --why","clean":"rm -rf dist","typecheck":"tsc --noEmit && tsc -p tsconfig.test.json","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","pretest:e2e":"pnpm build","test:e2e":"playwright test","test:all":"pnpm test && pnpm test:e2e","check":"pnpm format:check && pnpm lint && pnpm typecheck","release":"semantic-release","build":"tsup && tsc --emitDeclarationOnly"},"devDependencies":{"@commitlint/cli":"^21.2.1","@commitlint/config-conventional":"^21.2.0","@playwright/test":"^1.62.1","@semantic-release/changelog":"^7.0.0","@semantic-release/commit-analyzer":"^13.0.1","@semantic-release/git":"^11.0.1","@semantic-release/github":"^12.0.9","@semantic-release/npm":"^13.1.5","@semantic-release/release-notes-generator":"^14.1.1","@size-limit/preset-small-lib":"^13.0.3","@types/node":"^26.1.2","@vitest/coverage-v8":"^4.1.10","happy-dom":"^20.11.1","husky":"^9.1.7","lint-staged":"^17.3.0","oxlint":"^1.76.0","oxlint-tsgolint":"^7.0.2001","prettier":"^3.9.6","semantic-release":"^25.0.8","size-limit":"^13.0.3","tsup":"^8.5.1","typescript":"^7.0.2","vitest":"^4.1.10"},"lint-staged":{"{src,tests}/**/*.{ts,tsx}":["prettier --write","oxlint --type-aware --fix"],"*.{json,md}":["prettier --write"]},"gitHead":"ea9350ddce355103ad53e29ae85363845a83646a","_id":"@dariushstony/smart-storage@1.1.2","_nodeVersion":"26.7.0","_npmVersion":"11.19.0","dist":{"integrity":"sha512-q7ZwmImScs07j1vSaWQUiIqq0p2PhOrOfehDMCPgonMKnkUguOKDk+2JQWULkh2lokMUJVlzIpH06pCYiHFUjw==","shasum":"c50d9ec1aabf95fdce1752ca4e4e22b16f019747","tarball":"https://registry.npmjs.org/@dariushstony/smart-storage/-/smart-storage-1.1.2.tgz","fileCount":55,"unpackedSize":232904,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFK0RbGQeFfwZtwDWgSd6wIWGc5dPi+l7WQ3gTb+L5DPAiBu84GWWMoyUJm9YO6mHaIn6yPBl6NA3DieGy7M7ppLhA=="}]},"_npmUser":{"name":"dariushhadipour","email":"dariushhadi87@gmail.com"},"directories":{},"maintainers":[{"name":"dariushhadipour","email":"dariushhadi87@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/smart-storage_1.1.2_1788260302074_0.7730201880022898"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-06T17:28:56.806Z","modified":"2026-09-01T10:58:22.456Z","0.1.0":"2026-02-06T17:28:57.049Z","1.0.0":"2026-02-07T08:57:22.322Z","1.0.1":"2026-02-07T11:03:51.695Z","1.1.2":"2026-09-01T10:58:22.240Z"},"bugs":{"url":"https://github.com/DariushStony/smart-storage/issues"},"author":{"name":"Dariush Hadipour"},"license":"MIT","homepage":"https://github.com/DariushStony/smart-storage#readme","keywords":["storage","localStorage","sessionStorage","web-storage","ttl","cache","browser-storage","ssr-safe","typescript","transform","debounce","expiration"],"repository":{"type":"git","url":"git+https://github.com/DariushStony/smart-storage.git"},"description":"A robust, SSR-safe, production-ready wrapper around Web Storage (localStorage/sessionStorage) with TTL, transforms, debouncing, and automatic cleanup","maintainers":[{"name":"dariushhadipour","email":"dariushhadi87@gmail.com"}],"readme":"# @dariushstony/smart-storage\n\n[![npm version](https://badge.fury.io/js/@dariushstony%2Fsmart-storage.svg)](https://www.npmjs.com/package/@dariushstony/smart-storage)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)\n[![TypeScript](https://img.shields.io/badge/TypeScript-7.0-blue.svg)](https://www.typescriptlang.org/)\n\nA **robust, SSR-safe, and production-ready wrapper** around Web Storage (`localStorage` / `sessionStorage` / `in-memory`) with:\n\n- 🗄️ Three storage types: `'local'`, `'session'`, `'in-memory'`\n- ⏱️ TTL-based expiration\n- ⚡ Debounced writes\n- 🧹 Automatic cleanup\n- 🔄 SSR-safe with automatic fallback\n- 🛡️ Strong safety guarantees and detailed diagnostics\n- 📦 Dual package: ESM and CommonJS support\n\nDesigned for **real-world frontend applications** where correctness, performance, and edge-case handling matter.\n\n---\n\n## 📦 Installation\n\n```bash\nnpm install @dariushstony/smart-storage\n# or\nyarn add @dariushstony/smart-storage\n# or\npnpm add @dariushstony/smart-storage\n```\n\n**Works with:**\n\n- ✅ ESM (`import`)\n- ✅ CommonJS (`require`)\n- ✅ TypeScript\n- ✅ Node.js 18+\n- ✅ Modern browsers\n- ✅ SSR frameworks (Next.js, Nuxt, etc.)\n\n---\n\n## ⚠️ Security Warning (Read This First)\n\n**Web Storage is NOT secure.**\n\n- Data is fully accessible via JavaScript\n- Vulnerable to XSS\n- Easily inspectable by users\n\n❌ **Do NOT store**:\n\n- Auth tokens\n- Passwords\n- Sensitive user data\n\n✅ **Use instead**:\n\n- `httpOnly` cookies\n- Secure server-side sessions\n\nTreat **all stored data as potentially compromised** and validate on read.\n\n---\n\n## ✨ Features\n\n- ✅ Three storage types: `'local'`, `'session'`, `'in-memory'`\n- ✅ Safe SSR support (no `window` access on server)\n- ✅ TTL (time-to-live) expiration\n- ✅ Automatic expired-item cleanup\n- ✅ Debounced writes (default: 100ms)\n- ✅ Read-after-write consistency\n- ✅ QuotaExceeded recovery logic\n- ✅ Prototype-pollution protection\n- ✅ Singleton per storage slice + type\n- ✅ Detailed stats & diagnostics\n- ✅ Zero dependencies\n\n---\n\n## 📤 Exports\n\n```typescript\n// Value exports\nimport {\n  getStorageSlice, // Create custom storage slices\n  disposeStorageSlice, // Clean up temporary slices\n  StorageVault, // Class for advanced usage\n  StorageType, // Const object: { Local, Session, InMemory }\n  LoggingHandler, // Opt-in logging, added to the transform chain\n  TransformHandler, // Base class for custom transforms\n  InlineTransformHandler, // Wraps a plain { serialize, deserialize } object\n  TransformChain, // Pre-built chain of transform handlers\n  StorageStatistics, // Opt-in stats collection\n} from '@dariushstony/smart-storage';\n\n// Type exports\nimport type {\n  StorageLogger,\n  StorageTypeValue, // 'local' | 'session' | 'in-memory'\n  StorageVaultOptions,\n  StorageStats,\n  StorageTransform,\n  StoredData,\n  DataRecord,\n  IStorage,\n} from '@dariushstony/smart-storage';\n```\n\n> **Note:** `StorageType` is a runtime value (a `const` object), so import it\n> normally — not with `import type`. The corresponding union type is\n> `StorageTypeValue`.\n\n---\n\n## 🗄️ Storage Types\n\nStorageVault supports three storage backends:\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\n\n// 'local' - localStorage (persists across browser sessions)\nconst persistent = getStorageSlice('USER_DATA', {\n  storageType: 'local', // Default\n});\n\n// 'session' - sessionStorage (cleared when tab closes)\nconst temporary = getStorageSlice('WIZARD_STATE', {\n  storageType: 'session',\n});\n\n// 'in-memory' - Map (cleared on page reload, great for testing)\nconst testing = getStorageSlice('TEST_DATA', {\n  storageType: 'in-memory',\n});\n```\n\n### When to use each type\n\n| Type              | Persistence       | Use Case                                |\n| ----------------- | ----------------- | --------------------------------------- |\n| **`'local'`**     | Across sessions   | User preferences, cart, long-term cache |\n| **`'session'`**   | Until tab closes  | Wizard flows, temporary form data       |\n| **`'in-memory'`** | Until page reload | Testing, SSR fallback, temporary data   |\n\n---\n\n## 🚀 Quick Start\n\n### ESM (Modern JavaScript)\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\n\n// Create a storage slice (localStorage by default)\nconst storage = getStorageSlice('MY_APP');\n\n// Store data\nstorage.setItem('theme', 'dark');\n\n// Retrieve data\nconst theme = storage.getItem<string>('theme');\nconsole.log(theme); // → \"dark\"\n```\n\n### CommonJS (Node.js)\n\n```javascript\nconst { getStorageSlice } = require('@dariushstony/smart-storage');\n\n// Create a storage slice\nconst storage = getStorageSlice('MY_APP');\n\n// Works the same way!\nstorage.setItem('user', { name: 'dariush', id: 123 });\n```\n\n### TypeScript\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\n\ninterface User {\n  name: string;\n  id: number;\n}\n\nconst storage = getStorageSlice('MY_APP');\n\n// Store with TTL (auto-expiry)\nstorage.setItem<User>('user', { name: 'dariush', id: 123 });\n\n// Retrieve with type safety\nconst user = storage.getItem<User>('user');\nif (user) {\n  console.log(user.name); // TypeScript knows the shape!\n}\n```\n\n---\n\n## 🧩 Storage Slices (Recommended)\n\nAll data is stored as one JSON blob per slice. Slices help reduce re-serialization costs and isolate concerns.\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\n\n// Persistent user preferences\nconst userPrefs = getStorageSlice('USER_PREFERENCES', {\n  storageType: 'local', // Default\n});\n\n// Temporary session cache\nconst tempCache = getStorageSlice('TEMP_CACHE', {\n  storageType: 'session',\n});\n\n// Testing data\nconst testData = getStorageSlice('TEST_DATA', {\n  storageType: 'in-memory',\n});\n\nuserPrefs.setItem('theme', 'dark');\ntempCache.setItem('data', { value: 123 }, 5 * 60 * 1000); // 5 min TTL\n```\n\n### When to use slices\n\n- ✅ Split data by update frequency\n- ✅ Isolate large or experimental data\n- ✅ Avoid rewriting unrelated data on each update\n\n### Examples\n\n❌ **Bad** - Too granular:\n\n```typescript\ngetStorageSlice('USER_NAME');\ngetStorageSlice('USER_EMAIL');\n```\n\n✅ **Good** - Grouped logically:\n\n```typescript\ngetStorageSlice('USER_DATA');\n```\n\n---\n\n## ⏱ TTL (Time-to-Live)\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\n\nconst storage = getStorageSlice('MY_APP');\n\n// Expires in 1 hour\nstorage.setItem('token', 'abc123', 60 * 60 * 1000);\n\n// Never expires\nstorage.setItem('config', { theme: 'dark' });\n\n// Immediately deletes (TTL = 0)\nstorage.setItem('temp', 'data', 0);\n```\n\n### Get remaining TTL\n\n```typescript\nconst remainingMs = storage.getRemainingTTL('token');\nif (remainingMs) {\n  console.log(`Expires in ${Math.floor(remainingMs / 1000)} seconds`);\n}\n```\n\n### Update value without changing TTL\n\n```typescript\nstorage.setItem('counter', 0, 60000); // Expires in 1 minute\nstorage.updateItem('counter', 5); // Updates value, keeps same expiry\n```\n\n### Extend TTL\n\n```typescript\nstorage.extendTTL('token', 30 * 60 * 1000); // +30 minutes\n```\n\n**Note:** If the item had no expiry, `extendTTL` will add one starting from now.\n\n---\n\n## 🧹 Cleanup\n\n### Automatic\n\n- Expired items are removed on read\n- Cleanup runs automatically on quota errors\n\n### Manual\n\n```typescript\nconst storage = getStorageSlice('MY_APP');\nconst removedCount = storage.cleanupExpiredItems();\nconsole.log(`Removed ${removedCount} expired items`);\n```\n\n---\n\n## ⚡ Debounced Writes (Performance)\n\nWrites are debounced (default: 100ms) to batch rapid updates.\n\n- **Reads always see pending writes** (read-after-write consistency)\n- **Pending writes flush automatically on page unload** (no data loss)\n\n### Force immediate persistence\n\n```typescript\nconst storage = getStorageSlice('MY_APP');\nstorage.flush();\n```\n\n### Disable debouncing\n\n```typescript\nconst criticalStorage = getStorageSlice('CRITICAL_DATA', { debounceMs: 0 });\n```\n\n### Custom debounce timing\n\n```typescript\nconst analyticsStorage = getStorageSlice('ANALYTICS', { debounceMs: 500 });\n```\n\n---\n\n## 🖥 SSR Behavior\n\n- **Server:** Automatically uses in-memory storage (Map)\n- **Client:** Uses Web Storage (localStorage/sessionStorage based on `storageType`)\n- **Important:** Server data is NOT hydrated automatically\n\n### Recommended pattern\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\n\n// Server → pass initial data via props\n// Client → re-store in useEffect or client-side code\n\nconst storage = getStorageSlice('MY_APP');\n\n// In your client-side initialization:\nstorage.setItem('data', initialData);\n```\n\n### Explicit in-memory for testing\n\n```typescript\nconst testStorage = getStorageSlice('TEST', {\n  storageType: 'in-memory', // No real storage, perfect for tests\n  debounceMs: 0, // Immediate writes for predictable tests\n});\n```\n\n---\n\n## 🧠 Serialization Rules (JSON)\n\nUses `JSON.stringify()` internally.\n\n### Limitations\n\n- ❌ `Functions`, `undefined`, `Symbol` → silently dropped\n- ❌ Circular references → throws error\n- ⚠️ `Date` → becomes string (must convert back manually)\n- ⚠️ `Map`, `Set`, class instances → lose type information\n\n👉 **Serialize complex types manually before storing.**\n\n---\n\n## 📊 Stats & Debugging\n\nStatistics are a **pluggable concern** — they are not built into the vault, so\nyou pay nothing if you don't use them. Construct a `StorageStatistics` from the\nvault's accessors and call `collect()`:\n\n```typescript\nimport {\n  getStorageSlice,\n  StorageStatistics,\n} from '@dariushstony/smart-storage';\n\nconst storage = getStorageSlice('MY_APP');\n\nconst statistics = new StorageStatistics(\n  storage.getStorageAdapter(),\n  storage.getStorageKey(),\n  storage.getTransformChain(),\n  storage.getMaxSizeBytes()\n);\n\nconst stats = statistics.collect(() => storage.getAllData());\nconsole.log(stats);\n```\n\n### Returns\n\n```typescript\n{\n  itemCount: number;\n  sizeBytes: number;\n  stringLength: number;\n  maxSizeBytes: number;\n  quotaPercentage: number;\n  storageType: 'localStorage' | 'sessionStorage' | 'memory' | 'unavailable';\n}\n```\n\n### Example usage\n\n```typescript\nconst stats = statistics.collect(() => storage.getAllData());\nconsole.log(`Using ${stats.itemCount} items`);\nconsole.log(`Size: ${(stats.sizeBytes / 1024).toFixed(2)} KB`);\nconsole.log(`Quota: ${stats.quotaPercentage.toFixed(1)}%`);\n\nif (stats.quotaPercentage > 80) {\n  console.warn('Storage is over 80% full!');\n  storage.cleanupExpiredItems();\n}\n```\n\n---\n\n## 🗑 Clearing Data\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\n\nconst storage = getStorageSlice('MY_APP');\nstorage.clear(); // Clears only this slice\n```\n\n**Note:** Other slices are unaffected.\n\n---\n\n## ♻️ Disposing Slices\n\nUseful for temporary or short-lived slices. Removes the instance from the singleton cache and cleans up event listeners.\n\n```typescript\nimport {\n  getStorageSlice,\n  disposeStorageSlice,\n} from '@dariushstony/smart-storage';\n\nconst tempStorage = getStorageSlice('TEMP_SESSION', {\n  storageType: 'session',\n});\n\n// ... use storage ...\n\n// Clean up when done (must match storageType used when creating)\ndisposeStorageSlice('TEMP_SESSION', {\n  storageType: 'session',\n});\n```\n\n**Important:** Instances are cached by `storageType` + slice key, so\n`disposeStorageSlice` must be given the same `storageType` used to create the\nslice. Other options (`debounceMs`, `transforms`, …) do not affect lookup.\n\n---\n\n## 🧪 Testing Utilities\n\n### Clear all instances\n\nFlushes pending writes, cleans up listeners, and removes every vault instance\nfrom the singleton cache.\n\n```typescript\nimport { StorageVault } from '@dariushstony/smart-storage';\n\n// In test teardown:\nafterEach(() => {\n  StorageVault.clearAllInstances();\n});\n```\n\n### Use in-memory storage for tests\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\n\ndescribe('My tests', () => {\n  const testVault = getStorageSlice('TEST_DATA', {\n    storageType: 'in-memory', // Isolated, no real storage\n    debounceMs: 0, // Immediate writes\n  });\n\n  afterEach(() => {\n    testVault.clear(); // Clean up after each test\n  });\n\n  it('should store data', () => {\n    testVault.setItem('key', 'value');\n    expect(testVault.getItem('key')).toBe('value');\n  });\n});\n```\n\n---\n\n## 🧱 Error Handling & Logging\n\nLogging is a **pluggable concern**, not a constructor option. Add a\n`LoggingHandler` to the transform chain to enable it — remove it to disable\nlogging entirely, with zero overhead.\n\n```typescript\nimport { getStorageSlice, LoggingHandler } from '@dariushstony/smart-storage';\nimport type { StorageLogger } from '@dariushstony/smart-storage';\n\nconst customLogger: StorageLogger = {\n  log: (message, error) => {\n    console.error('[Storage]', message, error);\n    // Send to Sentry or your logging service\n    // Sentry.captureException(error, { extra: { message } });\n  },\n};\n\nconst vault = getStorageSlice('APP_DATA', {\n  storageType: 'local',\n  transforms: [new LoggingHandler(customLogger)],\n});\n```\n\n---\n\n## 🔧 Advanced Configuration\n\n```typescript\nconst customStorage = getStorageSlice('CUSTOM', {\n  storageType: 'session', // 'local' | 'session' | 'in-memory'\n  debounceMs: 200, // Custom debounce delay\n  maxSizeBytes: 10_000_000, // 10MB quota warning threshold\n  maxItemsInMemory: 2000, // Max items for in-memory fallback\n  transforms: [new LoggingHandler(customLogger)], // Opt-in logging\n});\n```\n\n### Configuration Options\n\n| Option             | Type                                       | Default     | Description                                         |\n| ------------------ | ------------------------------------------ | ----------- | --------------------------------------------------- |\n| `storageType`      | `'local' \\| 'session' \\| 'in-memory'`      | `'local'`   | Storage backend to use                              |\n| `debounceMs`       | `number`                                   | `100`       | Write debouncing delay (0 = immediate)              |\n| `maxSizeBytes`     | `number`                                   | `4_000_000` | Quota warning threshold (~4MB)                      |\n| `maxItemsInMemory` | `number`                                   | `1000`      | Max items for in-memory storage                     |\n| `transforms`       | `(TransformHandler \\| StorageTransform)[]` | `undefined` | Handlers/objects wrapped into a chain               |\n| `transformChain`   | `TransformChain`                           | `undefined` | Pre-built chain; takes precedence over `transforms` |\n\n> `storageKey` is also part of `StorageVaultOptions`, but `getStorageSlice()`\n> sets it from its own `sliceKey` argument, so you cannot pass it directly.\n> Its default when using `StorageVault` directly is `'APP_DATA'`.\n>\n> Logging and statistics are deliberately **not** options here — see\n> [Error Handling & Logging](#-error-handling--logging) and\n> [Stats & Debugging](#-stats--debugging).\n\n---\n\n## 📚 API Reference\n\n### Write Operations\n\n| Method                          | Returns   | Description                            |\n| ------------------------------- | --------- | -------------------------------------- |\n| `setItem(key, value, ttl?)`     | `boolean` | Stores a value with optional TTL       |\n| `updateItem(key, newValue)`     | `boolean` | Updates value without changing TTL     |\n| `removeItem(key)`               | `boolean` | Removes an item                        |\n| `clear()`                       | `boolean` | Clears all data for this slice         |\n| `extendTTL(key, additionalTTL)` | `boolean` | Extends TTL or adds one if none exists |\n\n### Read Operations\n\n| Method                 | Returns                   | Description                              |\n| ---------------------- | ------------------------- | ---------------------------------------- |\n| `getItem<T>(key)`      | `T \\| null`               | Retrieves a value                        |\n| `hasItem(key)`         | `boolean`                 | Checks if item exists and is not expired |\n| `getRemainingTTL(key)` | `number \\| null`          | Returns remaining TTL in milliseconds    |\n| `getAllKeys()`         | `string[]`                | Returns all valid keys                   |\n| `getAll()`             | `Record<string, unknown>` | Returns all valid items                  |\n\n### Maintenance Operations\n\n| Method                  | Returns  | Description                          |\n| ----------------------- | -------- | ------------------------------------ |\n| `cleanupExpiredItems()` | `number` | Removes expired items, returns count |\n| `flush()`               | `void`   | Flushes pending debounced writes     |\n| `getCurrentSize()`      | `number` | Returns storage size in bytes        |\n\n### Introspection\n\nThese accessors exist mainly to wire up pluggable concerns such as\n[`StorageStatistics`](#-stats--debugging).\n\n| Method                | Returns          | Description                            |\n| --------------------- | ---------------- | -------------------------------------- |\n| `getAllData()`        | `DataRecord`     | Raw records, including expiry metadata |\n| `getStorageAdapter()` | `IStorage`       | The underlying storage adapter         |\n| `getStorageKey()`     | `string`         | The key this slice is stored under     |\n| `getTransformChain()` | `TransformChain` | The active transform chain             |\n| `getMaxSizeBytes()`   | `number`         | The configured quota warning threshold |\n\n### Static Methods\n\n| Method                               | Returns        | Description                                                                                                  |\n| ------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------ |\n| `StorageVault.getInstance(opts)`     | `StorageVault` | Gets/creates the singleton keyed on `storageType` + `storageKey`; other options apply only on first creation |\n| `StorageVault.disposeInstance(opts)` | `boolean`      | Disposes a single instance; matched on `storageType` + `storageKey`                                          |\n| `StorageVault.clearAllInstances()`   | `void`         | Flushes and removes all instances                                                                            |\n\n---\n\n## 🔌 Transform Pipeline\n\nYou can chain multiple transforms (compression, encryption, encoding) to process data before storage:\n\n```typescript\nimport { getStorageSlice } from '@dariushstony/smart-storage';\nimport type { StorageTransform } from '@dariushstony/smart-storage';\n\n// Example: Compression transform (requires lz-string package)\nconst compressionTransform: StorageTransform = {\n  serialize: (data: string) => LZString.compress(data),\n  deserialize: (data: string) => LZString.decompress(data) || '',\n};\n\nconst vault = getStorageSlice('LARGE_DATA', {\n  transforms: [compressionTransform],\n});\n\n// Data is automatically compressed before storage and decompressed on read\nvault.setItem('bigObject', {/* large data */});\n```\n\nTransforms are applied in order during writes and reversed during reads.\n\n---\n\n## 💡 Common Patterns\n\n### Feature Flags with Auto-Expiry\n\n```typescript\nconst featureFlags = getStorageSlice('FEATURE_FLAGS', {\n  storageType: 'local',\n});\n\nfunction enableFeature(name: string, durationMs = 24 * 60 * 60 * 1000) {\n  featureFlags.setItem(`feature:${name}`, true, durationMs);\n}\n\nfunction isFeatureEnabled(name: string): boolean {\n  return featureFlags.hasItem(`feature:${name}`);\n}\n\nenableFeature('new-checkout', 7 * 24 * 60 * 60 * 1000); // 7 days\n```\n\n### Rate Limiting\n\n```typescript\nconst rateLimiter = getStorageSlice('RATE_LIMIT', {\n  storageType: 'local',\n});\n\nfunction canPerformAction(action: string, limitMs = 60000): boolean {\n  const key = `action:${action}`;\n  if (rateLimiter.hasItem(key)) {\n    return false; // Rate-limited\n  }\n\n  rateLimiter.setItem(key, true, limitMs);\n  return true;\n}\n\nif (canPerformAction('send-email', 5 * 60 * 1000)) {\n  console.log('Sending email...');\n}\n```\n\n### Form Draft Auto-Save\n\n```typescript\nconst draftStorage = getStorageSlice('FORM_DRAFTS', {\n  storageType: 'local',\n  debounceMs: 1000, // Save 1 second after typing stops\n});\n\nfunction saveDraft(formId: string, data: Record<string, unknown>) {\n  draftStorage.setItem(`draft:${formId}`, data, 24 * 60 * 60 * 1000);\n}\n\nfunction loadDraft(formId: string) {\n  return draftStorage.getItem<Record<string, unknown>>(`draft:${formId}`);\n}\n```\n\n### Temporary Wizard Flow\n\n```typescript\nconst wizardStorage = getStorageSlice('CHECKOUT_WIZARD', {\n  storageType: 'session', // Auto-cleared when tab closes\n});\n\nfunction saveWizardStep(step: number, data: any) {\n  wizardStorage.setItem('currentStep', step);\n  wizardStorage.setItem('formData', data);\n}\n\n// Data automatically cleared when user closes tab - no cleanup needed!\n```\n\n---\n\n## 🏁 Design Philosophy\n\n- **Prefer correctness over cleverness**\n- **Defensive by default**\n- **Explicit trade-offs**\n- **Optimized for real production constraints**\n\nThis is infrastructure code, not a toy utility.\n\n---\n\n## 📚 Additional Documentation\n\n- [Documentation index](docs/README.md) — start here\n- [Security Policy](SECURITY.md) — reporting vulnerabilities, and what is out of scope\n- [Code of Conduct](CODE_OF_CONDUCT.md) — community expectations\n- [Architecture](docs/ARCHITECTURE.md) — system design and technical decisions\n- [Storage Architecture](docs/STORAGE_ARCHITECTURE.md) — deep dive into storage mechanisms\n- [How to Use](docs/HOW_TO_USE_STORAGE.md) — simple examples and patterns\n- [Singleton Pattern](docs/SINGLETON_PATTERN_VISUAL.md) — how instances are cached per slice\n- [Project Structure](docs/STRUCTURE.md) — codebase organization\n- [Examples](examples/README.md) — runnable sample apps\n- [Contributing](CONTRIBUTING.md) — dev setup and workflow\n\n## 🧪 Tests\n\n```bash\npnpm test           # Unit tests (Vitest + happy-dom)\npnpm test:coverage  # With a coverage report\npnpm test:e2e       # E2E against real Chromium (builds first)\npnpm test:all       # Both\n```\n\nUnit tests cover the logic — TTL, transforms, singleton identity, key\nvalidation, debounce coalescing. The Playwright suite covers what a simulated\nDOM cannot honestly prove: persistence across real page reloads,\n`sessionStorage` clearing with the tab, genuine `pagehide` flushing, real quota\npressure, and cross-tab visibility. E2E specs load `dist/`, so they exercise the\nartifact that actually ships.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md#-testing) for which layer to use.\n\n## 🤝 Contributing\n\nContributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for dev\nsetup, commit conventions, and the testing guide.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'feat: add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\nBy participating you agree to the [Code of Conduct](CODE_OF_CONDUCT.md).\n\n## 📝 License\n\nThis project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.\n\n## 👤 Author\n\n**Dariush Hadipour**\n\n- GitHub: [@DariushStony](https://github.com/DariushStony)\n- Package: [@dariushstony/smart-storage](https://www.npmjs.com/package/@dariushstony/smart-storage)\n\n---\n\n## 🎓 Quick Reference\n\n### Storage Types\n\n```typescript\n'local'; // localStorage - persists across sessions\n'session'; // sessionStorage - cleared on tab close\n'in-memory'; // Map - cleared on reload, great for tests\n```\n\n### Common Use Cases\n\n| Use Case         | Storage Type             | TTL      | Debounce        |\n| ---------------- | ------------------------ | -------- | --------------- |\n| User preferences | `'local'`                | None     | 0ms (immediate) |\n| Shopping cart    | `'local'`                | None     | 100ms           |\n| API cache        | `'local'`                | 5-10 min | 200ms           |\n| Feature flags    | `'local'`                | 7 days   | 100ms           |\n| Wizard flow      | `'session'`              | None     | 100ms           |\n| Form drafts      | `'local'` or `'session'` | 24 hours | 1000ms          |\n| Testing          | `'in-memory'`            | Varies   | 0ms             |\n","readmeFilename":"README.md"}