{"_id":"@alema/typed-storage","_rev":"13-86325d2ad28c85813fb9205886dabe06","name":"@alema/typed-storage","dist-tags":{"latest":"1.0.5"},"versions":{"1.0.1":{"name":"@alema/typed-storage","version":"1.0.1","keywords":["typescript","storage","typed","localstorage","mmkv","react-native","key-value","schema","codec"],"author":{"name":"Alessandro Marra","email":"alessandromarra001@gmail.com"},"license":"MIT","_id":"@alema/typed-storage@1.0.1","maintainers":[{"name":"alexma01","email":"alessandromarra001@gmail.com"}],"dist":{"shasum":"278c27e267fc31ca31fde9a7206b048044a1a5f4","tarball":"https://registry.npmjs.org/@alema/typed-storage/-/typed-storage-1.0.1.tgz","fileCount":19,"integrity":"sha512-iiJc7Qj8F08hSx0lPrS5TXwVWVNb4ep4JqL3vHTBzNukastdxaRbFDoCPiJ/Kg3GK8eKjc09YNueVvvqZcJ+pw==","signatures":[{"sig":"MEUCIQCl0F8Q3iUw6a3AV45hAm0jOrJW0/gj4m/epQUXaYKWUAIgV3OAJCZtIJw2f1mF/uSNWVy/v6Qxj0gCen1qsLihCiI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20101},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","gitHead":"de95573728b12ab5a575a625d96061ab3d4dcb0d","scripts":{"lint":"eslint .","test":"jest","build":"tsc","prepare":"node scripts/prepare.cjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexma01","email":"alessandromarra001@gmail.com"},"_npmVersion":"10.9.0","description":"A TypeScript library for typed key-value storage with support for various codecs and storage adapters.","directories":{},"_nodeVersion":"22.12.0","_hasShrinkwrap":false,"packageManager":"yarn@4.11.0","devDependencies":{"jest":"^30.2.0","eslint":"^9.39.1","ts-jest":"^29.4.5","ts-node":"^10.9.2","lefthook":"^2.0.4","@eslint/js":"^9.39.1","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.10.1","typescript-eslint":"^8.47.0","eslint-config-prettier":"^10.1.8","@stylistic/eslint-plugin":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/typed-storage_1.0.1_1763559171203_0.7699941628988571","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@alema/typed-storage","version":"1.0.2","keywords":["typescript","storage","typed","localstorage","mmkv","react-native","key-value","schema","codec"],"author":{"name":"Alessandro Marra","email":"alessandromarra001@gmail.com"},"license":"MIT","_id":"@alema/typed-storage@1.0.2","maintainers":[{"name":"alexma01","email":"alessandromarra001@gmail.com"}],"homepage":"https://github.com/alexma01/ts-typed-storage#readme","bugs":{"url":"https://github.com/alexma01/ts-typed-storage/issues"},"dist":{"shasum":"bfe8b254c23596f300ff6aa4608b30dabf79f1bd","tarball":"https://registry.npmjs.org/@alema/typed-storage/-/typed-storage-1.0.2.tgz","fileCount":19,"integrity":"sha512-Vi9nAmMG/yBeYOk4SsOFFO/SHnScHgXVOgsUFDanCSShyasLNDc1NoGhX46QWnOFM5ymWSvOyaAR5Lv8xsqAFg==","signatures":[{"sig":"MEUCIQCXnFfouScVf+9LWU/hp96V12WTk49tc/Ap5qFeLhW/VgIgTpc/zwST8jT1zJN2RcFA1a5lvCXcMy4+waxaoj2l5No=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20012},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","gitHead":"f61186d956b1eb28da0b2fc297d66712091e6a12","scripts":{"lint":"eslint .","test":"jest","build":"tsc","prepare":"node scripts/prepare.cjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexma01","email":"alessandromarra001@gmail.com"},"repository":{"url":"git+https://github.com/alexma01/ts-typed-storage.git","type":"git"},"_npmVersion":"10.9.0","description":"A TypeScript library for typed key-value storage with support for various codecs and storage adapters.","directories":{},"_nodeVersion":"22.12.0","_hasShrinkwrap":false,"packageManager":"yarn@4.11.0","devDependencies":{"jest":"^30.2.0","eslint":"^9.39.1","ts-jest":"^29.4.5","ts-node":"^10.9.2","lefthook":"^2.0.4","@eslint/js":"^9.39.1","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.10.1","typescript-eslint":"^8.47.0","eslint-config-prettier":"^10.1.8","@stylistic/eslint-plugin":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/typed-storage_1.0.2_1763560451131_0.22366856516290756","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@alema/typed-storage","version":"1.0.3","keywords":["typescript","storage","typed","localstorage","mmkv","react-native","key-value","schema","codec"],"author":{"name":"Alessandro Marra","email":"alessandromarra001@gmail.com"},"license":"MIT","_id":"@alema/typed-storage@1.0.3","maintainers":[{"name":"alexma01","email":"alessandromarra001@gmail.com"}],"homepage":"https://github.com/alexma01/ts-typed-storage#readme","bugs":{"url":"https://github.com/alexma01/ts-typed-storage/issues"},"dist":{"shasum":"a50926c1e8ff2f50673791674d6b8c4d8f91a44e","tarball":"https://registry.npmjs.org/@alema/typed-storage/-/typed-storage-1.0.3.tgz","fileCount":19,"integrity":"sha512-VqGwH2fx7t+tBZ7eoeHzI7bgHwU0EyiwilwwFc3BLOmTM4QreYcCTMjFn6fy1YFmSUvxgVTqlXYRHG6m+ZqCOA==","signatures":[{"sig":"MEYCIQCO8Xrl4JiXP/NrecNcsXEvbIosPppIk/7H0DkeNP/vnAIhAO1LZefGh/NiBn48MYQUQgFRGNDOK5ozrnfJ6jkl7bU7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26389},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","gitHead":"e9b03d95793e5a8387e7b44597187d4e6a914084","scripts":{"lint":"eslint .","test":"jest","build":"tsc","prepare":"node scripts/prepare.cjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexma01","email":"alessandromarra001@gmail.com"},"repository":{"url":"git+https://github.com/alexma01/ts-typed-storage.git","type":"git"},"_npmVersion":"10.9.0","description":"A TypeScript library for typed key-value storage with support for various codecs and storage adapters.","directories":{},"_nodeVersion":"22.12.0","_hasShrinkwrap":false,"packageManager":"yarn@4.11.0","devDependencies":{"jest":"^30.2.0","eslint":"^9.39.1","ts-jest":"^29.4.5","ts-node":"^10.9.2","lefthook":"^2.0.4","@eslint/js":"^9.39.1","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.10.1","typescript-eslint":"^8.47.0","eslint-config-prettier":"^10.1.8","jest-environment-jsdom":"^30.2.0","@stylistic/eslint-plugin":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/typed-storage_1.0.3_1763566408870_0.6900445833365527","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@alema/typed-storage","version":"1.0.4","keywords":["typescript","storage","typed","localstorage","mmkv","react-native","key-value","schema","codec"],"author":{"name":"Alessandro Marra","email":"alessandromarra001@gmail.com"},"license":"MIT","_id":"@alema/typed-storage@1.0.4","maintainers":[{"name":"alexma01","email":"alessandromarra001@gmail.com"}],"homepage":"https://github.com/alexma01/ts-typed-storage#readme","bugs":{"url":"https://github.com/alexma01/ts-typed-storage/issues"},"dist":{"shasum":"cd416b6c98a2284dcc00f5555c2ceca9d50f8001","tarball":"https://registry.npmjs.org/@alema/typed-storage/-/typed-storage-1.0.4.tgz","fileCount":19,"integrity":"sha512-SCknGdLn6jbzCW/PhqnHm3ssevyWutqd5Qe31vGHxwRwZyRQTj6jF2/tNXNvtm0lRLlJ78BlYs5lF/TxA2deSQ==","signatures":[{"sig":"MEUCIBFzCyMixJtV/NITbFWku8ISDMqA+r1N51+rj9rsOGi9AiEAr7z5qsxWkN+mHUO537R4KLm1afvQUznkcVKxiorgLqw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24404},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","gitHead":"54c744a7fd82f6a42dcd7688f52b86988e5e324b","scripts":{"lint":"eslint .","test":"jest","build":"tsc","prepare":"node scripts/prepare.cjs","lint:fix":"eslint . --fix","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexma01","email":"alessandromarra001@gmail.com"},"repository":{"url":"git+https://github.com/alexma01/ts-typed-storage.git","type":"git"},"_npmVersion":"10.9.0","description":"A TypeScript library for typed key-value storage with support for various codecs and storage adapters.","directories":{},"_nodeVersion":"22.12.0","_hasShrinkwrap":false,"packageManager":"yarn@4.11.0","devDependencies":{"jest":"^30.2.0","eslint":"^9.39.1","ts-jest":"^29.4.5","ts-node":"^10.9.2","lefthook":"^2.0.4","@eslint/js":"^9.39.1","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.10.1","typescript-eslint":"^8.47.0","eslint-config-prettier":"^10.1.8","jest-environment-jsdom":"^30.2.0","@stylistic/eslint-plugin":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/typed-storage_1.0.4_1763675266374_0.01411789010771236","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@alema/typed-storage","version":"1.0.5","keywords":["typescript","storage","typed","localstorage","mmkv","react-native","key-value","schema","codec"],"main":"dist/index.js","module":"dist/index.js","types":"dist/index.d.ts","license":"MIT","scripts":{"prepare":"node scripts/prepare.cjs","lint":"eslint .","typecheck":"tsc --noEmit","lint:fix":"eslint . --fix","test":"jest","build":"tsc"},"author":{"name":"Alessandro Marra","email":"alessandromarra001@gmail.com"},"description":"A TypeScript library for typed key-value storage with support for various codecs and storage adapters.","repository":{"url":"git+https://github.com/alexma01/ts-typed-storage.git","type":"git"},"devDependencies":{"@eslint/js":"^9.39.1","@stylistic/eslint-plugin":"^5.6.0","@types/jest":"^30.0.0","@types/node":"^24.10.1","eslint":"^9.39.1","eslint-config-prettier":"^10.1.8","jest":"^30.2.0","jest-environment-jsdom":"^30.2.0","lefthook":"^2.0.4","ts-jest":"^29.4.5","ts-node":"^10.9.2","typescript":"^5.9.3","typescript-eslint":"^8.47.0"},"packageManager":"yarn@4.11.0","_id":"@alema/typed-storage@1.0.5","gitHead":"2d9538bba38dfe249cde13217efa1c5a9f654de9","bugs":{"url":"https://github.com/alexma01/ts-typed-storage/issues"},"homepage":"https://github.com/alexma01/ts-typed-storage#readme","_nodeVersion":"22.12.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-xKTCxm7Xs68EMRXpt6XwSnnQU8SClW0RbvguyqG4SPP2chKz/uf64wusYohZPJ7qDtN3hTGSw9h43rlOwRLx1w==","shasum":"9cd4528b56c19cc1938111630c31f8ee594b9e05","tarball":"https://registry.npmjs.org/@alema/typed-storage/-/typed-storage-1.0.5.tgz","fileCount":19,"unpackedSize":24523,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDRMQ9Gr9bchoNl3rILUs6FNmEzt7ZORTfM/GsX3YiV/AIhALi1vuranTGRDl66Q9Snm4LjLEzOM3BtFnnXrZ2hsN8t"}]},"_npmUser":{"name":"alexma01","email":"alessandromarra001@gmail.com"},"directories":{},"maintainers":[{"name":"alexma01","email":"alessandromarra001@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/typed-storage_1.0.5_1763675965511_0.31672390070814616"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-19T13:32:51.109Z","modified":"2025-11-20T21:59:25.923Z","1.0.0":"2025-11-19T10:52:52.015Z","1.0.1":"2025-11-19T13:32:51.396Z","1.0.2":"2025-11-19T13:54:11.296Z","1.0.3":"2025-11-19T15:33:29.098Z","1.0.4":"2025-11-20T21:47:46.577Z","1.0.5":"2025-11-20T21:59:25.691Z"},"bugs":{"url":"https://github.com/alexma01/ts-typed-storage/issues"},"author":{"name":"Alessandro Marra","email":"alessandromarra001@gmail.com"},"license":"MIT","homepage":"https://github.com/alexma01/ts-typed-storage#readme","keywords":["typescript","storage","typed","localstorage","mmkv","react-native","key-value","schema","codec"],"repository":{"url":"git+https://github.com/alexma01/ts-typed-storage.git","type":"git"},"description":"A TypeScript library for typed key-value storage with support for various codecs and storage adapters.","maintainers":[{"name":"alexma01","email":"alessandromarra001@gmail.com"}],"readme":"# @alema/typed-storage\n\nType-safe key–value storage on top of any **synchronous** storage engine  \n(e.g. `localStorage`, MMKV, in-memory maps), powered by **TypeScript + codecs**.\n\n- ✅ Strongly typed keys (editor autocomplete)\n- ✅ Strongly typed values per key\n- ✅ Simple API for defining schemas and using storage\n- ✅ Optional listeners for reacting to storage changes\n- ✅ Works with any sync storage backend via a tiny adapter\n\n---\n\n## Installation\n\n```bash\n# npm\nnpm install @alema/typed-storage\n\n# yarn\nyarn add @alema/typed-storage\n\n# pnpm\npnpm add @alema/typed-storage\n```\n\nRequires **TypeScript 5+**.\n\n---\n\n## Quick start\n\n### 1. Define your schema\n\nUse `defineStorageSchema` and `field` with the provided codecs:\n\n```ts\nimport {\n  defineStorageSchema,\n  field,\n  stringCodec,\n  numberCodec,\n  booleanCodec,\n  jsonCodec,\n} from \"@alema/typed-storage\";\n\nexport const appStorageSchema = defineStorageSchema({\n  userToken: field(stringCodec),\n  count: field(numberCodec),\n  userData: field(jsonCodec<{ name: string; age: number }>()),\n  isAuthenticated: field(booleanCodec),\n});\n\nexport type AppStorageSchema = typeof appStorageSchema;\n```\n\n- Keys are strongly typed (`\"userToken\" | \"count\" | \"userData\" | \"isAuthenticated\"`).\n- Values are correctly inferred (string, number, object, boolean).\n\n---\n\n### 2. Implement a storage adapter (example: `localStorage`)\n\nYou just need to match the `SyncStorageAdapter` shape.\n\n#### Basic adapter (without listeners)\n\n```ts\nimport type { SyncStorageAdapter } from \"@alema/typed-storage\";\n\nexport function createLocalStorageAdapter(): SyncStorageAdapter {\n  return {\n    getItem(key) {\n      if (typeof localStorage === \"undefined\") return null;\n      return localStorage.getItem(key);\n    },\n    setItem(key, value) {\n      if (typeof localStorage === \"undefined\") return;\n      localStorage.setItem(key, value);\n    },\n    deleteItem(key) {\n      if (typeof localStorage === \"undefined\") return;\n      localStorage.removeItem(key);\n    },\n    getAllKeys() {\n      if (typeof localStorage === \"undefined\") return [];\n      const keys: string[] = [];\n      for (let i = 0; i < localStorage.length; i++) {\n        const k = localStorage.key(i);\n        if (k != null) keys.push(k);\n      }\n      return keys;\n    },\n  };\n}\n```\n\nYou can build adapters for:\n\n- `localStorage` (web)\n- MMKV (React Native)\n- in-memory storage (for tests)\n- any other sync key–value store\n\n---\n\n### 3. Optional: adapter with listeners\n\nIf your adapter implements `addListener`,  \nthe typed storage will expose it as `storage.addListener`, which returns an unsubscribe function.\n\nExample `localStorage` adapter with in-memory listeners:\n\n```ts\nimport type { SyncStorageAdapter } from \"@alema/typed-storage\";\n\nexport function createLocalStorageAdapterWithListeners(): SyncStorageAdapter {\n  const listeners = new Map<string, Set<(newValue: string | null) => void>>();\n\n  const notify = (key: string, newValue: string | null) => {\n    const cbs = listeners.get(key);\n    if (!cbs) return;\n    for (const cb of cbs) {\n      cb(newValue);\n    }\n  };\n\n  return {\n    getItem(key) {\n      if (typeof localStorage === \"undefined\") return null;\n      return localStorage.getItem(key);\n    },\n    setItem(key, value) {\n      if (typeof localStorage === \"undefined\") return;\n      localStorage.setItem(key, value);\n      notify(key, value);\n    },\n    deleteItem(key) {\n      if (typeof localStorage === \"undefined\") return;\n      localStorage.removeItem(key);\n      notify(key, null);\n    },\n    getAllKeys() {\n      if (typeof localStorage === \"undefined\") return [];\n      const keys: string[] = [];\n      for (let i = 0; i < localStorage.length; i++) {\n        const k = localStorage.key(i);\n        if (k != null) keys.push(k);\n      }\n      return keys;\n    },\n    addListener(key, callback) {\n      let set = listeners.get(key);\n      if (!set) {\n        set = new Set();\n        listeners.set(key, set);\n      }\n      set.add(callback);\n\n      // unsubscribe function\n      return () => {\n        const current = listeners.get(key);\n        if (!current) return;\n        current.delete(callback);\n        if (current.size === 0) {\n          listeners.delete(key);\n        }\n      };\n    },\n  };\n}\n```\n\n---\n\n### 4. Create the typed storage instance\n\n```ts\nimport { createSyncTypedStorage } from \"@alema/typed-storage\";\nimport { appStorageSchema } from \"./schema\";\nimport { createLocalStorageAdapter } from \"./localStorageAdapter\";\n\nconst adapter = createLocalStorageAdapter();\n\nexport const appStorage = createSyncTypedStorage({\n  adapter,\n  schema: appStorageSchema,\n  namespace: \"app\", // optional prefix for physical keys\n});\n```\n\n\n```ts\nimport { createLocalStorageAdapterWithListeners } from \"./localStorageAdapterWithListeners\";\n\nconst adapter = createLocalStorageAdapterWithListeners();\n```\n\n---\n\n### 5. Use it in your app\n\n```ts\nimport { appStorage } from \"./storage\";\n\n// Write\nappStorage.set(\"userToken\", \"abc123\");\nappStorage.set(\"count\", 42);\nappStorage.set(\"userData\", { name: \"Alex\", age: 30 });\nappStorage.set(\"isAuthenticated\", true);\n\n// Read (fully typed)\nconst token = appStorage.get(\"userToken\");         // string\nconst count = appStorage.get(\"count\");             // number\nconst userData = appStorage.get(\"userData\");       // { name: string; age: number }\nconst isAuth = appStorage.get(\"isAuthenticated\");  // boolean\n\n// List keys (typed union)\nconst keys = appStorage.keys();\n// type: (\"userToken\" | \"count\" | \"userData\" | \"isAuthenticated\")[]\n```\n\nTypeScript will prevent invalid keys or wrong types:\n\n```ts\n// appStorage.set(\"unknownKey\", \"foo\");      // ❌ compile error (unknown key)\n// appStorage.set(\"count\", \"not a number\");  // ❌ compile error (wrong type)\n```\n\nAt runtime, unknown keys also throw an error.\n\n---\n\n### 6. Listen to storage changes (optional)\n\nIf your adapter implements listeners, you can react to changes on a specific key:\n\n```ts\n// Value type is inferred from the schema\nconst callback = (newValue: string | null) => {\n  console.log(\"userToken changed:\", newValue);\n};\n\nconst unsubscribe = appStorage.addListener(\"userToken\", callback);\n\nappStorage.set(\"userToken\", \"new-token\");\n// callback called with \"new-token\"\n\n// Later, when you don't need updates anymore\nunsubscribe();\n\nappStorage.set(\"userToken\", \"another-token\");\n// callback is NOT called anymore\n```\n\n> addListener is optional because not all adapters must implement it. If the adapter does not support listeners, addListener should return a no-op unsubscribe function.\n\n---\n\n## Public API\n\n### `defineStorageSchema(schema)`\n\nDefines a typed schema for your storage.\n\n```ts\nimport {\n  defineStorageSchema,\n  field,\n  stringCodec,\n  booleanCodec,\n} from \"@alema/typed-storage\";\n\nconst schema = defineStorageSchema({\n  userToken: field(stringCodec),\n  isAuthenticated: field(booleanCodec),\n});\n\ntype Schema = typeof schema;\n```\n\nUse this to get typed keys and values across your app.\n\n---\n\n### `field(codec)`\n\nCreates a field in your schema from a codec.\n\n```ts\nimport { field, stringCodec, numberCodec } from \"@alema/typed-storage\";\n\nconst schema = defineStorageSchema({\n  userToken: field(stringCodec),\n  count: field(numberCodec),\n});\n```\n\n---\n\n### Built-in codecs\n\n```ts\nimport {\n  stringCodec,\n  booleanCodec,\n  numberCodec,\n  jsonCodec,\n} from \"@alema/typed-storage\";\n```\n\n- `stringCodec` – for strings\n- `booleanCodec` – for booleans\n- `numberCodec` – for numbers\n- `jsonCodec<T>()` – for arbitrary JSON-serializable types\n\nExample:\n\n```ts\nconst schema = defineStorageSchema({\n  userPreferences: field(\n    jsonCodec<{ theme: \"light\" | \"dark\"; notifications: boolean }>(),\n  ),\n});\n```\n\n---\n\n### `createSyncTypedStorage({ adapter, schema, namespace? })`\n\nCreates the main storage client.\n\n```ts\nimport { createSyncTypedStorage } from \"@alema/typed-storage\";\n\nconst storage = createSyncTypedStorage({\n  adapter,       // your SyncStorageAdapter\n  schema,        // created via defineStorageSchema\n  namespace: \"app\", // optional\n});\n```\n\nThe returned object has:\n\n```ts\nstorage.get(key);\nstorage.set(key, value);\nstorage.remove(key);\nstorage.keys();\nstorage.clearAll();\nconst unsubscribe = storage.addListener?(key, callback);\n// later\nunsubscribe?.();\n```\n\nAll fully typed from the `schema`.\n\n---\n\n### `SyncStorageAdapter` (type)\n\nIf you want to build your own adapter:\n\n```ts\nimport type { SyncStorageAdapter } from \"@alema/typed-storage\";\n\nconst myAdapter: SyncStorageAdapter = {\n  getItem(key) { /* ... */ },\n  setItem(key, value) { /* ... */ },\n  deleteItem(key) { /* ... */ },\n  getAllKeys() { /* ... */ },\n\n  // optional: return an unsubscribe function\n  addListener?(key, callback) {\n    /* ... */\n    return () => {\n      /* cleanup */\n    };\n  },\n};\n```\n\n- `addListener` is **optional**.\n- If implemented, the typed storage layer will expose `storage.addListener`.\n\n---\n\n## Testing example (in-memory adapter)\n\nHandy for unit tests:\n\n```ts\nimport type { SyncStorageAdapter } from \"@alema/typed-storage\";\n\nexport function createInMemoryAdapter(): SyncStorageAdapter {\n  const store = new Map<string, string>();\n\n  return {\n    getItem(key) {\n      return store.get(key) ?? null;\n    },\n    setItem(key, value) {\n      store.set(key, value);\n    },\n    deleteItem(key) {\n      store.delete(key);\n    },\n    getAllKeys() {\n      return Array.from(store.keys());\n    },\n  };\n}\n```\n\nUsage in tests:\n\n```ts\nimport { createSyncTypedStorage } from \"@alema/typed-storage\";\nimport { createInMemoryAdapter } from \"./inMemoryAdapter\";\nimport { appStorageSchema } from \"./schema\";\n\nconst storage = createSyncTypedStorage({\n  adapter: createInMemoryAdapter(),\n  schema: appStorageSchema,\n});\n\nstorage.set(\"userToken\", \"test\");\nexpect(storage.get(\"userToken\")).toBe(\"test\");\n```\n\n---\n\n## License\n\nMIT – see `LICENSE` file for details.\n","readmeFilename":"README.md"}