polystore 0.16.0 → 0.16.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/index.d.ts +161 -0
- package/index.js +88 -38
- package/package.json +1 -1
- package/readme.md +74 -0
package/index.d.ts
CHANGED
|
@@ -52,28 +52,189 @@ declare class Store<TDefault extends Serializable = Serializable> {
|
|
|
52
52
|
promise: Promise<Client> | null;
|
|
53
53
|
client: Client;
|
|
54
54
|
constructor(clientPromise?: any);
|
|
55
|
+
/**
|
|
56
|
+
* Save the data on an autogenerated key, can add expiration as well:
|
|
57
|
+
*
|
|
58
|
+
* ```js
|
|
59
|
+
* const key1 = await store.add("value1");
|
|
60
|
+
* const key2 = await store.add({ hello: "world" });
|
|
61
|
+
* const key3 = await store.add("value3", { expires: "1h" });
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* **[→ Full .add() Docs](https://polystore.dev/documentation#add)**
|
|
65
|
+
*/
|
|
55
66
|
add(value: TDefault, options?: Options): Promise<string>;
|
|
56
67
|
add<T extends TDefault>(value: T, options?: Options): Promise<string>;
|
|
68
|
+
/**
|
|
69
|
+
* Save the data on the given key, can add expiration as well:
|
|
70
|
+
*
|
|
71
|
+
* ```js
|
|
72
|
+
* const key = await store.set("key1", "value1");
|
|
73
|
+
* await store.set("key2", { hello: "world" });
|
|
74
|
+
* await store.set("key3", "value3", { expires: "1h" });
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* **[→ Full .set() Docs](https://polystore.dev/documentation#set)**
|
|
78
|
+
*/
|
|
57
79
|
set(key: string, value: TDefault, options?: Options): Promise<string>;
|
|
58
80
|
set<T extends TDefault>(key: string, value: T, options?: Options): Promise<string>;
|
|
81
|
+
/**
|
|
82
|
+
* Read a single value from the KV store:
|
|
83
|
+
*
|
|
84
|
+
* ```js
|
|
85
|
+
* const value1 = await store.get("key1");
|
|
86
|
+
* // null (doesn't exist or has expired)
|
|
87
|
+
* const value2 = await store.get("key2");
|
|
88
|
+
* // "value2"
|
|
89
|
+
* const value3 = await store.get("key3");
|
|
90
|
+
* // { hello: "world" }
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* **[→ Full .get() Docs](https://polystore.dev/documentation#get)**
|
|
94
|
+
*/
|
|
59
95
|
get(key: string): Promise<TDefault | null>;
|
|
60
96
|
get<T extends TDefault>(key: string): Promise<T | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Check whether a key exists or not:
|
|
99
|
+
*
|
|
100
|
+
* ```js
|
|
101
|
+
* if (await store.has("key1")) { ... }
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* If you are going to use the value, it's better to just read it:
|
|
105
|
+
*
|
|
106
|
+
* ```js
|
|
107
|
+
* const val = await store.get("key1");
|
|
108
|
+
* if (val) { ... }
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* **[→ Full .has() Docs](https://polystore.dev/documentation#has)**
|
|
112
|
+
*/
|
|
61
113
|
has(key: string): Promise<boolean>;
|
|
114
|
+
/**
|
|
115
|
+
* Remove a single key and its value from the store:
|
|
116
|
+
*
|
|
117
|
+
* ```js
|
|
118
|
+
* const key = await store.del("key1");
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* **[→ Full .del() Docs](https://polystore.dev/documentation#del)**
|
|
122
|
+
*/
|
|
62
123
|
del(key: string): Promise<string>;
|
|
124
|
+
/**
|
|
125
|
+
* An iterator that goes through all of the key:value pairs in the client
|
|
126
|
+
*
|
|
127
|
+
* ```js
|
|
128
|
+
* for await (const [key, value] of store) {
|
|
129
|
+
* console.log(key, value);
|
|
130
|
+
* }
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* **[→ Full Iterator Docs](https://polystore.dev/documentation#iterator)**
|
|
134
|
+
*/
|
|
63
135
|
[Symbol.asyncIterator](): AsyncGenerator<[string, TDefault], void, unknown>;
|
|
64
136
|
[Symbol.asyncIterator]<T extends TDefault>(): AsyncGenerator<[
|
|
65
137
|
string,
|
|
66
138
|
T
|
|
67
139
|
], void, unknown>;
|
|
140
|
+
/**
|
|
141
|
+
* Return an array of the entries, in the [key, value] format:
|
|
142
|
+
*
|
|
143
|
+
* ```js
|
|
144
|
+
* const entries = await store.entries();
|
|
145
|
+
* // [["key1", "value1"], ["key2", { hello: "world" }], ...]
|
|
146
|
+
*
|
|
147
|
+
* // To limit it to a given prefix, use `.prefix()`:
|
|
148
|
+
* const sessions = await store.prefix("session:").entries();
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* **[→ Full .entries() Docs](https://polystore.dev/documentation#entries)**
|
|
152
|
+
*/
|
|
68
153
|
entries(): Promise<[string, TDefault][]>;
|
|
69
154
|
entries<T extends TDefault>(): Promise<[string, T][]>;
|
|
155
|
+
/**
|
|
156
|
+
* Return an array of the keys in the store:
|
|
157
|
+
*
|
|
158
|
+
* ```js
|
|
159
|
+
* const keys = await store.keys();
|
|
160
|
+
* // ["key1", "key2", ...]
|
|
161
|
+
*
|
|
162
|
+
* // To limit it to a given prefix, use `.prefix()`:
|
|
163
|
+
* const sessions = await store.prefix("session:").keys();
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* **[→ Full .keys() Docs](https://polystore.dev/documentation#keys)**
|
|
167
|
+
*/
|
|
70
168
|
keys(): Promise<string[]>;
|
|
169
|
+
/**
|
|
170
|
+
* Return an array of the values in the store:
|
|
171
|
+
*
|
|
172
|
+
* ```js
|
|
173
|
+
* const values = await store.values();
|
|
174
|
+
* // ["value1", { hello: "world" }, ...]
|
|
175
|
+
*
|
|
176
|
+
* // To limit it to a given prefix, use `.prefix()`:
|
|
177
|
+
* const sessions = await store.prefix("session:").values();
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* **[→ Full .values() Docs](https://polystore.dev/documentation#values)**
|
|
181
|
+
*/
|
|
71
182
|
values(): Promise<TDefault[]>;
|
|
72
183
|
values<T extends TDefault>(): Promise<T[]>;
|
|
184
|
+
/**
|
|
185
|
+
* Return an object with the keys:values in the store:
|
|
186
|
+
*
|
|
187
|
+
* ```js
|
|
188
|
+
* const obj = await store.all();
|
|
189
|
+
* // { key1: "value1", key2: { hello: "world" }, ... }
|
|
190
|
+
*
|
|
191
|
+
* // To limit it to a given prefix, use `.prefix()`:
|
|
192
|
+
* const sessions = await store.prefix("session:").all();
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* **[→ Full .all() Docs](https://polystore.dev/documentation#all)**
|
|
196
|
+
*/
|
|
73
197
|
all(): Promise<Record<string, TDefault>>;
|
|
74
198
|
all<T extends TDefault>(): Promise<Record<string, T>>;
|
|
199
|
+
/**
|
|
200
|
+
* Delete all of the records of the store:
|
|
201
|
+
*
|
|
202
|
+
* ```js
|
|
203
|
+
* await store.clear();
|
|
204
|
+
* ```
|
|
205
|
+
*
|
|
206
|
+
* It's useful for cache invalidation, clearing the data, and testing.
|
|
207
|
+
*
|
|
208
|
+
* **[→ Full .clear() Docs](https://polystore.dev/documentation#clear)**
|
|
209
|
+
*/
|
|
75
210
|
clear(): Promise<void>;
|
|
211
|
+
/**
|
|
212
|
+
* Create a substore where all the keys are stored with
|
|
213
|
+
* the given prefix:
|
|
214
|
+
*
|
|
215
|
+
* ```js
|
|
216
|
+
* const session = store.prefix("session:");
|
|
217
|
+
* await session.set("key1", "value1");
|
|
218
|
+
* console.log(await session.entries()); // session.
|
|
219
|
+
* // [["key1", "value1"]]
|
|
220
|
+
* console.log(await store.entries()); // store.
|
|
221
|
+
* // [["session:key1", "value1"]]
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* **[→ Full .prefix() Docs](https://polystore.dev/documentation#prefix)**
|
|
225
|
+
*/
|
|
76
226
|
prefix(prefix?: string): Store<TDefault>;
|
|
227
|
+
/**
|
|
228
|
+
* Stop the connection to the store, if any:
|
|
229
|
+
*
|
|
230
|
+
* ```js
|
|
231
|
+
* await session.set("key1", "value1");
|
|
232
|
+
* await store.close();
|
|
233
|
+
* await session.set("key2", "value2"); // error
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* **[→ Full .close() Docs](https://polystore.dev/documentation#close)**
|
|
237
|
+
*/
|
|
77
238
|
close(): Promise<void>;
|
|
78
239
|
}
|
|
79
240
|
declare function createStore(): Store<Serializable>;
|
package/index.js
CHANGED
|
@@ -431,22 +431,8 @@ var SQLite = class extends Client {
|
|
|
431
431
|
// expirations much easier, so it's really "somewhere in between"
|
|
432
432
|
EXPIRES = true;
|
|
433
433
|
static test = (client) => typeof client?.prepare === "function";
|
|
434
|
-
constructor(c) {
|
|
435
|
-
if (typeof c?.prepare("SELECT 1").get === "function") {
|
|
436
|
-
super({
|
|
437
|
-
run: (sql, ...args) => c.prepare(sql).run(...args),
|
|
438
|
-
get: (sql, ...args) => c.prepare(sql).get(...args),
|
|
439
|
-
all: (sql, ...args) => c.prepare(sql).all(...args)
|
|
440
|
-
});
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
super(c);
|
|
444
|
-
}
|
|
445
434
|
get = (id) => {
|
|
446
|
-
const row = this.client.get(
|
|
447
|
-
`SELECT value, expires_at FROM kv WHERE id = ?`,
|
|
448
|
-
id
|
|
449
|
-
);
|
|
435
|
+
const row = this.client.prepare(`SELECT value, expires_at FROM kv WHERE id = ?`).get(id);
|
|
450
436
|
if (!row) return null;
|
|
451
437
|
if (row.expires_at && row.expires_at < Date.now()) {
|
|
452
438
|
this.del(id);
|
|
@@ -457,21 +443,15 @@ var SQLite = class extends Client {
|
|
|
457
443
|
set = (id, data, { expires } = {}) => {
|
|
458
444
|
const value = this.encode(data);
|
|
459
445
|
const expires_at = expires ? Date.now() + expires * 1e3 : null;
|
|
460
|
-
this.client.
|
|
461
|
-
`INSERT INTO kv (id, value, expires_at)
|
|
462
|
-
|
|
463
|
-
ON CONFLICT(id)
|
|
464
|
-
DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at`,
|
|
465
|
-
id,
|
|
466
|
-
value,
|
|
467
|
-
expires_at
|
|
468
|
-
);
|
|
446
|
+
this.client.prepare(
|
|
447
|
+
`INSERT INTO kv (id, value, expires_at) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at`
|
|
448
|
+
).run(id, value, expires_at);
|
|
469
449
|
};
|
|
470
|
-
del = (id) => {
|
|
471
|
-
this.client.
|
|
450
|
+
del = async (id) => {
|
|
451
|
+
await this.client.prepare(`DELETE FROM kv WHERE id = ?`).run(id);
|
|
472
452
|
};
|
|
473
453
|
has = (id) => {
|
|
474
|
-
const row = this.client.
|
|
454
|
+
const row = this.client.prepare(`SELECT expires_at FROM kv WHERE id = ?`).get(id);
|
|
475
455
|
if (!row) return false;
|
|
476
456
|
if (row.expires_at && row.expires_at < Date.now()) {
|
|
477
457
|
this.del(id);
|
|
@@ -481,29 +461,24 @@ var SQLite = class extends Client {
|
|
|
481
461
|
};
|
|
482
462
|
*iterate(prefix = "") {
|
|
483
463
|
this.#clearExpired();
|
|
484
|
-
const sql = `
|
|
485
|
-
SELECT id, value FROM kv
|
|
486
|
-
WHERE (expires_at IS NULL OR expires_at > ?)
|
|
487
|
-
${prefix ? "AND id LIKE ?" : ""}
|
|
464
|
+
const sql = `SELECT id, value FROM kv WHERE (expires_at IS NULL OR expires_at > ?) ${prefix ? "AND id LIKE ?" : ""}
|
|
488
465
|
`;
|
|
489
466
|
const params = prefix ? [Date.now(), `${prefix}%`] : [Date.now()];
|
|
490
|
-
for (const row of this.client.all(
|
|
467
|
+
for (const row of this.client.prepare(sql).all(...params)) {
|
|
491
468
|
yield [row.id, this.decode(row.value)];
|
|
492
469
|
}
|
|
493
470
|
}
|
|
494
471
|
keys = (prefix = "") => {
|
|
495
472
|
this.#clearExpired();
|
|
496
|
-
const sql = `
|
|
497
|
-
|
|
498
|
-
WHERE (expires_at IS NULL OR expires_at > ?)
|
|
499
|
-
${prefix ? "AND id LIKE ?" : ""}
|
|
473
|
+
const sql = `SELECT id FROM kv WHERE (expires_at IS NULL OR expires_at > ?)
|
|
474
|
+
${prefix ? "AND id LIKE ?" : ""}
|
|
500
475
|
`;
|
|
501
476
|
const params = prefix ? [Date.now(), `${prefix}%`] : [Date.now()];
|
|
502
|
-
const rows = this.client.all(
|
|
477
|
+
const rows = this.client.prepare(sql).all(...params);
|
|
503
478
|
return rows.map((r) => r.id);
|
|
504
479
|
};
|
|
505
480
|
#clearExpired = () => {
|
|
506
|
-
this.client.
|
|
481
|
+
this.client.prepare(`DELETE FROM kv WHERE expires_at < ?`).run(Date.now());
|
|
507
482
|
};
|
|
508
483
|
clearAll = () => {
|
|
509
484
|
this.client.run(`DELETE FROM kv`);
|
|
@@ -686,6 +661,22 @@ var Store = class _Store {
|
|
|
686
661
|
return data.value;
|
|
687
662
|
}
|
|
688
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* Check whether a key exists or not:
|
|
666
|
+
*
|
|
667
|
+
* ```js
|
|
668
|
+
* if (await store.has("key1")) { ... }
|
|
669
|
+
* ```
|
|
670
|
+
*
|
|
671
|
+
* If you are going to use the value, it's better to just read it:
|
|
672
|
+
*
|
|
673
|
+
* ```js
|
|
674
|
+
* const val = await store.get("key1");
|
|
675
|
+
* if (val) { ... }
|
|
676
|
+
* ```
|
|
677
|
+
*
|
|
678
|
+
* **[→ Full .has() Docs](https://polystore.dev/documentation#has)**
|
|
679
|
+
*/
|
|
689
680
|
async has(key) {
|
|
690
681
|
await this.promise;
|
|
691
682
|
const id = this.PREFIX + key;
|
|
@@ -694,6 +685,15 @@ var Store = class _Store {
|
|
|
694
685
|
}
|
|
695
686
|
return await this.get(key) !== null;
|
|
696
687
|
}
|
|
688
|
+
/**
|
|
689
|
+
* Remove a single key and its value from the store:
|
|
690
|
+
*
|
|
691
|
+
* ```js
|
|
692
|
+
* const key = await store.del("key1");
|
|
693
|
+
* ```
|
|
694
|
+
*
|
|
695
|
+
* **[→ Full .del() Docs](https://polystore.dev/documentation#del)**
|
|
696
|
+
*/
|
|
697
697
|
async del(key) {
|
|
698
698
|
await this.promise;
|
|
699
699
|
const id = this.PREFIX + key;
|
|
@@ -752,6 +752,19 @@ var Store = class _Store {
|
|
|
752
752
|
return list;
|
|
753
753
|
}
|
|
754
754
|
}
|
|
755
|
+
/**
|
|
756
|
+
* Return an array of the keys in the store:
|
|
757
|
+
*
|
|
758
|
+
* ```js
|
|
759
|
+
* const keys = await store.keys();
|
|
760
|
+
* // ["key1", "key2", ...]
|
|
761
|
+
*
|
|
762
|
+
* // To limit it to a given prefix, use `.prefix()`:
|
|
763
|
+
* const sessions = await store.prefix("session:").keys();
|
|
764
|
+
* ```
|
|
765
|
+
*
|
|
766
|
+
* **[→ Full .keys() Docs](https://polystore.dev/documentation#keys)**
|
|
767
|
+
*/
|
|
755
768
|
async keys() {
|
|
756
769
|
await this.promise;
|
|
757
770
|
if (this.client.keys) {
|
|
@@ -776,6 +789,17 @@ var Store = class _Store {
|
|
|
776
789
|
const entries = await this.entries();
|
|
777
790
|
return Object.fromEntries(entries);
|
|
778
791
|
}
|
|
792
|
+
/**
|
|
793
|
+
* Delete all of the records of the store:
|
|
794
|
+
*
|
|
795
|
+
* ```js
|
|
796
|
+
* await store.clear();
|
|
797
|
+
* ```
|
|
798
|
+
*
|
|
799
|
+
* It's useful for cache invalidation, clearing the data, and testing.
|
|
800
|
+
*
|
|
801
|
+
* **[→ Full .clear() Docs](https://polystore.dev/documentation#clear)**
|
|
802
|
+
*/
|
|
779
803
|
async clear() {
|
|
780
804
|
await this.promise;
|
|
781
805
|
if (!this.PREFIX && this.client.clearAll) {
|
|
@@ -787,6 +811,21 @@ var Store = class _Store {
|
|
|
787
811
|
const keys = await this.keys();
|
|
788
812
|
await Promise.all(keys.map((key) => this.del(key)));
|
|
789
813
|
}
|
|
814
|
+
/**
|
|
815
|
+
* Create a substore where all the keys are stored with
|
|
816
|
+
* the given prefix:
|
|
817
|
+
*
|
|
818
|
+
* ```js
|
|
819
|
+
* const session = store.prefix("session:");
|
|
820
|
+
* await session.set("key1", "value1");
|
|
821
|
+
* console.log(await session.entries()); // session.
|
|
822
|
+
* // [["key1", "value1"]]
|
|
823
|
+
* console.log(await store.entries()); // store.
|
|
824
|
+
* // [["session:key1", "value1"]]
|
|
825
|
+
* ```
|
|
826
|
+
*
|
|
827
|
+
* **[→ Full .prefix() Docs](https://polystore.dev/documentation#prefix)**
|
|
828
|
+
*/
|
|
790
829
|
prefix(prefix = "") {
|
|
791
830
|
const store = new _Store(
|
|
792
831
|
Promise.resolve(this.promise).then(() => this.client)
|
|
@@ -794,6 +833,17 @@ var Store = class _Store {
|
|
|
794
833
|
store.PREFIX = this.PREFIX + prefix;
|
|
795
834
|
return store;
|
|
796
835
|
}
|
|
836
|
+
/**
|
|
837
|
+
* Stop the connection to the store, if any:
|
|
838
|
+
*
|
|
839
|
+
* ```js
|
|
840
|
+
* await session.set("key1", "value1");
|
|
841
|
+
* await store.close();
|
|
842
|
+
* await session.set("key2", "value2"); // error
|
|
843
|
+
* ```
|
|
844
|
+
*
|
|
845
|
+
* **[→ Full .close() Docs](https://polystore.dev/documentation#close)**
|
|
846
|
+
*/
|
|
797
847
|
async close() {
|
|
798
848
|
await this.promise;
|
|
799
849
|
if (this.client.close) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polystore",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.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
|
@@ -910,6 +910,80 @@ You'll need to be running the etcd store for this to work as expected.
|
|
|
910
910
|
</ul>
|
|
911
911
|
</details>
|
|
912
912
|
|
|
913
|
+
|
|
914
|
+
## SQLite
|
|
915
|
+
|
|
916
|
+
Supports both **`bun:sqlite`** and **`better-sqlite3`** directly. Pass an already-opened database instance to `kv()` and Polystore will use the `kv` table to store keys, values, and expirations:
|
|
917
|
+
|
|
918
|
+
> [!IMPORTANT]
|
|
919
|
+
> The table `kv` must already exist in the Database
|
|
920
|
+
|
|
921
|
+
```js
|
|
922
|
+
import kv from "polystore";
|
|
923
|
+
import Database from "better-sqlite3";
|
|
924
|
+
// Or: import Database from "bun:sqlite";
|
|
925
|
+
|
|
926
|
+
const db = new Database("data.db")
|
|
927
|
+
const store = kv(db);
|
|
928
|
+
|
|
929
|
+
await store.set("key1", "Hello world", { expires: "1h" });
|
|
930
|
+
console.log(await store.get("key1"));
|
|
931
|
+
// "Hello world"
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### SQLite schema
|
|
935
|
+
|
|
936
|
+
This is the required schema:
|
|
937
|
+
|
|
938
|
+
```sql
|
|
939
|
+
CREATE TABLE kv (
|
|
940
|
+
id TEXT PRIMARY KEY,
|
|
941
|
+
value TEXT,
|
|
942
|
+
expires_at INTEGER
|
|
943
|
+
);
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
You can create these with failsafes to make it much easier to initialize:
|
|
947
|
+
|
|
948
|
+
```js
|
|
949
|
+
db.run(`
|
|
950
|
+
CREATE TABLE IF NOT EXISTS kv (
|
|
951
|
+
id TEXT PRIMARY KEY,
|
|
952
|
+
value TEXT NOT NULL,
|
|
953
|
+
expires_at INTEGER
|
|
954
|
+
)
|
|
955
|
+
`);
|
|
956
|
+
db.run(
|
|
957
|
+
`CREATE INDEX IF NOT EXISTS idx_kv_expires_at ON kv (expires_at)`,
|
|
958
|
+
);
|
|
959
|
+
````
|
|
960
|
+
|
|
961
|
+
### SQLite expirations
|
|
962
|
+
|
|
963
|
+
If `expires` is provided, Polystore will convert it to a timestamp and persist it in `expires_at`. We handle reading/writing rows and expiration checks.
|
|
964
|
+
|
|
965
|
+
However, these are not auto-evicted since SQLite doesn't have a native expiration. To avoid having stale data that is not used anymore, it's recommended you set a periodic check and clear expired records manually:
|
|
966
|
+
|
|
967
|
+
```js
|
|
968
|
+
// Clear expired keys once every 10 minutes
|
|
969
|
+
setInterval(() => {
|
|
970
|
+
db.prepare(`DELETE FROM kv WHERE expires_at < ?`).run(Date.now());
|
|
971
|
+
}, 10 * 60 * 1000);
|
|
972
|
+
````
|
|
973
|
+
|
|
974
|
+
Note that Polystore is self-reliant and won't have any problem even if you don't set that script, it will never render a stale record. It's just for both your convenience and privacy reasons.
|
|
975
|
+
|
|
976
|
+
<details>
|
|
977
|
+
<summary>Why use polystore with <code>SQLite</code>?</summary>
|
|
978
|
+
<p>These benefits apply when wrapping a SQLite DB with polystore:</p>
|
|
979
|
+
<ul>
|
|
980
|
+
<li><strong>Intuitive expirations</strong>: specify expiration times like <code>10min</code> or <code>2h</code> without manual date handling.</li>
|
|
981
|
+
<li><strong>Substores</strong>: use prefixes to isolate sets of keys cleanly.</li>
|
|
982
|
+
<li><strong>Simple persistence</strong>: full on-disk durability with a minimal driver.</li>
|
|
983
|
+
</ul>
|
|
984
|
+
</details>
|
|
985
|
+
|
|
986
|
+
|
|
913
987
|
### Postgres
|
|
914
988
|
|
|
915
989
|
Use PostgreSQL with the `pg` library as a key-value store:
|