node-sqlite-kv 0.4.0 → 1.0.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.
- package/.prettierrc.json +11 -0
- package/.vscode/settings.json +4 -0
- package/LICENSE +21 -4
- package/README.md +80 -38
- package/dist/index.cjs +74 -45
- package/dist/index.d.cts +89 -14
- package/package.json +13 -27
- package/tsdown.config.mjs +17 -0
- package/dist/index.d.mts +0 -46
- package/dist/index.mjs +0 -191
package/.prettierrc.json
ADDED
package/LICENSE
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
6
15
|
|
|
7
|
-
THE SOFTWARE IS PROVIDED
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org/>
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<h1 align="center">node-sqlite-kv</h1>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A simple key-value store with `node:sqlite`.
|
|
4
|
+
|
|
5
|
+
> ⚠️ Node.js v22 or higher is required.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -17,46 +19,56 @@ bun add node-sqlite-kv
|
|
|
17
19
|
## Example
|
|
18
20
|
|
|
19
21
|
```js
|
|
20
|
-
import { KVSync } from "node-sqlite-kv"
|
|
22
|
+
import { JournalModes, KVSync } from "node-sqlite-kv"
|
|
21
23
|
|
|
24
|
+
// none of these options are required
|
|
22
25
|
const kv = new KVSync({
|
|
23
|
-
//
|
|
24
|
-
//
|
|
26
|
+
// sqlite journal mode; defaults to DELETE for
|
|
27
|
+
// in-memory stores, or WAL for persistent ones
|
|
28
|
+
journalMode: JournalModes.WAL,
|
|
29
|
+
|
|
30
|
+
// whether the database is open upon
|
|
31
|
+
// being instantiated; defaults to true
|
|
32
|
+
open: true,
|
|
33
|
+
|
|
34
|
+
// defaults to :memory: (in-memory storage)
|
|
25
35
|
path: "./data.sqlite",
|
|
26
36
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
// override the default table name of "kv"
|
|
38
|
+
// note: it's not recommended to use one file for
|
|
39
|
+
// multiple key value stores; this is only
|
|
40
|
+
// for the ability of changing the default name
|
|
41
|
+
tableName: "kv",
|
|
42
|
+
})
|
|
31
43
|
|
|
32
44
|
// set values
|
|
33
|
-
kv.set("number", 123)
|
|
34
|
-
kv.set("string", "hello world")
|
|
35
|
-
kv.set("boolean", true)
|
|
36
|
-
kv.set("null", null)
|
|
37
|
-
kv.set("array", [1, 2, 3])
|
|
38
|
-
kv.set("object", { settings: { theme: "dark" } })
|
|
39
|
-
kv.set("date", new Date())
|
|
45
|
+
kv.set("number", 123)
|
|
46
|
+
kv.set("string", "hello world")
|
|
47
|
+
kv.set("boolean", true)
|
|
48
|
+
kv.set("null", null)
|
|
49
|
+
kv.set("array", [1, 2, 3])
|
|
50
|
+
kv.set("object", { settings: { theme: "dark" } })
|
|
51
|
+
kv.set("date", new Date())
|
|
40
52
|
|
|
41
53
|
// get values
|
|
42
|
-
kv.get("number")
|
|
43
|
-
kv.get("string")
|
|
44
|
-
kv.get("boolean")
|
|
45
|
-
kv.get("null")
|
|
46
|
-
kv.get("array")
|
|
47
|
-
kv.get("object")
|
|
48
|
-
kv.get("date")
|
|
54
|
+
kv.get("number") // 123
|
|
55
|
+
kv.get("string") // "hello world"
|
|
56
|
+
kv.get("boolean") // true
|
|
57
|
+
kv.get("null") // null
|
|
58
|
+
kv.get("array") // [1, 2, 3]
|
|
59
|
+
kv.get("object") // { settings: { theme: "dark" } }
|
|
60
|
+
kv.get("date") // Date
|
|
49
61
|
|
|
50
62
|
// update values
|
|
51
|
-
kv.set("number", 999)
|
|
52
|
-
kv.get("number")
|
|
63
|
+
kv.set("number", 999)
|
|
64
|
+
kv.get("number") // 999
|
|
53
65
|
|
|
54
66
|
// delete values
|
|
55
|
-
kv.delete("array")
|
|
56
|
-
kv.get("array")
|
|
67
|
+
kv.delete("array")
|
|
68
|
+
kv.get("array") // undefined
|
|
57
69
|
|
|
58
70
|
// list all entries
|
|
59
|
-
kv.all()
|
|
71
|
+
kv.all()
|
|
60
72
|
// [
|
|
61
73
|
// { key: "string", value: "hello world" },
|
|
62
74
|
// { key: "number", value: 999 },
|
|
@@ -64,29 +76,59 @@ kv.all();
|
|
|
64
76
|
// // ...
|
|
65
77
|
// ];
|
|
66
78
|
|
|
79
|
+
// check if a key exists
|
|
80
|
+
kv.exists("string") // true
|
|
81
|
+
kv.exists("nonexistent") // false
|
|
82
|
+
|
|
83
|
+
// get total number of entries
|
|
84
|
+
kv.size() // 6
|
|
85
|
+
|
|
86
|
+
// get all keys
|
|
87
|
+
kv.keys() // ["string", "number", "boolean", "null", "object", "date"]
|
|
88
|
+
|
|
89
|
+
// get all values
|
|
90
|
+
kv.values() // ["hello world", 999, true, null, { settings: { theme: "dark" } }, Date]
|
|
91
|
+
|
|
67
92
|
// transactions
|
|
68
|
-
kv.set("user:1", { name: "Andrew", age: 19 })
|
|
69
|
-
kv.set("user:2", { name: "Josh", age: 22 })
|
|
70
|
-
kv.set("user:3", { name: "Gabe", age: 20 })
|
|
93
|
+
kv.set("user:1", { name: "Andrew", age: 19 })
|
|
94
|
+
kv.set("user:2", { name: "Josh", age: 22 })
|
|
95
|
+
kv.set("user:3", { name: "Gabe", age: 20 })
|
|
71
96
|
|
|
72
97
|
// ...store what changed in transactions
|
|
73
98
|
const { oldValues, newValues } = kv.transaction((tx) => {
|
|
74
|
-
tx.set("user:1", { name: "Andrew", age: 20 })
|
|
75
|
-
tx.set("user:4", { name: "Kris", age: 21 })
|
|
76
|
-
tx.delete("user:2")
|
|
77
|
-
})
|
|
99
|
+
tx.set("user:1", { name: "Andrew", age: 20 })
|
|
100
|
+
tx.set("user:4", { name: "Kris", age: 21 })
|
|
101
|
+
tx.delete("user:2")
|
|
102
|
+
})
|
|
78
103
|
|
|
79
104
|
// delete all entries
|
|
80
|
-
kv.clear()
|
|
105
|
+
kv.clear()
|
|
81
106
|
|
|
82
107
|
// close the database
|
|
83
|
-
kv.close()
|
|
108
|
+
kv.close()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### TS Generics Example
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { KVSync } from "node-sqlite-kv"
|
|
115
|
+
const kv = new KVSync({ path: "./data.sqlite" })
|
|
116
|
+
|
|
117
|
+
interface User {
|
|
118
|
+
name: string
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
kv.set("user", { name: "Andrew" })
|
|
122
|
+
kv.get<User>("user") // User | null
|
|
123
|
+
|
|
124
|
+
kv.set("example", 123)
|
|
125
|
+
kv.get<number>("example") // number | null
|
|
84
126
|
```
|
|
85
127
|
|
|
86
128
|
## Contributing
|
|
87
129
|
|
|
88
|
-
Pull requests are always
|
|
130
|
+
[pnpm](https://pnpm.io) is used throughout this project for packages and scripts. Pull requests are always welcome. For more major changes, please open an issue to discuss what you wish to change.
|
|
89
131
|
|
|
90
132
|
## License
|
|
91
133
|
|
|
92
|
-
[
|
|
134
|
+
[Unlicensed](LICENSE)
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -32,10 +33,8 @@ node_fs = __toESM(node_fs);
|
|
|
32
33
|
let node_path = require("node:path");
|
|
33
34
|
node_path = __toESM(node_path);
|
|
34
35
|
|
|
35
|
-
//#region src/classes/
|
|
36
|
-
/**
|
|
37
|
-
* Class representing a KVError
|
|
38
|
-
*/
|
|
36
|
+
//#region src/classes/kv-error.ts
|
|
37
|
+
/** Class representing a KVError */
|
|
39
38
|
var KVError = class extends Error {
|
|
40
39
|
static name = "KVError";
|
|
41
40
|
scope;
|
|
@@ -50,36 +49,40 @@ var KVError = class extends Error {
|
|
|
50
49
|
};
|
|
51
50
|
|
|
52
51
|
//#endregion
|
|
53
|
-
//#region src/
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
*/
|
|
57
|
-
const journalModes = {
|
|
52
|
+
//#region src/constants.ts
|
|
53
|
+
/** List of journal modes SQLite supports */
|
|
54
|
+
const JournalModes = {
|
|
58
55
|
Delete: "DELETE",
|
|
59
56
|
Memory: "MEMORY",
|
|
60
|
-
|
|
57
|
+
OFF: "OFF",
|
|
61
58
|
Persist: "PERSIST",
|
|
62
59
|
Truncate: "TRUNCATE",
|
|
63
60
|
WAL: "WAL"
|
|
64
61
|
};
|
|
65
62
|
|
|
66
63
|
//#endregion
|
|
67
|
-
//#region src/classes/
|
|
68
|
-
/**
|
|
69
|
-
* Class representing a synchronous key-value store
|
|
70
|
-
*/
|
|
64
|
+
//#region src/classes/kv-sync.ts
|
|
65
|
+
/** Class representing a synchronous key-value store */
|
|
71
66
|
var KVSync = class {
|
|
72
67
|
#db;
|
|
73
68
|
/**
|
|
69
|
+
* The name of the table with keys and values
|
|
70
|
+
* @default "kv"
|
|
71
|
+
*/
|
|
72
|
+
tableName = "kv";
|
|
73
|
+
/**
|
|
74
74
|
* Instantiate a new key-value store
|
|
75
|
-
* @param
|
|
75
|
+
* @param props KVSync options
|
|
76
76
|
*/
|
|
77
|
-
constructor(
|
|
78
|
-
const dbPath =
|
|
77
|
+
constructor(props) {
|
|
78
|
+
const dbPath = props?.path ?? ":memory:";
|
|
79
79
|
if (dbPath !== ":memory:") node_fs.default.mkdirSync(node_path.default.dirname(dbPath), { recursive: true });
|
|
80
|
-
this
|
|
81
|
-
this
|
|
82
|
-
|
|
80
|
+
this.tableName = props?.tableName ?? "kv";
|
|
81
|
+
this.#db = new node_sqlite.DatabaseSync(dbPath, { open: props?.open ?? true });
|
|
82
|
+
if (props?.open !== false) {
|
|
83
|
+
this.setJournalMode(props?.journalMode ?? (dbPath !== ":memory:" ? JournalModes.WAL : JournalModes.Delete));
|
|
84
|
+
this.#db.exec(`CREATE TABLE IF NOT EXISTS ${this.tableName} (key TEXT PRIMARY KEY NOT NULL, value BLOB NOT NULL) STRICT`);
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
/**
|
|
85
88
|
* Set a key in the database
|
|
@@ -91,19 +94,19 @@ var KVSync = class {
|
|
|
91
94
|
if (!this.#db.isOpen) throw new KVError("set", "Database is not open");
|
|
92
95
|
if (!key || typeof key !== "string") throw new KVError("set", "Key must be provided and be a non-empty string");
|
|
93
96
|
if (value === void 0) throw new KVError("set", "Provided value is undefined, did you mean to use delete()?");
|
|
94
|
-
this.#db.prepare(
|
|
97
|
+
this.#db.prepare(`INSERT OR REPLACE INTO ${this.tableName} (key, value) VALUES (?, ?)`).run(key, (0, node_v8.serialize)(value));
|
|
95
98
|
return value;
|
|
96
99
|
}
|
|
97
100
|
/**
|
|
98
101
|
* Get a value from the database
|
|
99
102
|
* @param key Key name
|
|
100
|
-
* @returns Value or
|
|
103
|
+
* @returns Value or undefined
|
|
101
104
|
*/
|
|
102
105
|
get(key) {
|
|
103
106
|
if (!this.#db.isOpen) throw new KVError("get", "Database is not open");
|
|
104
107
|
if (!key || typeof key !== "string") throw new KVError("get", "Key must be provided and be a non-empty string.");
|
|
105
|
-
const row = this.#db.prepare(
|
|
106
|
-
return row ? (0, node_v8.deserialize)(row.value) :
|
|
108
|
+
const row = this.#db.prepare(`SELECT value FROM ${this.tableName} WHERE key = ?`).get(key);
|
|
109
|
+
return row ? (0, node_v8.deserialize)(row.value) : void 0;
|
|
107
110
|
}
|
|
108
111
|
/**
|
|
109
112
|
* Delete a key from the database
|
|
@@ -113,7 +116,7 @@ var KVSync = class {
|
|
|
113
116
|
delete(key) {
|
|
114
117
|
if (!this.#db.isOpen) throw new KVError("delete", "Database is not open");
|
|
115
118
|
if (!key || typeof key !== "string") throw new KVError("delete", "Key must be provided and be a non-empty string.");
|
|
116
|
-
this.#db.prepare(
|
|
119
|
+
this.#db.prepare(`DELETE FROM ${this.tableName} WHERE key = ?`).run(key);
|
|
117
120
|
return this;
|
|
118
121
|
}
|
|
119
122
|
/**
|
|
@@ -122,8 +125,8 @@ var KVSync = class {
|
|
|
122
125
|
*/
|
|
123
126
|
all(filter) {
|
|
124
127
|
if (!this.#db.isOpen) throw new KVError("all", "Database is not open");
|
|
125
|
-
const rows = this.#db.prepare("SELECT key, value FROM kv").iterate();
|
|
126
128
|
const result = [];
|
|
129
|
+
const rows = this.#db.prepare(`SELECT key, value FROM ${this.tableName}`).iterate();
|
|
127
130
|
for (const row of rows) {
|
|
128
131
|
const key = row.key;
|
|
129
132
|
const value = (0, node_v8.deserialize)(row.value);
|
|
@@ -139,7 +142,7 @@ var KVSync = class {
|
|
|
139
142
|
*/
|
|
140
143
|
clear() {
|
|
141
144
|
if (!this.#db.isOpen) throw new KVError("clear", "Database is not open");
|
|
142
|
-
this.#db.exec(
|
|
145
|
+
this.#db.exec(`DELETE FROM ${this.tableName}`);
|
|
143
146
|
return this;
|
|
144
147
|
}
|
|
145
148
|
/**
|
|
@@ -148,8 +151,8 @@ var KVSync = class {
|
|
|
148
151
|
*/
|
|
149
152
|
setJournalMode(mode) {
|
|
150
153
|
if (!this.#db.isOpen) throw new KVError("setJournalMode", "Database is not open");
|
|
151
|
-
if (!Object.values(
|
|
152
|
-
this.#db.exec(`PRAGMA journal_mode = ${mode}
|
|
154
|
+
if (!Object.values(JournalModes).includes(mode)) throw new KVError("setJournalMode", `Invalid journal mode specified - received: "${mode}", expected one of: ${Object.values(JournalModes).join(", ")}`);
|
|
155
|
+
this.#db.exec(`PRAGMA journal_mode = ${mode}`);
|
|
153
156
|
return this;
|
|
154
157
|
}
|
|
155
158
|
/**
|
|
@@ -167,27 +170,27 @@ var KVSync = class {
|
|
|
167
170
|
tx.set = (key, value) => {
|
|
168
171
|
if (!oldMap.has(key)) {
|
|
169
172
|
const oldValue = this.get(key);
|
|
170
|
-
oldMap.set(key, oldValue
|
|
173
|
+
oldMap.set(key, oldValue);
|
|
171
174
|
}
|
|
172
175
|
newMap.set(key, value);
|
|
173
|
-
return value
|
|
176
|
+
return value;
|
|
174
177
|
};
|
|
175
178
|
tx.delete = (key) => {
|
|
176
179
|
if (!oldMap.has(key)) {
|
|
177
180
|
const oldValue = this.get(key);
|
|
178
|
-
oldMap.set(key, oldValue
|
|
181
|
+
oldMap.set(key, oldValue);
|
|
179
182
|
}
|
|
180
|
-
newMap.set(key,
|
|
183
|
+
newMap.set(key, void 0);
|
|
181
184
|
return tx;
|
|
182
185
|
};
|
|
183
186
|
try {
|
|
184
|
-
this.#db.exec("BEGIN TRANSACTION
|
|
187
|
+
this.#db.exec("BEGIN TRANSACTION");
|
|
185
188
|
callback(tx);
|
|
186
|
-
for (const [key, value] of newMap.entries()) if (value ===
|
|
189
|
+
for (const [key, value] of newMap.entries()) if (value === void 0) this.delete(key);
|
|
187
190
|
else this.set(key, value);
|
|
188
|
-
this.#db.exec("COMMIT
|
|
191
|
+
this.#db.exec("COMMIT");
|
|
189
192
|
} catch (error) {
|
|
190
|
-
this.#db.exec("ROLLBACK
|
|
193
|
+
this.#db.exec("ROLLBACK");
|
|
191
194
|
throw error;
|
|
192
195
|
}
|
|
193
196
|
return {
|
|
@@ -202,20 +205,46 @@ var KVSync = class {
|
|
|
202
205
|
};
|
|
203
206
|
}
|
|
204
207
|
/**
|
|
205
|
-
*
|
|
208
|
+
* Check if a key exists
|
|
209
|
+
* @param key Key name
|
|
210
|
+
* @returns Boolean representing whether a key exists
|
|
206
211
|
*/
|
|
212
|
+
exists(key) {
|
|
213
|
+
if (!this.#db.isOpen) throw new KVError("exists", "Database is not open");
|
|
214
|
+
if (!key || typeof key !== "string") throw new KVError("exists", "Key must be provided and be a non-empty string.");
|
|
215
|
+
return this.#db.prepare(`SELECT 1 FROM ${this.tableName} WHERE key = ?`).get(key) !== void 0;
|
|
216
|
+
}
|
|
217
|
+
/** Get total number of entries in the database */
|
|
218
|
+
size() {
|
|
219
|
+
if (!this.#db.isOpen) throw new KVError("size", "Database is not open");
|
|
220
|
+
return this.#db.prepare(`SELECT COUNT(*) as count FROM ${this.tableName}`).get().count;
|
|
221
|
+
}
|
|
222
|
+
/** Get all keys in the database */
|
|
223
|
+
keys() {
|
|
224
|
+
if (!this.#db.isOpen) throw new KVError("keys", "Database is not open");
|
|
225
|
+
return this.#db.prepare(`SELECT key FROM ${this.tableName}`).all().map((row) => row.key);
|
|
226
|
+
}
|
|
227
|
+
/** Get all values in the database */
|
|
228
|
+
values() {
|
|
229
|
+
if (!this.#db.isOpen) throw new KVError("values", "Database is not open");
|
|
230
|
+
return this.#db.prepare(`SELECT value FROM ${this.tableName}`).all().map((row) => (0, node_v8.deserialize)(row.value));
|
|
231
|
+
}
|
|
232
|
+
/** Open the database */
|
|
207
233
|
open() {
|
|
208
|
-
if (this.#db.isOpen) throw new KVError("open", "Database is open");
|
|
234
|
+
if (this.#db.isOpen) throw new KVError("open", "Database is already open");
|
|
235
|
+
this.#db.open();
|
|
236
|
+
this.#db.exec(`CREATE TABLE IF NOT EXISTS ${this.tableName} (key TEXT PRIMARY KEY NOT NULL, value BLOB NOT NULL) STRICT`);
|
|
237
|
+
return this;
|
|
209
238
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Close the database
|
|
212
|
-
*/
|
|
239
|
+
/** Close the database */
|
|
213
240
|
close() {
|
|
214
241
|
if (!this.#db.isOpen) throw new KVError("close", "Database is not open");
|
|
215
242
|
this.#db.close();
|
|
243
|
+
return this;
|
|
216
244
|
}
|
|
217
245
|
};
|
|
218
246
|
|
|
219
247
|
//#endregion
|
|
220
|
-
exports.
|
|
221
|
-
exports.
|
|
248
|
+
exports.JournalModes = JournalModes;
|
|
249
|
+
exports.KVError = KVError;
|
|
250
|
+
exports.KVSync = KVSync;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,46 +1,121 @@
|
|
|
1
|
-
//#region src/
|
|
2
|
-
|
|
1
|
+
//#region src/constants.d.ts
|
|
2
|
+
/** List of journal modes SQLite supports */
|
|
3
|
+
declare const JournalModes: {
|
|
3
4
|
readonly Delete: "DELETE";
|
|
4
5
|
readonly Memory: "MEMORY";
|
|
5
|
-
readonly
|
|
6
|
+
readonly OFF: "OFF";
|
|
6
7
|
readonly Persist: "PERSIST";
|
|
7
8
|
readonly Truncate: "TRUNCATE";
|
|
8
9
|
readonly WAL: "WAL";
|
|
9
10
|
};
|
|
10
11
|
//#endregion
|
|
11
12
|
//#region src/types.d.ts
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* SQLite journal mode
|
|
15
|
+
* @default DELETE (:memory: databases)
|
|
16
|
+
* @default WAL (persistent databases)
|
|
17
|
+
*/
|
|
18
|
+
type JournalMode = (typeof JournalModes)[keyof typeof JournalModes];
|
|
19
|
+
/** KVSync configuration options */
|
|
20
|
+
interface KVSyncProps {
|
|
15
21
|
journalMode?: JournalMode;
|
|
22
|
+
open?: boolean;
|
|
23
|
+
path?: SQLitePath;
|
|
24
|
+
tableName?: string;
|
|
16
25
|
}
|
|
26
|
+
/** File path, or :memory: (for SQLite use) */
|
|
17
27
|
type SQLitePath = ":memory:" | (string & {});
|
|
18
28
|
//#endregion
|
|
19
|
-
//#region src/classes/
|
|
29
|
+
//#region src/classes/kv-error.d.ts
|
|
30
|
+
/** Class representing a KVError */
|
|
31
|
+
declare class KVError extends Error {
|
|
32
|
+
static name: string;
|
|
33
|
+
readonly scope: string;
|
|
34
|
+
constructor(scope: string, ...args: unknown[]);
|
|
35
|
+
get name(): string;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/classes/kv-sync.d.ts
|
|
39
|
+
/** Class representing a synchronous key-value store */
|
|
20
40
|
declare class KVSync<T = any> {
|
|
21
41
|
#private;
|
|
22
|
-
|
|
42
|
+
/**
|
|
43
|
+
* The name of the table with keys and values
|
|
44
|
+
* @default "kv"
|
|
45
|
+
*/
|
|
46
|
+
tableName: string;
|
|
47
|
+
/**
|
|
48
|
+
* Instantiate a new key-value store
|
|
49
|
+
* @param props KVSync options
|
|
50
|
+
*/
|
|
51
|
+
constructor(props?: KVSyncProps);
|
|
52
|
+
/**
|
|
53
|
+
* Set a key in the database
|
|
54
|
+
* @param key Key name
|
|
55
|
+
* @param value Key value
|
|
56
|
+
* @returns Provided value
|
|
57
|
+
*/
|
|
23
58
|
set<K = T>(key: string, value: K | undefined): K;
|
|
24
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Get a value from the database
|
|
61
|
+
* @param key Key name
|
|
62
|
+
* @returns Value or undefined
|
|
63
|
+
*/
|
|
64
|
+
get<K = T>(key: string): K | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Delete a key from the database
|
|
67
|
+
* @param key Key name
|
|
68
|
+
* @returns KVSync instance
|
|
69
|
+
*/
|
|
25
70
|
delete(key: string): KVSync;
|
|
71
|
+
/**
|
|
72
|
+
* Get all data in the database
|
|
73
|
+
* @returns Array of objects containing keys and values
|
|
74
|
+
*/
|
|
26
75
|
all<K = T>(filter?: (key: string, value: K) => boolean): {
|
|
27
76
|
key: string;
|
|
28
77
|
value: K;
|
|
29
78
|
}[];
|
|
79
|
+
/**
|
|
80
|
+
* Remove all entries from the database
|
|
81
|
+
*/
|
|
30
82
|
clear(): KVSync;
|
|
83
|
+
/**
|
|
84
|
+
* Update the journal mode
|
|
85
|
+
* @param mode New journal mode
|
|
86
|
+
*/
|
|
31
87
|
setJournalMode(mode: JournalMode): this;
|
|
88
|
+
/**
|
|
89
|
+
* Perform a transaction
|
|
90
|
+
* @param callback Callback with KVSync instance
|
|
91
|
+
* @returns Object containing oldValues and newValues each containing arrays of keys and values
|
|
92
|
+
*/
|
|
32
93
|
transaction<R>(callback: (kv: KVSync<T>) => R): {
|
|
33
94
|
oldValues: {
|
|
34
95
|
key: string;
|
|
35
|
-
value: T |
|
|
96
|
+
value: T | undefined;
|
|
36
97
|
}[];
|
|
37
98
|
newValues: {
|
|
38
99
|
key: string;
|
|
39
|
-
value: T |
|
|
100
|
+
value: T | undefined;
|
|
40
101
|
}[];
|
|
41
102
|
};
|
|
42
|
-
|
|
43
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Check if a key exists
|
|
105
|
+
* @param key Key name
|
|
106
|
+
* @returns Boolean representing whether a key exists
|
|
107
|
+
*/
|
|
108
|
+
exists(key: string): boolean;
|
|
109
|
+
/** Get total number of entries in the database */
|
|
110
|
+
size(): number;
|
|
111
|
+
/** Get all keys in the database */
|
|
112
|
+
keys(): string[];
|
|
113
|
+
/** Get all values in the database */
|
|
114
|
+
values<K = T>(): K[];
|
|
115
|
+
/** Open the database */
|
|
116
|
+
open(): KVSync;
|
|
117
|
+
/** Close the database */
|
|
118
|
+
close(): KVSync;
|
|
44
119
|
}
|
|
45
120
|
//#endregion
|
|
46
|
-
export { JournalMode, KVSync,
|
|
121
|
+
export { JournalMode, JournalModes, KVError, KVSync, KVSyncProps, SQLitePath };
|
package/package.json
CHANGED
|
@@ -1,47 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-sqlite-kv",
|
|
3
3
|
"description": "Key-value store with node:sqlite",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"license": "UNLICENSED",
|
|
5
6
|
"repository": {
|
|
6
|
-
"url": "https://github.com/
|
|
7
|
+
"url": "https://github.com/andrewdku/node-sqlite-kv"
|
|
7
8
|
},
|
|
8
9
|
"author": {
|
|
9
|
-
"name": "
|
|
10
|
-
"url": "https://github.com/
|
|
10
|
+
"name": "andrewdku",
|
|
11
|
+
"url": "https://github.com/andrewdku"
|
|
11
12
|
},
|
|
12
|
-
"main": "./dist/index.
|
|
13
|
-
"
|
|
14
|
-
"types": "./dist/index.d.
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"exports": "./dist/index.cjs",
|
|
15
|
+
"types": "./dist/index.d.cts",
|
|
15
16
|
"type": "module",
|
|
16
|
-
"exports": {
|
|
17
|
-
"default": "./dist/index.cjs",
|
|
18
|
-
"import": "./dist/index.mjs",
|
|
19
|
-
"require": "./dist/index.cjs",
|
|
20
|
-
"types": "./dist/index.d.cts"
|
|
21
|
-
},
|
|
22
17
|
"engineStrict": true,
|
|
23
18
|
"engines": {
|
|
24
19
|
"node": ">=22.5.0"
|
|
25
20
|
},
|
|
26
|
-
"prettier": {
|
|
27
|
-
"jsxSingleQuote": false,
|
|
28
|
-
"printWidth": 80,
|
|
29
|
-
"semi": true,
|
|
30
|
-
"singleQuote": false,
|
|
31
|
-
"tabWidth": 4,
|
|
32
|
-
"trailingComma": "es5",
|
|
33
|
-
"useTabs": false
|
|
34
|
-
},
|
|
35
21
|
"devDependencies": {
|
|
36
|
-
"@types/node": "^25.
|
|
22
|
+
"@types/node": "^25.5.0",
|
|
37
23
|
"prettier": "^3.8.1",
|
|
38
|
-
"tsdown": "^0.
|
|
24
|
+
"tsdown": "^0.21.4",
|
|
39
25
|
"typescript": "^5.9.3"
|
|
40
26
|
},
|
|
41
27
|
"scripts": {
|
|
42
|
-
"build": "tsdown",
|
|
43
|
-
"format": "prettier
|
|
28
|
+
"build": "tsdown -c tsdown.config.mjs",
|
|
29
|
+
"format": "prettier --write . --config .prettierrc.json",
|
|
44
30
|
"lint": "tsc --noEmit; prettier ./src --check --ignore-path=.prettierignore",
|
|
45
|
-
"prepublish": "tsdown"
|
|
31
|
+
"prepublish": "tsdown -c tsdown.config.mjs"
|
|
46
32
|
}
|
|
47
33
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { defineConfig } from "tsdown"
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
cjsDefault: true,
|
|
7
|
+
clean: true,
|
|
8
|
+
deps: { skipNodeModulesBundle: true },
|
|
9
|
+
dts: true,
|
|
10
|
+
entry: ["src/index.ts"],
|
|
11
|
+
format: ["cjs"],
|
|
12
|
+
minify: false,
|
|
13
|
+
platform: "node",
|
|
14
|
+
removeNodeProtocol: false,
|
|
15
|
+
shims: true,
|
|
16
|
+
target: "node22",
|
|
17
|
+
})
|
package/dist/index.d.mts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
//#region src/utils/index.d.ts
|
|
2
|
-
declare const journalModes: {
|
|
3
|
-
readonly Delete: "DELETE";
|
|
4
|
-
readonly Memory: "MEMORY";
|
|
5
|
-
readonly Off: "OFF";
|
|
6
|
-
readonly Persist: "PERSIST";
|
|
7
|
-
readonly Truncate: "TRUNCATE";
|
|
8
|
-
readonly WAL: "WAL";
|
|
9
|
-
};
|
|
10
|
-
//#endregion
|
|
11
|
-
//#region src/types.d.ts
|
|
12
|
-
type JournalMode = (typeof journalModes)[keyof typeof journalModes];
|
|
13
|
-
interface KVSyncOptions {
|
|
14
|
-
path?: SQLitePath;
|
|
15
|
-
journalMode?: JournalMode;
|
|
16
|
-
}
|
|
17
|
-
type SQLitePath = ":memory:" | (string & {});
|
|
18
|
-
//#endregion
|
|
19
|
-
//#region src/classes/KVSync.d.ts
|
|
20
|
-
declare class KVSync<T = any> {
|
|
21
|
-
#private;
|
|
22
|
-
constructor(options?: KVSyncOptions);
|
|
23
|
-
set<K = T>(key: string, value: K | undefined): K;
|
|
24
|
-
get<K = T>(key: string): K | null;
|
|
25
|
-
delete(key: string): KVSync;
|
|
26
|
-
all<K = T>(filter?: (key: string, value: K) => boolean): {
|
|
27
|
-
key: string;
|
|
28
|
-
value: K;
|
|
29
|
-
}[];
|
|
30
|
-
clear(): KVSync;
|
|
31
|
-
setJournalMode(mode: JournalMode): this;
|
|
32
|
-
transaction<R>(callback: (kv: KVSync<T>) => R): {
|
|
33
|
-
oldValues: {
|
|
34
|
-
key: string;
|
|
35
|
-
value: T | null | undefined;
|
|
36
|
-
}[];
|
|
37
|
-
newValues: {
|
|
38
|
-
key: string;
|
|
39
|
-
value: T | null;
|
|
40
|
-
}[];
|
|
41
|
-
};
|
|
42
|
-
open(): void;
|
|
43
|
-
close(): void;
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
export { JournalMode, KVSync, KVSyncOptions, SQLitePath, journalModes };
|
package/dist/index.mjs
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { DatabaseSync } from "node:sqlite";
|
|
2
|
-
import { deserialize, serialize } from "node:v8";
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
|
|
6
|
-
//#region src/classes/KVError.ts
|
|
7
|
-
/**
|
|
8
|
-
* Class representing a KVError
|
|
9
|
-
*/
|
|
10
|
-
var KVError = class extends Error {
|
|
11
|
-
static name = "KVError";
|
|
12
|
-
scope;
|
|
13
|
-
constructor(scope, ...args) {
|
|
14
|
-
super(args.join(" "));
|
|
15
|
-
this.scope = scope;
|
|
16
|
-
Error.captureStackTrace?.(this, this.constructor);
|
|
17
|
-
}
|
|
18
|
-
get name() {
|
|
19
|
-
return `${this.constructor.name} (${this.scope})`;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/utils/index.ts
|
|
25
|
-
/**
|
|
26
|
-
* A list of journal modes SQLite supports
|
|
27
|
-
*/
|
|
28
|
-
const journalModes = {
|
|
29
|
-
Delete: "DELETE",
|
|
30
|
-
Memory: "MEMORY",
|
|
31
|
-
Off: "OFF",
|
|
32
|
-
Persist: "PERSIST",
|
|
33
|
-
Truncate: "TRUNCATE",
|
|
34
|
-
WAL: "WAL"
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
//#endregion
|
|
38
|
-
//#region src/classes/KVSync.ts
|
|
39
|
-
/**
|
|
40
|
-
* Class representing a synchronous key-value store
|
|
41
|
-
*/
|
|
42
|
-
var KVSync = class {
|
|
43
|
-
#db;
|
|
44
|
-
/**
|
|
45
|
-
* Instantiate a new key-value store
|
|
46
|
-
* @param options KVSync options
|
|
47
|
-
*/
|
|
48
|
-
constructor(options) {
|
|
49
|
-
const dbPath = options?.path ?? ":memory:";
|
|
50
|
-
if (dbPath !== ":memory:") fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
51
|
-
this.#db = new DatabaseSync(dbPath);
|
|
52
|
-
this.setJournalMode(options?.journalMode ?? (dbPath !== ":memory:" ? journalModes.WAL : journalModes.Delete));
|
|
53
|
-
this.#db.exec("CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY NOT NULL, value BLOB NOT NULL) STRICT;");
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Set a key in the database
|
|
57
|
-
* @param key Key name
|
|
58
|
-
* @param value Key value
|
|
59
|
-
* @returns Provided value
|
|
60
|
-
*/
|
|
61
|
-
set(key, value) {
|
|
62
|
-
if (!this.#db.isOpen) throw new KVError("set", "Database is not open");
|
|
63
|
-
if (!key || typeof key !== "string") throw new KVError("set", "Key must be provided and be a non-empty string");
|
|
64
|
-
if (value === void 0) throw new KVError("set", "Provided value is undefined, did you mean to use delete()?");
|
|
65
|
-
this.#db.prepare("INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)").run(key, serialize(value));
|
|
66
|
-
return value;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Get a value from the database
|
|
70
|
-
* @param key Key name
|
|
71
|
-
* @returns Value or null
|
|
72
|
-
*/
|
|
73
|
-
get(key) {
|
|
74
|
-
if (!this.#db.isOpen) throw new KVError("get", "Database is not open");
|
|
75
|
-
if (!key || typeof key !== "string") throw new KVError("get", "Key must be provided and be a non-empty string.");
|
|
76
|
-
const row = this.#db.prepare("SELECT value FROM kv WHERE key = ?;").get(key);
|
|
77
|
-
return row ? deserialize(row.value) : null;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Delete a key from the database
|
|
81
|
-
* @param key Key name
|
|
82
|
-
* @returns KVSync instance
|
|
83
|
-
*/
|
|
84
|
-
delete(key) {
|
|
85
|
-
if (!this.#db.isOpen) throw new KVError("delete", "Database is not open");
|
|
86
|
-
if (!key || typeof key !== "string") throw new KVError("delete", "Key must be provided and be a non-empty string.");
|
|
87
|
-
this.#db.prepare("DELETE FROM kv WHERE key = ?;").run(key);
|
|
88
|
-
return this;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Get all data in the database
|
|
92
|
-
* @returns Array of objects containing keys and values
|
|
93
|
-
*/
|
|
94
|
-
all(filter) {
|
|
95
|
-
if (!this.#db.isOpen) throw new KVError("all", "Database is not open");
|
|
96
|
-
const rows = this.#db.prepare("SELECT key, value FROM kv").iterate();
|
|
97
|
-
const result = [];
|
|
98
|
-
for (const row of rows) {
|
|
99
|
-
const key = row.key;
|
|
100
|
-
const value = deserialize(row.value);
|
|
101
|
-
if (!filter || filter(key, value)) result.push({
|
|
102
|
-
key,
|
|
103
|
-
value
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Remove all entries from the database
|
|
110
|
-
*/
|
|
111
|
-
clear() {
|
|
112
|
-
if (!this.#db.isOpen) throw new KVError("clear", "Database is not open");
|
|
113
|
-
this.#db.exec("DELETE FROM kv;");
|
|
114
|
-
return this;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Update the journal mode
|
|
118
|
-
* @param mode New journal mode
|
|
119
|
-
*/
|
|
120
|
-
setJournalMode(mode) {
|
|
121
|
-
if (!this.#db.isOpen) throw new KVError("setJournalMode", "Database is not open");
|
|
122
|
-
if (!Object.values(journalModes).includes(mode)) throw new KVError("setJournalMode", `Invalid journal mode specified - received: "${mode}", expected one of: ${Object.values(journalModes).join(", ")}`);
|
|
123
|
-
this.#db.exec(`PRAGMA journal_mode = ${mode};`);
|
|
124
|
-
return this;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Perform a transaction
|
|
128
|
-
* @param callback Callback with KVSync instance
|
|
129
|
-
* @returns Object containing oldValues and newValues each containing arrays of keys and values
|
|
130
|
-
*/
|
|
131
|
-
transaction(callback) {
|
|
132
|
-
if (!this.#db.isOpen) throw new KVError("transaction", "Database is not open");
|
|
133
|
-
if (!callback) throw new KVError("transaction", "A callback must be provided when using transaction().");
|
|
134
|
-
if (typeof callback !== "function") throw new KVError("transaction", `Transaction callback must be of type function. Received: ${typeof callback}`);
|
|
135
|
-
const oldMap = /* @__PURE__ */ new Map();
|
|
136
|
-
const newMap = /* @__PURE__ */ new Map();
|
|
137
|
-
const tx = Object.create(this);
|
|
138
|
-
tx.set = (key, value) => {
|
|
139
|
-
if (!oldMap.has(key)) {
|
|
140
|
-
const oldValue = this.get(key);
|
|
141
|
-
oldMap.set(key, oldValue === null ? void 0 : oldValue);
|
|
142
|
-
}
|
|
143
|
-
newMap.set(key, value);
|
|
144
|
-
return value ?? null;
|
|
145
|
-
};
|
|
146
|
-
tx.delete = (key) => {
|
|
147
|
-
if (!oldMap.has(key)) {
|
|
148
|
-
const oldValue = this.get(key);
|
|
149
|
-
oldMap.set(key, oldValue === null ? void 0 : oldValue);
|
|
150
|
-
}
|
|
151
|
-
newMap.set(key, null);
|
|
152
|
-
return tx;
|
|
153
|
-
};
|
|
154
|
-
try {
|
|
155
|
-
this.#db.exec("BEGIN TRANSACTION;");
|
|
156
|
-
callback(tx);
|
|
157
|
-
for (const [key, value] of newMap.entries()) if (value === null) this.delete(key);
|
|
158
|
-
else this.set(key, value);
|
|
159
|
-
this.#db.exec("COMMIT;");
|
|
160
|
-
} catch (error) {
|
|
161
|
-
this.#db.exec("ROLLBACK;");
|
|
162
|
-
throw error;
|
|
163
|
-
}
|
|
164
|
-
return {
|
|
165
|
-
oldValues: Array.from(oldMap.entries()).map(([key, value]) => ({
|
|
166
|
-
key,
|
|
167
|
-
value
|
|
168
|
-
})),
|
|
169
|
-
newValues: Array.from(newMap.entries()).map(([key, value]) => ({
|
|
170
|
-
key,
|
|
171
|
-
value
|
|
172
|
-
}))
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Open the database
|
|
177
|
-
*/
|
|
178
|
-
open() {
|
|
179
|
-
if (this.#db.isOpen) throw new KVError("open", "Database is open");
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Close the database
|
|
183
|
-
*/
|
|
184
|
-
close() {
|
|
185
|
-
if (!this.#db.isOpen) throw new KVError("close", "Database is not open");
|
|
186
|
-
this.#db.close();
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
//#endregion
|
|
191
|
-
export { KVSync, journalModes };
|