polystore 0.16.1 → 0.18.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.
Files changed (4) hide show
  1. package/index.d.ts +23 -11
  2. package/index.js +104 -45
  3. package/package.json +15 -7
  4. package/readme.md +377 -181
package/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- type Options = {
2
- expires?: number | null | string;
3
- };
1
+ type Expires = number | null | string;
4
2
  type StoreData<T extends Serializable = Serializable> = {
5
3
  value: T;
6
4
  expires: number | null;
@@ -9,13 +7,14 @@ type Serializable = string | number | boolean | null | (Serializable | null)[] |
9
7
  [key: string]: Serializable | null;
10
8
  };
11
9
  interface ClientExpires {
10
+ TYPE: string;
12
11
  EXPIRES: true;
13
12
  promise?: Promise<any>;
14
13
  test?: (client: any) => boolean;
15
14
  get<T extends Serializable>(key: string): Promise<T | null> | T | null;
16
- set<T extends Serializable>(key: string, value: T, options?: Options): Promise<any> | any;
15
+ set<T extends Serializable>(key: string, value: T, expires?: Expires): Promise<any> | any;
17
16
  iterate<T extends Serializable>(prefix: string): AsyncGenerator<[string, T], void, unknown> | Generator<[string, T], void, unknown>;
18
- add?<T extends Serializable>(prefix: string, value: T, options?: Options): Promise<string>;
17
+ add?<T extends Serializable>(prefix: string, value: T, expires?: Expires): Promise<string>;
19
18
  has?(key: string): Promise<boolean> | boolean;
20
19
  del?(key: string): Promise<any> | any;
21
20
  keys?(prefix: string): Promise<string[]> | string[];
@@ -27,13 +26,14 @@ interface ClientExpires {
27
26
  close?(): Promise<any> | any;
28
27
  }
29
28
  interface ClientNonExpires {
29
+ TYPE: string;
30
30
  EXPIRES: false;
31
31
  promise?: Promise<any>;
32
32
  test?: (client: any) => boolean;
33
33
  get<T extends Serializable>(key: string): Promise<StoreData<T> | null> | StoreData<T> | null;
34
- set<T extends Serializable>(key: string, value: StoreData<T> | null, options?: Options): Promise<any> | any;
34
+ set<T extends Serializable>(key: string, value: StoreData<T> | null, ttl?: Expires): Promise<any> | any;
35
35
  iterate<T extends Serializable>(prefix: string): AsyncGenerator<[string, StoreData<T>], void, unknown> | Generator<[string, StoreData<T>], void, unknown>;
36
- add?<T extends Serializable>(prefix: string, value: StoreData<T>, options?: Options): Promise<string>;
36
+ add?<T extends Serializable>(prefix: string, value: StoreData<T>, ttl?: Expires): Promise<string>;
37
37
  has?(key: string): Promise<boolean> | boolean;
38
38
  del?(key: string): Promise<any> | any;
39
39
  keys?(prefix: string): Promise<string[]> | string[];
@@ -51,6 +51,7 @@ declare class Store<TDefault extends Serializable = Serializable> {
51
51
  PREFIX: string;
52
52
  promise: Promise<Client> | null;
53
53
  client: Client;
54
+ type: string;
54
55
  constructor(clientPromise?: any);
55
56
  /**
56
57
  * Save the data on an autogenerated key, can add expiration as well:
@@ -63,8 +64,8 @@ declare class Store<TDefault extends Serializable = Serializable> {
63
64
  *
64
65
  * **[→ Full .add() Docs](https://polystore.dev/documentation#add)**
65
66
  */
66
- add(value: TDefault, options?: Options): Promise<string>;
67
- add<T extends TDefault>(value: T, options?: Options): Promise<string>;
67
+ add(value: TDefault, ttl?: Expires): Promise<string>;
68
+ add<T extends TDefault>(value: T, ttl?: Expires): Promise<string>;
68
69
  /**
69
70
  * Save the data on the given key, can add expiration as well:
70
71
  *
@@ -76,8 +77,8 @@ declare class Store<TDefault extends Serializable = Serializable> {
76
77
  *
77
78
  * **[→ Full .set() Docs](https://polystore.dev/documentation#set)**
78
79
  */
79
- set(key: string, value: TDefault, options?: Options): Promise<string>;
80
- set<T extends TDefault>(key: string, value: T, options?: Options): Promise<string>;
80
+ set(key: string, value: TDefault, ttl?: Expires): Promise<string>;
81
+ set<T extends TDefault>(key: string, value: T, ttl?: Expires): Promise<string>;
81
82
  /**
82
83
  * Read a single value from the KV store:
83
84
  *
@@ -121,6 +122,17 @@ declare class Store<TDefault extends Serializable = Serializable> {
121
122
  * **[→ Full .del() Docs](https://polystore.dev/documentation#del)**
122
123
  */
123
124
  del(key: string): Promise<string>;
125
+ /**
126
+ * @alias of .del(key: string)
127
+ * Remove a single key and its value from the store:
128
+ *
129
+ * ```js
130
+ * const key = await store.delete("key1");
131
+ * ```
132
+ *
133
+ * **[→ Full .del() Docs](https://polystore.dev/documentation#del)**
134
+ */
135
+ delete(key: string): Promise<string>;
124
136
  /**
125
137
  * An iterator that goes through all of the key:value pairs in the client
126
138
  *
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/clients/Client.ts
2
2
  var Client = class {
3
+ TYPE;
3
4
  EXPIRES = false;
4
5
  client;
5
6
  encode = (val) => JSON.stringify(val, null, 2);
@@ -11,6 +12,7 @@ var Client = class {
11
12
 
12
13
  // src/clients/api.ts
13
14
  var Api = class extends Client {
15
+ TYPE = "API";
14
16
  // Indicate that the file handler DOES handle expirations
15
17
  EXPIRES = true;
16
18
  static test = (client) => typeof client === "string" && /^https?:\/\//.test(client);
@@ -25,17 +27,18 @@ var Api = class extends Client {
25
27
  return this.decode(await res.text());
26
28
  };
27
29
  get = (key) => this.#api(key);
28
- set = async (key, value, { expires } = {}) => {
30
+ set = async (key, value, expires) => {
29
31
  const exp = typeof expires === "number" ? `?expires=${expires}` : "";
30
32
  await this.#api(key, exp, "PUT", this.encode(value));
31
33
  };
32
- del = async (key) => {
33
- await this.#api(key, "", "DELETE");
34
- };
34
+ del = (key) => this.#api(key, "", "DELETE");
35
35
  async *iterate(prefix = "") {
36
- const data = await this.#api("", `?prefix=${encodeURIComponent(prefix)}`);
36
+ const data = await this.#api(
37
+ "",
38
+ `?prefix=${encodeURIComponent(prefix)}`
39
+ );
37
40
  for (let [key, value] of Object.entries(data || {})) {
38
- if (value !== null && value !== void 0) {
41
+ if (value !== null) {
39
42
  yield [prefix + key, value];
40
43
  }
41
44
  }
@@ -44,17 +47,20 @@ var Api = class extends Client {
44
47
 
45
48
  // src/clients/cloudflare.ts
46
49
  var Cloudflare = class extends Client {
50
+ TYPE = "CLOUDFLARE";
47
51
  // It handles expirations natively
48
52
  EXPIRES = true;
49
- // Check whether the given store is a FILE-type
50
- static test = (client) => client?.constructor?.name === "KvNamespace" || client?.constructor?.name === "EdgeKVNamespace";
51
- get = async (key) => this.decode(await this.client.get(key));
52
- set = (key, data, opts) => {
53
- const expirationTtl = opts.expires ? Math.round(opts.expires) : void 0;
53
+ static testKeys = ["getWithMetadata", "get", "list", "delete"];
54
+ get = async (key) => {
55
+ const value = await this.client.get(key);
56
+ return this.decode(value);
57
+ };
58
+ set = async (key, data, expires) => {
59
+ const expirationTtl = expires ? Math.round(expires) : void 0;
54
60
  if (expirationTtl && expirationTtl < 60) {
55
61
  throw new Error("Cloudflare's min expiration is '60s'");
56
62
  }
57
- return this.client.put(key, this.encode(data), { expirationTtl });
63
+ await this.client.put(key, this.encode(data), { expirationTtl });
58
64
  };
59
65
  del = (key) => this.client.delete(key);
60
66
  // Since we have pagination, we don't want to get all of the
@@ -84,12 +90,13 @@ var Cloudflare = class extends Client {
84
90
  entries = async (prefix = "") => {
85
91
  const keys = await this.keys(prefix);
86
92
  const values = await Promise.all(keys.map((k) => this.get(k)));
87
- return keys.map((k, i) => [k, values[i]]);
93
+ return keys.map((k, i) => [k, values[i]]).filter((p) => p[1] !== null);
88
94
  };
89
95
  };
90
96
 
91
97
  // src/clients/cookie.ts
92
98
  var Cookie = class extends Client {
99
+ TYPE = "COOKIE";
93
100
  // It handles expirations natively
94
101
  EXPIRES = true;
95
102
  // Check if this is the right class for the given client
@@ -115,7 +122,7 @@ var Cookie = class extends Client {
115
122
  const all = this.#read();
116
123
  return key in all ? all[key] : null;
117
124
  };
118
- set = (key, data, { expires }) => {
125
+ set = (key, data, expires) => {
119
126
  const k = encodeURIComponent(key);
120
127
  const value = encodeURIComponent(this.encode(data ?? ""));
121
128
  let exp = "";
@@ -125,7 +132,7 @@ var Cookie = class extends Client {
125
132
  }
126
133
  document.cookie = `${k}=${value}${exp}`;
127
134
  };
128
- del = (key) => this.set(key, "", { expires: -100 });
135
+ del = (key) => this.set(key, "", -100);
129
136
  async *iterate(prefix = "") {
130
137
  for (let [key, value] of Object.entries(this.#read())) {
131
138
  if (!key.startsWith(prefix)) continue;
@@ -136,12 +143,18 @@ var Cookie = class extends Client {
136
143
 
137
144
  // src/clients/etcd.ts
138
145
  var Etcd = class extends Client {
146
+ TYPE = "ETCD3";
139
147
  // It desn't handle expirations natively
140
148
  EXPIRES = false;
141
149
  // Check if this is the right class for the given client
142
- static test = (client) => client?.constructor?.name === "Etcd3";
143
- get = (key) => this.client.get(key).json();
144
- set = (key, value) => this.client.put(key).value(this.encode(value));
150
+ static testKeys = ["leaseClient", "watchClient", "watchManager"];
151
+ get = async (key) => {
152
+ const data = await this.client.get(key).json();
153
+ return data;
154
+ };
155
+ set = async (key, value) => {
156
+ await this.client.put(key).value(this.encode(value));
157
+ };
145
158
  del = (key) => this.client.delete().key(key).exec();
146
159
  async *iterate(prefix = "") {
147
160
  const keys = await this.client.getAll().prefix(prefix).keys();
@@ -149,12 +162,6 @@ var Etcd = class extends Client {
149
162
  yield [key, await this.get(key)];
150
163
  }
151
164
  }
152
- keys = (prefix = "") => this.client.getAll().prefix(prefix).keys();
153
- entries = async (prefix = "") => {
154
- const keys = await this.keys(prefix);
155
- const values = await Promise.all(keys.map((k) => this.get(k)));
156
- return keys.map((k, i) => [k, values[i]]);
157
- };
158
165
  clear = async (prefix = "") => {
159
166
  if (!prefix) return this.client.delete().all();
160
167
  return this.client.delete().prefix(prefix);
@@ -163,6 +170,7 @@ var Etcd = class extends Client {
163
170
 
164
171
  // src/clients/file.ts
165
172
  var File = class extends Client {
173
+ TYPE = "FILE";
166
174
  // It desn't handle expirations natively
167
175
  EXPIRES = false;
168
176
  fsp;
@@ -256,6 +264,7 @@ var noFileOk = (error) => {
256
264
  throw error;
257
265
  };
258
266
  var Folder = class extends Client {
267
+ TYPE = "FOLDER";
259
268
  // It desn't handle expirations natively
260
269
  EXPIRES = false;
261
270
  fsp;
@@ -274,13 +283,16 @@ var Folder = class extends Client {
274
283
  });
275
284
  })();
276
285
  file = (key) => this.folder + key + ".json";
277
- get = (key) => {
278
- return this.fsp.readFile(this.file(key), "utf8").then(this.decode, noFileOk);
286
+ get = async (key) => {
287
+ const file = await this.fsp.readFile(this.file(key), "utf8").catch(noFileOk);
288
+ return this.decode(file);
279
289
  };
280
- set = (key, value) => {
281
- return this.fsp.writeFile(this.file(key), this.encode(value), "utf8");
290
+ set = async (key, value) => {
291
+ await this.fsp.writeFile(this.file(key), this.encode(value), "utf8");
292
+ };
293
+ del = async (key) => {
294
+ await this.fsp.unlink(this.file(key)).catch(noFileOk);
282
295
  };
283
- del = (key) => this.fsp.unlink(this.file(key)).catch(noFileOk);
284
296
  async *iterate(prefix = "") {
285
297
  const all = await this.fsp.readdir(this.folder);
286
298
  const keys = all.filter((f) => f.startsWith(prefix) && f.endsWith(".json"));
@@ -288,7 +300,7 @@ var Folder = class extends Client {
288
300
  const key = name.slice(0, -".json".length);
289
301
  try {
290
302
  const data = await this.get(key);
291
- yield [key, data];
303
+ if (data !== null && data !== void 0) yield [key, data];
292
304
  } catch {
293
305
  continue;
294
306
  }
@@ -298,6 +310,7 @@ var Folder = class extends Client {
298
310
 
299
311
  // src/clients/forage.ts
300
312
  var Forage = class extends Client {
313
+ TYPE = "FORAGE";
301
314
  // It desn't handle expirations natively
302
315
  EXPIRES = false;
303
316
  // Check if this is the right class for the given client
@@ -331,10 +344,11 @@ var notFound = (error) => {
331
344
  throw error;
332
345
  };
333
346
  var Level = class extends Client {
347
+ TYPE = "LEVEL";
334
348
  // It desn't handle expirations natively
335
349
  EXPIRES = false;
336
350
  // Check if this is the right class for the given client
337
- static test = (client) => client?.constructor?.name === "ClassicLevel";
351
+ static testKeys = ["attachResource", "detachResource", "prependOnceListener"];
338
352
  get = (key) => this.client.get(key, { valueEncoding }).catch(notFound);
339
353
  set = (key, value) => this.client.put(key, value, { valueEncoding });
340
354
  del = (key) => this.client.del(key);
@@ -365,6 +379,7 @@ var Level = class extends Client {
365
379
 
366
380
  // src/clients/memory.ts
367
381
  var Memory = class extends Client {
382
+ TYPE = "MEMORY";
368
383
  // It desn't handle expirations natively
369
384
  EXPIRES = false;
370
385
  // Check if this is the right class for the given client
@@ -382,12 +397,13 @@ var Memory = class extends Client {
382
397
 
383
398
  // src/clients/redis.ts
384
399
  var Redis = class extends Client {
400
+ TYPE = "REDIS";
385
401
  // Indicate if this client handles expirations (true = it does)
386
402
  EXPIRES = true;
387
403
  // Check if this is the right class for the given client
388
404
  static test = (client) => client && client.pSubscribe && client.sSubscribe;
389
405
  get = async (key) => this.decode(await this.client.get(key));
390
- set = async (key, value, { expires } = {}) => {
406
+ set = async (key, value, expires) => {
391
407
  const EX = expires ? Math.round(expires) : void 0;
392
408
  return this.client.set(key, this.encode(value), { EX });
393
409
  };
@@ -425,12 +441,34 @@ var Redis = class extends Client {
425
441
 
426
442
  // src/clients/sqlite.ts
427
443
  var SQLite = class extends Client {
444
+ TYPE = "SQLITE";
428
445
  // This one is doing manual time management internally even though
429
446
  // sqlite does not natively support expirations. This is because it does
430
447
  // support creating a `expires_at:Date` column that makes managing
431
- // expirations much easier, so it's really "somewhere in between"
448
+ // expirations much easier, so it's really "somewhere in between"
432
449
  EXPIRES = true;
433
- static test = (client) => typeof client?.prepare === "function";
450
+ // The table name to use
451
+ table = "kv";
452
+ // Make sure the folder already exists, so attempt to create it
453
+ // It fails if it already exists, hence the catch case
454
+ promise = (async () => {
455
+ if (!/^[a-zA-Z_]+$/.test(this.table)) {
456
+ throw new Error(`Invalid table name ${this.table}`);
457
+ }
458
+ this.client.exec(`
459
+ CREATE TABLE IF NOT EXISTS ${this.table} (
460
+ id TEXT PRIMARY KEY,
461
+ value TEXT NOT NULL,
462
+ expires_at INTEGER
463
+ )
464
+ `);
465
+ this.client.exec(
466
+ `CREATE INDEX IF NOT EXISTS idx_${this.table}_expires_at ON ${this.table} (expires_at)`
467
+ );
468
+ })();
469
+ static test = (client) => {
470
+ return typeof client?.prepare === "function" && typeof client?.exec === "function";
471
+ };
434
472
  get = (id) => {
435
473
  const row = this.client.prepare(`SELECT value, expires_at FROM kv WHERE id = ?`).get(id);
436
474
  if (!row) return null;
@@ -440,7 +478,7 @@ var SQLite = class extends Client {
440
478
  }
441
479
  return this.decode(row.value);
442
480
  };
443
- set = (id, data, { expires } = {}) => {
481
+ set = (id, data, expires) => {
444
482
  const value = this.encode(data);
445
483
  const expires_at = expires ? Date.now() + expires * 1e3 : null;
446
484
  this.client.prepare(
@@ -481,7 +519,7 @@ ${prefix ? "AND id LIKE ?" : ""}
481
519
  this.client.prepare(`DELETE FROM kv WHERE expires_at < ?`).run(Date.now());
482
520
  };
483
521
  clearAll = () => {
484
- this.client.run(`DELETE FROM kv`);
522
+ this.client.exec(`DELETE FROM kv`);
485
523
  };
486
524
  close = () => {
487
525
  this.client.close?.();
@@ -490,6 +528,7 @@ ${prefix ? "AND id LIKE ?" : ""}
490
528
 
491
529
  // src/clients/storage.ts
492
530
  var WebStorage = class extends Client {
531
+ TYPE = "STORAGE";
493
532
  // It desn't handle expirations natively
494
533
  EXPIRES = false;
495
534
  // Check if this is the right class for the given client
@@ -573,21 +612,28 @@ var Store = class _Store {
573
612
  PREFIX = "";
574
613
  promise;
575
614
  client;
615
+ type = "UNKNOWN";
576
616
  constructor(clientPromise = /* @__PURE__ */ new Map()) {
577
617
  this.promise = Promise.resolve(clientPromise).then(async (client) => {
578
618
  this.client = this.#find(client);
579
619
  this.#validate(this.client);
580
620
  this.promise = null;
581
621
  await this.client.promise;
622
+ this.type = this.client?.TYPE || this.type;
582
623
  return client;
583
624
  });
584
625
  }
585
626
  #find(store) {
586
627
  if (store instanceof _Store) return store.client;
587
628
  for (let client of Object.values(clients_default)) {
588
- if (client.test && client.test(store)) {
629
+ if ("test" in client && client.test(store)) {
589
630
  return new client(store);
590
631
  }
632
+ if ("testKeys" in client && typeof store === "object") {
633
+ if (client.testKeys.every((key) => store[key])) {
634
+ return new client(store);
635
+ }
636
+ }
591
637
  }
592
638
  if (typeof store === "function" && /^class\s/.test(Function.prototype.toString.call(store))) {
593
639
  return new store();
@@ -618,29 +664,29 @@ var Store = class _Store {
618
664
  if (key) this.del(key);
619
665
  return false;
620
666
  }
621
- async add(value, options = {}) {
667
+ async add(value, ttl) {
622
668
  await this.promise;
623
- let expires = parse(options.expires);
669
+ let expires = parse(ttl);
624
670
  if (this.client.add) {
625
671
  if (this.client.EXPIRES) {
626
- return await this.client.add(this.PREFIX, value, { expires });
672
+ return await this.client.add(this.PREFIX, value, expires);
627
673
  }
628
674
  expires = unix(expires);
629
675
  const key2 = await this.client.add(this.PREFIX, { expires, value });
630
676
  return key2;
631
677
  }
632
678
  const key = createId();
633
- return this.set(key, value, { expires });
679
+ return this.set(key, value, expires);
634
680
  }
635
- async set(key, value, options = {}) {
681
+ async set(key, value, ttl) {
636
682
  await this.promise;
637
683
  const id = this.PREFIX + key;
638
- let expires = parse(options.expires);
684
+ let expires = parse(ttl);
639
685
  if (value === null || typeof expires === "number" && expires <= 0) {
640
686
  return this.del(key);
641
687
  }
642
688
  if (this.client.EXPIRES) {
643
- await this.client.set(id, value, { expires });
689
+ await this.client.set(id, value, expires);
644
690
  return key;
645
691
  }
646
692
  expires = unix(expires);
@@ -702,12 +748,25 @@ var Store = class _Store {
702
748
  return key;
703
749
  }
704
750
  if (this.client.EXPIRES) {
705
- await this.client.set(id, null, { expires: 0 });
751
+ await this.client.set(id, null, 0);
706
752
  } else {
707
753
  await this.client.set(id, null);
708
754
  }
709
755
  return key;
710
756
  }
757
+ /**
758
+ * @alias of .del(key: string)
759
+ * Remove a single key and its value from the store:
760
+ *
761
+ * ```js
762
+ * const key = await store.delete("key1");
763
+ * ```
764
+ *
765
+ * **[→ Full .del() Docs](https://polystore.dev/documentation#del)**
766
+ */
767
+ async delete(key) {
768
+ return this.del(key);
769
+ }
711
770
  async *[Symbol.asyncIterator]() {
712
771
  await this.promise;
713
772
  if (this.client.EXPIRES) {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "polystore",
3
- "version": "0.16.1",
3
+ "version": "0.18.0",
4
4
  "description": "A small compatibility layer for many popular KV stores like localStorage, Redis, FileSystem, etc.",
5
- "homepage": "https://polystore.dev/",
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",
@@ -16,11 +16,13 @@
16
16
  "index.d.ts"
17
17
  ],
18
18
  "scripts": {
19
- "analyze": "npm run build && esbuild src/index.ts --bundle --packages=external --format=esm --minify --outfile=index.min.js && gzip-size index.min.js && rm index.min.js",
19
+ "analyze": "npm run build && esbuild src/index.ts --bundle --packages=external --format=esm --minify --outfile=index.min.js && echo 'Final size:' && gzip-size index.min.js && rm index.min.js",
20
20
  "build": "bunx tsup src/index.ts --format esm --dts --out-dir . --target node24",
21
21
  "lint": "npx tsc --noEmit",
22
22
  "start": "bun test --watch",
23
- "test": "bun test",
23
+ "test": "npm run test:bun && npm run test:jest",
24
+ "test:bun": "bun test ./test/index.test.ts",
25
+ "test:jest": "jest ./test/index.test.ts --detectOpenHandles --forceExit",
24
26
  "run:db": "etcd",
25
27
  "run:server": "bun ./src/server.ts"
26
28
  },
@@ -35,21 +37,27 @@
35
37
  "license": "MIT",
36
38
  "devDependencies": {
37
39
  "@deno/kv": "^0.8.1",
40
+ "@types/better-sqlite3": "^7.6.13",
38
41
  "@types/bun": "^1.3.3",
42
+ "@types/jest": "^30.0.0",
39
43
  "@types/jsdom": "^27.0.0",
40
- "better-sqlite3": "^12.5.0",
44
+ "better-sqlite3": "^12.6.0",
41
45
  "check-dts": "^0.8.0",
42
- "cross-fetch": "^4.0.0",
46
+ "cross-fetch": "^4.1.0",
43
47
  "dotenv": "^16.3.1",
44
48
  "edge-mock": "^0.0.15",
45
49
  "esbuild": "^0.27.0",
46
50
  "etcd3": "^1.1.2",
47
51
  "gzip-size-cli": "^5.1.0",
52
+ "jest": "^30.2.0",
48
53
  "jsdom": "^27.2.0",
49
54
  "level": "^8.0.1",
50
55
  "localforage": "^1.10.0",
51
56
  "redis": "^4.6.10",
52
- "tsup": "^8.5.1"
57
+ "ts-jest": "^29.4.6",
58
+ "ts-node": "^10.9.2",
59
+ "tsup": "^8.5.1",
60
+ "typescript": "^5.9.3"
53
61
  },
54
62
  "documentation": {
55
63
  "title": "🏬 Polystore - A universal library for standardizing any KV-store",