polystore 0.4.5 → 0.6.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,22 +1,28 @@
1
1
  {
2
2
  "name": "polystore",
3
- "version": "0.4.5",
3
+ "version": "0.6.0",
4
4
  "description": "A small compatibility layer for many popular KV stores like localStorage, Redis, FileSystem, etc.",
5
5
  "homepage": "https://github.com/franciscop/polystore",
6
6
  "repository": "https://github.com/franciscop/polystore.git",
7
7
  "bugs": "https://github.com/franciscop/polystore/issues",
8
8
  "funding": "https://www.paypal.me/franciscopresencia/19",
9
9
  "author": "Francisco Presencia <public@francisco.io> (https://francisco.io/)",
10
- "main": "index.min.js",
11
- "types": "index.d.ts",
10
+ "main": "src/index.js",
11
+ "types": "src/index.d.ts",
12
12
  "type": "module",
13
13
  "scripts": {
14
- "build": "esbuild src/index.js --bundle --minify --sourcemap --outfile=index.min.js --format=esm",
15
- "size": "echo $(gzip -c index.min.js | wc -c) bytes",
14
+ "size": "echo $(gzip -c src/index.js | wc -c) bytes",
16
15
  "start": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch --coverage --detectOpenHandles",
17
- "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --detectOpenHandles && check-dts"
16
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --detectOpenHandles && check-dts src/index.types.ts"
18
17
  },
19
- "keywords": [],
18
+ "keywords": [
19
+ "kv",
20
+ "store",
21
+ "polystore",
22
+ "key-value",
23
+ "key",
24
+ "value"
25
+ ],
20
26
  "license": "MIT",
21
27
  "devDependencies": {
22
28
  "check-dts": "^0.7.2",
package/readme.md CHANGED
@@ -1,4 +1,4 @@
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/blob/master/.github/workflows/tests.yml) [![gzip size](https://img.badgesize.io/franciscop/polystore/master/index.min.js.svg?compression=gzip)](https://github.com/franciscop/polystore/blob/master/index.min.js)
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/blob/master/.github/workflows/tests.yml) [![gzip size](https://img.badgesize.io/franciscop/polystore/master/src/index.js.svg?compression=gzip)](https://github.com/franciscop/polystore/blob/master/src/index.js)
2
2
 
3
3
  A small compatibility layer for many KV stores like localStorage, Redis, FileSystem, etc:
4
4
 
@@ -14,9 +14,12 @@ This is the [API](#api) with all of the methods (they are all `async`):
14
14
 
15
15
  - `.get(key): any`: retrieve a single value, or `null` if it doesn't exist or is expired.
16
16
  - `.set(key, value, options?)`: save a single value, which can be anything that is serializable.
17
+ - `.add(value, options?)`: same as with `.set()`, but auto-generate the key.
17
18
  - `.has(key): boolean`: check whether the key is in the store or not.
18
19
  - `.del(key)`: delete a single value from the store.
19
20
  - `.keys(prefix?): string[]`: get a list of all the available strings in the store.
21
+ - `.values(prefix?): any[]`: get a list of all the values in the store.
22
+ - `.entries(prefix?): [string, any][]`: get a list of all the key-value pairs in the store.
20
23
  - `.clear()`: delete ALL of the data in the store, effectively resetting it.
21
24
  - `.close()`: (only _some_ stores) ends the connection to the store.
22
25
 
@@ -27,12 +30,12 @@ Available stores:
27
30
  - **Session Storage** `sessionStorage` (fe): persist the data in the browser's sessionStorage
28
31
  - **Cookies** `"cookie"` (fe): persist the data using cookies
29
32
  - **LocalForage** `localForage` (fe): persist the data on IndexedDB
30
- - **Redis Client** `redisClient` (be): persist the data in the Redis instance that you connect to
31
33
  - **FS File** `new URL('file:///...')` (be): store the data in a single JSON file
32
- - (WIP) **Cloudflare KV** `env.KV_NAMESPACE` (be): use Cloudflare's KV store
34
+ - **Redis Client** `redisClient` (be): use the Redis instance that you connect to
35
+ - **Cloudflare KV** `env.KV_NAMESPACE` (be): use Cloudflare's KV store
33
36
  - (WIP) **Consul KV** `new Consul()` (fe+be): use Hashicorp's Consul KV store (https://www.npmjs.com/package/consul#kv)
34
37
 
35
- I build this library to be used as a "building block" of other libraries, so that _your library_ can accept many cache stores effortlessly! It's isomorphic (Node.js and the Browser) and tiny (1~2KB). For example, let's say you create an API library, then you can accept the stores from your client:
38
+ I made this library to be used as a "building block" of other libraries, so that _your library_ can accept many cache stores effortlessly! It's isomorphic (Node.js and the Browser) and tiny (~2KB). For example, let's say you create an API library, then you can accept the stores from your client:
36
39
 
37
40
  ```js
38
41
  import MyApi from "my-api";
@@ -91,47 +94,71 @@ If the value is returned, it can be a simple type like `boolean`, `string` or `n
91
94
 
92
95
  ### .set()
93
96
 
94
- Create or update a value in the store. Will return a promise that resolves when the value has been saved. The value needs to be serializable:
97
+ Create or update a value in the store. Will return a promise that resolves with the key when the value has been saved. The value needs to be serializable:
95
98
 
96
99
  ```js
97
- await store.set(key: string, value: any, options?: { expire: number|string });
100
+ await store.set(key: string, value: any, options?: { expires: number|string });
98
101
 
99
102
  await store.set("key1", "Hello World");
100
- await store.set("key2", ["my", "grocery", "list"], { expire: "1h" });
101
- await store.set("key3", { name: "Francisco" }, { expire: 60 * 60 * 1000 });
103
+ await store.set("key2", ["my", "grocery", "list"], { expires: "1h" });
104
+ await store.set("key3", { name: "Francisco" }, { expires: 60 * 60 * 1000 });
102
105
  ```
103
106
 
104
107
  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.
105
108
 
106
109
  - By default the keys _don't expire_.
107
- - Setting the `value` to `null`, or the `expire` to `0` is the equivalent of deleting the key+value.
108
- - Conversely, setting `expire` to `null` or `undefined` will make the value never to expire.
110
+ - Setting the `value` to `null`, or the `expires` to `0` is the equivalent of deleting the key+value.
111
+ - Conversely, setting `expires` to `null` or `undefined` will make the value never to expire.
109
112
 
110
- #### Expire
113
+ #### Expires
111
114
 
112
- When the `expire` option is set, it can be a number (ms) or a string representing some time:
115
+ When the `expires` option is set, it can be a number (**seconds**) or a string representing some time:
113
116
 
114
117
  ```js
115
118
  // Valid "expire" values:
116
119
  0 - expire immediately (AKA delete it)
117
- 100 - expire after 100ms
118
- 60 * 60 * 1000 - expire after 1h
119
- 3_600_000 - expire after 1h
120
+ 0.1 - expire after 100ms*
121
+ 60 * 60 - expire after 1h
122
+ 3_600 - expire after 1h
120
123
  "10s" - expire after 10 seconds
121
124
  "2minutes" - expire after 2 minutes
122
125
  "5d" - expire after 5 days
123
126
  ```
124
127
 
128
+ \* 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
129
+
125
130
  These are all the units available:
126
131
 
127
132
  > "ms", "millisecond", "s", "sec", "second", "m", "min", "minute", "h", "hr", "hour", "d", "day", "w", "wk", "week", "b" (month), "month", "y", "yr", "year"
128
133
 
134
+ ### .add()
135
+
136
+ Create a value in the store with a random key string. Will return a promise that resolves with the key when the value has been saved. The value needs to be serializable:
137
+
138
+ ```js
139
+ const key:string = await store.add(value: any, options?: { expires: number|string });
140
+
141
+ const key1 = await store.add("Hello World");
142
+ const key2 = await store.add(["my", "grocery", "list"], { expires: "1h" });
143
+ const key3 = await store.add({ name: "Francisco" }, { expires: 60 * 60 * 1000 });
144
+ ```
145
+
146
+ 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.
147
+
148
+ Here is the safety: "If you generate 1 million keys/second, it will take ~14 million years in order to have a 1% probability of at least one collision."
149
+
150
+ > 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.
151
+
129
152
  ### .has()
130
153
 
131
154
  Check whether the key is available in the store and not expired:
132
155
 
133
156
  ```js
134
157
  await store.has(key: string);
158
+
159
+ if (await store.has('cookie-consent')) {
160
+ loadCookies();
161
+ }
135
162
  ```
136
163
 
137
164
  ### .del()
@@ -150,6 +177,45 @@ Get all of the keys in the store, optionally filtered by a prefix:
150
177
  await store.keys(filter?: string);
151
178
  ```
152
179
 
180
+ > We ensure that all of the keys returned by this method are _not_ expired, while discarding any potentially expired key. See [**expiration explained**](#expiration-explained) for more details.
181
+
182
+ ### .values()
183
+
184
+ Get all of the values in the store, optionally filtered by a **key** prefix:
185
+
186
+ ```js
187
+ await store.values(filter?: string);
188
+ ```
189
+
190
+ This is useful specially when you already have the id/key within the value as an object, then you can just get a list of all of them:
191
+
192
+ ```js
193
+ const sessions = await store.values("session:");
194
+ // A list of all the sessions
195
+
196
+ const companies = await store.values("company:");
197
+ // A list of all the companies
198
+ ```
199
+
200
+ > We ensure that all of the values returned by this method are _not_ expired, while discarding any potentially expired key. See [**expiration explained**](#expiration-explained) for more details.
201
+
202
+ ### .entries()
203
+
204
+ Get all of the entries (key:value tuples) in the store, optionally filtered by a **key** prefix:
205
+
206
+ ```js
207
+ await store.entries(filter?: string);
208
+ ```
209
+
210
+ It is in a format that you can easily build an object out of it:
211
+
212
+ ```js
213
+ const sessionEntries = await store.entries("session:");
214
+ const sessions = Object.fromEntries(sessionEntries);
215
+ ```
216
+
217
+ > We ensure that all of the entries returned by this method are _not_ expired, while discarding any potentially expired key. See [**expiration explained**](#expiration-explained) for more details.
218
+
153
219
  ### .clear()
154
220
 
155
221
  Remove all of the data from the store:
@@ -164,35 +230,33 @@ Create a sub-store where all the operations use the given prefix:
164
230
 
165
231
  ```js
166
232
  const store = kv(new Map());
167
- const sub = store.prefix("session:");
168
-
169
- const sub = kv(new Map(), { prefix: "session:" });
233
+ const session = store.prefix("session:");
170
234
  ```
171
235
 
172
236
  Then all of the operations will be converted internally to add the prefix when reading, writing, etc:
173
237
 
174
238
  ```js
175
- const val = await sub.get("key1"); // .get('session:key1');
176
- await sub.set("key2", "some data"); // .set('session:key2', ...);
177
- const val = await sub.has("key3"); // .has('session:key3');
178
- await sub.del("key4"); // .del('session:key4');
179
- await sub.keys(); // .keys('session:');
239
+ const val = await session.get("key1"); // .get('session:key1');
240
+ await session.set("key2", "some data"); // .set('session:key2', ...);
241
+ const val = await session.has("key3"); // .has('session:key3');
242
+ await session.del("key4"); // .del('session:key4');
243
+ await session.keys(); // .keys('session:');
180
244
  // ['key1', 'key2', ...] Note no prefix here
181
- await sub.clear(); // delete only keys with the prefix
245
+ await session.clear(); // delete only keys with the prefix
182
246
  ```
183
247
 
184
248
  This will probably never be stable given the nature of some engines, so as an alternative please consider using two stores instead of prefixes:
185
249
 
186
250
  ```js
187
251
  const store = kv(new Map());
188
- const sessionStore = kv(new Map());
252
+ const session = kv(new Map());
189
253
  ```
190
254
 
191
255
  The main reason this is not stable is because [_some_ store engines don't allow for atomic deletion of keys given a prefix](https://stackoverflow.com/q/4006324/938236). While we do still clear them internally in those cases, that is a non-atomic operation and it could have some trouble if some other thread is reading/writing the data _at the same time_.
192
256
 
193
257
  ## Stores
194
258
 
195
- Accepts directly the store, or a promise that resolves into a store. All of the stores, including those that natively _don't_ support it, are enhanced with `Promises` and `expire` times, so they all work the same way.
259
+ Accepts directly the store, or a promise that resolves into a store. All of the stores, including those that natively _don't_ support it, are enhanced with `Promises` and `expires` times, so they all work the same way.
196
260
 
197
261
  ### Memory
198
262
 
@@ -253,18 +317,18 @@ console.log(await store.get("key1"));
253
317
 
254
318
  It is fairly limited for how powerful cookies are, but in exchange it has the same API as any other method or KV store. It works with browser-side Cookies (no http-only).
255
319
 
256
- > Note: the cookie expire resolution is in the seconds. While it still expects you to pass the number of ms as with the other methods (or [a string like `1h`](#expire)), times shorter than 1 second like `expire: 200` (ms) don't make sense for this storage method and won't properly save them.
320
+ > Note: the cookie expire resolution is in the seconds, so times shorter than 1 second like `expires: 0.02` (20 ms) don't make sense for this storage method and won't properly save them.
257
321
 
258
322
  ### Local Forage
259
323
 
260
- Supports localForage (with any driver it uses) so that you have a unified API. It also _adds_ the `expire` option to the setters!
324
+ Supports localForage (with any driver it uses) so that you have a unified API. It also _adds_ the `expires` option to the setters!
261
325
 
262
326
  ```js
263
327
  import kv from "polystore";
264
328
  import localForage from "localforage";
265
329
 
266
330
  const store = kv(localForage);
267
- await store.set("key1", "Hello world", { expire: "1h" });
331
+ await store.set("key1", "Hello world", { expires: "1h" });
268
332
  console.log(await store.get("key1"));
269
333
  ```
270
334
 
@@ -282,7 +346,7 @@ await store.set("key1", "Hello world");
282
346
  console.log(await store.get("key1"));
283
347
  ```
284
348
 
285
- > Note: the Redis client expire resolution is in the seconds. While it still expects you to pass the number of ms as with the other methods (or [a string like `1h`](#expire)), times shorter than 1 second like `expire: 200` (ms) don't make sense for this storage method and won't properly save them.
349
+ > Note: the Redis client expire resolution is in the seconds, so times shorter than 1 second like `expires: 0.02` (20 ms) don't make sense for this storage method and won't properly save them.
286
350
 
287
351
  ### FS File
288
352
 
@@ -299,7 +363,88 @@ const store = kv(new URL(`file://${process.cwd()}/cache.json`));
299
363
 
300
364
  ### Cloudflare KV
301
365
 
366
+ Supports the official Cloudflare's KV stores. Follow [the official guide](https://developers.cloudflare.com/kv/get-started/), then load it like this:
367
+
302
368
  ```js
303
369
  import kv from "polystore";
304
- // TODO
370
+
371
+ export default {
372
+ async fetch(request, env, ctx) {
373
+ const store = kv(env.YOUR_KV_NAMESPACE);
374
+
375
+ await store.set("KEY", "VALUE");
376
+ const value = await store.get("KEY");
377
+
378
+ if (!value) {
379
+ return new Response("Value not found", { status: 404 });
380
+ }
381
+ return new Response(value);
382
+ },
383
+ };
384
+ ```
385
+
386
+ Why? The Cloudflare native KV store only accepts strings and has you manually calculating timeouts, but as usual with `polystore` you can set/get any serializable value and set the timeout in a familiar format:
387
+
388
+ ```js
389
+ // GOOD - with polystore
390
+ await store.set("user", { name: "Francisco" }, { expires: "2days" });
391
+
392
+ // COMPLEX - With native Cloudflare KV
393
+ const serialValue = JSON.stringify({ name: "Francisco" });
394
+ const twoDaysInSeconds = 2 * 24 * 3600;
395
+ await env.YOUR_KV_NAMESPACE.put("user", serialValue, {
396
+ expirationTtl: twoDaysInSeconds,
397
+ });
398
+ ```
399
+
400
+ ## Expiration explained
401
+
402
+ While different engines do expiration slightly differently internally, in creating polystore we want to ensure certain constrains, which _can_ affect performance. For example, if you do this operation:
403
+
404
+ ```js
405
+ // in-memory store
406
+ const store = polystore(new Map());
407
+ await store.set("a", "b", { expires: "1s" });
408
+
409
+ // These checks of course work:
410
+ console.log(await store.keys()); // ['a']
411
+ console.log(await store.has("a")); // true
412
+ console.log(await store.get("a")); // 'b'
413
+
414
+ // Make sure the key is expired
415
+ await delay(2000); // 2s
416
+
417
+ // Not only the .get() is null, but `.has()` returns false, and .keys() ignores it
418
+ console.log(await store.keys()); // []
419
+ console.log(await store.has("a")); // false
420
+ console.log(await store.get("a")); // null
421
+ ```
422
+
423
+ This is great because with polystore we do ensure that if a key has expired, it doesn't show up in `.keys()`, `.entries()`, `.values()`, `.has()` or `.get()`.
424
+
425
+ However, in some stores this does come with some potential performance disadvantages. For example, both the in-memory example above and localStorage _don't_ have a native expiration/eviction process, so we have to store that information as metadata, meaning that even to check if a key exists we need to read and decode its value. For one or few keys it's not a problem, but for large sets this can become an issue.
426
+
427
+ 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.
428
+
429
+ ## Creating a store
430
+
431
+ A store needs at least 4 methods with these signatures:
432
+
433
+ ```js
434
+ const store = {};
435
+ store.get = (key: string) => Promise<any>;
436
+ store.set = (key: string, value: any, { expires: number }) => Promise<string>;
437
+ store.entries = (prefix: string = "") => Promise<[key:string, value:any][]>;
438
+ store.clear = () => Promise<null>;
439
+ ```
440
+
441
+ All of the other methods will be implemented on top of these if not available, but you can provide those as well for optimizations, incompatible APIs, etc. For example, `.set('a', null)` _should_ delete the key `a`, and for this you may provide a native implementation:
442
+
443
+ ```js
444
+ const native = myNativeStore();
445
+
446
+ const store = {};
447
+ store.get = (key) => native.getItem(key);
448
+ // ...
449
+ store.del = (key) => native.deleteItem(key);
305
450
  ```
package/src/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ type Key = string;
2
+ type Options = { expires?: number | string | null };
3
+
4
+ type Store = {
5
+ get: (key: Key) => Promise<any>;
6
+ add: (value: any, options?: Options) => Promise<Key>;
7
+ set: (key: Key, value: any, options?: Options) => Promise<Key>;
8
+ has: (key: Key) => Promise<boolean>;
9
+ del: (key: Key) => Promise<null>;
10
+
11
+ keys: (prefix?: string) => Promise<string[]>;
12
+ values: (prefix?: string) => Promise<any[]>;
13
+ entries: (prefix?: string) => Promise<[key: string, value: any][]>;
14
+
15
+ clear: () => Promise<null>;
16
+ close?: () => Promise<null>;
17
+ };
18
+
19
+ export default function (store?: any): Store;
package/src/index.js CHANGED
@@ -2,8 +2,8 @@ const layers = {};
2
2
 
3
3
  const times = /(-?(?:\d+\.?\d*|\d*\.?\d+)(?:e[-+]?\d+)?)\s*([\p{L}]*)/iu;
4
4
 
5
- parse.millisecond = parse.ms = 1;
6
- parse.second = parse.sec = parse.s = parse[""] = parse.ms * 1000;
5
+ parse.millisecond = parse.ms = 0.001;
6
+ parse.second = parse.sec = parse.s = parse[""] = 1;
7
7
  parse.minute = parse.min = parse.m = parse.s * 60;
8
8
  parse.hour = parse.hr = parse.h = parse.m * 60;
9
9
  parse.day = parse.d = parse.h * 24;
@@ -22,135 +22,238 @@ function parse(str) {
22
22
  const unitValue = parse[units] || parse[units.replace(/s$/, "")];
23
23
  if (!unitValue) return null;
24
24
  const result = unitValue * parseFloat(value, 10);
25
- return Math.abs(Math.round(result));
25
+ return Math.abs(Math.round(result * 1000) / 1000);
26
26
  }
27
27
 
28
+ // "nanoid" imported manually
29
+ // Something about improved GZIP performance with this string
30
+ const urlAlphabet =
31
+ "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
32
+
33
+ export let random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
34
+
35
+ function generateId() {
36
+ let size = 24;
37
+ let id = "";
38
+ let bytes = crypto.getRandomValues(new Uint8Array(size));
39
+ while (size--) {
40
+ // Using the bitwise AND operator to "cap" the value of
41
+ // the random byte from 255 to 63, in that way we can make sure
42
+ // that the value will be a valid index for the "chars" string.
43
+ id += urlAlphabet[bytes[size] & 61];
44
+ }
45
+ return id;
46
+ }
47
+
48
+ layers.extra = (store) => {
49
+ const add = async (value, options) => store.set(generateId(), value, options);
50
+ const has = async (key) => (await store.get(key)) !== null;
51
+ const del = async (key) => store.set(key, null);
52
+ const keys = async (prefix = "") => {
53
+ const all = await store.entries(prefix);
54
+ return all.map((p) => p[0]);
55
+ };
56
+ const values = async (prefix = "") => {
57
+ const all = await store.entries(prefix);
58
+ return all.map((p) => p[1]);
59
+ };
60
+ return { add, has, del, keys, values, ...store };
61
+ };
62
+
63
+ // Adds an expiration layer to those stores that don't have it;
64
+ // it's not perfect since it's not deleted until it's read, but
65
+ // hey it's better than nothing
28
66
  layers.expire = (store) => {
29
67
  // Item methods
30
68
  const get = async (key) => {
31
- if (!(await store.has(key))) return null;
32
- const { data, expire } = await store.get(key);
33
- if (expire === null) return data;
69
+ const data = await store.get(key);
70
+ if (!data) return null;
71
+ const { value, expire } = data;
72
+ // It never expires
73
+ if (expire === null) return value;
34
74
  const diff = expire - new Date().getTime();
35
75
  if (diff <= 0) return null;
36
- return data;
76
+ return value;
37
77
  };
38
- const set = async (key, data, { expire = null } = {}) => {
39
- const time = parse(expire);
40
- const expDiff = time !== null ? new Date().getTime() + time : null;
41
- return store.set(key, { expire: expDiff, data });
78
+ const set = async (key, value, { expire, expires } = {}) => {
79
+ const time = parse(expire || expires);
80
+ // Already expired, or do _not_ save it, then delete it
81
+ if (value === null || time === 0) return store.set(key, null);
82
+ const expDiff = time !== null ? new Date().getTime() + time * 1000 : null;
83
+ return store.set(key, { expire: expDiff, value });
42
84
  };
43
- const has = async (key) => (await store.get(key)) !== null;
44
- const del = store.del;
45
85
 
46
86
  // Group methods
47
- const keys = store.keys;
48
- const clear = store.clear;
87
+ const entries = async (prefix = "") => {
88
+ const all = await store.entries(prefix);
89
+ const now = new Date().getTime();
90
+ return all
91
+ .filter(([, data]) => {
92
+ // There's no data, so remove this
93
+ if (!data || data === null) return false;
94
+
95
+ // It never expires, so keep it
96
+ const { expire } = data;
97
+ if (expire === null) return true;
98
+
99
+ // It's expired, so remove it
100
+ if (expire - now <= 0) return false;
101
+
102
+ // It's not expired, keep it
103
+ return true;
104
+ })
105
+ .map(([key, data]) => [key, data.value]);
106
+ };
49
107
 
50
- return { get, set, has, del, keys, clear };
108
+ // We want to force overwrite here!
109
+ return { ...store, get, set, entries };
51
110
  };
52
111
 
53
112
  layers.memory = (store) => {
54
113
  // Item methods
55
- const get = async (key) => store.get(key) || null;
56
- const set = async (key, data) => store.set(key, data);
57
- const has = async (key) => store.has(key);
58
- const del = async (key) => store.delete(key);
114
+ const get = async (key) => store.get(key) ?? null;
115
+ const set = async (key, data) => {
116
+ if (data === null) {
117
+ await store.delete(key);
118
+ } else {
119
+ await store.set(key, data);
120
+ }
121
+ return key;
122
+ };
59
123
 
60
124
  // Group methods
61
- const keys = async (prefix = "") =>
62
- [...(await store.keys())].filter((k) => k.startsWith(prefix));
125
+ const entries = async (prefix = "") => {
126
+ const entries = [...store.entries()];
127
+ return entries.filter((p) => p[0].startsWith(prefix));
128
+ };
63
129
  const clear = () => store.clear();
64
130
 
65
- return { get, set, has, del, keys, clear };
131
+ return { get, set, entries, clear };
66
132
  };
67
133
 
68
134
  layers.storage = (store) => {
69
135
  // Item methods
70
136
  const get = async (key) => (store[key] ? JSON.parse(store[key]) : null);
71
- const set = async (key, data) => store.setItem(key, JSON.stringify(data));
72
- const has = async (key) => key in store;
73
- const del = async (key) => store.removeItem(key);
137
+ const set = async (key, data) => {
138
+ if (data === null) {
139
+ await store.removeItem(key);
140
+ } else {
141
+ await store.setItem(key, JSON.stringify(data));
142
+ }
143
+ return key;
144
+ };
74
145
 
75
146
  // Group methods
76
- const keys = async (prefix = "") =>
77
- Object.keys(store).filter((k) => k.startsWith(prefix));
147
+ const entries = async (prefix = "") => {
148
+ const entries = Object.entries(store);
149
+ return entries
150
+ .map((p) => [p[0], p[1] ? JSON.parse(p[1]) : null])
151
+ .filter((p) => p[0].startsWith(prefix));
152
+ };
78
153
  const clear = () => store.clear();
79
154
 
80
- return { get, set, has, del, keys, clear };
155
+ return { get, set, entries, clear };
81
156
  };
82
157
 
158
+ // Cookies auto-expire, so we cannot do expiration checks manually
83
159
  layers.cookie = () => {
84
- const get = async (key) => {
85
- const value =
86
- document.cookie
87
- .split("; ")
88
- .filter(Boolean)
89
- .find((row) => row.startsWith(key + "="))
90
- ?.split("=")[1] || null;
91
- return JSON.parse(decodeURIComponent(value));
160
+ const getAll = () => {
161
+ const all = {};
162
+ for (let entry of document.cookie
163
+ .split(";")
164
+ .map((k) => k.trim())
165
+ .filter(Boolean)) {
166
+ const [key, data] = entry.split("=");
167
+ try {
168
+ all[key.trim()] = JSON.parse(decodeURIComponent(data.trim()));
169
+ } catch (error) {
170
+ // no-op (some 3rd party can set cookies independently)
171
+ }
172
+ }
173
+ return all;
92
174
  };
93
175
 
94
- const set = async (key, data, { expire = null } = {}) => {
95
- const time = parse(expire);
96
- const now = new Date().getTime();
97
- const expireStr =
98
- time !== null ? `; expires=${new Date(now + time).toUTCString()}` : "";
99
- const value = encodeURIComponent(JSON.stringify(data));
100
- document.cookie = key + "=" + value + expireStr;
176
+ const get = async (key) => getAll()[key] ?? null;
177
+
178
+ const set = async (key, data, { expire, expires } = {}) => {
179
+ if (data === null) {
180
+ await set(key, "", { expire: -100 });
181
+ } else {
182
+ const time = parse(expire || expires);
183
+ const now = new Date().getTime();
184
+ // NOTE: 0 is already considered here!
185
+ const expireStr =
186
+ time !== null
187
+ ? `; expires=${new Date(now + time * 1000).toUTCString()}`
188
+ : "";
189
+ const value = encodeURIComponent(JSON.stringify(data));
190
+ document.cookie = key + "=" + value + expireStr;
191
+ }
192
+ return key;
101
193
  };
102
- const has = async (key) => (await keys()).includes(key);
103
- const del = async (key) => set(key, "", { expire: -100 });
104
194
 
105
195
  // Group methods
106
- const keys = async (prefix = "") =>
107
- document.cookie
108
- .split(";")
109
- .map((l) => l.split("=")[0].trim())
110
- .filter(Boolean)
111
- .filter((k) => k.startsWith(prefix));
196
+ const entries = async (prefix = "") => {
197
+ const all = Object.entries(getAll());
198
+ return all.filter((p) => p[0].startsWith(prefix));
199
+ };
200
+
112
201
  const clear = async () => {
113
- await Promise.all((await keys()).map(del));
202
+ const keys = Object.keys(getAll());
203
+ await Promise.all(keys.map((key) => set(key, null)));
114
204
  };
115
205
 
116
- return { get, set, has, del, keys, clear };
206
+ return { get, set, entries, clear };
117
207
  };
118
208
 
209
+ // Plain 'redis' and not ioredis or similar
119
210
  layers.redis = (store) => {
120
211
  const get = async (key) => {
121
- const client = await store;
122
- const value = await client.get(key);
212
+ const value = await store.get(key);
123
213
  if (!value) return null;
124
214
  return JSON.parse(value);
125
215
  };
126
- const set = async (key, value, { expire = null } = {}) => {
127
- if (value === null || expire === 0) return del(key);
128
- const client = await store;
129
- const exp = parse(expire);
130
- const EX = exp ? Math.round(exp / 1000) : undefined;
131
- return client.set(key, JSON.stringify(value), { EX });
216
+ const set = async (key, value, { expire, expires } = {}) => {
217
+ const time = parse(expire || expires);
218
+ if (value === null || time === 0) return del(key);
219
+ const EX = time ? Math.round(time) : undefined;
220
+ await store.set(key, JSON.stringify(value), { EX });
221
+ return key;
132
222
  };
133
- const has = async (key) => Boolean(await (await store).exists(key));
134
- const del = async (key) => (await store).del(key);
135
-
136
- const keys = async (prefix = "") => (await store).keys(prefix + "*");
137
- const clear = async () => (await store).flushAll();
138
- const close = async () => (await store).quit();
223
+ const has = async (key) => Boolean(await store.exists(key));
224
+ const del = async (key) => store.del(key);
225
+
226
+ const keys = async (prefix = "") => store.keys(prefix + "*");
227
+ const entries = async (prefix = "") => {
228
+ const keys = await store.keys(prefix + "*");
229
+ const values = await Promise.all(keys.map((k) => get(k)));
230
+ return keys.map((k, i) => [k, values[i]]);
231
+ };
232
+ const clear = async () => store.flushAll();
233
+ const close = async () => store.quit();
139
234
 
140
- return { get, set, has, del, keys, clear, close };
235
+ return { get, set, has, del, keys, entries, clear, close };
141
236
  };
142
237
 
143
238
  layers.localForage = (store) => {
144
- const get = (key) => store.getItem(key);
145
- const set = (key, value) => store.setItem(key, value);
146
- const has = async (key) => (await get(key)) !== null;
147
- const del = (key) => store.removeItem(key);
148
-
149
- const keys = async (prefix = "") =>
150
- (await store.keys()).filter((k) => k.startsWith(prefix));
151
- const clear = () => store.clear();
239
+ const get = async (key) => store.getItem(key);
240
+ const set = async (key, value) => {
241
+ if (value === null) {
242
+ await store.removeItem(key);
243
+ } else {
244
+ await store.setItem(key, value);
245
+ }
246
+ return key;
247
+ };
248
+ const entries = async (prefix = "") => {
249
+ const all = await store.keys();
250
+ const keys = all.filter((k) => k.startsWith(prefix));
251
+ const values = await Promise.all(keys.map((key) => store.getItem(key)));
252
+ return keys.map((key, i) => [key, values[i]]);
253
+ };
254
+ const clear = async () => store.clear();
152
255
 
153
- return { get, set, has, del, keys, clear };
256
+ return { get, set, entries, clear };
154
257
  };
155
258
 
156
259
  layers.cloudflare = (store) => {
@@ -159,18 +262,29 @@ layers.cloudflare = (store) => {
159
262
  if (!data) return null;
160
263
  return JSON.parse(data);
161
264
  };
162
- const set = async (key, value, { expire }) => {
163
- if (value === null || expire === 0) return del(key);
265
+ const set = async (key, value, { expire, expires } = {}) => {
266
+ const time = parse(expire || expires);
267
+ if (value === null || time === 0) return del(key);
164
268
  const client = await store;
165
- const exp = parse(expire);
166
- const expirationTtl = exp ? Math.round(exp / 1000) : undefined;
167
- return client.put(key, JSON.stringify(value), { expirationTtl });
269
+ const expirationTtl = time ? Math.round(time) : undefined;
270
+ client.put(key, JSON.stringify(value), { expirationTtl });
271
+ return key;
168
272
  };
169
273
  const has = async (key) => Boolean(await store.get(key));
170
274
  const del = (key) => store.delete(key);
171
- const keys = (prefix) => store.list({ prefix });
275
+
276
+ // Group methods
277
+ const keys = async (prefix = "") => {
278
+ const raw = await store.list({ prefix });
279
+ return raw.keys;
280
+ };
281
+ const entries = async (prefix = "") => {
282
+ const all = await keys(prefix);
283
+ const values = await Promise.all(all.map((k) => get(k)));
284
+ return all.map((key, i) => [key, values[i]]);
285
+ };
172
286
  const clear = () => {};
173
- return { get, set, has, del, keys, clear };
287
+ return { get, set, has, del, entries, keys, clear };
174
288
  };
175
289
 
176
290
  layers.file = (file) => {
@@ -202,57 +316,60 @@ layers.file = (file) => {
202
316
  };
203
317
  const set = async (key, value) => {
204
318
  const data = await getContent();
205
- data[key] = value;
319
+ if (value === null) {
320
+ delete data[key];
321
+ } else {
322
+ data[key] = value;
323
+ }
206
324
  await setContent(data);
325
+ return key;
207
326
  };
208
327
  const has = async (key) => (await get(key)) !== null;
209
- const del = async (key) => {
210
- const data = await getContent();
211
- delete data[key];
212
- await setContent(data);
213
- };
214
- const keys = async (prefix = "") => {
328
+ const del = async (key) => set(key, null);
329
+
330
+ // Group methods
331
+ const entries = async (prefix = "") => {
215
332
  const data = await getContent();
216
- return Object.keys(data).filter((k) => k.startsWith(prefix));
333
+ return Object.entries(data).filter((p) => p[0].startsWith(prefix));
217
334
  };
218
335
  const clear = async () => {
219
336
  await setContent({});
220
337
  };
221
- return { get, set, has, del, keys, clear };
338
+ return { get, set, has, del, entries, clear };
222
339
  };
223
340
 
224
341
  const getStore = async (store) => {
225
342
  // Convert it to the normalized kv, then add the expiry layer on top
226
343
  if (store instanceof Map) {
227
- return layers.expire(layers.memory(store));
344
+ return layers.extra(layers.expire(layers.memory(store)));
228
345
  }
229
346
 
230
347
  if (typeof localStorage !== "undefined" && store === localStorage) {
231
- return layers.expire(layers.storage(store));
348
+ return layers.extra(layers.expire(layers.storage(store)));
232
349
  }
233
350
 
234
351
  if (typeof sessionStorage !== "undefined" && store === sessionStorage) {
235
- return layers.expire(layers.storage(store));
352
+ return layers.extra(layers.expire(layers.storage(store)));
236
353
  }
237
354
 
238
355
  if (store === "cookie") {
239
- return layers.cookie();
356
+ return layers.extra(layers.cookie());
240
357
  }
241
358
 
242
359
  if (store.defineDriver && store.dropInstance && store.INDEXEDDB) {
243
- return layers.expire(layers.localForage(store));
360
+ return layers.extra(layers.expire(layers.localForage(store)));
244
361
  }
245
362
 
246
363
  if (store.protocol && store.protocol === "file:") {
247
- return layers.expire(layers.file(store));
364
+ return layers.extra(layers.expire(layers.file(store)));
248
365
  }
249
366
 
250
367
  if (store.pSubscribe && store.sSubscribe) {
251
- return layers.redis(store);
368
+ return layers.extra(layers.redis(store));
252
369
  }
253
370
 
254
371
  if (store?.constructor?.name === "KvNamespace") {
255
- return layers.cloudflare(store);
372
+ return layers.extra(layers.cloudflare(store));
256
373
  }
257
374
 
258
375
  // ¯\_(ツ)_/¯
@@ -263,17 +380,22 @@ export default function compat(storeClient = new Map()) {
263
380
  return new Proxy(
264
381
  {},
265
382
  {
266
- get: (_, key) => {
383
+ get: (instance, key) => {
267
384
  return async (...args) => {
268
- const store = await getStore(await storeClient);
385
+ // Only once, even if called twice in succession, since the
386
+ // second time will go straight to the await
387
+ if (!instance.store && !instance.promise) {
388
+ instance.promise = getStore(await storeClient);
389
+ }
390
+ instance.store = await instance.promise;
269
391
  // Throw at the first chance when the store failed to init:
270
- if (!store) {
392
+ if (!instance.store) {
271
393
  throw new Error("Store is not valid");
272
394
  }
273
395
  // The store.close() is the only one allowed to be called even
274
396
  // if it doesn't exist, since it's optional in some stores
275
- if (!store[key] && key === "close") return null;
276
- return store[key](...args);
397
+ if (!instance.store[key] && key === "close") return null;
398
+ return instance.store[key](...args);
277
399
  };
278
400
  },
279
401
  }
package/src/index.test.js CHANGED
@@ -46,10 +46,20 @@ for (let [name, store] of stores) {
46
46
  expect(await store.has("a")).toBe(false);
47
47
  });
48
48
 
49
+ it("can add() arbitrary values", async () => {
50
+ const key = await store.add("b");
51
+ expect(typeof key).toBe("string");
52
+ expect(await store.get(key)).toBe("b");
53
+ expect(await store.has(key)).toBe(true);
54
+ expect(key.length).toBe(24);
55
+ expect(key).toMatch(/^[a-zA-Z0-9]{24}$/);
56
+ });
57
+
49
58
  it("can store values", async () => {
50
- await store.set("a", "b");
59
+ const key = await store.set("a", "b");
51
60
  expect(await store.get("a")).toBe("b");
52
61
  expect(await store.has("a")).toBe(true);
62
+ expect(key).toBe("a");
53
63
  });
54
64
 
55
65
  it("can store basic types", async () => {
@@ -62,8 +72,8 @@ for (let [name, store] of stores) {
62
72
  });
63
73
 
64
74
  it("can store arrays of JSON values", async () => {
65
- await store.set("a", ["b"]);
66
- expect(await store.get("a")).toEqual(["b"]);
75
+ await store.set("a", ["b", "c"]);
76
+ expect(await store.get("a")).toEqual(["b", "c"]);
67
77
  expect(await store.has("a")).toBe(true);
68
78
  });
69
79
 
@@ -73,22 +83,83 @@ for (let [name, store] of stores) {
73
83
  expect(await store.has("a")).toBe(true);
74
84
  });
75
85
 
76
- it("can retrieve the prefixed keys with colon", async () => {
86
+ it("can get the keys", async () => {
87
+ await store.set("a", "b");
88
+ await store.set("c", "d");
89
+ expect(await store.keys()).toEqual(["a", "c"]);
90
+ });
91
+
92
+ it("can get the values", async () => {
93
+ await store.set("a", "b");
94
+ await store.set("c", "d");
95
+ expect(await store.values()).toEqual(["b", "d"]);
96
+ });
97
+
98
+ it("can get the entries", async () => {
99
+ await store.set("a", "b");
100
+ await store.set("c", "d");
101
+ expect(await store.entries()).toEqual([
102
+ ["a", "b"],
103
+ ["c", "d"],
104
+ ]);
105
+ });
106
+
107
+ it("can get the keys with a colon prefix", async () => {
77
108
  await store.set("a:0", "a0");
78
109
  await store.set("a:1", "a1");
79
110
  await store.set("b:0", "b0");
80
- await store.set("a:2", "b2");
111
+ await store.set("a:2", "a2");
81
112
  expect((await store.keys("a:")).sort()).toEqual(["a:0", "a:1", "a:2"]);
82
113
  });
83
114
 
84
- it("can retrieve the prefixed keys with dash", async () => {
115
+ it("can get the values with a colon prefix", async () => {
116
+ await store.set("a:0", "a0");
117
+ await store.set("a:1", "a1");
118
+ await store.set("b:0", "b0");
119
+ await store.set("a:2", "a2");
120
+ expect((await store.values("a:")).sort()).toEqual(["a0", "a1", "a2"]);
121
+ });
122
+
123
+ it("can get the entries with a colon prefix", async () => {
124
+ await store.set("a:0", "a0");
125
+ await store.set("a:1", "a1");
126
+ await store.set("b:0", "b0");
127
+ await store.set("a:2", "a2");
128
+ expect((await store.entries("a:")).sort()).toEqual([
129
+ ["a:0", "a0"],
130
+ ["a:1", "a1"],
131
+ ["a:2", "a2"],
132
+ ]);
133
+ });
134
+
135
+ it("can get the keys with a dash prefix", async () => {
85
136
  await store.set("a-0", "a0");
86
137
  await store.set("a-1", "a1");
87
138
  await store.set("b-0", "b0");
88
- await store.set("a-2", "b2");
139
+ await store.set("a-2", "a2");
89
140
  expect((await store.keys("a-")).sort()).toEqual(["a-0", "a-1", "a-2"]);
90
141
  });
91
142
 
143
+ it("can get the values with a dash prefix", async () => {
144
+ await store.set("a-0", "a0");
145
+ await store.set("a-1", "a1");
146
+ await store.set("b-0", "b0");
147
+ await store.set("a-2", "a2");
148
+ expect((await store.values("a-")).sort()).toEqual(["a0", "a1", "a2"]);
149
+ });
150
+
151
+ it("can get the entries with a dash prefix", async () => {
152
+ await store.set("a-0", "a0");
153
+ await store.set("a-1", "a1");
154
+ await store.set("b-0", "b0");
155
+ await store.set("a-2", "a2");
156
+ expect((await store.entries("a-")).sort()).toEqual([
157
+ ["a-0", "a0"],
158
+ ["a-1", "a1"],
159
+ ["a-2", "a2"],
160
+ ]);
161
+ });
162
+
92
163
  it("can delete the data", async () => {
93
164
  await store.set("a", "b");
94
165
  expect(await store.get("a")).toBe("b");
@@ -96,12 +167,6 @@ for (let [name, store] of stores) {
96
167
  expect(await store.get("a")).toBe(null);
97
168
  });
98
169
 
99
- it("can get the keys", async () => {
100
- await store.set("a", "b");
101
- await store.set("c", "d");
102
- expect(await store.keys()).toEqual(["a", "c"]);
103
- });
104
-
105
170
  it("can clear all the values", async () => {
106
171
  await store.set("a", "b");
107
172
  await store.set("c", "d");
@@ -113,33 +178,37 @@ for (let [name, store] of stores) {
113
178
  });
114
179
 
115
180
  describe("expires", () => {
116
- it("expire = 0 means immediately", async () => {
117
- await store.set("a", "b", { expire: 0 });
181
+ it("expires = 0 means immediately", async () => {
182
+ await store.set("a", "b", { expires: 0 });
118
183
  expect(await store.get("a")).toBe(null);
184
+ expect(await store.has("a")).toBe(false);
185
+ expect(await store.keys()).toEqual([]);
186
+ expect(await store.values()).toEqual([]);
187
+ expect(await store.entries()).toEqual([]);
119
188
  });
120
189
 
121
- it("expire = potato means undefined = forever", async () => {
122
- await store.set("a", "b", { expire: "potato" });
190
+ it("expires = potato means undefined = forever", async () => {
191
+ await store.set("a", "b", { expires: "potato" });
123
192
  expect(await store.get("a")).toBe("b");
124
193
  await delay(100);
125
194
  expect(await store.get("a")).toBe("b");
126
195
  });
127
196
 
128
- it("expire = 5potato means undefined = forever", async () => {
129
- await store.set("a", "b", { expire: "5potato" });
197
+ it("expires = 5potato means undefined = forever", async () => {
198
+ await store.set("a", "b", { expires: "5potato" });
130
199
  expect(await store.get("a")).toBe("b");
131
200
  await delay(100);
132
201
  expect(await store.get("a")).toBe("b");
133
202
  });
134
203
 
135
- it("expire = null means never to expire it", async () => {
136
- await store.set("a", "b", { expire: null });
204
+ it("expires = null means never to expire it", async () => {
205
+ await store.set("a", "b", { expires: null });
137
206
  expect(await store.get("a")).toBe("b");
138
207
  await delay(100);
139
208
  expect(await store.get("a")).toBe("b");
140
209
  });
141
210
 
142
- it("expire = undefined means never to expire it", async () => {
211
+ it("expires = undefined means never to expire it", async () => {
143
212
  await store.set("a", "b");
144
213
  expect(await store.get("a")).toBe("b");
145
214
  await delay(100);
@@ -147,42 +216,43 @@ for (let [name, store] of stores) {
147
216
  });
148
217
 
149
218
  if (name !== "kv('cookie')" && name !== "kv(redis)") {
150
- it("can use 10 expire", async () => {
151
- await store.set("a", "b", { expire: 10 });
219
+ it("can use 0.01 expire", async () => {
220
+ // 10ms
221
+ await store.set("a", "b", { expires: 0.01 });
152
222
  expect(await store.get("a")).toBe("b");
153
223
  await delay(100);
154
224
  expect(await store.get("a")).toBe(null);
155
225
  });
156
226
 
157
227
  it("can use 0.01s expire", async () => {
158
- await store.set("a", "b", { expire: "0.01s" });
228
+ await store.set("a", "b", { expires: "0.01s" });
159
229
  expect(await store.get("a")).toBe("b");
160
230
  await delay(100);
161
231
  expect(await store.get("a")).toBe(null);
162
232
  });
163
233
 
164
234
  it("can use 0.01seconds expire", async () => {
165
- await store.set("a", "b", { expire: "0.01seconds" });
235
+ await store.set("a", "b", { expires: "0.01seconds" });
166
236
  expect(await store.get("a")).toBe("b");
167
237
  await delay(100);
168
238
  expect(await store.get("a")).toBe(null);
169
239
  });
170
240
 
171
241
  it("can use 10ms expire", async () => {
172
- await store.set("a", "b", { expire: "10ms" });
242
+ await store.set("a", "b", { expires: "10ms" });
173
243
  expect(await store.get("a")).toBe("b");
174
244
  await delay(100);
175
245
  expect(await store.get("a")).toBe(null);
176
246
  });
177
247
  } else {
178
- it("can use 1000 expire", async () => {
179
- await store.set("a", "b", { expire: 1000 });
248
+ it("can use 1 (second) expire", async () => {
249
+ await store.set("a", "b", { expires: 1 });
180
250
  expect(await store.get("a")).toBe("b");
181
251
  await delay(2000);
182
252
  expect(await store.get("a")).toBe(null);
183
253
  });
184
254
  it("can use 1s expire", async () => {
185
- await store.set("a", "b", { expire: "1s" });
255
+ await store.set("a", "b", { expires: "1s" });
186
256
  expect(await store.get("a")).toBe("b");
187
257
  await delay(2000);
188
258
  expect(await store.get("a")).toBe(null);
@@ -3,11 +3,15 @@ import kv from "..";
3
3
  const store = kv();
4
4
 
5
5
  (async () => {
6
- const val = await store.get("key");
6
+ await store.get("key");
7
7
  await store.set("key", "value");
8
8
  await store.set("key", "value", {});
9
- await store.set("key", "value", { expire: 100 });
10
- await store.set("key", "value", { expire: "100s" });
9
+ await store.set("key", "value", { expires: 100 });
10
+ await store.set("key", "value", { expires: "100s" });
11
+ const key1: string = await store.add("value");
12
+ const key2: string = await store.add("value", { expires: 100 });
13
+ const key3: string = await store.add("value", { expires: "100s" });
14
+ console.log(key1, key2, key3);
11
15
  if (await store.has("key")) {
12
16
  }
13
17
  })();
package/index.d.ts DELETED
@@ -1,13 +0,0 @@
1
- type Expire = number | string | null;
2
-
3
- type Store = {
4
- get: (key: string) => Promise<any>;
5
- set: (key: string, value: any, opts?: { expire?: Expire }) => Promise<null>;
6
- has: (key: string) => Promise<boolean>;
7
- del: (key: string) => Promise<null>;
8
-
9
- keys: (prefix?: string) => Promise<string[]>;
10
- clear: () => Promise<null>;
11
- };
12
-
13
- export default function (store?: any): Store;
package/index.min.js DELETED
@@ -1,2 +0,0 @@
1
- var o={},m=/(-?(?:\d+\.?\d*|\d*\.?\d+)(?:e[-+]?\d+)?)\s*([\p{L}]*)/iu;a.millisecond=a.ms=1;a.second=a.sec=a.s=a[""]=a.ms*1e3;a.minute=a.min=a.m=a.s*60;a.hour=a.hr=a.h=a.m*60;a.day=a.d=a.h*24;a.week=a.wk=a.w=a.d*7;a.year=a.yr=a.y=a.d*365.25;a.month=a.b=a.y/12;function a(t){if(t==null)return null;if(typeof t=="number")return t;t=t.toLowerCase().replace(/[,_]/g,"");let[u,l,r]=m.exec(t)||[];if(!r)return null;let c=a[r]||a[r.replace(/s$/,"")];if(!c)return null;let p=c*parseFloat(l,10);return Math.abs(Math.round(p))}o.expire=t=>{let u=async n=>{if(!await t.has(n))return null;let{data:e,expire:d}=await t.get(n);return d===null?e:d-new Date().getTime()<=0?null:e},l=async(n,e,{expire:d=null}={})=>{let s=a(d),i=s!==null?new Date().getTime()+s:null;return t.set(n,{expire:i,data:e})},r=async n=>await t.get(n)!==null,c=t.del,p=t.keys,y=t.clear;return{get:u,set:l,has:r,del:c,keys:p,clear:y}};o.memory=t=>({get:async n=>t.get(n)||null,set:async(n,e)=>t.set(n,e),has:async n=>t.has(n),del:async n=>t.delete(n),keys:async(n="")=>[...await t.keys()].filter(e=>e.startsWith(n)),clear:()=>t.clear()});o.storage=t=>({get:async n=>t[n]?JSON.parse(t[n]):null,set:async(n,e)=>t.setItem(n,JSON.stringify(e)),has:async n=>n in t,del:async n=>t.removeItem(n),keys:async(n="")=>Object.keys(t).filter(e=>e.startsWith(n)),clear:()=>t.clear()});o.cookie=()=>{let t=async y=>{let n=document.cookie.split("; ").filter(Boolean).find(e=>e.startsWith(y+"="))?.split("=")[1]||null;return JSON.parse(decodeURIComponent(n))},u=async(y,n,{expire:e=null}={})=>{let d=a(e),s=new Date().getTime(),i=d!==null?`; expires=${new Date(s+d).toUTCString()}`:"",f=encodeURIComponent(JSON.stringify(n));document.cookie=y+"="+f+i},l=async y=>(await c()).includes(y),r=async y=>u(y,"",{expire:-100}),c=async(y="")=>document.cookie.split(";").map(n=>n.split("=")[0].trim()).filter(Boolean).filter(n=>n.startsWith(y));return{get:t,set:u,has:l,del:r,keys:c,clear:async()=>{await Promise.all((await c()).map(r))}}};o.redis=t=>{let u=async e=>{let s=await(await t).get(e);return s?JSON.parse(s):null},l=async(e,d,{expire:s=null}={})=>{if(d===null||s===0)return c(e);let i=await t,f=a(s),w=f?Math.round(f/1e3):void 0;return i.set(e,JSON.stringify(d),{EX:w})},r=async e=>!!await(await t).exists(e),c=async e=>(await t).del(e);return{get:u,set:l,has:r,del:c,keys:async(e="")=>(await t).keys(e+"*"),clear:async()=>(await t).flushAll(),close:async()=>(await t).quit()}};o.localForage=t=>{let u=n=>t.getItem(n);return{get:u,set:(n,e)=>t.setItem(n,e),has:async n=>await u(n)!==null,del:n=>t.removeItem(n),keys:async(n="")=>(await t.keys()).filter(e=>e.startsWith(n)),clear:()=>t.clear()}};o.cloudflare=t=>{let u=async n=>{let e=await t.get(n);return e?JSON.parse(e):null},l=async(n,e,{expire:d})=>{if(e===null||d===0)return c(n);let s=await t,i=a(d),f=i?Math.round(i/1e3):void 0;return s.put(n,JSON.stringify(e),{expirationTtl:f})},r=async n=>!!await t.get(n),c=n=>t.delete(n);return{get:u,set:l,has:r,del:c,keys:n=>t.list({prefix:n}),clear:()=>{}}};o.file=t=>{let u=(async()=>{let i=await import(["node:fs","promises"].join("/"));return await i.writeFile(t.pathname,"{}",{flag:"wx"}).catch(f=>{if(f.code!=="EEXIST")throw f}),i})(),l=async()=>{let i=await(await u).readFile(t.pathname,"utf8");return i?JSON.parse(i):{}},r=async s=>{await(await u).writeFile(t.pathname,JSON.stringify(s,null,2))},c=async s=>(await l())[s]??null;return{get:c,set:async(s,i)=>{let f=await l();f[s]=i,await r(f)},has:async s=>await c(s)!==null,del:async s=>{let i=await l();delete i[s],await r(i)},keys:async(s="")=>{let i=await l();return Object.keys(i).filter(f=>f.startsWith(s))},clear:async()=>{await r({})}}};var g=async t=>t instanceof Map?o.expire(o.memory(t)):typeof localStorage<"u"&&t===localStorage||typeof sessionStorage<"u"&&t===sessionStorage?o.expire(o.storage(t)):t==="cookie"?o.cookie():t.defineDriver&&t.dropInstance&&t.INDEXEDDB?o.expire(o.localForage(t)):t.protocol&&t.protocol==="file:"?o.expire(o.file(t)):t.pSubscribe&&t.sSubscribe?o.redis(t):t?.constructor?.name==="KvNamespace"?o.cloudflare(t):null;function h(t=new Map){return new Proxy({},{get:(u,l)=>async(...r)=>{let c=await g(await t);if(!c)throw new Error("Store is not valid");return!c[l]&&l==="close"?null:c[l](...r)}})}export{h as default};
2
- //# sourceMappingURL=index.min.js.map
package/index.min.js.map DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["src/index.js"],
4
- "sourcesContent": ["const layers = {};\n\nconst times = /(-?(?:\\d+\\.?\\d*|\\d*\\.?\\d+)(?:e[-+]?\\d+)?)\\s*([\\p{L}]*)/iu;\n\nparse.millisecond = parse.ms = 1;\nparse.second = parse.sec = parse.s = parse[\"\"] = parse.ms * 1000;\nparse.minute = parse.min = parse.m = parse.s * 60;\nparse.hour = parse.hr = parse.h = parse.m * 60;\nparse.day = parse.d = parse.h * 24;\nparse.week = parse.wk = parse.w = parse.d * 7;\nparse.year = parse.yr = parse.y = parse.d * 365.25;\nparse.month = parse.b = parse.y / 12;\n\n// Returns the time in milliseconds\nfunction parse(str) {\n if (str === null || str === undefined) return null;\n if (typeof str === \"number\") return str;\n // ignore commas/placeholders\n str = str.toLowerCase().replace(/[,_]/g, \"\");\n let [_, value, units] = times.exec(str) || [];\n if (!units) return null;\n const unitValue = parse[units] || parse[units.replace(/s$/, \"\")];\n if (!unitValue) return null;\n const result = unitValue * parseFloat(value, 10);\n return Math.abs(Math.round(result));\n}\n\nlayers.expire = (store) => {\n // Item methods\n const get = async (key) => {\n if (!(await store.has(key))) return null;\n const { data, expire } = await store.get(key);\n if (expire === null) return data;\n const diff = expire - new Date().getTime();\n if (diff <= 0) return null;\n return data;\n };\n const set = async (key, data, { expire = null } = {}) => {\n const time = parse(expire);\n const expDiff = time !== null ? new Date().getTime() + time : null;\n return store.set(key, { expire: expDiff, data });\n };\n const has = async (key) => (await store.get(key)) !== null;\n const del = store.del;\n\n // Group methods\n const keys = store.keys;\n const clear = store.clear;\n\n return { get, set, has, del, keys, clear };\n};\n\nlayers.memory = (store) => {\n // Item methods\n const get = async (key) => store.get(key) || null;\n const set = async (key, data) => store.set(key, data);\n const has = async (key) => store.has(key);\n const del = async (key) => store.delete(key);\n\n // Group methods\n const keys = async (prefix = \"\") =>\n [...(await store.keys())].filter((k) => k.startsWith(prefix));\n const clear = () => store.clear();\n\n return { get, set, has, del, keys, clear };\n};\n\nlayers.storage = (store) => {\n // Item methods\n const get = async (key) => (store[key] ? JSON.parse(store[key]) : null);\n const set = async (key, data) => store.setItem(key, JSON.stringify(data));\n const has = async (key) => key in store;\n const del = async (key) => store.removeItem(key);\n\n // Group methods\n const keys = async (prefix = \"\") =>\n Object.keys(store).filter((k) => k.startsWith(prefix));\n const clear = () => store.clear();\n\n return { get, set, has, del, keys, clear };\n};\n\nlayers.cookie = () => {\n const get = async (key) => {\n const value =\n document.cookie\n .split(\"; \")\n .filter(Boolean)\n .find((row) => row.startsWith(key + \"=\"))\n ?.split(\"=\")[1] || null;\n return JSON.parse(decodeURIComponent(value));\n };\n\n const set = async (key, data, { expire = null } = {}) => {\n const time = parse(expire);\n const now = new Date().getTime();\n const expireStr =\n time !== null ? `; expires=${new Date(now + time).toUTCString()}` : \"\";\n const value = encodeURIComponent(JSON.stringify(data));\n document.cookie = key + \"=\" + value + expireStr;\n };\n const has = async (key) => (await keys()).includes(key);\n const del = async (key) => set(key, \"\", { expire: -100 });\n\n // Group methods\n const keys = async (prefix = \"\") =>\n document.cookie\n .split(\";\")\n .map((l) => l.split(\"=\")[0].trim())\n .filter(Boolean)\n .filter((k) => k.startsWith(prefix));\n const clear = async () => {\n await Promise.all((await keys()).map(del));\n };\n\n return { get, set, has, del, keys, clear };\n};\n\nlayers.redis = (store) => {\n const get = async (key) => {\n const client = await store;\n const value = await client.get(key);\n if (!value) return null;\n return JSON.parse(value);\n };\n const set = async (key, value, { expire = null } = {}) => {\n if (value === null || expire === 0) return del(key);\n const client = await store;\n const exp = parse(expire);\n const EX = exp ? Math.round(exp / 1000) : undefined;\n return client.set(key, JSON.stringify(value), { EX });\n };\n const has = async (key) => Boolean(await (await store).exists(key));\n const del = async (key) => (await store).del(key);\n\n const keys = async (prefix = \"\") => (await store).keys(prefix + \"*\");\n const clear = async () => (await store).flushAll();\n const close = async () => (await store).quit();\n\n return { get, set, has, del, keys, clear, close };\n};\n\nlayers.localForage = (store) => {\n const get = (key) => store.getItem(key);\n const set = (key, value) => store.setItem(key, value);\n const has = async (key) => (await get(key)) !== null;\n const del = (key) => store.removeItem(key);\n\n const keys = async (prefix = \"\") =>\n (await store.keys()).filter((k) => k.startsWith(prefix));\n const clear = () => store.clear();\n\n return { get, set, has, del, keys, clear };\n};\n\nlayers.cloudflare = (store) => {\n const get = async (key) => {\n const data = await store.get(key);\n if (!data) return null;\n return JSON.parse(data);\n };\n const set = async (key, value, { expire }) => {\n if (value === null || expire === 0) return del(key);\n const client = await store;\n const exp = parse(expire);\n const expirationTtl = exp ? Math.round(exp / 1000) : undefined;\n return client.put(key, JSON.stringify(value), { expirationTtl });\n };\n const has = async (key) => Boolean(await store.get(key));\n const del = (key) => store.delete(key);\n const keys = (prefix) => store.list({ prefix });\n const clear = () => {};\n return { get, set, has, del, keys, clear };\n};\n\nlayers.file = (file) => {\n const fsProm = (async () => {\n // For the bundler, it doesn't like it otherwise\n const lib = [\"node:fs\", \"promises\"].join(\"/\");\n const fsp = await import(lib);\n // We want to make sure the file already exists, so attempt to\n // create it (but not OVERWRITE it, that's why the x flag) and\n // it fails if it already exists\n await fsp.writeFile(file.pathname, \"{}\", { flag: \"wx\" }).catch((err) => {\n if (err.code !== \"EEXIST\") throw err;\n });\n return fsp;\n })();\n const getContent = async () => {\n const fsp = await fsProm;\n const text = await fsp.readFile(file.pathname, \"utf8\");\n if (!text) return {};\n return JSON.parse(text);\n };\n const setContent = async (data) => {\n const fsp = await fsProm;\n await fsp.writeFile(file.pathname, JSON.stringify(data, null, 2));\n };\n const get = async (key) => {\n const data = await getContent();\n return data[key] ?? null;\n };\n const set = async (key, value) => {\n const data = await getContent();\n data[key] = value;\n await setContent(data);\n };\n const has = async (key) => (await get(key)) !== null;\n const del = async (key) => {\n const data = await getContent();\n delete data[key];\n await setContent(data);\n };\n const keys = async (prefix = \"\") => {\n const data = await getContent();\n return Object.keys(data).filter((k) => k.startsWith(prefix));\n };\n const clear = async () => {\n await setContent({});\n };\n return { get, set, has, del, keys, clear };\n};\n\nconst getStore = async (store) => {\n // Convert it to the normalized kv, then add the expiry layer on top\n if (store instanceof Map) {\n return layers.expire(layers.memory(store));\n }\n\n if (typeof localStorage !== \"undefined\" && store === localStorage) {\n return layers.expire(layers.storage(store));\n }\n\n if (typeof sessionStorage !== \"undefined\" && store === sessionStorage) {\n return layers.expire(layers.storage(store));\n }\n\n if (store === \"cookie\") {\n return layers.cookie();\n }\n\n if (store.defineDriver && store.dropInstance && store.INDEXEDDB) {\n return layers.expire(layers.localForage(store));\n }\n\n if (store.protocol && store.protocol === \"file:\") {\n return layers.expire(layers.file(store));\n }\n\n if (store.pSubscribe && store.sSubscribe) {\n return layers.redis(store);\n }\n\n if (store?.constructor?.name === \"KvNamespace\") {\n return layers.cloudflare(store);\n }\n\n // \u00AF\\_(\u30C4)_/\u00AF\n return null;\n};\n\nexport default function compat(storeClient = new Map()) {\n return new Proxy(\n {},\n {\n get: (_, key) => {\n return async (...args) => {\n const store = await getStore(await storeClient);\n // Throw at the first chance when the store failed to init:\n if (!store) {\n throw new Error(\"Store is not valid\");\n }\n // The store.close() is the only one allowed to be called even\n // if it doesn't exist, since it's optional in some stores\n if (!store[key] && key === \"close\") return null;\n return store[key](...args);\n };\n },\n }\n );\n}\n"],
5
- "mappings": "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",
6
- "names": ["layers", "times", "parse", "str", "_", "value", "units", "unitValue", "result", "store", "get", "key", "data", "expire", "set", "time", "expDiff", "has", "del", "keys", "clear", "prefix", "k", "row", "now", "expireStr", "l", "client", "exp", "EX", "expirationTtl", "file", "fsProm", "fsp", "err", "getContent", "text", "setContent", "getStore", "compat", "storeClient", "args"]
7
- }
package/tsconfig.json DELETED
@@ -1 +0,0 @@
1
- { "exclude": ["node_modules"] }