polystore 0.21.2 → 0.22.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,65 +1,83 @@
1
1
  {
2
2
  "name": "polystore",
3
- "version": "0.21.2",
3
+ "version": "0.22.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,
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"
17
+ ],
18
+ "scripts": {
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",
21
+ "lint": "tsc",
22
+ "start": "bun test --watch",
23
+ "service:db": "etcd",
24
+ "service:redis": "brew services start redis",
25
+ "service:postgres": "brew services start postgresql",
26
+ "service:server": "bun ./src/server.ts",
27
+ "services": "concurrently \"npm run service:db\" \"npm run service:redis\" \"npm run service:postgres\" \"npm run service:server\"",
28
+ "test": "bun run test:bun && bun run test:jest",
29
+ "test:bun": "bun test ./test/index.test.ts ./src/plugins/",
30
+ "test:jest": "jest ./test/index.test.ts --detectOpenHandles --forceExit"
31
+ },
32
+ "workspaces": [
33
+ "examples/*"
34
+ ],
12
35
  "main": "index.js",
36
+ "type": "module",
13
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
+ ],
14
48
  "exports": {
15
49
  ".": {
16
50
  "types": "./index.d.ts",
17
51
  "import": "./index.js"
18
52
  },
19
53
  "./express": {
20
- "types": "./src/integrations/express.d.ts",
21
- "import": "./src/integrations/express.js"
54
+ "types": "./src/plugins/express/index.d.ts",
55
+ "import": "./src/plugins/express/index.js"
22
56
  },
23
57
  "./hono-sessions": {
24
- "types": "./src/integrations/hono-sessions.d.ts",
25
- "import": "./src/integrations/hono-sessions.js"
58
+ "types": "./src/plugins/hono-sessions/index.d.ts",
59
+ "import": "./src/plugins/hono-sessions/index.js"
60
+ },
61
+ "./better-auth": {
62
+ "types": "./src/plugins/better-auth/index.d.ts",
63
+ "import": "./src/plugins/better-auth/index.js"
26
64
  }
27
65
  },
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"
35
- ],
36
- "scripts": {
37
- "analyze": "npm 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",
39
- "lint": "tsc",
40
- "start": "bun test --watch",
41
- "service:db": "etcd",
42
- "service:redis": "brew services start redis",
43
- "service:postgres": "brew services start postgresql",
44
- "service:server": "bun ./src/server.ts",
45
- "services": "concurrently \"npm run service:db\" \"npm run service:redis\" \"npm run service:postgres\" \"npm run service:server\"",
46
- "test": "npm run test:bun && npm run test:jest",
47
- "test:bun": "bun test ./test/index.test.ts ./src/integrations/",
48
- "test:jest": "jest ./test/index.test.ts --detectOpenHandles --forceExit"
66
+ "documentation.page": {
67
+ "title": "🏬 Polystore - A universal library for standardizing any KV-store",
68
+ "home": "assets/home.html",
69
+ "homepage": "https://polystore.dev/",
70
+ "menu": {
71
+ "Documentation": "/documentation",
72
+ "Issues": "https://github.com/franciscop/polystore/issues",
73
+ "Get help": "https://superpeer.com/francisco/-/javascript-and-react-help",
74
+ "Github": "https://github.com/franciscop/polystore"
75
+ }
49
76
  },
50
- "keywords": [
51
- "kv",
52
- "store",
53
- "polystore",
54
- "key-value",
55
- "key",
56
- "value"
57
- ],
58
- "license": "MIT",
59
77
  "devDependencies": {
60
78
  "@deno/kv": "^0.8.1",
61
79
  "@types/better-sqlite3": "^7.6.13",
62
- "@types/bun": "^1.3.3",
80
+ "@types/bun": "^1.3.0",
63
81
  "@types/express": "^5.0.6",
64
82
  "@types/express-session": "^1.18.2",
65
83
  "@types/jest": "^30.0.0",
@@ -67,9 +85,7 @@
67
85
  "@types/pg": "^8.11.10",
68
86
  "@types/supertest": "^7.2.0",
69
87
  "better-sqlite3": "^12.6.0",
70
- "check-dts": "^0.8.0",
71
88
  "concurrently": "^9.2.1",
72
- "cross-fetch": "^4.1.0",
73
89
  "dotenv": "^16.3.1",
74
90
  "edge-mock": "^0.0.15",
75
91
  "esbuild": "^0.27.0",
@@ -87,19 +103,8 @@
87
103
  "redis": "^4.6.10",
88
104
  "supertest": "^7.2.2",
89
105
  "ts-jest": "^29.4.6",
90
- "ts-node": "^10.9.2",
91
106
  "tsup": "^8.5.1",
92
- "typescript": "^5.9.3"
107
+ "typescript": "^6.0.2"
93
108
  },
94
- "documentation": {
95
- "title": "🏬 Polystore - A universal library for standardizing any KV-store",
96
- "home": "assets/home.html",
97
- "homepage": "https://polystore.dev/",
98
- "menu": {
99
- "Documentation": "/documentation",
100
- "Issues": "https://github.com/franciscop/polystore/issues",
101
- "Get help": "https://superpeer.com/francisco/-/javascript-and-react-help",
102
- "Github": "https://github.com/franciscop/polystore"
103
- }
104
- }
109
+ "sideEffects": false
105
110
  }
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 |
@@ -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,36 @@ 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
+
1319
1353
  ### fch
1320
1354
 
1321
1355
  [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 +1412,7 @@ export default server({ session }).get("/", (ctx) => {
1378
1412
 
1379
1413
  ### Performance
1380
1414
 
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.
1415
+ > 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
1416
 
1383
1417
  While all of our stores support `expires`, `.prefix()` and group operations, the nature of those makes them to have different performance characteristics.
1384
1418
 
@@ -1390,9 +1424,9 @@ While all of our stores support `expires`, `.prefix()` and group operations, the
1390
1424
 
1391
1425
  ### Expirations
1392
1426
 
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.
1427
+ > 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
1428
 
1395
- We unify all of the clients diverse expiration methods into a single, easy one with `expires` (**seconds** | string):
1429
+ We unify all of the adapters' diverse expiration methods into a single, easy one with `expires` (**seconds** | string):
1396
1430
 
1397
1431
  ```js
1398
1432
  // in-memory store
@@ -1438,13 +1472,13 @@ However, in some stores this does come with some potential performance disadvant
1438
1472
 
1439
1473
  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
1474
 
1441
- These details are explained in the respective client information.
1475
+ These details are explained in the respective adapter information.
1442
1476
 
1443
1477
  ### Substores
1444
1478
 
1445
1479
  > There's some [basic `.prefix()` API info](#prefix) for everyday usage, this section is the in-depth explanation.
1446
1480
 
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.
1481
+ 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
1482
 
1449
1483
  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
1484
 
@@ -1465,15 +1499,15 @@ Polystore methods return promises and surface errors from the underlying client.
1465
1499
  Invalid JSON payloads, invalid value encoding, or data that was written outside Polystore and cannot be decoded with its metadata expectations.
1466
1500
 
1467
1501
  3. **Usage/configuration errors**
1468
- Invalid client setup, invalid URLs/paths, or unsupported operations in a specific runtime.
1502
+ Invalid adapter setup, invalid URLs/paths, or unsupported operations in a specific runtime.
1469
1503
 
1470
1504
  Recommended patterns:
1471
1505
 
1472
1506
  - Use `try/catch` around all write/read operations in production paths.
1473
1507
  - 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.
1508
+ - Log enough context (`adapter type`, `key`, operation name) without logging sensitive values.
1509
+ - For remote adapters, consider retry/backoff only for transient failures.
1510
+ - Call `.close()` during shutdown when the adapter supports it.
1477
1511
 
1478
1512
  Example:
1479
1513
 
@@ -1524,43 +1558,43 @@ class MyClient {
1524
1558
  }
1525
1559
  ```
1526
1560
 
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.
1561
+ 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
1562
 
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.
1563
+ **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
1564
 
1531
1565
  **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
1566
 
1533
1567
  ```js
1534
1568
  // What the user of polystore does:
1535
- const store = await kv(client).prefix("hello:").prefix("world:");
1569
+ const store = await kv(adapter).prefix("hello:").prefix("world:");
1536
1570
 
1537
- // User calls this, then the client is called with that:
1571
+ // User calls this, then the adapter is called with that:
1538
1572
  const value = await store.get("a");
1539
- // client.get("hello:world:a");
1573
+ // adapter.get("hello:world:a");
1540
1574
 
1541
- // User calls this, then the client is called with that:
1575
+ // User calls this, then the adapter is called with that:
1542
1576
  for await (const [key, value] of store) {}
1543
- // client.iterate("hello:world:");
1577
+ // adapter.iterate("hello:world:");
1544
1578
  ```
1545
1579
 
1546
1580
  > Note: all of the _group methods_ that return keys, should return them **with the prefix**:
1547
1581
 
1548
1582
  ```js
1549
- client.keys = (prefix) => {
1583
+ adapter.keys = (prefix) => {
1550
1584
  // Filter the keys, and return them INCLUDING the prefix!
1551
1585
  return Object.keys(subStore).filter((key) => key.startsWith(prefix));
1552
1586
  };
1553
1587
  ```
1554
1588
 
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).
1589
+ 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
1590
 
1557
1591
 
1558
1592
 
1559
1593
  ## Examples
1560
1594
 
1561
- ### Plain Object client
1595
+ ### Plain Object adapter
1562
1596
 
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:
1597
+ 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
1598
 
1565
1599
  ```js
1566
1600
  const dataSource = {};
@@ -1590,10 +1624,10 @@ We don't set `HAS_EXPIRATION` to true since plain objects do NOT support expirat
1590
1624
 
1591
1625
  ### Custom ID generation
1592
1626
 
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:
1627
+ 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
1628
 
1595
1629
  ```js
1596
- class MyClient {
1630
+ class MyAdapter {
1597
1631
 
1598
1632
  // Add the opt method .add() to have more control over the ID generation
1599
1633
  async add (prefix, data, expires) {
@@ -1623,10 +1657,10 @@ const id2 = await store.prefix("hello:").add({ hello: "world" });
1623
1657
 
1624
1658
  ### Serializing the data
1625
1659
 
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:
1660
+ 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
1661
 
1628
1662
  ```js
1629
- class MyClient {
1663
+ class MyAdapter {
1630
1664
  get(key) {
1631
1665
  const data = dataSource[key];
1632
1666
  return data ? JSON.parse(data) : null;
@@ -1741,7 +1775,7 @@ You can store either the raw data, or the processed data. Depending on whether t
1741
1775
 
1742
1776
  ### Dev vs Prod
1743
1777
 
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):
1778
+ 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
1779
 
1746
1780
  ```ts
1747
1781
  // store.ts
@@ -0,0 +1,18 @@
1
+ import { Store } from 'polystore';
2
+
3
+ type SecondaryStorage = {
4
+ get(key: string): Promise<string | null>;
5
+ set(key: string, value: string, ttl?: number): Promise<void>;
6
+ delete(key: string): Promise<void>;
7
+ };
8
+ declare class PolystoreBetterAuthStorage implements SecondaryStorage {
9
+ private store;
10
+ constructor(store: Store);
11
+ prefix(prefix?: string): PolystoreBetterAuthStorage;
12
+ get(key: string): Promise<string | null>;
13
+ set(key: string, value: string, ttl?: number): Promise<void>;
14
+ delete(key: string): Promise<void>;
15
+ }
16
+ declare function betterAuthStorage(store?: any): PolystoreBetterAuthStorage;
17
+
18
+ export { PolystoreBetterAuthStorage, betterAuthStorage as default };
@@ -0,0 +1,27 @@
1
+ // src/plugins/better-auth/index.ts
2
+ import kv from "polystore";
3
+ var PolystoreBetterAuthStorage = class _PolystoreBetterAuthStorage {
4
+ store;
5
+ constructor(store) {
6
+ this.store = store;
7
+ }
8
+ prefix(prefix = "") {
9
+ return new _PolystoreBetterAuthStorage(this.store.prefix(prefix));
10
+ }
11
+ get(key) {
12
+ return this.store.get(key);
13
+ }
14
+ async set(key, value, ttl) {
15
+ await this.store.set(key, value, ttl ? { expires: ttl } : void 0);
16
+ }
17
+ async delete(key) {
18
+ await this.store.del(key);
19
+ }
20
+ };
21
+ function betterAuthStorage(store = /* @__PURE__ */ new Map()) {
22
+ return new PolystoreBetterAuthStorage(kv(store));
23
+ }
24
+ export {
25
+ PolystoreBetterAuthStorage,
26
+ betterAuthStorage as default
27
+ };
@@ -0,0 +1,20 @@
1
+ import session, { SessionData } from 'express-session';
2
+ import { Store } from 'polystore';
3
+
4
+ type Callback = (err?: any) => void;
5
+ declare class PolystoreSessionStore extends session.Store {
6
+ private store;
7
+ constructor(store: Store);
8
+ prefix(prefix?: string): PolystoreSessionStore;
9
+ get(sid: string, cb: (err: any, session?: SessionData | null) => void): void;
10
+ set(sid: string, data: SessionData, cb?: Callback): void;
11
+ destroy(sid: string, cb?: Callback): void;
12
+ touch(sid: string, data: SessionData, cb?: Callback): void;
13
+ all(cb: (err: any, sessions?: SessionData[] | {
14
+ [sid: string]: SessionData;
15
+ } | null) => void): void;
16
+ clear(cb?: Callback): void;
17
+ }
18
+ declare function expressStore(store?: Map<any, any>): PolystoreSessionStore;
19
+
20
+ export { PolystoreSessionStore, expressStore as default };
@@ -1,4 +1,4 @@
1
- // src/integrations/express.ts
1
+ // src/plugins/express/index.ts
2
2
  import session from "express-session";
3
3
  import kv from "polystore";
4
4
  var ttlFromSession = (data) => {
@@ -33,8 +33,8 @@ var PolystoreSessionStore = class _PolystoreSessionStore extends session.Store {
33
33
  this.store.clear().then(() => cb?.()).catch((err) => cb?.(err));
34
34
  }
35
35
  };
36
- function expressStore(client = /* @__PURE__ */ new Map()) {
37
- return new PolystoreSessionStore(kv(client));
36
+ function expressStore(store = /* @__PURE__ */ new Map()) {
37
+ return new PolystoreSessionStore(kv(store));
38
38
  }
39
39
  export {
40
40
  PolystoreSessionStore,