polystore 0.11.5 → 0.12.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 +7 -3
- package/readme.md +51 -27
- package/src/clients/cloudflare.js +3 -0
- package/src/index.js +3 -4
- package/src/utils.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polystore",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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
6
|
"repository": "https://github.com/franciscop/polystore.git",
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"size": "echo $(gzip -c src/index.js | wc -c) bytes",
|
|
18
|
+
"lint": "check-dts test/index.types.ts",
|
|
18
19
|
"start": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch --coverage --detectOpenHandles",
|
|
19
|
-
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --ci --watchAll=false --detectOpenHandles
|
|
20
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --ci --watchAll=false --detectOpenHandles"
|
|
20
21
|
},
|
|
21
22
|
"keywords": [
|
|
22
23
|
"kv",
|
|
@@ -56,6 +57,9 @@
|
|
|
56
57
|
"setupFiles": [
|
|
57
58
|
"./test/setup.js"
|
|
58
59
|
],
|
|
59
|
-
"transform": {}
|
|
60
|
+
"transform": {},
|
|
61
|
+
"modulePathIgnorePatterns": [
|
|
62
|
+
"test/cloudflare"
|
|
63
|
+
]
|
|
60
64
|
}
|
|
61
65
|
}
|
package/readme.md
CHANGED
|
@@ -99,18 +99,6 @@ const store = kv(MyClientOrStoreInstance);
|
|
|
99
99
|
// use the store
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
If the instance you pass contains a `connect()` or `open()` method, polystore **will** call that without any argument:
|
|
103
|
-
|
|
104
|
-
```js
|
|
105
|
-
// Simpler
|
|
106
|
-
const store = kv(createClient());
|
|
107
|
-
|
|
108
|
-
// NO NEED
|
|
109
|
-
const client = createClient();
|
|
110
|
-
client.connect();
|
|
111
|
-
const store = kv(client);
|
|
112
|
-
```
|
|
113
|
-
|
|
114
102
|
While you can keep a reference to the store and access it directly, we strongly recommend if you are going to use a store, to only access it through `polystore`, since we might add custom serialization and extra properties for e.g. expiration time:
|
|
115
103
|
|
|
116
104
|
```js
|
|
@@ -141,7 +129,7 @@ console.log(await store.get("key3")); // { name: "Francisco" }
|
|
|
141
129
|
|
|
142
130
|
If the value is returned, it can be a simple type like `boolean`, `string` or `number`, or it can be a plain Object or Array, or a combination of those.
|
|
143
131
|
|
|
144
|
-
> The value cannot be more complex or non-serializable values like a `Date()`, `Infinity`, `undefined
|
|
132
|
+
> The value cannot be more complex or non-serializable values like a `Date()`, `Infinity`, `undefined`, a `Symbol`, etc.
|
|
145
133
|
|
|
146
134
|
### .set()
|
|
147
135
|
|
|
@@ -152,7 +140,7 @@ await store.set(key: string, value: any, options?: { expires: number|string });
|
|
|
152
140
|
|
|
153
141
|
await store.set("key1", "Hello World");
|
|
154
142
|
await store.set("key2", ["my", "grocery", "list"], { expires: "1h" });
|
|
155
|
-
await store.set("key3", { name: "Francisco" }, { expires: 60 * 60
|
|
143
|
+
await store.set("key3", { name: "Francisco" }, { expires: 60 * 60 });
|
|
156
144
|
```
|
|
157
145
|
|
|
158
146
|
The value can be a simple type like `boolean`, `string` or `number`, or it can be a plain Object or Array, or a combination of those. It **cannot** be a more complex or non-serializable values like a `Date()`, `Infinity`, `undefined` (casted to `null`), a `Symbol`, etc.
|
|
@@ -176,7 +164,7 @@ When the `expires` option is set, it can be a number (**seconds**) or a string r
|
|
|
176
164
|
"5d" - expire after 5 days
|
|
177
165
|
```
|
|
178
166
|
|
|
179
|
-
\* not all stores support sub-second expirations, notably Redis and Cookies don't, so it's safer to always use an integer or an amount larger than 1s
|
|
167
|
+
\* not all stores support sub-second expirations, notably Redis and Cookies don't, so it's safer to always use an integer or an amount larger than 1s. There will be a note in each store for this.
|
|
180
168
|
|
|
181
169
|
These are all the units available:
|
|
182
170
|
|
|
@@ -191,7 +179,7 @@ const key:string = await store.add(value: any, options?: { expires: number|strin
|
|
|
191
179
|
|
|
192
180
|
const key1 = await store.add("Hello World");
|
|
193
181
|
const key2 = await store.add(["my", "grocery", "list"], { expires: "1h" });
|
|
194
|
-
const key3 = await store.add({ name: "Francisco" }, { expires: 60 * 60
|
|
182
|
+
const key3 = await store.add({ name: "Francisco" }, { expires: 60 * 60 });
|
|
195
183
|
```
|
|
196
184
|
|
|
197
185
|
The generated key is 24 AlphaNumeric characters (including upper and lower case) generated with random cryptography to make sure it's unguessable, high entropy and safe to use in most contexts like URLs, queries, etc. We use [`nanoid`](https://github.com/ai/nanoid/) with a custom dictionary, so you can check the entropy [in this dictionary](https://zelark.github.io/nano-id-cc/) by removing the "\_" and "-", and setting it to 24 characters.
|
|
@@ -200,9 +188,26 @@ Here is the safety: "If you generate 1 million keys/second, it will take ~14 mil
|
|
|
200
188
|
|
|
201
189
|
> Note: please make sure to read the [`.set()`](#set) section for all the details, since `.set()` and `.add()` behave the same way except for the first argument.
|
|
202
190
|
|
|
191
|
+
The main reason why `.add()` exists is to allow it to work with the prefix seamlessly:
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
const session = store.prefix("session:");
|
|
195
|
+
|
|
196
|
+
// Creates a key with the prefix (returns only the key)
|
|
197
|
+
const key1 = await session.add("value1");
|
|
198
|
+
// "c4ONlvweshXPUEy76q3eFHPL"
|
|
199
|
+
|
|
200
|
+
console.log(await session.keys()); // on the "session" store
|
|
201
|
+
// ["c4ONlvweshXPUEy76q3eFHPL"]
|
|
202
|
+
console.log(await store.keys()); // on the root store
|
|
203
|
+
// ["session:c4ONlvweshXPUEy76q3eFHPL"]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Remember that [substores with `.prefix()`](#prefix) behave as if they were an independent store, so when adding, manipulating, etc. a value you should treat the key as if it had no prefix. This is explained in detail in the [.prefix()](#prefix) documentation.
|
|
207
|
+
|
|
203
208
|
### .has()
|
|
204
209
|
|
|
205
|
-
Check whether the key is available in the store and not expired:
|
|
210
|
+
Check whether the key:value is available in the store and not expired:
|
|
206
211
|
|
|
207
212
|
```js
|
|
208
213
|
await store.has(key: string);
|
|
@@ -233,6 +238,17 @@ async function fetchUser(id) {
|
|
|
233
238
|
}
|
|
234
239
|
```
|
|
235
240
|
|
|
241
|
+
An example with a prefix:
|
|
242
|
+
|
|
243
|
+
```js
|
|
244
|
+
const session = store.prefix("session:");
|
|
245
|
+
|
|
246
|
+
// These three perform the same operation internally
|
|
247
|
+
const has1 = await session.has("key1");
|
|
248
|
+
const has2 = await store.prefix("session:").has("key1");
|
|
249
|
+
const has3 = await store.has("session:key1");
|
|
250
|
+
```
|
|
251
|
+
|
|
236
252
|
### .del()
|
|
237
253
|
|
|
238
254
|
Remove a single key from the store and return the key itself:
|
|
@@ -241,7 +257,24 @@ Remove a single key from the store and return the key itself:
|
|
|
241
257
|
await store.del(key: string);
|
|
242
258
|
```
|
|
243
259
|
|
|
244
|
-
It will ignore the operation if the key or value don't exist already (but won't thorw).
|
|
260
|
+
It will ignore the operation if the key or value don't exist already (but won't thorw). The API makes it easy to delete multiple keys at once:
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
const keys = ["key1", "key2"];
|
|
264
|
+
await Promise.all(keys.map(store.del));
|
|
265
|
+
console.log(done);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
An example with a prefix:
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
const session = store.prefix("session:");
|
|
272
|
+
|
|
273
|
+
// These three perform the same operation internally
|
|
274
|
+
await session.del("key1");
|
|
275
|
+
await store.prefix("session:").del("key1");
|
|
276
|
+
await store.del("session:key1");
|
|
277
|
+
```
|
|
245
278
|
|
|
246
279
|
### _Iterator_
|
|
247
280
|
|
|
@@ -385,15 +418,6 @@ console.log(await store.get("key1"));
|
|
|
385
418
|
// "Hello world"
|
|
386
419
|
```
|
|
387
420
|
|
|
388
|
-
It can also be initialized empty, then it'll use the in-memory store:
|
|
389
|
-
|
|
390
|
-
```js
|
|
391
|
-
import kv from "polystore";
|
|
392
|
-
|
|
393
|
-
const store = kv();
|
|
394
|
-
const store = kv(new Map());
|
|
395
|
-
```
|
|
396
|
-
|
|
397
421
|
<details>
|
|
398
422
|
<summary>Why use polystore with <code>new Map()</code>?</summary>
|
|
399
423
|
<p>Besides the other benefits already documented elsewhere, these are the ones specifically for wrapping Map() with polystore:</p>
|
|
@@ -23,6 +23,9 @@ export default class Cloudflare {
|
|
|
23
23
|
|
|
24
24
|
async set(key, value, { expires } = {}) {
|
|
25
25
|
const expirationTtl = expires ? Math.round(expires) : undefined;
|
|
26
|
+
if (expirationTtl && expirationTtl < 60) {
|
|
27
|
+
throw new Error("Cloudflare's min expiration is '60s'");
|
|
28
|
+
}
|
|
26
29
|
return this.client.put(key, JSON.stringify(value), { expirationTtl });
|
|
27
30
|
}
|
|
28
31
|
|
package/src/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createId, isClass, parse } from "./utils.js";
|
|
|
4
4
|
class Store {
|
|
5
5
|
PREFIX = "";
|
|
6
6
|
|
|
7
|
-
constructor(clientPromise
|
|
7
|
+
constructor(clientPromise) {
|
|
8
8
|
this.promise = Promise.resolve(clientPromise).then(async (client) => {
|
|
9
9
|
this.client = this.#find(client);
|
|
10
10
|
this.#validate(this.client);
|
|
@@ -33,10 +33,9 @@ class Store {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
#validate(client) {
|
|
36
|
+
if (!client) throw new Error("No client received");
|
|
36
37
|
if (!client.set || !client.get || !client.iterate) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
"A client should have at least a .get(), .set() and .iterate()"
|
|
39
|
-
);
|
|
38
|
+
throw new Error("Client should have .get(), .set() and .iterate()");
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
if (!client.EXPIRES) {
|
package/src/utils.js
CHANGED
|
@@ -9,7 +9,7 @@ parse.week = parse.wk = parse.w = parse.d * 7;
|
|
|
9
9
|
parse.year = parse.yr = parse.y = parse.d * 365.25;
|
|
10
10
|
parse.month = parse.b = parse.y / 12;
|
|
11
11
|
|
|
12
|
-
// Returns the time in
|
|
12
|
+
// Returns the time in seconds
|
|
13
13
|
export function parse(str) {
|
|
14
14
|
if (str === null || str === undefined) return null;
|
|
15
15
|
if (typeof str === "number") return str;
|