polystore 0.21.3 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,41 +1,23 @@
1
1
  {
2
2
  "name": "polystore",
3
- "version": "0.21.3",
3
+ "version": "0.23.0",
4
4
  "description": "A small compatibility layer for many popular KV stores like localStorage, Redis, FileSystem, etc.",
5
5
  "homepage": "https://polystore.dev",
6
- "repository": "https://github.com/franciscop/polystore.git",
7
- "bugs": "https://github.com/franciscop/polystore/issues",
8
- "funding": "https://www.paypal.me/franciscopresencia/19",
6
+ "repository": "github:franciscop/polystore",
9
7
  "author": "Francisco Presencia <public@francisco.io> (https://francisco.io/)",
10
- "type": "module",
11
- "sideEffects": false,
12
- "main": "index.js",
13
- "types": "index.d.ts",
14
- "exports": {
15
- ".": {
16
- "types": "./index.d.ts",
17
- "import": "./index.js"
18
- },
19
- "./express": {
20
- "types": "./src/integrations/express.d.ts",
21
- "import": "./src/integrations/express.js"
22
- },
23
- "./hono-sessions": {
24
- "types": "./src/integrations/hono-sessions.d.ts",
25
- "import": "./src/integrations/hono-sessions.js"
26
- }
27
- },
28
- "files": [
29
- "index.js",
30
- "index.d.ts",
31
- "src/integrations/express.js",
32
- "src/integrations/express.d.ts",
33
- "src/integrations/hono-sessions.js",
34
- "src/integrations/hono-sessions.d.ts"
8
+ "funding": "https://www.paypal.me/franciscopresencia/19",
9
+ "license": "MIT",
10
+ "keywords": [
11
+ "kv",
12
+ "store",
13
+ "polystore",
14
+ "key-value",
15
+ "key",
16
+ "value"
35
17
  ],
36
18
  "scripts": {
37
- "analyze": "bun run build && esbuild src/index.ts --bundle --packages=external --format=esm --minify --outfile=index.min.js && echo 'Final size:' && gzip-size index.min.js && rm index.min.js",
38
- "build": "bunx tsup src/index.ts --format esm --dts --out-dir . --target node24 && bunx tsup src/integrations/express.ts src/integrations/hono-sessions.ts --format esm --dts --out-dir src/integrations --target node24 --external polystore --external express-session --external hono-sessions",
19
+ "size": "echo $(gzip -c index.js | wc -c) bytes",
20
+ "build": "bunx tsup src/index.ts --format esm --dts --out-dir . --target node24 && bunx tsup src/plugins/express/index.ts --format esm --dts --out-dir src/plugins/express --target node24 --external polystore --external express-session && bunx tsup src/plugins/hono-sessions/index.ts --format esm --dts --out-dir src/plugins/hono-sessions --target node24 --external polystore --external hono-sessions && bunx tsup src/plugins/better-auth/index.ts --format esm --dts --out-dir src/plugins/better-auth --target node24 --external polystore && bunx tsup src/plugins/axios-cache-interceptor/index.ts --format esm --dts --out-dir src/plugins/axios-cache-interceptor --target node24 --external polystore --external axios-cache-interceptor",
39
21
  "lint": "tsc",
40
22
  "start": "bun test --watch",
41
23
  "service:db": "etcd",
@@ -44,28 +26,73 @@
44
26
  "service:server": "bun ./src/server.ts",
45
27
  "services": "concurrently \"npm run service:db\" \"npm run service:redis\" \"npm run service:postgres\" \"npm run service:server\"",
46
28
  "test": "bun run test:bun && bun run test:jest",
47
- "test:bun": "bun test ./test/index.test.ts ./src/integrations/",
29
+ "test:bun": "bun test ./test/index.test.ts ./src/plugins/",
48
30
  "test:jest": "jest ./test/index.test.ts --detectOpenHandles --forceExit"
49
31
  },
50
- "keywords": [
51
- "kv",
52
- "store",
53
- "polystore",
54
- "key-value",
55
- "key",
56
- "value"
32
+ "workspaces": [
33
+ "examples/*"
57
34
  ],
58
- "license": "MIT",
35
+ "main": "index.js",
36
+ "type": "module",
37
+ "types": "index.d.ts",
38
+ "files": [
39
+ "index.js",
40
+ "index.d.ts",
41
+ "src/plugins/express/index.js",
42
+ "src/plugins/express/index.d.ts",
43
+ "src/plugins/hono-sessions/index.js",
44
+ "src/plugins/hono-sessions/index.d.ts",
45
+ "src/plugins/better-auth/index.js",
46
+ "src/plugins/better-auth/index.d.ts",
47
+ "src/plugins/axios-cache-interceptor/index.js",
48
+ "src/plugins/axios-cache-interceptor/index.d.ts"
49
+ ],
50
+ "exports": {
51
+ ".": {
52
+ "types": "./index.d.ts",
53
+ "import": "./index.js"
54
+ },
55
+ "./express": {
56
+ "types": "./src/plugins/express/index.d.ts",
57
+ "import": "./src/plugins/express/index.js"
58
+ },
59
+ "./hono-sessions": {
60
+ "types": "./src/plugins/hono-sessions/index.d.ts",
61
+ "import": "./src/plugins/hono-sessions/index.js"
62
+ },
63
+ "./better-auth": {
64
+ "types": "./src/plugins/better-auth/index.d.ts",
65
+ "import": "./src/plugins/better-auth/index.js"
66
+ },
67
+ "./axios-cache-interceptor": {
68
+ "types": "./src/plugins/axios-cache-interceptor/index.d.ts",
69
+ "import": "./src/plugins/axios-cache-interceptor/index.js"
70
+ }
71
+ },
72
+ "documentation.page": {
73
+ "title": "🏬 Polystore - A universal library for standardizing any KV-store",
74
+ "home": "assets/home.html",
75
+ "homepage": "https://polystore.dev/",
76
+ "menu": {
77
+ "Documentation": "/documentation",
78
+ "Issues": "https://github.com/franciscop/polystore/issues",
79
+ "Get help": "https://superpeer.com/francisco/-/javascript-and-react-help",
80
+ "Github": "https://github.com/franciscop/polystore"
81
+ }
82
+ },
83
+ "dependencies": {},
59
84
  "devDependencies": {
60
85
  "@deno/kv": "^0.8.1",
61
86
  "@types/better-sqlite3": "^7.6.13",
62
- "@types/bun": "^1.3.3",
87
+ "@types/bun": "^1.3.0",
63
88
  "@types/express": "^5.0.6",
64
89
  "@types/express-session": "^1.18.2",
65
90
  "@types/jest": "^30.0.0",
66
91
  "@types/jsdom": "^27.0.0",
67
92
  "@types/pg": "^8.11.10",
68
93
  "@types/supertest": "^7.2.0",
94
+ "axios": "^1.9.0",
95
+ "axios-cache-interceptor": "^1.12.0",
69
96
  "better-sqlite3": "^12.6.0",
70
97
  "concurrently": "^9.2.1",
71
98
  "dotenv": "^16.3.1",
@@ -86,17 +113,7 @@
86
113
  "supertest": "^7.2.2",
87
114
  "ts-jest": "^29.4.6",
88
115
  "tsup": "^8.5.1",
89
- "typescript": "^5.9.3"
116
+ "typescript": "^6.0.2"
90
117
  },
91
- "documentation": {
92
- "title": "🏬 Polystore - A universal library for standardizing any KV-store",
93
- "home": "assets/home.html",
94
- "homepage": "https://polystore.dev/",
95
- "menu": {
96
- "Documentation": "/documentation",
97
- "Issues": "https://github.com/franciscop/polystore/issues",
98
- "Get help": "https://superpeer.com/francisco/-/javascript-and-react-help",
99
- "Github": "https://github.com/franciscop/polystore"
100
- }
101
- }
118
+ "sideEffects": false
102
119
  }
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
- # Polystore [![npm install polystore](https://img.shields.io/badge/npm%20install-polystore-blue.svg)](https://www.npmjs.com/package/polystore) [![test badge](https://github.com/franciscop/polystore/workflows/tests/badge.svg "test badge")](https://github.com/franciscop/polystore/actions) [![gzip size](https://badgen.net/bundlephobia/minzip/polystore?label=gzip&color=green)](https://bundlephobia.com/package/polystore) [![dependencies](https://img.shields.io/badge/dependencies-0-limegreen.svg)](https://github.com/franciscop/polystore/blob/master/package.json)
1
+ # Polystore [![polystore](https://img.shields.io/npm/v/polystore?label=polystore&color=greenlime)](https://www.npmjs.com/package/polystore) [![tests](https://github.com/franciscop/polystore/workflows/tests/badge.svg)](https://github.com/franciscop/polystore/actions) [![gzip size](https://img.badgesize.io/franciscop/polystore/master/index.js.svg?label=gzip&logo=&compression=gzip)](https://github.com/franciscop/polystore/blob/master/index.js) [![dependencies](https://img.shields.io/badge/dependencies-0-limegreen.svg)](https://github.com/franciscop/polystore/blob/master/package.json)
2
2
 
3
- A key-value library to unify the API of [many clients](#clients): localStorage, Redis, FileSystem, SQLite, etc.
3
+ A key-value library to unify the API of [many adapters](#adapters): localStorage, Redis, FileSystem, SQLite, etc.
4
4
 
5
5
  ```js
6
6
  import kv from "polystore";
@@ -8,7 +8,7 @@ const store1 = kv(new Map()); // in-memory
8
8
  const store2 = kv(localStorage); // Persist in the browser
9
9
  const store3 = kv(redisClient); // Use a Redis client for backend persistence
10
10
  const store4 = kv(yourOwnStore); // Create a store based on your code
11
- // Many more clients available
11
+ // Many more adapters available
12
12
  ```
13
13
 
14
14
  These are all the methods of the [API](#api) (they are all `async`):
@@ -29,7 +29,7 @@ These are all the methods of the [API](#api) (they are all `async`):
29
29
  - [`.prune()`](#prune): delete only the **expired** data from the store.
30
30
  - [`.close()`](#close): (only _some_ stores) ends the connection to the store.
31
31
 
32
- Available clients for the KV store:
32
+ Available adapters for the KV store:
33
33
 
34
34
  - [**Memory** `new Map()`](#memory) (fe+be): an in-memory API to keep your KV store.
35
35
  - [**Local Storage** `localStorage`](#local-storage) (fe): persist the data in the browser's localStorage.
@@ -42,7 +42,7 @@ Available clients for the KV store:
42
42
  - [**File** `"file:///[...].json"`](#file) (be): store the data in a single JSON file in your FS.
43
43
  - [**Folder** `"file:///[...]/"`](#folder) (be): store each key in a folder as json files.
44
44
  - [**Cloudflare KV** `env.KV_NAMESPACE`](#cloudflare-kv) (be): use Cloudflare's KV store.
45
- - [**Postgres** `pool`](#postgres) (be): use PostgreSQL with the pg library.
45
+ - [**Postgres** `pg`](#postgres) (be): use PostgreSQL with the pg library.
46
46
  - [**Level** `new Level('example', { valueEncoding: 'json' })`](#level) (fe+be): support the whole Level ecosystem.
47
47
  - [**Etcd** `new Etcd3()`](#etcd) (be): the Microsoft's high performance KV store.
48
48
  - [**_Custom_** `{}`](#creating-a-store) (fe+be): create your own store with just 3 methods!
@@ -61,7 +61,7 @@ MyApi({ cache: env.KV_NAMESPACE }); // OR
61
61
 
62
62
  ## Getting started
63
63
 
64
- First, install `polystore` and whatever [supported client](#clients) that you prefer. Let's see Redis as an example here:
64
+ First, install `polystore` and whatever [supported adapter](#adapters) that you prefer. Let's see Redis as an example here:
65
65
 
66
66
  ```sh
67
67
  npm i polystore redis
@@ -93,7 +93,7 @@ await store.del(key);
93
93
 
94
94
  ## API
95
95
 
96
- The base `kv()` initialization is shared across clients ([see full clients list](#clients)); an argument that receives the client or a string representing the client and then the options:
96
+ The base `kv()` initialization is shared across adapters ([see full adapters list](#adapters)); an argument that receives the adapter or a string representing the adapter and then the options:
97
97
 
98
98
  ```js
99
99
  import kv from "polystore";
@@ -104,9 +104,6 @@ const store = kv(MyClientInstance, { expires: null, prefix: "" });
104
104
  // use the store
105
105
  ```
106
106
 
107
- > [!IMPORTANT]
108
- > The library delivers excellent performance for item-level operations (GET, SET, ADD, HAS, DEL). For other methods or detailed guidance, check the performance considerations and consult your specific client’s documentation.
109
-
110
107
  You can enforce **types** for the values either at store creation or at the method level:
111
108
 
112
109
  ```ts
@@ -129,14 +126,14 @@ store.set<number>("abc", "hello"); // FAILS
129
126
 
130
127
  Store values must be JSON-like data. The Serializable type represents values composed of `string`, `number`, `boolean`, `null`, and `arrays` and plain `objects` whose values are serializable. Class instances or non-plain objects will lose their prototypes and methods when stored.
131
128
 
132
- These are the exported types, `Client`, `Serializable`, `Store` and `Options`:
129
+ These are the exported types, `Adapter`, `Serializable`, `Store` and `Options`:
133
130
 
134
131
  ```ts
135
132
  import kv from "polystore";
136
- import type { Client, Serializable, Store, Options } from "polystore";
133
+ import type { Adapter, Serializable, Store, Options } from "polystore";
137
134
 
138
- const client: Client = ...; // See #creating-a-store
139
- const store: Store = kv(client, opts as Options);
135
+ const adapter: Adapter = ...; // See #creating-a-store
136
+ const store: Store = kv(adapter, opts as Options);
140
137
  const key = await store.set('hello', 'b', opts as Options)
141
138
  const value: Serializable = await store.get('hello');
142
139
  ```
@@ -308,9 +305,9 @@ const key3 = await store.add({ name: "Francisco" }, { expires: 60 * 60 });
308
305
 
309
306
  The value and options are similar to [`.set()`](#set), except for the lack of the first argument, since `.add()` automatically generates the key.
310
307
 
311
- The default key is 24 AlphaNumeric characters (upper+lower case), however this can change if you are using a `.prefix()` or some clients might generate it differently (only custom clients can do that right now).
308
+ The default key is 24 AlphaNumeric characters (upper+lower case), however this can change if you are using a `.prefix()` or some adapters might generate it differently (only custom adapters can do that right now).
312
309
 
313
- Some clients will generate their own key, e.g. you can connect to a SQL client that does auto-incremental integers (always casted to `string` since a `key` is always a string in Polystore).
310
+ Some adapters will generate their own key, e.g. you can connect to a SQL client that does auto-incremental integers (always casted to `string` since a `key` is always a string in Polystore).
314
311
 
315
312
  <details>
316
313
  <summary>Key Generation details</summary>
@@ -553,7 +550,7 @@ This operation is O(n) and should typically be run in a scheduled job or mainten
553
550
 
554
551
  ### .close()
555
552
 
556
- Close the connection (if any) from the client:
553
+ Close the connection (if any) from the adapter:
557
554
 
558
555
  ```js
559
556
  await store.close();
@@ -561,7 +558,7 @@ await store.close();
561
558
 
562
559
  ### .prefix()
563
560
 
564
- Creates **a new instance** of the Store, _with the same client_ as you provided, but now any key you read, write, etc. will be passed with the given prefix to the client. You only write `.prefix()` once and then don't need to worry about any prefix for any method anymore, it's all automatic. You don't need to await for it:
561
+ Creates **a new instance** of the Store, _with the same adapter_ as you provided, but now any key you read, write, etc. will be passed with the given prefix to the adapter. You only write `.prefix()` once and then don't need to worry about any prefix for any method anymore, it's all automatic. You don't need to await for it:
565
562
 
566
563
  ```js
567
564
  const store = kv(new Map());
@@ -586,7 +583,7 @@ for await (const [key, value] of session) {
586
583
  }
587
584
  ```
588
585
 
589
- Different clients have better/worse support for substores, and in some cases some operations might be slower. This should be documented on each client's documentation (see below). As an alternative, you can always create two different stores instead of a substore:
586
+ Different adapters have better/worse support for substores, and in some cases some operations might be slower. This should be documented on each adapter's documentation (see below). As an alternative, you can always create two different stores instead of a substore:
590
587
 
591
588
  ```js
592
589
  // Two in-memory stores
@@ -625,15 +622,15 @@ You can combine it with .prefix():
625
622
  const sessions = store.prefix("session:").expires("1day");
626
623
  ```
627
624
 
628
- ## Clients
625
+ ## Adapters
629
626
 
630
- A client is the library that manages the low-level store operations. For example, the Redis Client, or the browser's `localStorage` API. In some exceptions it's just a string and we do a bit more work on Polystore, like with `"cookie"` or `"file:///users/me/data.json"`.
627
+ An adapter is the library that manages the low-level store operations. For example, the Redis adapter, or the browser's `localStorage` API. In some exceptions it's just a string and we do a bit more work on Polystore, like with `"cookie"` or `"file:///users/me/data.json"`.
631
628
 
632
629
  Polystore provides a unified API you can use `Promises`, `expires` and `.prefix()` even with those stores that do not support these operations natively.
633
630
 
634
631
  Quick overview:
635
632
 
636
- | Client | Runtime | Persistence | Native expiration | Notes |
633
+ | Adapter | Runtime | Persistence | Native expiration | Notes |
637
634
  |---|---|---|---|---|
638
635
  | [Memory](#memory) | Node.js + Browser | ❌ | ❌ | Great for tests and ephemeral caches |
639
636
  | [Local Storage](#local-storage) | Browser | ✅ | ❌ | Persistent browser storage |
@@ -642,7 +639,7 @@ Quick overview:
642
639
  | [Local Forage](#local-forage) | Browser | ✅ | ❓ | Better capacity than localStorage |
643
640
  | [Redis](#redis) | Node.js | ✅ | ✅ | Good distributed cache backend |
644
641
  | [SQLite](#sqlite) | Node.js | ✅ | ❌ | Simple local persistence |
645
- | [Fetch API](#fetch-api) | Any with `fetch` | ❓ | ❓ | Bring your own KV HTTP API |
642
+ | [Fetch API](#fetch-api) | Node.js + Browser | ❓ | ❓ | Bring your own API |
646
643
  | [File](#file) | Node.js | ✅ | ❌ | Single JSON file store |
647
644
  | [Folder](#folder) | Node.js | ✅ | ❌ | One-file-per-key store |
648
645
  | [Cloudflare KV](#cloudflare-kv) | Cloudflare | ✅ | ✅ | Edge-native KV |
@@ -650,7 +647,7 @@ Quick overview:
650
647
  | [Etcd](#etcd) | Node.js | ✅ | ✅ | Distributed KV |
651
648
  | [Postgres](#postgres) | Node.js | ✅ | ❌ | Table-backed KV |
652
649
 
653
- While you can keep a reference to the client and access it directly, we strongly recommend to only access it through `polystore`, since we might add custom serialization and extra properties for e.g. expiration time:
650
+ While you can keep a reference to the underlying adapter and access it directly, we strongly recommend to only access it through `polystore`, since we might add custom serialization and extra properties for e.g. expiration time:
654
651
 
655
652
  ```js
656
653
  const map = new Map();
@@ -1225,12 +1222,14 @@ This keeps a single table while preserving namespace-style grouping through pref
1225
1222
 
1226
1223
  Please see the [creating a store](#creating-a-store) section for all the details!
1227
1224
 
1228
- ## Integrations
1225
+ ## Plugins
1229
1226
 
1230
- Polystore has some easy integrations for you to use it as a simple connector.
1227
+ Polystore has easy plugins to drop into popular frameworks and auth libraries. Each one has a runnable example in the [`examples/`](https://github.com/franciscop/polystore/tree/master/examples) folder.
1231
1228
 
1232
1229
  ### Express
1233
1230
 
1231
+ > [Full example →](https://github.com/franciscop/polystore/tree/master/examples/express)
1232
+
1234
1233
  Use any Polystore-compatible store as an [express-session](https://github.com/expressjs/session) store:
1235
1234
 
1236
1235
  ```js
@@ -1243,7 +1242,7 @@ app.use(session({
1243
1242
  }));
1244
1243
  ```
1245
1244
 
1246
- By default it uses an in-memory `Map`, which is fine for development. For production, pass any Polystore client:
1245
+ By default it uses an in-memory `Map`, which is fine for development. For production, pass any Polystore adapter:
1247
1246
 
1248
1247
  ```js
1249
1248
  import { createClient } from "redis";
@@ -1255,7 +1254,7 @@ const store = expressStore(createClient().connect());
1255
1254
  app.use(session({ secret: "my-secret", store }));
1256
1255
  ```
1257
1256
 
1258
- Any client works — Redis, Postgres, SQLite, file-based, etc. Session TTL is read automatically from `cookie.originalMaxAge` so you don't need to configure it separately.
1257
+ Any adapter works — Redis, Postgres, SQLite, file-based, etc. Session TTL is read automatically from `cookie.originalMaxAge` so you don't need to configure it separately.
1259
1258
 
1260
1259
  Use `.prefix()` to namespace sessions, for example in a multi-tenant app:
1261
1260
 
@@ -1270,30 +1269,35 @@ app.use((req, res, next) => {
1270
1269
 
1271
1270
  ### Hono Sessions
1272
1271
 
1272
+ > [Full example →](https://github.com/franciscop/polystore/tree/master/examples/hono-sessions)
1273
+
1273
1274
  Use any Polystore-compatible store as a [hono-sessions](https://github.com/jcs224/hono_sessions) store:
1274
1275
 
1275
1276
  ```js
1276
1277
  import { Hono } from "hono";
1277
1278
  import { sessionMiddleware } from "hono-sessions";
1278
- import honoStore from "polystore/hono-sessions";
1279
+ import kv from "polystore/hono-sessions";
1279
1280
 
1280
- const app = new Hono();
1281
+ const store = kv();
1281
1282
 
1283
+ const app = new Hono();
1282
1284
  app.use("*", sessionMiddleware({
1283
- store: honoStore(),
1285
+ store,
1284
1286
  encryptionKey: process.env.SESSION_KEY,
1285
1287
  expireAfterSeconds: 3600,
1286
1288
  }));
1287
1289
  ```
1288
1290
 
1289
- By default it uses an in-memory `Map`. For production, pass any Polystore client:
1291
+ By default it uses an in-memory `Map`. For production, pass any Polystore adapter:
1290
1292
 
1291
1293
  ```js
1292
1294
  import { createClient } from "redis";
1293
- import honoStore from "polystore/hono-sessions";
1295
+ import kv from "polystore/hono-sessions";
1296
+
1297
+ const store = kv(createClient().connect());
1294
1298
 
1295
1299
  app.use("*", sessionMiddleware({
1296
- store: honoStore(createClient().connect()),
1300
+ store,
1297
1301
  encryptionKey: process.env.SESSION_KEY,
1298
1302
  expireAfterSeconds: 3600,
1299
1303
  }));
@@ -1316,6 +1320,77 @@ app.use("*", (c, next) => {
1316
1320
  ```
1317
1321
 
1318
1322
 
1323
+ ### Better Auth
1324
+
1325
+ > [Full example →](https://github.com/franciscop/polystore/tree/master/examples/better-auth)
1326
+
1327
+ Use any Polystore-compatible store as the [`secondaryStorage`](https://www.better-auth.com/docs/concepts/database#secondary-storage) for [Better Auth](https://better-auth.com). No database required — Polystore handles session caching and token storage:
1328
+
1329
+ ```js
1330
+ import { betterAuth } from "better-auth";
1331
+ import betterAuthStorage from "polystore/better-auth";
1332
+
1333
+ export const auth = betterAuth({
1334
+ secondaryStorage: betterAuthStorage(), // in-memory by default
1335
+ emailAndPassword: { enabled: true },
1336
+ });
1337
+ ```
1338
+
1339
+ For production, swap in any Polystore adapter:
1340
+
1341
+ ```js
1342
+ import { createClient } from "redis";
1343
+
1344
+ secondaryStorage: betterAuthStorage(createClient().connect())
1345
+ ```
1346
+
1347
+ Use `.prefix()` to namespace keys in a shared store:
1348
+
1349
+ ```js
1350
+ secondaryStorage: betterAuthStorage(createClient().connect()).prefix("auth:")
1351
+ ```
1352
+
1353
+ ### Axios Cache Interceptor
1354
+
1355
+ > [Full example →](https://github.com/franciscop/polystore/tree/master/examples/axios-cache-interceptor)
1356
+
1357
+ Use any Polystore-compatible store as the cache storage for [axios-cache-interceptor](https://axios-cache-interceptor.js.org/):
1358
+
1359
+ ```js
1360
+ import axios from "axios";
1361
+ import { setupCache } from "axios-cache-interceptor";
1362
+ import axiosCacheStorage from "polystore/axios-cache-interceptor";
1363
+
1364
+ const http = setupCache(axios, {
1365
+ storage: axiosCacheStorage(), // in-memory by default
1366
+ });
1367
+
1368
+ // First request hits the network, second is served from cache
1369
+ const { data } = await http.get("https://api.example.com/users");
1370
+ await http.get("https://api.example.com/users"); // cached
1371
+ ```
1372
+
1373
+ For production, swap in any Polystore adapter:
1374
+
1375
+ ```js
1376
+ import { createClient } from "redis";
1377
+ import axiosCacheStorage from "polystore/axios-cache-interceptor";
1378
+
1379
+ const http = setupCache(axios, {
1380
+ storage: axiosCacheStorage(createClient().connect()),
1381
+ });
1382
+ ```
1383
+
1384
+ Use `.prefix()` to namespace cache keys in a shared store:
1385
+
1386
+ ```js
1387
+ const http = setupCache(axios, {
1388
+ storage: axiosCacheStorage(createClient().connect()).prefix("api-cache:"),
1389
+ });
1390
+ ```
1391
+
1392
+ Cache TTL is derived automatically from each response's cache headers (via `Cache-Control: max-age`, `Expires`, etc.) so you don't need to configure it separately. The cache entry is stored with a matching expiration so backends like Redis will evict it automatically.
1393
+
1319
1394
  ### fch
1320
1395
 
1321
1396
  [Fch](https://www.npmjs.com/package/fch) is a lightweight fetch wrapper that uses Polystore natively for caching. Pass any Polystore store as the `cache` option and GET responses are cached automatically:
@@ -1378,7 +1453,7 @@ export default server({ session }).get("/", (ctx) => {
1378
1453
 
1379
1454
  ### Performance
1380
1455
 
1381
- > TL;DR: if you only use the item operations (add, set, get, has, del) and your client supports expiration natively, you have nothing to worry about! Otherwise, please read on.
1456
+ > TL;DR: if you only use the item operations (add, set, get, has, del) and your adapter supports expiration natively, you have nothing to worry about! Otherwise, please read on.
1382
1457
 
1383
1458
  While all of our stores support `expires`, `.prefix()` and group operations, the nature of those makes them to have different performance characteristics.
1384
1459
 
@@ -1390,9 +1465,9 @@ While all of our stores support `expires`, `.prefix()` and group operations, the
1390
1465
 
1391
1466
  ### Expirations
1392
1467
 
1393
- > Warning: if a client doesn't support expiration natively, we will hide expired keys on the API calls for a nice DX, but _old data might not be evicted automatically_. See [the notes in Performance](#performance) for details on how to work around this.
1468
+ > Warning: if an adapter doesn't support expiration natively, we will hide expired keys on the API calls for a nice DX, but _old data might not be evicted automatically_. See [the notes in Performance](#performance) for details on how to work around this.
1394
1469
 
1395
- We unify all of the clients diverse expiration methods into a single, easy one with `expires` (**seconds** | string):
1470
+ We unify all of the adapters' diverse expiration methods into a single, easy one with `expires` (**seconds** | string):
1396
1471
 
1397
1472
  ```js
1398
1473
  // in-memory store
@@ -1438,13 +1513,13 @@ However, in some stores this does come with some potential performance disadvant
1438
1513
 
1439
1514
  For other stores like Redis this is not a problem, because the low-level operations already do them natively, so we don't need to worry about this for performance at the user-level. Instead, Redis and cookies have the problem that they only have expiration resolution at the second level. Meaning that 800ms is not a valid Redis expiration time, it has to be 1s, 2s, etc.
1440
1515
 
1441
- These details are explained in the respective client information.
1516
+ These details are explained in the respective adapter information.
1442
1517
 
1443
1518
  ### Substores
1444
1519
 
1445
1520
  > There's some [basic `.prefix()` API info](#prefix) for everyday usage, this section is the in-depth explanation.
1446
1521
 
1447
- What `.prefix()` does is it creates **a new instance** of the Store, _with the same client_ as you provided, but now any key you read, write, etc. will be passed with the given prefix to the client. The issue is that support from the underlying clients is inconsistent.
1522
+ What `.prefix()` does is it creates **a new instance** of the Store, _with the same adapter_ as you provided, but now any key you read, write, etc. will be passed with the given prefix to the adapter. The issue is that support from the underlying adapters is inconsistent.
1448
1523
 
1449
1524
  When dealing with large or complex amounts of data in a KV store, sometimes it's useful to divide them by categories. Some examples might be:
1450
1525
 
@@ -1465,15 +1540,15 @@ Polystore methods return promises and surface errors from the underlying client.
1465
1540
  Invalid JSON payloads, invalid value encoding, or data that was written outside Polystore and cannot be decoded with its metadata expectations.
1466
1541
 
1467
1542
  3. **Usage/configuration errors**
1468
- Invalid client setup, invalid URLs/paths, or unsupported operations in a specific runtime.
1543
+ Invalid adapter setup, invalid URLs/paths, or unsupported operations in a specific runtime.
1469
1544
 
1470
1545
  Recommended patterns:
1471
1546
 
1472
1547
  - Use `try/catch` around all write/read operations in production paths.
1473
1548
  - Prefer returning safe fallbacks for cache-like usage (`null`, stale response, or refetch).
1474
- - Log enough context (`client type`, `key`, operation name) without logging sensitive values.
1475
- - For remote clients, consider retry/backoff only for transient failures.
1476
- - Call `.close()` during shutdown when the client supports it.
1549
+ - Log enough context (`adapter type`, `key`, operation name) without logging sensitive values.
1550
+ - For remote adapters, consider retry/backoff only for transient failures.
1551
+ - Call `.close()` during shutdown when the adapter supports it.
1477
1552
 
1478
1553
  Example:
1479
1554
 
@@ -1524,43 +1599,43 @@ class MyClient {
1524
1599
  }
1525
1600
  ```
1526
1601
 
1527
- Note that this is NOT the public API, it's the internal **client** API. It's simpler than the public API since we do some of the heavy lifting as an intermediate layer (e.g. for the client, the `expires` will always be a `null` or `number`, never `undefined` or a `string`), but also it differs from polystore's public API, like `.add()` has a different signature, and the group methods all take a explicit prefix.
1602
+ Note that this is NOT the public API, it's the internal **adapter** API. It's simpler than the public API since we do some of the heavy lifting as an intermediate layer (e.g. for the adapter, the `expires` will always be a `null` or `number`, never `undefined` or a `string`), but also it differs from polystore's public API, like `.add()` has a different signature, and the group methods all take a explicit prefix.
1528
1603
 
1529
- **Expires**: if you set the `HAS_EXPIRATION = true`, then you are indicating that the client WILL manage the lifecycle of the data. This includes all methods, for example if an item is expired, then its key should not be returned in `.keys()`, it's value should not be returned in `.values()`, and the method `.has()` will return `false`. The good news is that you will always receive the option `expires`, which is either `null` (no expiration) or a `number` indicating the **seconds** for the key/value to will expire.
1604
+ **Expires**: if you set the `HAS_EXPIRATION = true`, then you are indicating that the adapter WILL manage the lifecycle of the data. This includes all methods, for example if an item is expired, then its key should not be returned in `.keys()`, it's value should not be returned in `.values()`, and the method `.has()` will return `false`. The good news is that you will always receive the option `expires`, which is either `null` (no expiration) or a `number` indicating the **seconds** for the key/value to will expire.
1530
1605
 
1531
1606
  **Prefix**: we manage the `prefix` as an invisible layer on top, you only need to be aware of it in the `.add()` method, as well as in the group methods:
1532
1607
 
1533
1608
  ```js
1534
1609
  // What the user of polystore does:
1535
- const store = await kv(client).prefix("hello:").prefix("world:");
1610
+ const store = await kv(adapter).prefix("hello:").prefix("world:");
1536
1611
 
1537
- // User calls this, then the client is called with that:
1612
+ // User calls this, then the adapter is called with that:
1538
1613
  const value = await store.get("a");
1539
- // client.get("hello:world:a");
1614
+ // adapter.get("hello:world:a");
1540
1615
 
1541
- // User calls this, then the client is called with that:
1616
+ // User calls this, then the adapter is called with that:
1542
1617
  for await (const [key, value] of store) {}
1543
- // client.iterate("hello:world:");
1618
+ // adapter.iterate("hello:world:");
1544
1619
  ```
1545
1620
 
1546
1621
  > Note: all of the _group methods_ that return keys, should return them **with the prefix**:
1547
1622
 
1548
1623
  ```js
1549
- client.keys = (prefix) => {
1624
+ adapter.keys = (prefix) => {
1550
1625
  // Filter the keys, and return them INCLUDING the prefix!
1551
1626
  return Object.keys(subStore).filter((key) => key.startsWith(prefix));
1552
1627
  };
1553
1628
  ```
1554
1629
 
1555
- While the signatures are different, you can check each entries on the output of Polystore API to see what is expected for the methods of the client to do, e.g. `.clear()` will remove all of the items that match the prefix (or everything if there's no prefix).
1630
+ While the signatures are different, you can check each entries on the output of Polystore API to see what is expected for the methods of the adapter to do, e.g. `.clear()` will remove all of the items that match the prefix (or everything if there's no prefix).
1556
1631
 
1557
1632
 
1558
1633
 
1559
1634
  ## Examples
1560
1635
 
1561
- ### Plain Object client
1636
+ ### Plain Object adapter
1562
1637
 
1563
- This is a good example of how simple a store can be, however do not use it literally since it behaves the same as the already-supported `new Map()`, only use it as the base for your own clients:
1638
+ This is a good example of how simple a store can be, however do not use it literally since it behaves the same as the already-supported `new Map()`, only use it as the base for your own adapters:
1564
1639
 
1565
1640
  ```js
1566
1641
  const dataSource = {};
@@ -1590,10 +1665,10 @@ We don't set `HAS_EXPIRATION` to true since plain objects do NOT support expirat
1590
1665
 
1591
1666
  ### Custom ID generation
1592
1667
 
1593
- You might want to provide your custom key generation algorithm, which I'm going to call `customId()` for example purposes. The only place where `polystore` generates IDs is in `add`, so you can provide your client with a custom generator:
1668
+ You might want to provide your custom key generation algorithm, which I'm going to call `customId()` for example purposes. The only place where `polystore` generates IDs is in `add`, so you can provide your adapter with a custom generator:
1594
1669
 
1595
1670
  ```js
1596
- class MyClient {
1671
+ class MyAdapter {
1597
1672
 
1598
1673
  // Add the opt method .add() to have more control over the ID generation
1599
1674
  async add (prefix, data, expires) {
@@ -1623,10 +1698,10 @@ const id2 = await store.prefix("hello:").add({ hello: "world" });
1623
1698
 
1624
1699
  ### Serializing the data
1625
1700
 
1626
- If you need to serialize the data before storing it, you can do it within your custom client. Here's an example of how you can handle data serialization when setting values:
1701
+ If you need to serialize the data before storing it, you can do it within your custom adapter. Here's an example of how you can handle data serialization when setting values:
1627
1702
 
1628
1703
  ```js
1629
- class MyClient {
1704
+ class MyAdapter {
1630
1705
  get(key) {
1631
1706
  const data = dataSource[key];
1632
1707
  return data ? JSON.parse(data) : null;
@@ -1741,7 +1816,7 @@ You can store either the raw data, or the processed data. Depending on whether t
1741
1816
 
1742
1817
  ### Dev vs Prod
1743
1818
 
1744
- With Polystore it's easy to configure your KV solution to use a different client in dev vs production. We've found particularly useful to use an easy-to-debug client in dev like [Folder](#folder) and a high-performance client in production like [Redis](#redis):
1819
+ With Polystore it's easy to configure your KV solution to use a different adapter in dev vs production. We've found particularly useful to use an easy-to-debug adapter in dev like [Folder](#folder) and a high-performance adapter in production like [Redis](#redis):
1745
1820
 
1746
1821
  ```ts
1747
1822
  // store.ts
@@ -0,0 +1,17 @@
1
+ import { Store } from 'polystore';
2
+ import { AxiosStorage, CacheRequestConfig, StorageValue, NotEmptyStorageValue } from 'axios-cache-interceptor';
3
+
4
+ declare class PolystoreAxiosCacheStorage implements AxiosStorage {
5
+ "is-storage": number;
6
+ private store;
7
+ private _storage;
8
+ constructor(store: Store);
9
+ prefix(prefix?: string): PolystoreAxiosCacheStorage;
10
+ get(key: string, currentRequest?: CacheRequestConfig): Promise<StorageValue>;
11
+ set(key: string, value: NotEmptyStorageValue, currentRequest?: CacheRequestConfig): Promise<void>;
12
+ remove(key: string, currentRequest?: CacheRequestConfig): Promise<void>;
13
+ clear(): Promise<void>;
14
+ }
15
+ declare function axiosCacheStorage(store?: any): PolystoreAxiosCacheStorage;
16
+
17
+ export { PolystoreAxiosCacheStorage, axiosCacheStorage as default };