polystore 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polystore",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
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",
package/readme.md CHANGED
@@ -462,13 +462,23 @@ console.log(await store.get("key1"));
462
462
 
463
463
  <details>
464
464
  <summary>Why use polystore with <code>new Map()</code>?</summary>
465
- <p>Besides the other benefits already documented elsewhere, these are the ones specifically for wrapping Map() with polystore:</p>
465
+ <p>These benefits are for wrapping Map() with polystore:</p>
466
466
  <ul>
467
467
  <li><strong>Expiration</strong>: you can now set lifetime to your values so that they are automatically evicted when the time passes. <a href="#expiration-explained">Expiration explained</a>.</li>
468
468
  <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
469
469
  </ul>
470
470
  </details>
471
471
 
472
+ ```js
473
+ // GOOD - with polystore
474
+ await store.set("key1", { name: "Francisco" }, { expires: "2days" });
475
+
476
+ // COMPLEX - With sessionStorage
477
+ const data = new Map();
478
+ data.set("key1", { name: "Francisco" });
479
+ // Expiration not supported
480
+ ```
481
+
472
482
  ### Local Storage
473
483
 
474
484
  The traditional localStorage that we all know and love, this time with a unified API, and promises:
@@ -485,6 +495,26 @@ console.log(await store.get("key1"));
485
495
 
486
496
  Same limitations as always apply to localStorage, if you think you are going to use too much storage try instead our integration with [Local Forage](#local-forage)!
487
497
 
498
+ <details>
499
+ <summary>Why use polystore with <code>localStorage</code>?</summary>
500
+ <p>These benefits are for wrapping localStorage with polystore:</p>
501
+ <ul>
502
+ <li><strong>Data structures</strong>: with Polystore you can pass more complex data structures and we'll handle the serialization/deserialization.</li>
503
+ <li><strong>Expiration</strong>: you can now set lifetime to your values so that they are automatically evicted when the time passes. <a href="#expiration-explained">Expiration explained</a>.</li>
504
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
505
+ </ul>
506
+ </details>
507
+
508
+ ```js
509
+ // GOOD - with polystore
510
+ await store.set("key1", { name: "Francisco" }, { expires: "2days" });
511
+
512
+ // COMPLEX - With localStorage
513
+ const serialValue = JSON.stringify({ name: "Francisco" });
514
+ localStorage.set("key1", serialValue);
515
+ // Expiration not supported
516
+ ```
517
+
488
518
  ### Session Storage
489
519
 
490
520
  Same as localStorage, but now for the session only:
@@ -499,6 +529,26 @@ console.log(await store.get("key1"));
499
529
  // "Hello world"
500
530
  ```
501
531
 
532
+ <details>
533
+ <summary>Why use polystore with <code>sessionStorage</code>?</summary>
534
+ <p>These benefits are for wrapping sessionStorage with polystore:</p>
535
+ <ul>
536
+ <li><strong>Data structures</strong>: with Polystore you can pass more complex data structures and we'll handle the serialization/deserialization.</li>
537
+ <li><strong>Expiration</strong>: you can now set lifetime to your values so that they are automatically evicted when the time passes. <a href="#expiration-explained">Expiration explained</a>.</li>
538
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
539
+ </ul>
540
+ </details>
541
+
542
+ ```js
543
+ // GOOD - with polystore
544
+ await store.set("key1", { name: "Francisco" }, { expires: "2days" });
545
+
546
+ // COMPLEX - With sessionStorage
547
+ const serialValue = JSON.stringify({ name: "Francisco" });
548
+ sessionStorage.set("key1", serialValue);
549
+ // Expiration not supported
550
+ ```
551
+
502
552
  ### Cookies
503
553
 
504
554
  Supports native browser cookies, including setting the expire time:
@@ -517,6 +567,16 @@ It is fairly limited for how powerful cookies are, but in exchange it has the sa
517
567
 
518
568
  > Note: the cookie expire resolution is in the seconds, so times shorter than 1 second like `expires: 0.02` (20 ms) don't make sense for this storage method and won't properly save them.
519
569
 
570
+ <details>
571
+ <summary>Why use polystore with <code>cookies</code>?</summary>
572
+ <p>These benefits are for wrapping cookies with polystore:</p>
573
+ <ul>
574
+ <li><strong>Data structures</strong>: with Polystore you can pass more complex data structures and we'll handle the serialization/deserialization.</li>
575
+ <li><strong>Intuitive expirations</strong>: use plain English to specify the expiration time like <code>10min</code>. <a href="#expiration-explained">Expiration explained</a>.</li>
576
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
577
+ </ul>
578
+ </details>
579
+
520
580
  ### Local Forage
521
581
 
522
582
  Supports localForage (with any driver it uses) so that you have a unified API. It also _adds_ the `expires` option to the setters!
@@ -532,6 +592,15 @@ console.log(await store.get("key1"));
532
592
  // "Hello world"
533
593
  ```
534
594
 
595
+ <details>
596
+ <summary>Why use polystore with <code>localStorage</code>?</summary>
597
+ <p>These benefits are for wrapping localStorage with polystore:</p>
598
+ <ul>
599
+ <li><strong>Intuitive expirations</strong>: use plain English to specify the expiration time like <code>10min</code>. <a href="#expiration-explained">Expiration explained</a>.</li>
600
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
601
+ </ul>
602
+ </details>
603
+
535
604
  ### Redis Client
536
605
 
537
606
  Supports the official Node Redis Client. You can pass either the client or the promise:
@@ -551,6 +620,15 @@ You don't need to `await` for the connect or similar, this will process it prope
551
620
 
552
621
  > Note: the Redis client expire resolution is in the seconds, so times shorter than 1 second like `expires: 0.02` (20 ms) don't make sense for this storage method and won't properly save them.
553
622
 
623
+ <details>
624
+ <summary>Why use polystore with <code>Redis</code>?</summary>
625
+ <p>These benefits are for wrapping Redis with polystore:</p>
626
+ <ul>
627
+ <li><strong>Intuitive expirations</strong>: use plain English to specify the expiration time like <code>10min</code>. <a href="#expiration-explained">Expiration explained</a>.</li>
628
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
629
+ </ul>
630
+ </details>
631
+
554
632
  ### File
555
633
 
556
634
  Treat a JSON file in your filesystem as the source for the KV store:
@@ -576,6 +654,30 @@ const store1 = kv(new URL(`file://${process.cwd()}/cache.json`));
576
654
  const store2 = kv(new URL(`file://${import.meta.dirname}/data.json`));
577
655
  ```
578
656
 
657
+ <details>
658
+ <summary>Why use polystore with a file?</summary>
659
+ <p>These benefits are for wrapping a file with polystore:</p>
660
+ <ul>
661
+ <li><strong>Data structures</strong>: with Polystore you can pass more complex data structures and we'll handle the serialization/deserialization.</li>
662
+ <li><strong>Expiration</strong>: you can now set lifetime to your values so that they are automatically evicted when the time passes. <a href="#expiration-explained">Expiration explained</a>.</li>
663
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
664
+ </ul>
665
+ </details>
666
+
667
+ ```js
668
+ // GOOD - with polystore
669
+ await store.set("key1", { name: "Francisco" }, { expires: "2days" });
670
+
671
+ // COMPLEX - With native file managing
672
+ const file = './data/users.json';
673
+ const str = await fsp.readFile(file, "utf-8");
674
+ const data = JSON.parse(str);
675
+ data["key1"] = { name: "Francisco" };
676
+ const serialValue = JSON.stringify(data);
677
+ await fsp.writeFile(file, serialValue);
678
+ // Expiration not supported (and error handling not shown)
679
+ ```
680
+
579
681
  ### Folder
580
682
 
581
683
  Treat a single folder in your filesystem as the source for the KV store, with each key being within a file:
@@ -601,6 +703,27 @@ const store1 = kv(new URL(`file://${process.cwd()}/cache/`));
601
703
  const store2 = kv(new URL(`file://${import.meta.dirname}/data/`));
602
704
  ```
603
705
 
706
+ <details>
707
+ <summary>Why use polystore with a folder?</summary>
708
+ <p>These benefits are for wrapping a folder with polystore:</p>
709
+ <ul>
710
+ <li><strong>Data structures</strong>: with Polystore you can pass more complex data structures and we'll handle the serialization/deserialization.</li>
711
+ <li><strong>Expiration</strong>: you can now set lifetime to your values so that they are automatically evicted when the time passes. <a href="#expiration-explained">Expiration explained</a>.</li>
712
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
713
+ </ul>
714
+ </details>
715
+
716
+ ```js
717
+ // GOOD - with polystore
718
+ await store.set("key1", { name: "Francisco" }, { expires: "2days" });
719
+
720
+ // COMPLEX - With native folder
721
+ const file = './data/user/key1.json';
722
+ const serialValue = JSON.stringify({ name: "Francisco" });
723
+ await fsp.writeFile(file, serialValue);
724
+ // Expiration not supported (and error handling not shown)
725
+ ```
726
+
604
727
  ### Cloudflare KV
605
728
 
606
729
  Supports the official Cloudflare's KV stores. Follow [the official guide](https://developers.cloudflare.com/kv/get-started/), then load it like this:
@@ -621,7 +744,15 @@ export default {
621
744
  };
622
745
  ```
623
746
 
624
- Why use polystore? The Cloudflare native KV store only accepts strings and has you manually calculating timeouts, but as usual with `polystore` you can set/get any serializable value and set the timeout in a familiar format:
747
+ <details>
748
+ <summary>Why use polystore with Cloudflare's KV?</summary>
749
+ <p>These benefits are for wrapping Cloudflare's KV with polystore:</p>
750
+ <ul>
751
+ <li><strong>Data structures</strong>: with Polystore you can pass more complex data structures and we'll handle the serialization/deserialization.</li>
752
+ <li><strong>Intuitive expirations</strong>: use plain English to specify the expiration time like <code>10min</code>. <a href="#expiration-explained">Expiration explained</a>.</li>
753
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
754
+ </ul>
755
+ </details>
625
756
 
626
757
  ```js
627
758
  // GOOD - with polystore
@@ -650,7 +781,13 @@ console.log(await store.get("key1"));
650
781
  // "Hello world"
651
782
  ```
652
783
 
653
- Why use polystore? The main reason is that we add expiration on top of Level, which is not supported by Level:
784
+ <details>
785
+ <summary>Why use polystore with Level?</summary>
786
+ <p>These benefits are for wrapping Level with polystore:</p>
787
+ <ul>
788
+ <li><strong>Intuitive expirations</strong>: use plain English to specify the expiration time like <code>10min</code>. <a href="#expiration-explained">Expiration explained</a>.</li>
789
+ </ul>
790
+ </details>
654
791
 
655
792
  ```js
656
793
  // GOOD - with polystore
@@ -675,6 +812,15 @@ console.log(await store.get("key1"));
675
812
  // "Hello world"
676
813
  ```
677
814
 
815
+ <details>
816
+ <summary>Why use polystore with Etcd?</summary>
817
+ <p>These benefits are for wrapping Etcd with polystore:</p>
818
+ <ul>
819
+ <li><strong>Intuitive expirations</strong>: use plain English to specify the expiration time like <code>10min</code>. <a href="#expiration-explained">Expiration explained</a>.</li>
820
+ <li><strong>Substores</strong>: you can also create substores and manage partial data with ease. <a href="#prefix">Details about substores</a>.</li>
821
+ </ul>
822
+ </details>
823
+
678
824
  ### Custom store
679
825
 
680
826
  Please see the [creating a store](#creating-a-store) section for all the details!
@@ -782,8 +928,7 @@ const value = await store.get("a");
782
928
  // client.get("hello:world:a");
783
929
 
784
930
  // User calls this, then the client is called with that:
785
- for await (const entry of store.iterate()) {
786
- }
931
+ for await (const [key, value] of store) {}
787
932
  // client.iterate("hello:world:");
788
933
  ```
789
934
 
@@ -23,7 +23,7 @@ export default class Folder {
23
23
  constructor(folder) {
24
24
  this.folder =
25
25
  typeof folder === "string"
26
- ? folder.slice("folder://".length).replace(/\/$/, "") + "/"
26
+ ? folder.slice("file://".length).replace(/\/$/, "") + "/"
27
27
  : folder.pathname.replace(/\/$/, "") + "/";
28
28
 
29
29
  // Run this once on launch; import the FS module and reset the file
@@ -48,7 +48,7 @@ export default class Folder {
48
48
  async set(key, value) {
49
49
  const fsp = await this.promise;
50
50
  const file = this.folder + key + ".json";
51
- await fsp.writeFile(file, JSON.stringify(value), "utf8");
51
+ await fsp.writeFile(file, JSON.stringify(value, null, 2), "utf8");
52
52
  return file;
53
53
  }
54
54
 
@@ -61,15 +61,10 @@ export default class Folder {
61
61
 
62
62
  async *iterate(prefix = "") {
63
63
  const fsp = await this.promise;
64
- const all = await fsp.readdir(this.folder, { withFileTypes: true });
65
- const files = all.filter((f) => !f.isDirectory());
66
- const keys = files
67
- .map((file) =>
68
- (file.path.replace(/\/$/, "") + "/" + file.name)
69
- .replace(this.folder, "")
70
- .replace(".json", ""),
71
- )
72
- .filter((k) => k.startsWith(prefix));
64
+ const all = await fsp.readdir(this.folder);
65
+ const keys = all
66
+ .filter((f) => f.startsWith(prefix) && f.endsWith(".json"))
67
+ .map((name) => name.slice(0, -".json".length));
73
68
  for (const key of keys) {
74
69
  const data = await this.get(key);
75
70
  yield [key, data];