+19
-2
@@ -397,3 +397,3 @@ "use strict"; | ||
| * Get the current TTL. | ||
| * @returns {number} The current TTL. | ||
| * @returns {number} The current TTL in milliseconds. | ||
| */ | ||
@@ -405,3 +405,3 @@ get ttl() { | ||
| * Set the current TTL. | ||
| * @param {number} ttl The TTL to set. | ||
| * @param {number} ttl The TTL to set in milliseconds. | ||
| */ | ||
@@ -545,2 +545,6 @@ set ttl(ttl) { | ||
| if (deserializedData === void 0 || deserializedData === null) { | ||
| this.hooks.trigger("postGet" /* POST_GET */, { | ||
| key: keyPrefixed, | ||
| value: void 0 | ||
| }); | ||
| this.stats.miss(); | ||
@@ -551,2 +555,6 @@ return void 0; | ||
| await this.delete(key); | ||
| this.hooks.trigger("postGet" /* POST_GET */, { | ||
| key: keyPrefixed, | ||
| value: void 0 | ||
| }); | ||
| this.stats.miss(); | ||
@@ -631,2 +639,6 @@ return void 0; | ||
| if (rawData === void 0 || rawData === null) { | ||
| this.hooks.trigger("postGetRaw" /* POST_GET_RAW */, { | ||
| key: keyPrefixed, | ||
| value: void 0 | ||
| }); | ||
| this.stats.miss(); | ||
@@ -638,2 +650,6 @@ return void 0; | ||
| deserializedData.expires < Date.now()) { | ||
| this.hooks.trigger("postGetRaw" /* POST_GET_RAW */, { | ||
| key: keyPrefixed, | ||
| value: void 0 | ||
| }); | ||
| this.stats.miss(); | ||
@@ -977,1 +993,2 @@ await this.delete(key); | ||
| }); | ||
| /* v8 ignore next -- @preserve */ |
+3
-3
@@ -136,3 +136,3 @@ type EventListener = (...arguments_: any[]) => void; | ||
| /** | ||
| * Default TTL. Can be overridden by specifying a TTL on `.set()`. | ||
| * Default TTL in milliseconds. Can be overridden by specifying a TTL on `.set()`. | ||
| * @default undefined | ||
@@ -230,3 +230,3 @@ */ | ||
| * Get the current TTL. | ||
| * @returns {number} The current TTL. | ||
| * @returns {number} The current TTL in milliseconds. | ||
| */ | ||
@@ -236,3 +236,3 @@ get ttl(): number | undefined; | ||
| * Set the current TTL. | ||
| * @param {number} ttl The TTL to set. | ||
| * @param {number} ttl The TTL to set in milliseconds. | ||
| */ | ||
@@ -239,0 +239,0 @@ set ttl(ttl: number | undefined); |
+3
-3
@@ -136,3 +136,3 @@ type EventListener = (...arguments_: any[]) => void; | ||
| /** | ||
| * Default TTL. Can be overridden by specifying a TTL on `.set()`. | ||
| * Default TTL in milliseconds. Can be overridden by specifying a TTL on `.set()`. | ||
| * @default undefined | ||
@@ -230,3 +230,3 @@ */ | ||
| * Get the current TTL. | ||
| * @returns {number} The current TTL. | ||
| * @returns {number} The current TTL in milliseconds. | ||
| */ | ||
@@ -236,3 +236,3 @@ get ttl(): number | undefined; | ||
| * Set the current TTL. | ||
| * @param {number} ttl The TTL to set. | ||
| * @param {number} ttl The TTL to set in milliseconds. | ||
| */ | ||
@@ -239,0 +239,0 @@ set ttl(ttl: number | undefined); |
+19
-2
@@ -371,3 +371,3 @@ // src/index.ts | ||
| * Get the current TTL. | ||
| * @returns {number} The current TTL. | ||
| * @returns {number} The current TTL in milliseconds. | ||
| */ | ||
@@ -379,3 +379,3 @@ get ttl() { | ||
| * Set the current TTL. | ||
| * @param {number} ttl The TTL to set. | ||
| * @param {number} ttl The TTL to set in milliseconds. | ||
| */ | ||
@@ -519,2 +519,6 @@ set ttl(ttl) { | ||
| if (deserializedData === void 0 || deserializedData === null) { | ||
| this.hooks.trigger("postGet" /* POST_GET */, { | ||
| key: keyPrefixed, | ||
| value: void 0 | ||
| }); | ||
| this.stats.miss(); | ||
@@ -525,2 +529,6 @@ return void 0; | ||
| await this.delete(key); | ||
| this.hooks.trigger("postGet" /* POST_GET */, { | ||
| key: keyPrefixed, | ||
| value: void 0 | ||
| }); | ||
| this.stats.miss(); | ||
@@ -605,2 +613,6 @@ return void 0; | ||
| if (rawData === void 0 || rawData === null) { | ||
| this.hooks.trigger("postGetRaw" /* POST_GET_RAW */, { | ||
| key: keyPrefixed, | ||
| value: void 0 | ||
| }); | ||
| this.stats.miss(); | ||
@@ -612,2 +624,6 @@ return void 0; | ||
| deserializedData.expires < Date.now()) { | ||
| this.hooks.trigger("postGetRaw" /* POST_GET_RAW */, { | ||
| key: keyPrefixed, | ||
| value: void 0 | ||
| }); | ||
| this.stats.miss(); | ||
@@ -951,1 +967,2 @@ await this.delete(key); | ||
| }; | ||
| /* v8 ignore next -- @preserve */ |
+11
-11
| { | ||
| "name": "keyv", | ||
| "version": "5.5.3", | ||
| "version": "5.5.4", | ||
| "description": "Simple key-value storage with support for multiple backends", | ||
@@ -50,16 +50,16 @@ "type": "module", | ||
| "devDependencies": { | ||
| "@biomejs/biome": "^2.2.3", | ||
| "@faker-js/faker": "^10.0.0", | ||
| "@vitest/coverage-v8": "^3.2.4", | ||
| "rimraf": "^6.0.1", | ||
| "@biomejs/biome": "^2.3.3", | ||
| "@faker-js/faker": "^10.1.0", | ||
| "@vitest/coverage-v8": "^4.0.6", | ||
| "rimraf": "^6.1.0", | ||
| "timekeeper": "^2.3.1", | ||
| "tsd": "^0.33.0", | ||
| "vitest": "^3.2.4", | ||
| "@keyv/mongo": "^3.0.3", | ||
| "vitest": "^4.0.6", | ||
| "@keyv/compress-brotli": "^2.0.5", | ||
| "@keyv/compress-lz4": "^1.0.1", | ||
| "@keyv/compress-gzip": "^2.0.3", | ||
| "@keyv/compress-lz4": "^1.0.1", | ||
| "@keyv/memcache": "^2.0.2", | ||
| "@keyv/sqlite": "^4.0.5", | ||
| "@keyv/compress-brotli": "^2.0.5", | ||
| "@keyv/test-suite": "^2.1.1" | ||
| "@keyv/sqlite": "^4.0.6", | ||
| "@keyv/test-suite": "^2.1.1", | ||
| "@keyv/mongo": "^3.0.5" | ||
| }, | ||
@@ -66,0 +66,0 @@ "tsd": { |
+107
-7
@@ -37,2 +37,3 @@ <h1 align="center"><img width="250" src="https://jaredwray.com/images/keyv.svg" alt="keyv"></h1> | ||
| - [Third-party Storage Adapters](#third-party-storage-adapters) | ||
| - [Using BigMap to Scale](#using-bigmap-to-scale) | ||
| - [Compression](#compression) | ||
@@ -212,4 +213,8 @@ - [API](#api) | ||
| POST_GET | ||
| PRE_GET_RAW | ||
| POST_GET_RAW | ||
| PRE_GET_MANY | ||
| POST_GET_MANY | ||
| PRE_GET_MANY_RAW | ||
| POST_GET_MANY_RAW | ||
| PRE_SET | ||
@@ -227,3 +232,34 @@ POST_SET | ||
| ## Get Hooks | ||
| The `POST_GET` and `POST_GET_RAW` hooks fire on both cache hits and misses. When a cache miss occurs (key doesn't exist or is expired), the hooks receive `undefined` as the value. | ||
| ```js | ||
| // POST_GET hook - fires on both hits and misses | ||
| const keyv = new Keyv(); | ||
| keyv.hooks.addHandler(KeyvHooks.POST_GET, (data) => { | ||
| if (data.value === undefined) { | ||
| console.log(`Cache miss for key: ${data.key}`); | ||
| } else { | ||
| console.log(`Cache hit for key: ${data.key}`, data.value); | ||
| } | ||
| }); | ||
| await keyv.get('existing-key'); // Logs cache hit with value | ||
| await keyv.get('missing-key'); // Logs cache miss with undefined | ||
| ``` | ||
| ```js | ||
| // POST_GET_RAW hook - same behavior as POST_GET | ||
| const keyv = new Keyv(); | ||
| keyv.hooks.addHandler(KeyvHooks.POST_GET_RAW, (data) => { | ||
| console.log(`Key: ${data.key}, Value:`, data.value); | ||
| }); | ||
| await keyv.getRaw('foo'); // Logs with value or undefined | ||
| ``` | ||
| ## Set Hooks | ||
| ```js | ||
| //PRE_SET hook | ||
@@ -251,2 +287,4 @@ const keyv = new Keyv(); | ||
| ## Delete Hooks | ||
| In `PRE_DELETE` and `POST_DELETE` hooks, the value could be a single item or an `Array`. This is based on the fact that `delete` can accept a single key or an `Array` of keys. | ||
@@ -320,13 +358,75 @@ | ||
| - [quick-lru](https://github.com/sindresorhus/quick-lru) - Simple "Least Recently Used" (LRU) cache | ||
| - [@resolid/keyv-sqlite](https://github.com/huijiewei/keyv-sqlite) - A new SQLite storage adapter for Keyv | ||
| - [keyv-arango](https://github.com/TimMikeladze/keyv-arango) - ArangoDB storage adapter for Keyv | ||
| - [keyv-azuretable](https://github.com/howlowck/keyv-azuretable) - Azure Table Storage/API adapter for Keyv | ||
| - [keyv-browser](https://github.com/zaaack/keyv-browser) - Browser storage adapter for Keyv, including localStorage and indexedDB. | ||
| - [keyv-cloudflare](https://npm.im/keyv-cloudflare) - Storage adapter for Cloudflare Workers KV | ||
| - [keyv-dynamodb](https://www.npmjs.com/package/keyv-dynamodb) - DynamoDB storage adapter for Keyv | ||
| - [keyv-file](https://github.com/zaaack/keyv-file) - File system storage adapter for Keyv | ||
| - [keyv-firestore ](https://github.com/goto-bus-stop/keyv-firestore) – Firebase Cloud Firestore adapter for Keyv | ||
| - [keyv-lru](https://www.npmjs.com/package/keyv-lru) - LRU storage adapter for Keyv | ||
| - [keyv-momento](https://github.com/momentohq/node-keyv-adaptor/) - Momento storage adapter for Keyv | ||
| - [keyv-mssql](https://github.com/pmorgan3/keyv-mssql) - Microsoft Sql Server adapter for Keyv | ||
| - [keyv-null](https://www.npmjs.com/package/keyv-null) - Null storage adapter for Keyv | ||
| - [keyv-firestore ](https://github.com/goto-bus-stop/keyv-firestore) – Firebase Cloud Firestore adapter for Keyv | ||
| - [keyv-mssql](https://github.com/pmorgan3/keyv-mssql) - Microsoft Sql Server adapter for Keyv | ||
| - [keyv-azuretable](https://github.com/howlowck/keyv-azuretable) - Azure Table Storage/API adapter for Keyv | ||
| - [keyv-arango](https://github.com/TimMikeladze/keyv-arango) - ArangoDB storage adapter for Keyv | ||
| - [keyv-momento](https://github.com/momentohq/node-keyv-adaptor/) - Momento storage adapter for Keyv | ||
| - [@resolid/keyv-sqlite](https://github.com/huijiewei/keyv-sqlite) - A new SQLite storage adapter for Keyv | ||
| - [keyv-upstash](https://github.com/mahdavipanah/keyv-upstash) - Upstash Redis adapter for Keyv | ||
| - [quick-lru](https://github.com/sindresorhus/quick-lru) - Simple "Least Recently Used" (LRU) cache | ||
| # Using BigMap to Scale | ||
| ## Understanding JavaScript Map Limitations | ||
| JavaScript's built-in `Map` object has a practical limit of approximately **16.7 million entries** (2^24). When you try to store more entries than this limit, you'll encounter performance degradation or runtime errors. This limitation is due to how JavaScript engines internally manage Map objects. | ||
| For applications that need to cache millions of entries in memory, this becomes a significant constraint. Common scenarios include: | ||
| - High-traffic caching layers | ||
| - Session stores for large-scale applications | ||
| - In-memory data processing of large datasets | ||
| - Real-time analytics with millions of data points | ||
| ## Why BigMap? | ||
| `@keyv/bigmap` solves this limitation by using a **distributed hash approach** with multiple internal Map instances. Instead of storing all entries in a single Map, BigMap distributes entries across multiple Maps using a hash function. This allows you to scale beyond the 16.7 million entry limit while maintaining the familiar Map API. | ||
| ### Key Benefits: | ||
| - **Scales beyond Map limits**: Store 20+ million entries without issues | ||
| - **Map-compatible API**: Drop-in replacement for standard Map | ||
| - **Performance**: Uses efficient DJB2 hashing for fast key distribution | ||
| - **Type-safe**: Built with TypeScript and supports generics | ||
| - **Customizable**: Configure store size and hash functions | ||
| ## Using BigMap with Keyv | ||
| BigMap can be used directly with Keyv as a storage adapter, providing scalable in-memory storage with full TTL support. | ||
| ### Installation | ||
| ```bash | ||
| npm install --save keyv @keyv/bigmap | ||
| ``` | ||
| ### Basic Usage | ||
| The simplest way to use BigMap with Keyv is through the `createKeyv` helper function: | ||
| ```js | ||
| import { createKeyv } from '@keyv/bigmap'; | ||
| const keyv = createKeyv(); | ||
| // Set values with TTL (time in milliseconds) | ||
| await keyv.set('user:1', { name: 'Alice', email: 'alice@example.com' }, 60000); // Expires in 60 seconds | ||
| // Get values | ||
| const user = await keyv.get('user:1'); | ||
| console.log(user); // { name: 'Alice', email: 'alice@example.com' } | ||
| // Delete values | ||
| await keyv.delete('user:1'); | ||
| // Clear all values | ||
| await keyv.clear(); | ||
| ``` | ||
| For more details about BigMap, see the [@keyv/bigmap documentation](https://github.com/jaredwray/keyv/tree/main/packages/bigmap). | ||
| # Compression | ||
@@ -333,0 +433,0 @@ |
117654
4.32%2313
1.49%836
13.59%