polystore 0.9.2 → 0.9.3

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.
@@ -8,12 +8,12 @@ jobs:
8
8
 
9
9
  strategy:
10
10
  matrix:
11
- node-version: [18.x]
11
+ node-version: [18.x, 20.x]
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@v1
14
+ - uses: actions/checkout@v4
15
15
  - name: Use Node.js ${{ matrix.node-version }}
16
- uses: actions/setup-node@v1
16
+ uses: actions/setup-node@v4
17
17
  with:
18
18
  node-version: ${{ matrix.node-version }}
19
19
  - name: install dependencies
Binary file
Binary file
package/assets/home.html CHANGED
@@ -4,17 +4,17 @@
4
4
  <div>
5
5
  <h1>Polystore</h1>
6
6
  <p style="max-width: 620px">
7
- A universal library for standardizing KV-stores. Great when you don't want
8
- to depend on a specific implementation, or for library builders that want
9
- to accept any KV store available.
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
10
  </p>
11
11
  <pre class="small">npm install polystore</pre>
12
12
  <div class="buttons">
13
13
  <a class="button" href="/documentation">Documentation</a>
14
14
  <a
15
15
  class="pseudo button"
16
- href="https://www.paypal.me/franciscopresencia/19"
17
- >Donate</a
16
+ href="https://superpeer.com/francisco/-/javascript-and-react-help"
17
+ >Professional JS help</a
18
18
  >
19
19
  </div>
20
20
  </div>
@@ -25,8 +25,8 @@ import { createClient } from "redis";
25
25
  const REDIS = process.env.REDIS_URL;
26
26
  const store = kv(createClient(REDIS));
27
27
 
28
- await store.set(&quot;key1&quot;, { hello: &quot;world&quot; });
29
- console.log(await store.get(&quot;key1&quot;));
28
+ await store.set(key, data, { expires: "1h" });
29
+ console.log(await store.get(key));
30
30
  // { hello: &quot;world&quot; }</code></pre>
31
31
  </div>
32
32
  </section>
@@ -209,7 +209,7 @@ console.log(await store.get(&quot;key1&quot;));
209
209
  </p>
210
210
  <p>
211
211
  <a class="pseudo button" href="/documentation#clients">
212
- Documentation
212
+ Clients Docs
213
213
  </a>
214
214
  </p>
215
215
  </div>
@@ -233,26 +233,25 @@ const store6 = kv(yourOwnStore);</code></pre>
233
233
  <div class="content">
234
234
  <h2>🏖️ Clean and intuitive API</h2>
235
235
  <p>
236
- A set of high-performance atomic operations with <code>.add()</code>,
236
+ A set of high-performance item operations with <code>.add()</code>,
237
237
  <code>.set()</code>, <code>.get()</code>, <code>.has()</code> or
238
- <code>.del()</code>
238
+ <code>.del()</code>. We also provide group operations to manage your
239
+ data easily.
239
240
  </p>
240
241
  <p>
241
- You can also work with all of entries with <code>.keys()</code>,
242
- <code>.values()</code>, <code>.entries()</code>, <code>.all()</code> and
243
- <code>.clear()</code>.
242
+ <a class="pseudo button" href="/documentation#api">API Docs</a>
244
243
  </p>
245
244
  </div>
246
245
  </div>
247
246
  <div>
248
- <pre><code class="language-js">const key1 = await store.add(&quot;value2&quot;);
249
- const key2 = await store.set(&quot;key1&quot;, &quot;value1&quot;);
247
+ <pre><code class="language-js">import kv from "polystore";
248
+ const store = kv(new Map());
249
+
250
+ const key1 = await store.add(&quot;value1&quot;);
251
+ const key2 = await store.set(&quot;key2&quot;, &quot;value2&quot;);
252
+ const val1 = await store.get(&quot;key1&quot;);
250
253
  const isthere = await store.has(&quot;key1&quot;);
251
-
252
- const allKeys = await store.keys();
253
- const allValues = await store.values();
254
- const obj = await store.all();
255
- // etc</code></pre>
254
+ await store.del(key1);</code></pre>
256
255
  </div>
257
256
  </section>
258
257
 
@@ -261,28 +260,26 @@ const obj = await store.all();
261
260
  <div class="content">
262
261
  <h2>🛗 Create substores</h2>
263
262
  <p>
264
- We include the `.prefix()` method to allow you to work with a subset of
265
- items easily.
263
+ Create a new substore with <code>.prefix()</code>, then you can ignore
264
+ anything related to the prefix and treat it as if it was a brand new
265
+ store.
266
266
  </p>
267
267
  <p>
268
- e.g. if you have a session store and want to create a subset of sessions
269
- only for auth, you would do
270
- <code>const auth = session.prefix('auth:');</code> and then use
271
- <code>auth</code> like if it was an independent, KV store.
268
+ <a class="pseudo button" href="/documentation#substores"
269
+ >Substore Docs
270
+ </a>
272
271
  </p>
273
272
  </div>
274
273
  </div>
275
274
  <div>
276
- <pre><code class="language-js">import kv from &quot;polystore&quot;;
277
- const store = kv(new Map());
275
+ <pre><code class="language-js">const session = store.prefix("session:");
276
+ session.set("key1", "value1");
278
277
 
279
- const auth = store.prefix(&quot;auth:&quot;);
280
- auth.set(&quot;key1&quot;, &quot;value1&quot;);
281
- console.log(await auth.entries());
282
- // [[&quot;key1&quot;, &quot;value1&quot;]]
278
+ console.log(await session.all());
279
+ // { "key1": "value1" }
283
280
 
284
- console.log(await store.entries());
285
- // [[&quot;auth:key1&quot;, &quot;value1&quot;]]</code></pre>
281
+ console.log(await store.all());
282
+ // { "session:key1": "value1" }</code></pre>
286
283
  </div>
287
284
  </section>
288
285
 
@@ -293,10 +290,8 @@ console.log(await store.entries());
293
290
  <div class="content">
294
291
  <h2>⏰ Easy expiration time</h2>
295
292
  <p>
296
- Unified expiration time so you can do
297
- <code>{ expires: "1day" }</code> across ANY client. With polystore just
298
- write in English and forget about calculating TTL, Unix time, seconds vs
299
- milliseconds bugs, etc.
293
+ Simply write <code>{ expires: "1day" }</code> with ANY client and forget
294
+ about calculating TTL, Unix time, seconds vs milliseconds bugs, etc.
300
295
  </p>
301
296
  <p>
302
297
  <a class="pseudo button" href="/documentation#expiration-explained">
@@ -5,7 +5,6 @@
5
5
  "menu": {
6
6
  "Documentation": "/documentation",
7
7
  "Issues": "https://github.com/franciscop/polystore/issues",
8
- "Contribute": "https://github.com/franciscop/polystore/blob/master/Contributing.md",
9
8
  "Get help": "https://superpeer.com/francisco/-/javascript-and-react-help",
10
9
  "Github": "https://github.com/franciscop/polystore"
11
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polystore",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
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",
@@ -9,6 +9,7 @@
9
9
  "author": "Francisco Presencia <public@francisco.io> (https://francisco.io/)",
10
10
  "main": "src/index.js",
11
11
  "type": "module",
12
+ "types": "src/index.d.ts",
12
13
  "scripts": {
13
14
  "size": "echo $(gzip -c src/index.js | wc -c) bytes",
14
15
  "start": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch --coverage --detectOpenHandles",
package/readme.md CHANGED
@@ -175,11 +175,32 @@ Check whether the key is available in the store and not expired:
175
175
  ```js
176
176
  await store.has(key: string);
177
177
 
178
- if (await store.has('cookie-consent')) {
178
+ if (await store.has("cookie-consent")) {
179
179
  loadCookies();
180
180
  }
181
181
  ```
182
182
 
183
+ In many cases, internally the check for `.has()` is the same as `.get()`, so if you are going to use the value straight away it's usually better to just read it:
184
+
185
+ ```js
186
+ const val = await store.get("key1");
187
+ if (val) { ... }
188
+ ```
189
+
190
+ An example of an exception of the above is when you use it as a cache, then you can write code like this:
191
+
192
+ ```js
193
+ // First time for a given user does a network roundtrip, while
194
+ // the second time for the same user gets it from cache
195
+ async function fetchUser(id) {
196
+ if (!(await store.has(id))) {
197
+ const { data } = await axios.get(`/users/${id}`);
198
+ await store.set(id, data, { expires: "1h" });
199
+ }
200
+ return store.get(id);
201
+ }
202
+ ```
203
+
183
204
  ### .del()
184
205
 
185
206
  Remove a single key from the store and return the key itself:
@@ -1,12 +1,12 @@
1
- type Key = string;
2
1
  type Options = { expires?: number | string | null };
2
+ type Value = null | string | { [key: string]: Value } | Value[];
3
3
 
4
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>;
5
+ get: (key: string) => Promise<Value>;
6
+ add: (value: any, options?: Options) => Promise<string>;
7
+ set: (key: string, value: any, options?: Options) => Promise<string>;
8
+ has: (key: string) => Promise<boolean>;
9
+ del: (key: string) => Promise<null>;
10
10
 
11
11
  keys: () => Promise<string[]>;
12
12
  values: () => Promise<any[]>;
package/src/index.js CHANGED
@@ -1,32 +1,7 @@
1
1
  import clients from "./clients/index.js";
2
- import { createId, parse } from "./utils.js";
3
-
4
- function isClass(func) {
5
- return (
6
- typeof func === "function" &&
7
- /^class\s/.test(Function.prototype.toString.call(func))
8
- );
9
- }
10
-
11
- const getClient = (store) => {
12
- // Already a fully compliant KV store
13
- if (store instanceof Store) return store.client;
14
-
15
- // One of the supported ones, so we receive an instance and
16
- // wrap it with the client wrapper
17
- for (let client of Object.values(clients)) {
18
- if (client.test && client.test(store)) {
19
- return new client(store);
20
- }
21
- }
22
-
23
- // A raw one, we just receive the single instance to use directly
24
- if (isClass(store)) {
25
- return new store();
26
- }
27
- return store;
28
- };
2
+ import { createId, isClass, parse } from "./utils.js";
29
3
 
4
+ // #region Store
30
5
  class Store {
31
6
  PREFIX = "";
32
7
 
@@ -34,14 +9,34 @@ class Store {
34
9
  this.promise = Promise.resolve(clientPromise).then(async (client) => {
35
10
  if (client?.open) await client.open();
36
11
  if (client?.connect) await client.connect();
37
- client = getClient(client);
38
- this.#validate(client);
39
- this.client = client;
12
+ this.client = this.#find(client);
13
+ this.#validate(this.client);
40
14
  this.promise = null;
41
15
  return client;
42
16
  });
43
17
  }
44
18
 
19
+ // #region #client()
20
+ #find(store) {
21
+ // Already a fully compliant KV store
22
+ if (store instanceof Store) return store.client;
23
+
24
+ // One of the supported ones, so we receive an instance and
25
+ // wrap it with the client wrapper
26
+ for (let client of Object.values(clients)) {
27
+ if (client.test && client.test(store)) {
28
+ return new client(store);
29
+ }
30
+ }
31
+
32
+ // A raw one, we just receive the single instance to use directly
33
+ if (isClass(store)) {
34
+ return new store();
35
+ }
36
+ return store;
37
+ }
38
+
39
+ // #region #validate()
45
40
  #validate(client) {
46
41
  if (!client.set || !client.get || !client.entries) {
47
42
  throw new Error(
@@ -68,69 +63,104 @@ class Store {
68
63
  }
69
64
  }
70
65
 
71
- async add(data, options = {}) {
66
+ // #region .add()
67
+ /**
68
+ * Save the data on an autogenerated key, can add expiration as well:
69
+ *
70
+ * ```js
71
+ * const key1 = await store.add("value1");
72
+ * const key2 = await store.add({ hello: "world" });
73
+ * const key3 = await store.add("value3", { expires: "1h" });
74
+ * ```
75
+ *
76
+ * **[→ Full .add() Docs](https://polystore.dev/documentation#add)**
77
+ * @param {Value} value
78
+ * @param {{ expires: string }} options
79
+ * @returns {Promise<string>}
80
+ */
81
+ async add(value, options = {}) {
72
82
  await this.promise;
73
83
  const expires = parse(options.expire ?? options.expires);
74
84
 
75
85
  // Use the underlying one from the client if found
76
86
  if (this.client.add) {
77
87
  if (this.client.EXPIRES) {
78
- return this.client.add(this.PREFIX, data, { expires });
88
+ return this.client.add(this.PREFIX, value, { expires });
79
89
  }
80
90
 
81
91
  // In the data we need the timestamp since we need it "absolute":
82
92
  const now = new Date().getTime();
83
93
  const expDiff = expires === null ? null : now + expires * 1000;
84
- return this.client.add(this.PREFIX, { expires: expDiff, value: data });
94
+ return this.client.add(this.PREFIX, { expires: expDiff, value });
85
95
  }
86
96
 
87
97
  const id = createId();
88
- await this.set(id, data, { expires });
98
+ await this.set(id, value, { expires });
89
99
  return id; // The plain one without the prefix
90
100
  }
91
101
 
92
- async set(id, data, options = {}) {
102
+ // #region .set()
103
+ /**
104
+ * Save the data on the given key, can add expiration as well:
105
+ *
106
+ * ```js
107
+ * const key = await store.set("key1", "value1");
108
+ * await store.set("key2", { hello: "world" });
109
+ * await store.set("key3", "value3", { expires: "1h" });
110
+ * ```
111
+ *
112
+ * **[→ Full .set() Docs](https://polystore.dev/documentation#set)**
113
+ * @param {string} key
114
+ * @param {Value} value
115
+ * @param {{ expires: string }} options
116
+ * @returns {Promise<string>}
117
+ */
118
+ async set(key, value, options = {}) {
93
119
  await this.promise;
94
- const key = this.PREFIX + id;
120
+ const id = this.PREFIX + key;
95
121
  const expires = parse(options.expire ?? options.expires);
96
122
 
97
123
  // Quick delete
98
- if (data === null) {
99
- await this.del(key, null);
100
- return id;
124
+ if (value === null) {
125
+ await this.del(id);
126
+ return key;
101
127
  }
102
128
 
103
129
  // The client manages the expiration, so let it manage it
104
130
  if (this.client.EXPIRES) {
105
- await this.client.set(key, data, { expires });
106
- return id;
131
+ await this.client.set(id, value, { expires });
132
+ return key;
107
133
  }
108
134
 
109
135
  // Already expired, then delete it
110
136
  if (expires === 0) {
111
137
  await this.del(id);
112
- return id;
138
+ return key;
113
139
  }
114
140
 
115
141
  // In the data we need the timestamp since we need it "absolute":
116
142
  const now = new Date().getTime();
117
143
  const expDiff = expires === null ? null : now + expires * 1000;
118
- await this.client.set(key, { expires: expDiff, value: data });
119
- return id;
144
+ await this.client.set(id, { expires: expDiff, value });
145
+ return key;
120
146
  }
121
147
 
148
+ // #region .get()
122
149
  /**
123
150
  * Read a single value from the KV store:
124
151
  *
125
152
  * ```js
126
- * const key = await store.set("key1", "value1");
127
- * const value = await store.get("key1");
128
- * // "value1"
153
+ * const value1 = await store.get("key1");
154
+ * // null (it doesn't exist, or it has expired)
155
+ * const value2 = await store.get("key2");
156
+ * // "value2"
157
+ * const value3 = await store.get("key3");
158
+ * // { hello: "world" }
129
159
  * ```
130
160
  *
131
161
  * **[→ Full .get() Docs](https://polystore.dev/documentation#get)**
132
- * @param {(string)} key
133
- * @returns {(any)} value
162
+ * @param {string} key
163
+ * @returns {Promise<Value>}
134
164
  */
135
165
  async get(key) {
136
166
  await this.promise;
@@ -164,31 +194,78 @@ class Store {
164
194
  return value;
165
195
  }
166
196
 
167
- async has(id) {
197
+ // #region .has()
198
+ /**
199
+ * Check whether a key exists or not:
200
+ *
201
+ * ```js
202
+ * if (await store.has("key1")) { ... }
203
+ * ```
204
+ *
205
+ * If you are going to use the value, it's better to just read it:
206
+ *
207
+ * ```js
208
+ * const val = await store.get("key1");
209
+ * if (val) { ... }
210
+ * ```
211
+ *
212
+ *
213
+ * **[→ Full .has() Docs](https://polystore.dev/documentation#has)**
214
+ * @param {string} key
215
+ * @returns {Promise<boolean>}
216
+ */
217
+ async has(key) {
168
218
  await this.promise;
169
- const key = this.PREFIX + id;
219
+ const id = this.PREFIX + key;
170
220
 
171
221
  if (this.client.has) {
172
- return this.client.has(key);
222
+ return this.client.has(id);
173
223
  }
174
224
 
175
225
  const value = await this.get(key);
176
226
  return value !== null;
177
227
  }
178
228
 
179
- async del(id) {
229
+ // #region .del()
230
+ /**
231
+ * Remove a single key and its value from the store:
232
+ *
233
+ * ```js
234
+ * const key = await store.del("key1");
235
+ * ```
236
+ *
237
+ * **[→ Full .del() Docs](https://polystore.dev/documentation#del)**
238
+ * @param {string} key
239
+ * @returns {Promise<string>}
240
+ */
241
+ async del(key) {
180
242
  await this.promise;
181
- const key = this.PREFIX + id;
243
+ const id = this.PREFIX + key;
182
244
 
183
245
  if (this.client.del) {
184
- await this.client.del(key);
185
- return id;
246
+ await this.client.del(id);
247
+ return key;
186
248
  }
187
249
 
188
- await this.client.set(key, null, { expires: 0 });
189
- return id;
250
+ await this.client.set(id, null, { expires: 0 });
251
+ return key;
190
252
  }
191
253
 
254
+ // #region .entries()
255
+ /**
256
+ * Return an array of the entries, in the [key, value] format:
257
+ *
258
+ * ```js
259
+ * const entries = await store.entries();
260
+ * // [["key1", "value1"], ["key2", { hello: "world" }], ...]
261
+ *
262
+ * // To limit it to a given prefix, use `.prefix()`:
263
+ * const sessions = await store.prefix("session:").entries();
264
+ * ```
265
+ *
266
+ * **[→ Full .entries() Docs](https://polystore.dev/documentation#entries)**
267
+ * @returns {Promise<[string, Value][]>}
268
+ */
192
269
  async entries() {
193
270
  await this.promise;
194
271
 
@@ -225,6 +302,21 @@ class Store {
225
302
  .map(([key, data]) => [key, data.value]);
226
303
  }
227
304
 
305
+ // #region .values()
306
+ /**
307
+ * Return an array of the values in the store:
308
+ *
309
+ * ```js
310
+ * const values = await store.values();
311
+ * // ["value1", { hello: "world" }, ...]
312
+ *
313
+ * // To limit it to a given prefix, use `.prefix()`:
314
+ * const sessions = await store.prefix("session:").values();
315
+ * ```
316
+ *
317
+ * **[→ Full .values() Docs](https://polystore.dev/documentation#values)**
318
+ * @returns {Promise<Value[]>}
319
+ */
228
320
  async values() {
229
321
  await this.promise;
230
322
 
@@ -255,6 +347,21 @@ class Store {
255
347
  return entries.map((e) => e[1]);
256
348
  }
257
349
 
350
+ // #region .keys()
351
+ /**
352
+ * Return an array of the keys in the store:
353
+ *
354
+ * ```js
355
+ * const keys = await store.keys();
356
+ * // ["key1", "key2", ...]
357
+ *
358
+ * // To limit it to a given prefix, use `.prefix()`:
359
+ * const sessions = await store.prefix("session:").keys();
360
+ * ```
361
+ *
362
+ * **[→ Full .keys() Docs](https://polystore.dev/documentation#keys)**
363
+ * @returns {Promise<string[]>}
364
+ */
258
365
  async keys() {
259
366
  await this.promise;
260
367
 
@@ -268,6 +375,21 @@ class Store {
268
375
  return entries.map((e) => e[0]);
269
376
  }
270
377
 
378
+ // #region .all()
379
+ /**
380
+ * Return an object with the keys:values in the store:
381
+ *
382
+ * ```js
383
+ * const obj = await store.all();
384
+ * // { key1: "value1", key2: { hello: "world" }, ... }
385
+ *
386
+ * // To limit it to a given prefix, use `.prefix()`:
387
+ * const sessions = await store.prefix("session:").all();
388
+ * ```
389
+ *
390
+ * **[→ Full .all() Docs](https://polystore.dev/documentation#all)**
391
+ * @returns {Promise<{ [key:string]: Value }>}
392
+ */
271
393
  async all() {
272
394
  await this.promise;
273
395
 
@@ -285,6 +407,19 @@ class Store {
285
407
  return Object.fromEntries(entries);
286
408
  }
287
409
 
410
+ // #region .clear()
411
+ /**
412
+ * Delete all of the records of the store:
413
+ *
414
+ * ```js
415
+ * await store.clear();
416
+ * ```
417
+ *
418
+ * It's useful for cache invalidation, clearing the data, and testing.
419
+ *
420
+ * **[→ Full .clear() Docs](https://polystore.dev/documentation#clear)**
421
+ * @returns {Promise<null>}
422
+ */
288
423
  async clear() {
289
424
  await this.promise;
290
425
 
@@ -294,9 +429,26 @@ class Store {
294
429
 
295
430
  const keys = await this.keys();
296
431
  // Note: this gives trouble of concurrent deletes in the FS
297
- return await Promise.all(keys.map((key) => this.del(key)));
432
+ await Promise.all(keys.map((key) => this.del(key)));
298
433
  }
299
434
 
435
+ // #region .prefix()
436
+ /**
437
+ * Create a substore where all the keys are stored with
438
+ * the given prefix:
439
+ *
440
+ * ```js
441
+ * const session = store.prefix("session:");
442
+ * await session.set("key1", "value1");
443
+ * console.log(await session.entries()); // session.
444
+ * // [["key1", "value1"]]
445
+ * console.log(await store.entries()); // store.
446
+ * // [["session:key1", "value1"]]
447
+ * ```
448
+ *
449
+ * **[→ Full .prefix() Docs](https://polystore.dev/documentation#prefix)**
450
+ * @returns {Store}
451
+ */
300
452
  prefix(prefix = "") {
301
453
  const store = new Store(
302
454
  Promise.resolve(this.promise).then((client) => client || this.client)
@@ -305,6 +457,7 @@ class Store {
305
457
  return store;
306
458
  }
307
459
 
460
+ // #region .close()
308
461
  async close() {
309
462
  await this.promise;
310
463
 
package/src/index.test.js CHANGED
@@ -7,7 +7,7 @@ import { Level } from "level";
7
7
  import localForage from "localforage";
8
8
  import { createClient } from "redis";
9
9
 
10
- import kv from "./";
10
+ import kv from "./index.js";
11
11
  import customFull from "./test/customFull.js";
12
12
  import customSimple from "./test/customSimple.js";
13
13
 
@@ -113,7 +113,9 @@ for (let [name, store] of stores) {
113
113
 
114
114
  it("can perform a CRUD", async () => {
115
115
  expect(await store.get("a")).toBe(null);
116
+ expect(await store.has("a")).toBe(false);
116
117
  expect(await store.set("a", "b")).toBe("a");
118
+ expect(await store.has("a")).toBe(true);
117
119
  expect(await store.get("a")).toBe("b");
118
120
  expect(await store.del("a")).toBe("a");
119
121
  expect(await store.get("a")).toBe(null);
@@ -510,6 +512,12 @@ for (let [name, store] of stores) {
510
512
  expect(await store.get("session:a")).toBe("b");
511
513
  });
512
514
 
515
+ it("checks the has properly", async () => {
516
+ expect(await session.has("a")).toBe(false);
517
+ await session.set("a", "b");
518
+ expect(await session.has("a")).toBe(true);
519
+ });
520
+
513
521
  it("can add with the prefix", async () => {
514
522
  const id = await session.add("b");
515
523
  expect(id.length).toBe(24);
package/src/utils.js CHANGED
@@ -10,7 +10,7 @@ parse.year = parse.yr = parse.y = parse.d * 365.25;
10
10
  parse.month = parse.b = parse.y / 12;
11
11
 
12
12
  // Returns the time in milliseconds
13
- function parse(str) {
13
+ export function parse(str) {
14
14
  if (str === null || str === undefined) return null;
15
15
  if (typeof str === "number") return str;
16
16
  // ignore commas/placeholders
@@ -28,7 +28,7 @@ function parse(str) {
28
28
  const urlAlphabet =
29
29
  "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
30
30
 
31
- function createId() {
31
+ export function createId() {
32
32
  let size = 24;
33
33
  let id = "";
34
34
  let bytes = crypto.getRandomValues(new Uint8Array(size));
@@ -41,4 +41,9 @@ function createId() {
41
41
  return id;
42
42
  }
43
43
 
44
- export { parse, createId };
44
+ export function isClass(func) {
45
+ return (
46
+ typeof func === "function" &&
47
+ /^class\s/.test(Function.prototype.toString.call(func))
48
+ );
49
+ }