polystore 0.11.0 → 0.11.2
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 +8 -8
- package/.github/FUNDING.yml +0 -1
- package/.github/workflows/tests.yml +0 -24
- package/assets/autocomplete.png +0 -0
- package/assets/autocomplete.webp +0 -0
- package/assets/favicon.png +0 -0
- package/assets/home.html +0 -378
- package/assets/splash.png +0 -0
- package/documentation.page.json +0 -11
- package/src/index.test.js +0 -631
- package/src/index.types.ts +0 -18
- package/src/test/customFull.js +0 -45
- package/src/test/customSimple.js +0 -27
- package/src/test/data.json +0 -1
- package/src/test/setup.js +0 -23
package/package.json
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polystore",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
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",
|
|
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": "src/index.js",
|
|
11
10
|
"type": "module",
|
|
11
|
+
"main": "src/index.js",
|
|
12
12
|
"types": "src/index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"src/"
|
|
15
|
+
],
|
|
13
16
|
"scripts": {
|
|
14
17
|
"size": "echo $(gzip -c src/index.js | wc -c) bytes",
|
|
15
18
|
"start": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch --coverage --detectOpenHandles",
|
|
16
|
-
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --ci --watchAll=false --detectOpenHandles && check-dts
|
|
19
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --ci --watchAll=false --detectOpenHandles && check-dts test/index.types.ts"
|
|
17
20
|
},
|
|
18
21
|
"keywords": [
|
|
19
22
|
"kv",
|
|
@@ -51,11 +54,8 @@
|
|
|
51
54
|
"jest": {
|
|
52
55
|
"testEnvironment": "jsdom",
|
|
53
56
|
"setupFiles": [
|
|
54
|
-
"./
|
|
57
|
+
"./test/setup.js"
|
|
55
58
|
],
|
|
56
|
-
"transform": {}
|
|
57
|
-
"modulePathIgnorePatterns": [
|
|
58
|
-
"src/test/"
|
|
59
|
-
]
|
|
59
|
+
"transform": {}
|
|
60
60
|
}
|
|
61
61
|
}
|
package/.github/FUNDING.yml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
custom: https://www.paypal.me/franciscopresencia/19
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: tests
|
|
2
|
-
|
|
3
|
-
on: [push]
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
build:
|
|
7
|
-
runs-on: ubuntu-latest
|
|
8
|
-
|
|
9
|
-
strategy:
|
|
10
|
-
matrix:
|
|
11
|
-
node-version: [18.x, 20.x]
|
|
12
|
-
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v4
|
|
15
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
16
|
-
uses: actions/setup-node@v4
|
|
17
|
-
with:
|
|
18
|
-
node-version: ${{ matrix.node-version }}
|
|
19
|
-
- name: install dependencies
|
|
20
|
-
run: npm install
|
|
21
|
-
- name: npm test
|
|
22
|
-
run: npm test
|
|
23
|
-
env:
|
|
24
|
-
CI: true
|
package/assets/autocomplete.png
DELETED
|
Binary file
|
package/assets/autocomplete.webp
DELETED
|
Binary file
|
package/assets/favicon.png
DELETED
|
Binary file
|
package/assets/home.html
DELETED
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
<br />
|
|
2
|
-
|
|
3
|
-
<section class="hero flex center nowrap">
|
|
4
|
-
<div>
|
|
5
|
-
<h1>Polystore</h1>
|
|
6
|
-
<p style="max-width: 620px">
|
|
7
|
-
A library to unify KV-stores. Allows you to write code that works on any
|
|
8
|
-
KV store, both on the front-end and backend. Supports substores and
|
|
9
|
-
intuitive expiration times. Get started:
|
|
10
|
-
</p>
|
|
11
|
-
<pre class="small">npm install polystore</pre>
|
|
12
|
-
<div class="buttons">
|
|
13
|
-
<a class="button" href="/documentation">Documentation</a>
|
|
14
|
-
<a
|
|
15
|
-
class="pseudo button"
|
|
16
|
-
href="https://superpeer.com/francisco/-/javascript-and-react-help"
|
|
17
|
-
>Get JS help</a
|
|
18
|
-
>
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
<div style="width: 570px; max-width: 100%">
|
|
22
|
-
<pre><code class="language-js">import kv from "polystore";
|
|
23
|
-
import { createClient } from "redis";
|
|
24
|
-
|
|
25
|
-
const REDIS = process.env.REDIS_URL;
|
|
26
|
-
const store = kv(createClient(REDIS));
|
|
27
|
-
|
|
28
|
-
await store.set(key, data, { expires: "1h" });
|
|
29
|
-
console.log(await store.get(key));
|
|
30
|
-
// { hello: "world" }</code></pre>
|
|
31
|
-
</div>
|
|
32
|
-
</section>
|
|
33
|
-
|
|
34
|
-
<div class="separator">
|
|
35
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 20">
|
|
36
|
-
<circle cx="10" cy="10" r="3" />
|
|
37
|
-
<line x1="20" y1="10" x2="260" y2="10" />
|
|
38
|
-
<circle cx="270" cy="10" r="3" />
|
|
39
|
-
<circle class="empty" cx="283" cy="10" r="3" />
|
|
40
|
-
<circle class="empty" cx="300" cy="10" r="5" />
|
|
41
|
-
<circle class="empty" cx="317" cy="10" r="3" />
|
|
42
|
-
<circle cx="330" cy="10" r="3" />
|
|
43
|
-
<line x1="340" y1="10" x2="580" y2="10" />
|
|
44
|
-
<circle cx="590" cy="10" r="3" />
|
|
45
|
-
</svg>
|
|
46
|
-
</div>
|
|
47
|
-
|
|
48
|
-
<section class="features flex one two-500 three-900">
|
|
49
|
-
<div>
|
|
50
|
-
<div class="feature">
|
|
51
|
-
<header>
|
|
52
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
53
|
-
<path
|
|
54
|
-
d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zM6 1v3M10 1v3M14 1v3"
|
|
55
|
-
/>
|
|
56
|
-
</svg>
|
|
57
|
-
<h3>Easy peasy</h3>
|
|
58
|
-
</header>
|
|
59
|
-
<p>
|
|
60
|
-
It's a KV store. It has <code>add()</code>, <code>set()</code>,
|
|
61
|
-
<code>get()</code>, <code>has()</code>, <code>del()</code>
|
|
62
|
-
<a href="/documentation#api" target="_blank">and more</a>.
|
|
63
|
-
</p>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
|
|
67
|
-
<div>
|
|
68
|
-
<div class="feature">
|
|
69
|
-
<header>
|
|
70
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
71
|
-
<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" />
|
|
72
|
-
<path d="M14 2v6h6M16 13H8M16 17H8M10 9H8" />
|
|
73
|
-
</svg>
|
|
74
|
-
<h3>Documented</h3>
|
|
75
|
-
</header>
|
|
76
|
-
<p>
|
|
77
|
-
<a href="/documentation#getting-started">Getting started</a>,
|
|
78
|
-
<a href="/documentation#api">API</a>,
|
|
79
|
-
<a href="/documentation#clients">Clients</a> and
|
|
80
|
-
<a href="/documentation#creating-a-store">custom stores</a> for your
|
|
81
|
-
convenience.
|
|
82
|
-
</p>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
<div>
|
|
86
|
-
<div class="feature">
|
|
87
|
-
<header>
|
|
88
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
89
|
-
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
|
|
90
|
-
<polyline points="22 4 12 14.01 9 11.01"></polyline>
|
|
91
|
-
</svg>
|
|
92
|
-
<h3>Tested and Typed</h3>
|
|
93
|
-
</header>
|
|
94
|
-
<p>
|
|
95
|
-
<a
|
|
96
|
-
href="https://github.com/franciscop/polystore/actions"
|
|
97
|
-
target="_blank"
|
|
98
|
-
>600+ tests</a
|
|
99
|
-
>, TS definitions and JSDocs for the best experience using the library.
|
|
100
|
-
</p>
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
<div>
|
|
104
|
-
<div class="feature">
|
|
105
|
-
<header>
|
|
106
|
-
<svg
|
|
107
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
108
|
-
width="24"
|
|
109
|
-
height="24"
|
|
110
|
-
viewBox="0 0 24 24"
|
|
111
|
-
fill="none"
|
|
112
|
-
stroke="currentColor"
|
|
113
|
-
stroke-width="2"
|
|
114
|
-
stroke-linecap="round"
|
|
115
|
-
stroke-linejoin="round"
|
|
116
|
-
class="feather feather-globe"
|
|
117
|
-
>
|
|
118
|
-
<circle cx="12" cy="12" r="10"></circle>
|
|
119
|
-
<line x1="2" y1="12" x2="22" y2="12"></line>
|
|
120
|
-
<path
|
|
121
|
-
d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"
|
|
122
|
-
></path>
|
|
123
|
-
</svg>
|
|
124
|
-
<h3>Universal Javascript</h3>
|
|
125
|
-
</header>
|
|
126
|
-
<p>
|
|
127
|
-
Use it with React, Angular, Plain JS, Node.js, Bun, Tauri, Electron,
|
|
128
|
-
etc.
|
|
129
|
-
</p>
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
|
-
<div>
|
|
134
|
-
<div class="feature">
|
|
135
|
-
<header>
|
|
136
|
-
<svg
|
|
137
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
138
|
-
width="24"
|
|
139
|
-
height="24"
|
|
140
|
-
viewBox="0 0 24 24"
|
|
141
|
-
fill="none"
|
|
142
|
-
stroke="currentColor"
|
|
143
|
-
stroke-width="2"
|
|
144
|
-
stroke-linecap="round"
|
|
145
|
-
stroke-linejoin="round"
|
|
146
|
-
>
|
|
147
|
-
<path
|
|
148
|
-
d="M20.24 12.24a6 6 0 00-8.49-8.49L5 10.5V19h8.5zM16 8L2 22M17.5 15H9"
|
|
149
|
-
/>
|
|
150
|
-
</svg>
|
|
151
|
-
<h3>Tiny Footprint</h3>
|
|
152
|
-
</header>
|
|
153
|
-
<p>
|
|
154
|
-
At
|
|
155
|
-
<a href="https://bundlephobia.com/package/polystore" target="_blank"
|
|
156
|
-
>just <strong>3kb</strong></a
|
|
157
|
-
>
|
|
158
|
-
(min+gzip), the impact on your app loading time is minimal.
|
|
159
|
-
</p>
|
|
160
|
-
</div>
|
|
161
|
-
</div>
|
|
162
|
-
|
|
163
|
-
<div>
|
|
164
|
-
<div class="feature">
|
|
165
|
-
<header>
|
|
166
|
-
<svg
|
|
167
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
168
|
-
width="24"
|
|
169
|
-
height="24"
|
|
170
|
-
viewBox="0 0 24 24"
|
|
171
|
-
fill="none"
|
|
172
|
-
stroke="currentColor"
|
|
173
|
-
stroke-width="2"
|
|
174
|
-
stroke-linecap="round"
|
|
175
|
-
stroke-linejoin="round"
|
|
176
|
-
class="feather feather-clock"
|
|
177
|
-
>
|
|
178
|
-
<circle cx="12" cy="12" r="10"></circle>
|
|
179
|
-
<polyline points="12 6 12 12 16 14"></polyline>
|
|
180
|
-
</svg>
|
|
181
|
-
<h3>Intuitive expirations</h3>
|
|
182
|
-
</header>
|
|
183
|
-
<p>
|
|
184
|
-
Write the expiration as <code>100s</code>, <code>1week</code>, etc. and
|
|
185
|
-
forget time-related bugs.
|
|
186
|
-
</p>
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
</section>
|
|
190
|
-
|
|
191
|
-
<div class="separator">
|
|
192
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 20">
|
|
193
|
-
<circle cx="10" cy="10" r="3" />
|
|
194
|
-
<line x1="20" y1="10" x2="260" y2="10" />
|
|
195
|
-
<circle cx="270" cy="10" r="3" />
|
|
196
|
-
<circle class="empty" cx="283" cy="10" r="3" />
|
|
197
|
-
<circle class="empty" cx="300" cy="10" r="5" />
|
|
198
|
-
<circle class="empty" cx="317" cy="10" r="3" />
|
|
199
|
-
<circle cx="330" cy="10" r="3" />
|
|
200
|
-
<line x1="340" y1="10" x2="580" y2="10" />
|
|
201
|
-
<circle cx="590" cy="10" r="3" />
|
|
202
|
-
</svg>
|
|
203
|
-
</div>
|
|
204
|
-
|
|
205
|
-
<section class="hero flex one two-900 center">
|
|
206
|
-
<div>
|
|
207
|
-
<div class="content">
|
|
208
|
-
<h2>🧩 Support for many clients</h2>
|
|
209
|
-
<p>
|
|
210
|
-
We support 10+ clients with documentation for each of them. Adding your
|
|
211
|
-
own client is also easy, you only need to define 3 methods:
|
|
212
|
-
<code>.get()</code>, <code>.set()</code> and <code>.entries()</code>.
|
|
213
|
-
</p>
|
|
214
|
-
<p>
|
|
215
|
-
<a class="pseudo button" href="/documentation#clients">
|
|
216
|
-
Clients Docs
|
|
217
|
-
</a>
|
|
218
|
-
</p>
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
<div>
|
|
222
|
-
<pre><code class="language-js">import kv from "polystore";
|
|
223
|
-
|
|
224
|
-
const store1 = kv(new Map());
|
|
225
|
-
const store2 = kv(localStorage);
|
|
226
|
-
const store3 = kv(redisClient);
|
|
227
|
-
const store4 = kv("cookie");
|
|
228
|
-
const store5 = kv("file:///users/me/kv.json");
|
|
229
|
-
const store6 = kv(yourOwnStore);</code></pre>
|
|
230
|
-
</div>
|
|
231
|
-
</section>
|
|
232
|
-
|
|
233
|
-
<br />
|
|
234
|
-
|
|
235
|
-
<section class="hero flex one two-900 center">
|
|
236
|
-
<div>
|
|
237
|
-
<div class="content">
|
|
238
|
-
<h2>🏖️ Clean and intuitive API</h2>
|
|
239
|
-
<p>
|
|
240
|
-
A set of high-performance item operations with <code>.add()</code>,
|
|
241
|
-
<code>.set()</code>, <code>.get()</code>, <code>.has()</code> or
|
|
242
|
-
<code>.del()</code>. We also provide group operations to manage your
|
|
243
|
-
data easily.
|
|
244
|
-
</p>
|
|
245
|
-
<p>
|
|
246
|
-
<a class="pseudo button" href="/documentation#api">API Docs</a>
|
|
247
|
-
</p>
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
<div>
|
|
251
|
-
<pre><code class="language-js">import kv from "polystore";
|
|
252
|
-
const store = kv(new Map());
|
|
253
|
-
|
|
254
|
-
const key1 = await store.add("value1");
|
|
255
|
-
const key2 = await store.set("key2", "value2");
|
|
256
|
-
const val1 = await store.get("key1");
|
|
257
|
-
const isthere = await store.has("key1");
|
|
258
|
-
await store.del(key1);</code></pre>
|
|
259
|
-
</div>
|
|
260
|
-
</section>
|
|
261
|
-
|
|
262
|
-
<section class="hero flex one two-900 center">
|
|
263
|
-
<div>
|
|
264
|
-
<div class="content">
|
|
265
|
-
<h2>🛗 Create substores</h2>
|
|
266
|
-
<p>
|
|
267
|
-
Create a new substore with <code>.prefix()</code>, then you can ignore
|
|
268
|
-
anything related to the prefix and treat it as if it was a brand new
|
|
269
|
-
store.
|
|
270
|
-
</p>
|
|
271
|
-
<p>
|
|
272
|
-
<a class="pseudo button" href="/documentation#substores"
|
|
273
|
-
>Substore Docs
|
|
274
|
-
</a>
|
|
275
|
-
</p>
|
|
276
|
-
</div>
|
|
277
|
-
</div>
|
|
278
|
-
<div>
|
|
279
|
-
<pre><code class="language-js">const session = store.prefix("session:");
|
|
280
|
-
session.set("key1", "value1");
|
|
281
|
-
|
|
282
|
-
console.log(await session.all());
|
|
283
|
-
// { "key1": "value1" }
|
|
284
|
-
|
|
285
|
-
console.log(await store.all());
|
|
286
|
-
// { "session:key1": "value1" }</code></pre>
|
|
287
|
-
</div>
|
|
288
|
-
</section>
|
|
289
|
-
|
|
290
|
-
<br />
|
|
291
|
-
|
|
292
|
-
<section class="hero flex one two-900 center">
|
|
293
|
-
<div>
|
|
294
|
-
<div class="content">
|
|
295
|
-
<h2>⏰ Easy expiration time</h2>
|
|
296
|
-
<p>
|
|
297
|
-
Simply write <code>{ expires: "1day" }</code> with ANY client and forget
|
|
298
|
-
about calculating TTL, Unix time, seconds vs milliseconds bugs, etc.
|
|
299
|
-
</p>
|
|
300
|
-
<p>
|
|
301
|
-
<a class="pseudo button" href="/documentation#expiration-explained">
|
|
302
|
-
Documentation
|
|
303
|
-
</a>
|
|
304
|
-
</p>
|
|
305
|
-
</div>
|
|
306
|
-
</div>
|
|
307
|
-
<div>
|
|
308
|
-
<pre><code class="language-js">await store.get("key"); // null
|
|
309
|
-
|
|
310
|
-
await store.set("key", "value", { expires: "1s" });
|
|
311
|
-
await store.get("key"); // "value"
|
|
312
|
-
|
|
313
|
-
await sleep(2000);
|
|
314
|
-
|
|
315
|
-
await store.get("key"); // null</code></pre>
|
|
316
|
-
</div>
|
|
317
|
-
</section>
|
|
318
|
-
|
|
319
|
-
<br />
|
|
320
|
-
|
|
321
|
-
<section class="hero flex one two-900 center">
|
|
322
|
-
<div>
|
|
323
|
-
<div class="content">
|
|
324
|
-
<h2>✨ Magic Autocomplete</h2>
|
|
325
|
-
<p>
|
|
326
|
-
Added jsdocs so the expected parameters and return value will be clearly
|
|
327
|
-
defined in your IDE/Code Editor.
|
|
328
|
-
</p>
|
|
329
|
-
<p>
|
|
330
|
-
We added the <em>description</em>, a representative <em>example</em> and
|
|
331
|
-
even a <em>link</em> for more information for
|
|
332
|
-
<strong>every one</strong> of the methods available! We want you to have
|
|
333
|
-
the best development experience possible with Polystore!
|
|
334
|
-
</p>
|
|
335
|
-
</div>
|
|
336
|
-
</div>
|
|
337
|
-
<div>
|
|
338
|
-
<div class="splash">
|
|
339
|
-
<img
|
|
340
|
-
width="535"
|
|
341
|
-
src="https://raw.githubusercontent.com/franciscop/polystore/master/assets/autocomplete.webp"
|
|
342
|
-
/>
|
|
343
|
-
</div>
|
|
344
|
-
</div>
|
|
345
|
-
</section>
|
|
346
|
-
|
|
347
|
-
<div class="separator">
|
|
348
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 20">
|
|
349
|
-
<circle cx="10" cy="10" r="3" />
|
|
350
|
-
<line x1="20" y1="10" x2="260" y2="10" />
|
|
351
|
-
<circle cx="270" cy="10" r="3" />
|
|
352
|
-
<circle class="empty" cx="283" cy="10" r="3" />
|
|
353
|
-
<circle class="empty" cx="300" cy="10" r="5" />
|
|
354
|
-
<circle class="empty" cx="317" cy="10" r="3" />
|
|
355
|
-
<circle cx="330" cy="10" r="3" />
|
|
356
|
-
<line x1="340" y1="10" x2="580" y2="10" />
|
|
357
|
-
<circle cx="590" cy="10" r="3" />
|
|
358
|
-
</svg>
|
|
359
|
-
</div>
|
|
360
|
-
|
|
361
|
-
<div style="text-align: center">
|
|
362
|
-
<p>
|
|
363
|
-
Created by <a href="https://francisco.io/" target="_blank">Francisco</a> and
|
|
364
|
-
<a
|
|
365
|
-
href="https://github.com/franciscop/polystore/graphs/contributors"
|
|
366
|
-
target="_blank"
|
|
367
|
-
>other contributors</a
|
|
368
|
-
>.
|
|
369
|
-
</p>
|
|
370
|
-
<p>
|
|
371
|
-
Need help?
|
|
372
|
-
<a
|
|
373
|
-
href="https://superpeer.com/francisco/-/javascript-and-react-help"
|
|
374
|
-
target="_blank"
|
|
375
|
-
>Book a call</a
|
|
376
|
-
>.
|
|
377
|
-
</p>
|
|
378
|
-
</div>
|
package/assets/splash.png
DELETED
|
Binary file
|
package/documentation.page.json
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "🏬 Polystore - A universal library for standardizing any KV-store",
|
|
3
|
-
"home": "assets/home.html",
|
|
4
|
-
"homepage": "https://polystore.dev/",
|
|
5
|
-
"menu": {
|
|
6
|
-
"Documentation": "/documentation",
|
|
7
|
-
"Issues": "https://github.com/franciscop/polystore/issues",
|
|
8
|
-
"Get help": "https://superpeer.com/francisco/-/javascript-and-react-help",
|
|
9
|
-
"Github": "https://github.com/franciscop/polystore"
|
|
10
|
-
}
|
|
11
|
-
}
|
package/src/index.test.js
DELETED
|
@@ -1,631 +0,0 @@
|
|
|
1
|
-
import "dotenv/config";
|
|
2
|
-
|
|
3
|
-
import { jest } from "@jest/globals";
|
|
4
|
-
import { EdgeKVNamespace as KVNamespace } from "edge-mock";
|
|
5
|
-
import { Etcd3 } from "etcd3";
|
|
6
|
-
import { Level } from "level";
|
|
7
|
-
import localForage from "localforage";
|
|
8
|
-
import { createClient } from "redis";
|
|
9
|
-
|
|
10
|
-
import kv from "./index.js";
|
|
11
|
-
import customFull from "./test/customFull.js";
|
|
12
|
-
import customSimple from "./test/customSimple.js";
|
|
13
|
-
|
|
14
|
-
const stores = [];
|
|
15
|
-
stores.push(["kv()", kv()]);
|
|
16
|
-
stores.push(["kv(new Map())", kv(new Map())]);
|
|
17
|
-
stores.push(["kv(localStorage)", kv(localStorage)]);
|
|
18
|
-
stores.push(["kv(sessionStorage)", kv(sessionStorage)]);
|
|
19
|
-
stores.push(["kv(localForage)", kv(localForage)]);
|
|
20
|
-
const path = `file://${process.cwd()}/src/test/data.json`;
|
|
21
|
-
stores.push([`kv(new URL("${path}"))`, kv(new URL(path))]);
|
|
22
|
-
const path2 = `file://${process.cwd()}/src/test/data.json`;
|
|
23
|
-
stores.push([`kv("${path2}")`, kv(path2)]);
|
|
24
|
-
stores.push([`kv("cookie")`, kv("cookie")]);
|
|
25
|
-
stores.push(["kv(new KVNamespace())", kv(new KVNamespace())]);
|
|
26
|
-
stores.push([`kv(new Level("data"))`, kv(new Level("data"))]);
|
|
27
|
-
if (process.env.REDIS) {
|
|
28
|
-
stores.push(["kv(redis)", kv(createClient())]);
|
|
29
|
-
}
|
|
30
|
-
if (process.env.ETCD) {
|
|
31
|
-
// Note: need to add to .env "ETCD=true" and run `etcd` in the terminal
|
|
32
|
-
stores.push(["kv(new Etcd3())", kv(new Etcd3())]);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
stores.push(["kv(customSimple)", kv(customSimple)]);
|
|
36
|
-
stores.push(["kv(customFull)", kv(customFull)]);
|
|
37
|
-
|
|
38
|
-
const delay = (t) => new Promise((done) => setTimeout(done, t));
|
|
39
|
-
|
|
40
|
-
class Base {
|
|
41
|
-
get() {}
|
|
42
|
-
set() {}
|
|
43
|
-
*iterate() {}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
global.console = {
|
|
47
|
-
...console,
|
|
48
|
-
warn: jest.fn(),
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
describe("potato", () => {
|
|
52
|
-
it("a potato is not a valid store", async () => {
|
|
53
|
-
await expect(() => kv("potato").get("any")).rejects.toThrow();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("an empty object is not a valid store", async () => {
|
|
57
|
-
await expect(() => kv({}).get("any")).rejects.toThrow({
|
|
58
|
-
message: "A client should have at least a .get(), .set() and .iterate()",
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("cannot handle no EXPIRES + keys", async () => {
|
|
63
|
-
await expect(() =>
|
|
64
|
-
kv(
|
|
65
|
-
class extends Base {
|
|
66
|
-
has() {}
|
|
67
|
-
}
|
|
68
|
-
).get("any")
|
|
69
|
-
).rejects.toThrow({
|
|
70
|
-
message:
|
|
71
|
-
"You can only define client.has() when the client manages the expiration; otherwise please do NOT define .has() and let us manage it",
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("cannot handle no EXPIRES + keys", async () => {
|
|
76
|
-
await expect(() =>
|
|
77
|
-
kv(
|
|
78
|
-
class extends Base {
|
|
79
|
-
keys() {}
|
|
80
|
-
}
|
|
81
|
-
).get("any")
|
|
82
|
-
).rejects.toThrow({
|
|
83
|
-
message:
|
|
84
|
-
"You can only define client.keys() when the client manages the expiration; otherwise please do NOT define .keys() and let us manage them",
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("warns the user with no EXPIRES + .values()", async () => {
|
|
89
|
-
const warn = jest
|
|
90
|
-
.spyOn(console, "warn")
|
|
91
|
-
.mockImplementationOnce(() => "Hello");
|
|
92
|
-
await kv(
|
|
93
|
-
class extends Base {
|
|
94
|
-
values() {}
|
|
95
|
-
}
|
|
96
|
-
).get("any");
|
|
97
|
-
expect(warn).toHaveBeenCalled(); // But at least we warn them
|
|
98
|
-
warn.mockClear();
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
for (let [name, store] of stores) {
|
|
103
|
-
describe(name, () => {
|
|
104
|
-
beforeEach(async () => {
|
|
105
|
-
await store.clear();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
afterAll(async () => {
|
|
109
|
-
await store.clear();
|
|
110
|
-
await store.close();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("can perform a CRUD", async () => {
|
|
114
|
-
expect(await store.get("a")).toBe(null);
|
|
115
|
-
expect(await store.has("a")).toBe(false);
|
|
116
|
-
expect(await store.set("a", "b")).toBe("a");
|
|
117
|
-
expect(await store.has("a")).toBe(true);
|
|
118
|
-
expect(await store.get("a")).toBe("b");
|
|
119
|
-
expect(await store.del("a")).toBe("a");
|
|
120
|
-
expect(await store.get("a")).toBe(null);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("is empty on the start", async () => {
|
|
124
|
-
expect(await store.get("a")).toBe(null);
|
|
125
|
-
expect(await store.has("a")).toBe(false);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("can add() arbitrary values", async () => {
|
|
129
|
-
const key = await store.add("b");
|
|
130
|
-
expect(typeof key).toBe("string");
|
|
131
|
-
expect(await store.get(key)).toBe("b");
|
|
132
|
-
expect(await store.has(key)).toBe(true);
|
|
133
|
-
expect(key.length).toBe(24);
|
|
134
|
-
expect(key).toMatch(/^[a-zA-Z0-9]{24}$/);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("can store values", async () => {
|
|
138
|
-
const key = await store.set("a", "b");
|
|
139
|
-
expect(await store.get("a")).toBe("b");
|
|
140
|
-
expect(await store.has("a")).toBe(true);
|
|
141
|
-
expect(key).toBe("a");
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("can store values with a semicolon", async () => {
|
|
145
|
-
const key = await store.set("a", "b;c");
|
|
146
|
-
expect(await store.get("a")).toBe("b;c");
|
|
147
|
-
expect(await store.has("a")).toBe(true);
|
|
148
|
-
expect(key).toBe("a");
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("can store values with an equal", async () => {
|
|
152
|
-
const key = await store.set("a", "b=c");
|
|
153
|
-
expect(await store.get("a")).toBe("b=c");
|
|
154
|
-
expect(await store.has("a")).toBe(true);
|
|
155
|
-
expect(key).toBe("a");
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("can store values with a semicolon in the key", async () => {
|
|
159
|
-
const key = await store.set("a;b", "c");
|
|
160
|
-
expect(await store.get("a;b")).toBe("c");
|
|
161
|
-
expect(await store.has("a;b")).toBe(true);
|
|
162
|
-
expect(key).toBe("a;b");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("can store values with an equal in the key", async () => {
|
|
166
|
-
const key = await store.set("a=b", "c");
|
|
167
|
-
expect(await store.get("a=b")).toBe("c");
|
|
168
|
-
expect(await store.has("a=b")).toBe(true);
|
|
169
|
-
expect(key).toBe("a=b");
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("can store basic types", async () => {
|
|
173
|
-
await store.set("a", 10);
|
|
174
|
-
expect(await store.get("a")).toEqual(10);
|
|
175
|
-
await store.set("a", "b");
|
|
176
|
-
expect(await store.get("a")).toEqual("b");
|
|
177
|
-
await store.set("a", true);
|
|
178
|
-
expect(await store.get("a")).toEqual(true);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("can store arrays of JSON values", async () => {
|
|
182
|
-
await store.set("a", ["b", "c"]);
|
|
183
|
-
expect(await store.get("a")).toEqual(["b", "c"]);
|
|
184
|
-
expect(await store.has("a")).toBe(true);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("can store objects", async () => {
|
|
188
|
-
await store.set("a", { b: "c" });
|
|
189
|
-
expect(await store.get("a")).toEqual({ b: "c" });
|
|
190
|
-
expect(await store.has("a")).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("can get the keys", async () => {
|
|
194
|
-
await store.set("a", "b");
|
|
195
|
-
await store.set("c", "d");
|
|
196
|
-
expect(await store.keys()).toEqual(["a", "c"]);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it("can get the values", async () => {
|
|
200
|
-
await store.set("a", "b");
|
|
201
|
-
await store.set("c", "d");
|
|
202
|
-
expect(await store.values()).toEqual(["b", "d"]);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("can get the entries", async () => {
|
|
206
|
-
await store.set("a", "b");
|
|
207
|
-
await store.set("c", "d");
|
|
208
|
-
expect(await store.entries()).toEqual([
|
|
209
|
-
["a", "b"],
|
|
210
|
-
["c", "d"],
|
|
211
|
-
]);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("can get the all object", async () => {
|
|
215
|
-
await store.set("a", "b");
|
|
216
|
-
await store.set("c", "d");
|
|
217
|
-
expect(await store.all()).toEqual({
|
|
218
|
-
a: "b",
|
|
219
|
-
c: "d",
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it("can get the keys with a colon prefix", async () => {
|
|
224
|
-
await store.set("a:0", "a0");
|
|
225
|
-
await store.set("a:1", "a1");
|
|
226
|
-
await store.set("b:0", "b0");
|
|
227
|
-
await store.set("a:2", "a2");
|
|
228
|
-
expect((await store.keys()).sort()).toEqual(["a:0", "a:1", "a:2", "b:0"]);
|
|
229
|
-
expect((await store.prefix("a:").keys()).sort()).toEqual(["0", "1", "2"]);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it("can get the values with a colon prefix", async () => {
|
|
233
|
-
await store.set("a:0", "a0");
|
|
234
|
-
await store.set("a:1", "a1");
|
|
235
|
-
await store.set("b:0", "b0");
|
|
236
|
-
await store.set("a:2", "a2");
|
|
237
|
-
expect((await store.prefix("a:").values()).sort()).toEqual([
|
|
238
|
-
"a0",
|
|
239
|
-
"a1",
|
|
240
|
-
"a2",
|
|
241
|
-
]);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("can get the entries with a colon prefix", async () => {
|
|
245
|
-
await store.set("a:0", "a0");
|
|
246
|
-
await store.set("a:1", "a1");
|
|
247
|
-
await store.set("b:0", "b0");
|
|
248
|
-
await store.set("a:2", "a2");
|
|
249
|
-
expect((await store.entries()).sort()).toEqual([
|
|
250
|
-
["a:0", "a0"],
|
|
251
|
-
["a:1", "a1"],
|
|
252
|
-
["a:2", "a2"],
|
|
253
|
-
["b:0", "b0"],
|
|
254
|
-
]);
|
|
255
|
-
expect((await store.prefix("a:").entries()).sort()).toEqual([
|
|
256
|
-
["0", "a0"],
|
|
257
|
-
["1", "a1"],
|
|
258
|
-
["2", "a2"],
|
|
259
|
-
]);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("can get the all object with a colon prefix", async () => {
|
|
263
|
-
await store.set("a:0", "a0");
|
|
264
|
-
await store.set("a:1", "a1");
|
|
265
|
-
await store.set("b:0", "b0");
|
|
266
|
-
await store.set("a:2", "a2");
|
|
267
|
-
expect(await store.prefix("a:").all()).toEqual({
|
|
268
|
-
0: "a0",
|
|
269
|
-
1: "a1",
|
|
270
|
-
2: "a2",
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("can get the keys with a dash prefix", async () => {
|
|
275
|
-
await store.set("a-0", "a0");
|
|
276
|
-
await store.set("a-1", "a1");
|
|
277
|
-
await store.set("b-0", "b0");
|
|
278
|
-
await store.set("a-2", "a2");
|
|
279
|
-
expect((await store.keys()).sort()).toEqual(["a-0", "a-1", "a-2", "b-0"]);
|
|
280
|
-
expect((await store.prefix("a-").keys()).sort()).toEqual(["0", "1", "2"]);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it("can get the values with a dash prefix", async () => {
|
|
284
|
-
await store.set("a-0", "a0");
|
|
285
|
-
await store.set("a-1", "a1");
|
|
286
|
-
await store.set("b-0", "b0");
|
|
287
|
-
await store.set("a-2", "a2");
|
|
288
|
-
expect((await store.prefix("a-").values()).sort()).toEqual([
|
|
289
|
-
"a0",
|
|
290
|
-
"a1",
|
|
291
|
-
"a2",
|
|
292
|
-
]);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it("can get the entries with a dash prefix", async () => {
|
|
296
|
-
await store.set("a-0", "a0");
|
|
297
|
-
await store.set("a-1", "a1");
|
|
298
|
-
await store.set("b-0", "b0");
|
|
299
|
-
await store.set("a-2", "a2");
|
|
300
|
-
expect((await store.prefix("a-").entries()).sort()).toEqual([
|
|
301
|
-
["0", "a0"],
|
|
302
|
-
["1", "a1"],
|
|
303
|
-
["2", "a2"],
|
|
304
|
-
]);
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
it("can get the all object with a dash prefix", async () => {
|
|
308
|
-
await store.set("a-0", "a0");
|
|
309
|
-
await store.set("a-1", "a1");
|
|
310
|
-
await store.set("b-0", "b0");
|
|
311
|
-
await store.set("a-2", "a2");
|
|
312
|
-
expect(await store.prefix("a-").all()).toEqual({
|
|
313
|
-
0: "a0",
|
|
314
|
-
1: "a1",
|
|
315
|
-
2: "a2",
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it("can delete the data", async () => {
|
|
320
|
-
await store.set("a", "b");
|
|
321
|
-
expect(await store.get("a")).toBe("b");
|
|
322
|
-
await store.del("a");
|
|
323
|
-
expect(await store.get("a")).toBe(null);
|
|
324
|
-
expect(await store.keys()).toEqual([]);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
it("can delete the data by setting it to null", async () => {
|
|
328
|
-
await store.set("a", "b");
|
|
329
|
-
expect(await store.get("a")).toBe("b");
|
|
330
|
-
await store.set("a", null);
|
|
331
|
-
expect(await store.get("a")).toBe(null);
|
|
332
|
-
expect(await store.keys()).toEqual([]);
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
it("can clear all the values", async () => {
|
|
336
|
-
await store.set("a", "b");
|
|
337
|
-
await store.set("c", "d");
|
|
338
|
-
expect(await store.get("a")).toBe("b");
|
|
339
|
-
await store.clear();
|
|
340
|
-
expect(await store.get("a")).toBe(null);
|
|
341
|
-
await store.set("a", "b");
|
|
342
|
-
expect(await store.get("a")).toBe("b");
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
describe("iteration", () => {
|
|
346
|
-
beforeEach(async () => {
|
|
347
|
-
await store.clear();
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
it("supports raw iteration", async () => {
|
|
351
|
-
await store.set("a", "b");
|
|
352
|
-
await store.set("c", "d");
|
|
353
|
-
|
|
354
|
-
const entries = [];
|
|
355
|
-
for await (const entry of store) {
|
|
356
|
-
entries.push(entry);
|
|
357
|
-
}
|
|
358
|
-
expect(entries).toEqual([
|
|
359
|
-
["a", "b"],
|
|
360
|
-
["c", "d"],
|
|
361
|
-
]);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
it("supports raw prefix iteration", async () => {
|
|
365
|
-
await store.set("a:a", "b");
|
|
366
|
-
await store.set("b:a", "d");
|
|
367
|
-
await store.set("a:c", "d");
|
|
368
|
-
await store.set("b:c", "d");
|
|
369
|
-
|
|
370
|
-
const entries = [];
|
|
371
|
-
for await (const entry of store.prefix("a:")) {
|
|
372
|
-
entries.push(entry);
|
|
373
|
-
}
|
|
374
|
-
expect(entries.sort()).toEqual([
|
|
375
|
-
["a", "b"],
|
|
376
|
-
["c", "d"],
|
|
377
|
-
]);
|
|
378
|
-
});
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
describe("expires", () => {
|
|
382
|
-
// The mock implementation does NOT support expiration 😪
|
|
383
|
-
if (name === "kv(new KVNamespace())") return;
|
|
384
|
-
|
|
385
|
-
afterEach(() => {
|
|
386
|
-
jest.restoreAllMocks();
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
it("expires = 0 means immediately", async () => {
|
|
390
|
-
await store.set("a", "b", { expires: 0 });
|
|
391
|
-
expect(await store.get("a")).toBe(null);
|
|
392
|
-
expect(await store.has("a")).toBe(false);
|
|
393
|
-
expect(await store.keys()).toEqual([]);
|
|
394
|
-
expect(await store.values()).toEqual([]);
|
|
395
|
-
expect(await store.entries()).toEqual([]);
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
it("expires = potato means undefined = forever", async () => {
|
|
399
|
-
await store.set("a", "b", { expires: "potato" });
|
|
400
|
-
expect(await store.get("a")).toBe("b");
|
|
401
|
-
await delay(100);
|
|
402
|
-
expect(await store.get("a")).toBe("b");
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
it("expires = 5potato means undefined = forever", async () => {
|
|
406
|
-
await store.set("a", "b", { expires: "5potato" });
|
|
407
|
-
expect(await store.get("a")).toBe("b");
|
|
408
|
-
await delay(100);
|
|
409
|
-
expect(await store.get("a")).toBe("b");
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
it("expires = null means never to expire it", async () => {
|
|
413
|
-
await store.set("a", "b", { expires: null });
|
|
414
|
-
expect(await store.get("a")).toBe("b");
|
|
415
|
-
await delay(100);
|
|
416
|
-
expect(await store.get("a")).toBe("b");
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
it("expires = undefined means never to expire it", async () => {
|
|
420
|
-
await store.set("a", "b");
|
|
421
|
-
expect(await store.get("a")).toBe("b");
|
|
422
|
-
await delay(100);
|
|
423
|
-
expect(await store.get("a")).toBe("b");
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
if (
|
|
427
|
-
name !== `kv("cookie")` &&
|
|
428
|
-
name !== `kv(redis)` &&
|
|
429
|
-
name !== `kv(new Etcd3())`
|
|
430
|
-
) {
|
|
431
|
-
it("can use 0.01 expire", async () => {
|
|
432
|
-
// 10ms
|
|
433
|
-
await store.set("a", "b", { expires: 0.01 });
|
|
434
|
-
expect(await store.keys()).toEqual(["a"]);
|
|
435
|
-
expect(await store.values()).toEqual(["b"]);
|
|
436
|
-
expect(await store.get("a")).toBe("b");
|
|
437
|
-
await delay(100);
|
|
438
|
-
expect(await store.keys()).toEqual([]);
|
|
439
|
-
expect(await store.values()).toEqual([]);
|
|
440
|
-
expect(await store.get("a")).toBe(null);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
it("can use 0.01s expire", async () => {
|
|
444
|
-
await store.set("a", "b", { expires: "0.01s" });
|
|
445
|
-
expect(await store.keys()).toEqual(["a"]);
|
|
446
|
-
expect(await store.values()).toEqual(["b"]);
|
|
447
|
-
expect(await store.get("a")).toBe("b");
|
|
448
|
-
await delay(100);
|
|
449
|
-
expect(await store.keys()).toEqual([]);
|
|
450
|
-
expect(await store.values()).toEqual([]);
|
|
451
|
-
expect(await store.get("a")).toBe(null);
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
it("can use 0.01seconds expire", async () => {
|
|
455
|
-
await store.set("a", "b", { expires: "0.01seconds" });
|
|
456
|
-
expect(await store.keys()).toEqual(["a"]);
|
|
457
|
-
expect(await store.values()).toEqual(["b"]);
|
|
458
|
-
expect(await store.get("a")).toBe("b");
|
|
459
|
-
await delay(100);
|
|
460
|
-
expect(await store.keys()).toEqual([]);
|
|
461
|
-
expect(await store.values()).toEqual([]);
|
|
462
|
-
expect(await store.get("a")).toBe(null);
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
it("can use 10ms expire", async () => {
|
|
466
|
-
await store.set("a", "b", { expires: "10ms" });
|
|
467
|
-
expect(await store.keys()).toEqual(["a"]);
|
|
468
|
-
expect(await store.values()).toEqual(["b"]);
|
|
469
|
-
expect(await store.get("a")).toBe("b");
|
|
470
|
-
await delay(100);
|
|
471
|
-
expect(await store.keys()).toEqual([]);
|
|
472
|
-
expect(await store.values()).toEqual([]);
|
|
473
|
-
expect(await store.get("a")).toBe(null);
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
it("removes the expired key with .get()", async () => {
|
|
477
|
-
await store.set("a", "b", { expires: "10ms" });
|
|
478
|
-
const spy = jest.spyOn(store, "del");
|
|
479
|
-
expect(spy).not.toHaveBeenCalled();
|
|
480
|
-
await delay(100);
|
|
481
|
-
expect(spy).not.toHaveBeenCalled(); // Nothing we can do 😪
|
|
482
|
-
expect(await store.get("a")).toBe(null);
|
|
483
|
-
expect(spy).toHaveBeenCalled();
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it("removes the expired key with .keys()", async () => {
|
|
487
|
-
await store.set("a", "b", { expires: "10ms" });
|
|
488
|
-
const spy = jest.spyOn(store, "del");
|
|
489
|
-
expect(spy).not.toHaveBeenCalled();
|
|
490
|
-
await delay(100);
|
|
491
|
-
expect(spy).not.toHaveBeenCalled(); // Nothing we can do 😪
|
|
492
|
-
expect(await store.keys()).toEqual([]);
|
|
493
|
-
expect(spy).toHaveBeenCalled();
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
it("CANNOT remove the expired key with .values()", async () => {
|
|
497
|
-
await store.set("a", "b", { expires: "10ms" });
|
|
498
|
-
const spy = jest.spyOn(store, "del");
|
|
499
|
-
expect(spy).not.toHaveBeenCalled();
|
|
500
|
-
await delay(100);
|
|
501
|
-
expect(spy).not.toHaveBeenCalled(); // Nothing we can do 😪
|
|
502
|
-
expect(await store.values()).toEqual([]);
|
|
503
|
-
if (!store.client.EXPIRES && store.client.values) {
|
|
504
|
-
expect(spy).not.toHaveBeenCalled(); // Nothing we can do 😪😪
|
|
505
|
-
} else {
|
|
506
|
-
expect(spy).toHaveBeenCalled();
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
} else {
|
|
510
|
-
it("can use 1 (second) expire", async () => {
|
|
511
|
-
await store.set("a", "b", { expires: 1 });
|
|
512
|
-
expect(await store.keys()).toEqual(["a"]);
|
|
513
|
-
expect(await store.values()).toEqual(["b"]);
|
|
514
|
-
expect(await store.get("a")).toBe("b");
|
|
515
|
-
await delay(2000);
|
|
516
|
-
expect(await store.keys()).toEqual([]);
|
|
517
|
-
expect(await store.values()).toEqual([]);
|
|
518
|
-
expect(await store.get("a")).toBe(null);
|
|
519
|
-
});
|
|
520
|
-
it("can use 1s expire", async () => {
|
|
521
|
-
await store.set("a", "b", { expires: "1s" });
|
|
522
|
-
expect(await store.keys()).toEqual(["a"]);
|
|
523
|
-
expect(await store.values()).toEqual(["b"]);
|
|
524
|
-
expect(await store.get("a")).toBe("b");
|
|
525
|
-
await delay(2000);
|
|
526
|
-
expect(await store.keys()).toEqual([]);
|
|
527
|
-
expect(await store.values()).toEqual([]);
|
|
528
|
-
expect(await store.get("a")).toBe(null);
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
describe(".prefix()", () => {
|
|
534
|
-
let session;
|
|
535
|
-
beforeAll(() => {
|
|
536
|
-
session = store.prefix("session:");
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
it("has the same methods", () => {
|
|
540
|
-
expect(Object.keys(store)).toEqual(Object.keys(session));
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
it("can write/read one", async () => {
|
|
544
|
-
const id = await session.set("a", "b");
|
|
545
|
-
expect(id).toBe("a");
|
|
546
|
-
expect(await session.get("a")).toBe("b");
|
|
547
|
-
expect(await store.get("session:a")).toBe("b");
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
it("checks the has properly", async () => {
|
|
551
|
-
expect(await session.has("a")).toBe(false);
|
|
552
|
-
await session.set("a", "b");
|
|
553
|
-
expect(await session.has("a")).toBe(true);
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
it("can add with the prefix", async () => {
|
|
557
|
-
const id = await session.add("b");
|
|
558
|
-
expect(id.length).toBe(24);
|
|
559
|
-
expect(id).not.toMatch(/^session\:/);
|
|
560
|
-
|
|
561
|
-
const keys = await store.keys();
|
|
562
|
-
expect(keys[0]).toMatch(/^session\:/);
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
it("the group operations return the proper values", async () => {
|
|
566
|
-
await session.set("a", "b");
|
|
567
|
-
|
|
568
|
-
expect(await session.keys()).toEqual(["a"]);
|
|
569
|
-
expect(await session.values()).toEqual(["b"]);
|
|
570
|
-
expect(await session.entries()).toEqual([["a", "b"]]);
|
|
571
|
-
|
|
572
|
-
expect(await store.keys()).toEqual(["session:a"]);
|
|
573
|
-
expect(await store.values()).toEqual(["b"]);
|
|
574
|
-
expect(await store.entries()).toEqual([["session:a", "b"]]);
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
it("clears only the substore", async () => {
|
|
578
|
-
await store.set("a", "b");
|
|
579
|
-
await session.set("c", "d");
|
|
580
|
-
|
|
581
|
-
expect((await store.keys()).sort()).toEqual(["a", "session:c"]);
|
|
582
|
-
await session.clear();
|
|
583
|
-
expect(await store.keys()).toEqual(["a"]);
|
|
584
|
-
});
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
describe(".prefix().prefix()", () => {
|
|
588
|
-
let auth;
|
|
589
|
-
beforeAll(() => {
|
|
590
|
-
auth = store.prefix("session:").prefix("auth:");
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
it("can write/read one", async () => {
|
|
594
|
-
const id = await auth.set("a", "b");
|
|
595
|
-
expect(id).toBe("a");
|
|
596
|
-
expect(await auth.get("a")).toBe("b");
|
|
597
|
-
expect(await store.get("session:auth:a")).toBe("b");
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
it("can add with the prefix", async () => {
|
|
601
|
-
const id = await auth.add("b");
|
|
602
|
-
expect(id.length).toBe(24);
|
|
603
|
-
expect(id).not.toMatch(/^session\:/);
|
|
604
|
-
|
|
605
|
-
const keys = await store.keys();
|
|
606
|
-
expect(keys[0]).toMatch(/^session\:auth\:/);
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
it("the group operations return the proper values", async () => {
|
|
610
|
-
await auth.set("a", "b");
|
|
611
|
-
|
|
612
|
-
expect(await auth.keys()).toEqual(["a"]);
|
|
613
|
-
expect(await auth.values()).toEqual(["b"]);
|
|
614
|
-
expect(await auth.entries()).toEqual([["a", "b"]]);
|
|
615
|
-
|
|
616
|
-
expect(await store.keys()).toEqual(["session:auth:a"]);
|
|
617
|
-
expect(await store.values()).toEqual(["b"]);
|
|
618
|
-
expect(await store.entries()).toEqual([["session:auth:a", "b"]]);
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
it("clears only the substore", async () => {
|
|
622
|
-
await store.set("a", "b");
|
|
623
|
-
await auth.set("c", "d");
|
|
624
|
-
|
|
625
|
-
expect((await store.keys()).sort()).toEqual(["a", "session:auth:c"]);
|
|
626
|
-
await auth.clear();
|
|
627
|
-
expect(await store.keys()).toEqual(["a"]);
|
|
628
|
-
});
|
|
629
|
-
});
|
|
630
|
-
});
|
|
631
|
-
}
|
package/src/index.types.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import kv from "..";
|
|
2
|
-
|
|
3
|
-
const store = kv();
|
|
4
|
-
|
|
5
|
-
(async () => {
|
|
6
|
-
await store.get("key");
|
|
7
|
-
await store.set("key", "value");
|
|
8
|
-
await store.set("key", "value", {});
|
|
9
|
-
await store.set("key", "value", { expires: 100 });
|
|
10
|
-
await store.set("key", "value", { expires: "100s" });
|
|
11
|
-
await store.prefix("a:").prefix("b:").get("hello");
|
|
12
|
-
const key1: string = await store.add("value");
|
|
13
|
-
const key2: string = await store.add("value", { expires: 100 });
|
|
14
|
-
const key3: string = await store.add("value", { expires: "100s" });
|
|
15
|
-
console.log(key1, key2, key3);
|
|
16
|
-
if (await store.has("key")) {
|
|
17
|
-
}
|
|
18
|
-
})();
|
package/src/test/customFull.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
const dataSource = {};
|
|
2
|
-
|
|
3
|
-
export default class MyClient {
|
|
4
|
-
get(key) {
|
|
5
|
-
return dataSource[key];
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
set(key, value) {
|
|
9
|
-
dataSource[key] = value;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
add(prefix, value) {
|
|
13
|
-
const id = Math.random().toString(16).slice(2).padStart(24, "0");
|
|
14
|
-
this.set(prefix + id, value);
|
|
15
|
-
return id;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
del(key) {
|
|
19
|
-
delete dataSource[key];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
*iterate(prefix) {
|
|
23
|
-
const entries = this.entries(prefix);
|
|
24
|
-
for (const entry of entries) {
|
|
25
|
-
yield entry;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Filter them by the prefix, note that `prefix` will always be a string
|
|
30
|
-
entries(prefix) {
|
|
31
|
-
const entries = Object.entries(dataSource);
|
|
32
|
-
if (!prefix) return entries;
|
|
33
|
-
return entries.filter(([key]) => key.startsWith(prefix));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
values(prefix) {
|
|
37
|
-
const list = this.entries(prefix);
|
|
38
|
-
return list.map((e) => e[1]);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Cannot have a keys() if it's an unamanaged store
|
|
42
|
-
// keys(prefix) {
|
|
43
|
-
// // This should throw
|
|
44
|
-
// }
|
|
45
|
-
}
|
package/src/test/customSimple.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const dataSource = {};
|
|
2
|
-
|
|
3
|
-
export default class MyClient {
|
|
4
|
-
get(key) {
|
|
5
|
-
return dataSource[key];
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
// No need to stringify it or anything for a plain object storage
|
|
9
|
-
set(key, value) {
|
|
10
|
-
if (value === null) {
|
|
11
|
-
delete dataSource[key];
|
|
12
|
-
} else {
|
|
13
|
-
dataSource[key] = value;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Filter them by the prefix, note that `prefix` will always be a string
|
|
18
|
-
*iterate(prefix) {
|
|
19
|
-
const raw = Object.entries(dataSource);
|
|
20
|
-
const entries = prefix
|
|
21
|
-
? raw.filter(([key, value]) => key.startsWith(prefix))
|
|
22
|
-
: raw;
|
|
23
|
-
for (const entry of entries) {
|
|
24
|
-
yield entry;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
package/src/test/data.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
package/src/test/setup.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import * as util from "util";
|
|
2
|
-
|
|
3
|
-
// ref: https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
|
|
4
|
-
// ref: https://github.com/jsdom/jsdom/issues/2524
|
|
5
|
-
if (typeof TextEncoder === "undefined") {
|
|
6
|
-
Object.defineProperty(window, "TextEncoder", {
|
|
7
|
-
writable: true,
|
|
8
|
-
value: util.TextEncoder,
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
if (typeof TextDecoder === "undefined") {
|
|
12
|
-
Object.defineProperty(window, "TextDecoder", {
|
|
13
|
-
writable: true,
|
|
14
|
-
value: util.TextDecoder,
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (typeof setImmediate === "undefined") {
|
|
19
|
-
Object.defineProperty(window, "setImmediate", {
|
|
20
|
-
writable: true,
|
|
21
|
-
value: (cb) => setTimeout(cb, 0),
|
|
22
|
-
});
|
|
23
|
-
}
|