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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polystore",
3
- "version": "0.11.5",
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 && check-dts test/index.types.ts"
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` (casted to `null`), a Symbol, etc.
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 * 1000 });
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 * 1000 });
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 = new Map()) {
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 milliseconds
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;