node-sqlite-kv 0.4.0 → 1.1.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/LICENSE CHANGED
@@ -1,7 +1,24 @@
1
- Copyright 2026 Andrew (e60m5ss / wlix)
1
+ This is free and unencumbered software released into the public domain.
2
2
 
3
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
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
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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 AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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
- > Key-value store with node:sqlite (Node.js v22.5.0 or higher is required)
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
- // use :memory: for in-memory storage
24
- // path is optional, defaults to :memory:
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
- // optional journal mode
28
- // default: DELETE
29
- journalMode: "WAL",
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"); // 123
43
- kv.get("string"); // "hello world"
44
- kv.get("boolean"); // true
45
- kv.get("null"); // null
46
- kv.get("array"); // [1, 2, 3]
47
- kv.get("object"); // { settings: { theme: "dark" } }
48
- kv.get("date"); // 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"); // 999
63
+ kv.set("number", 999)
64
+ kv.get("number") // 999
53
65
 
54
66
  // delete values
55
- kv.delete("array"); // [1, 2, 3]
56
- kv.get("array"); // null
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 welcomed. For more major changes, please open an issue to discuss what you wish to change.
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
- [MIT](LICENSE)
134
+ [Unlicensed](LICENSE)
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
- //#region rolldown:runtime
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/KVError.ts
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/utils/index.ts
54
- /**
55
- * A list of journal modes SQLite supports
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
- Off: "OFF",
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/KVSync.ts
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 options KVSync options
75
+ * @param props KVSync options
76
76
  */
77
- constructor(options) {
78
- const dbPath = options?.path ?? ":memory:";
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.#db = new node_sqlite.DatabaseSync(dbPath);
81
- this.setJournalMode(options?.journalMode ?? (dbPath !== ":memory:" ? journalModes.WAL : journalModes.Delete));
82
- this.#db.exec("CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY NOT NULL, value BLOB NOT NULL) STRICT;");
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,36 @@ 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("INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)").run(key, (0, node_v8.serialize)(value));
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
  /**
101
+ * Set a key only if it doesn't already exist
102
+ * @param key Key name
103
+ * @param value Key value
104
+ * @returns True if key was set, false if already existed
105
+ */
106
+ setnx(key, value) {
107
+ if (!this.#db.isOpen) throw new KVError("setnx", "Database is not open");
108
+ if (!key || typeof key !== "string") throw new KVError("setnx", "Key must be provided and be a non-empty string");
109
+ if (value === void 0) throw new KVError("setnx", "Provided value is undefined, did you mean to use delete()?");
110
+ try {
111
+ this.#db.prepare(`INSERT INTO ${this.tableName} (key, value) VALUES (?, ?)`).run(key, (0, node_v8.serialize)(value));
112
+ return true;
113
+ } catch {
114
+ return false;
115
+ }
116
+ }
117
+ /**
98
118
  * Get a value from the database
99
119
  * @param key Key name
100
- * @returns Value or null
120
+ * @returns Value or undefined
101
121
  */
102
122
  get(key) {
103
123
  if (!this.#db.isOpen) throw new KVError("get", "Database is not open");
104
124
  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("SELECT value FROM kv WHERE key = ?;").get(key);
106
- return row ? (0, node_v8.deserialize)(row.value) : null;
125
+ const row = this.#db.prepare(`SELECT value FROM ${this.tableName} WHERE key = ?`).get(key);
126
+ return row ? (0, node_v8.deserialize)(row.value) : void 0;
107
127
  }
108
128
  /**
109
129
  * Delete a key from the database
@@ -113,7 +133,7 @@ var KVSync = class {
113
133
  delete(key) {
114
134
  if (!this.#db.isOpen) throw new KVError("delete", "Database is not open");
115
135
  if (!key || typeof key !== "string") throw new KVError("delete", "Key must be provided and be a non-empty string.");
116
- this.#db.prepare("DELETE FROM kv WHERE key = ?;").run(key);
136
+ this.#db.prepare(`DELETE FROM ${this.tableName} WHERE key = ?`).run(key);
117
137
  return this;
118
138
  }
119
139
  /**
@@ -122,8 +142,8 @@ var KVSync = class {
122
142
  */
123
143
  all(filter) {
124
144
  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
145
  const result = [];
146
+ const rows = this.#db.prepare(`SELECT key, value FROM ${this.tableName}`).iterate();
127
147
  for (const row of rows) {
128
148
  const key = row.key;
129
149
  const value = (0, node_v8.deserialize)(row.value);
@@ -139,7 +159,7 @@ var KVSync = class {
139
159
  */
140
160
  clear() {
141
161
  if (!this.#db.isOpen) throw new KVError("clear", "Database is not open");
142
- this.#db.exec("DELETE FROM kv;");
162
+ this.#db.exec(`DELETE FROM ${this.tableName}`);
143
163
  return this;
144
164
  }
145
165
  /**
@@ -148,8 +168,8 @@ var KVSync = class {
148
168
  */
149
169
  setJournalMode(mode) {
150
170
  if (!this.#db.isOpen) throw new KVError("setJournalMode", "Database is not open");
151
- if (!Object.values(journalModes).includes(mode)) throw new KVError("setJournalMode", `Invalid journal mode specified - received: "${mode}", expected one of: ${Object.values(journalModes).join(", ")}`);
152
- this.#db.exec(`PRAGMA journal_mode = ${mode};`);
171
+ if (!Object.values(JournalModes).includes(mode)) throw new KVError("setJournalMode", `Invalid journal mode specified - received: "${mode}", expected one of: ${Object.values(JournalModes).join(", ")}`);
172
+ this.#db.exec(`PRAGMA journal_mode = ${mode}`);
153
173
  return this;
154
174
  }
155
175
  /**
@@ -163,31 +183,45 @@ var KVSync = class {
163
183
  if (typeof callback !== "function") throw new KVError("transaction", `Transaction callback must be of type function. Received: ${typeof callback}`);
164
184
  const oldMap = /* @__PURE__ */ new Map();
165
185
  const newMap = /* @__PURE__ */ new Map();
186
+ const setnxKeys = /* @__PURE__ */ new Set();
166
187
  const tx = Object.create(this);
167
188
  tx.set = (key, value) => {
168
189
  if (!oldMap.has(key)) {
169
190
  const oldValue = this.get(key);
170
- oldMap.set(key, oldValue === null ? void 0 : oldValue);
191
+ oldMap.set(key, oldValue);
171
192
  }
172
193
  newMap.set(key, value);
173
- return value ?? null;
194
+ return value;
174
195
  };
175
196
  tx.delete = (key) => {
176
197
  if (!oldMap.has(key)) {
177
198
  const oldValue = this.get(key);
178
- oldMap.set(key, oldValue === null ? void 0 : oldValue);
199
+ oldMap.set(key, oldValue);
179
200
  }
180
- newMap.set(key, null);
201
+ newMap.set(key, void 0);
181
202
  return tx;
182
203
  };
204
+ tx.setnx = (key, value) => {
205
+ if (!oldMap.has(key)) {
206
+ const oldValue = this.get(key);
207
+ oldMap.set(key, oldValue);
208
+ }
209
+ if (oldMap.get(key) === void 0) {
210
+ newMap.set(key, value);
211
+ setnxKeys.add(key);
212
+ return true;
213
+ }
214
+ return false;
215
+ };
183
216
  try {
184
- this.#db.exec("BEGIN TRANSACTION;");
217
+ this.#db.exec("BEGIN TRANSACTION");
185
218
  callback(tx);
186
- for (const [key, value] of newMap.entries()) if (value === null) this.delete(key);
219
+ for (const [key, value] of newMap.entries()) if (value === void 0) this.delete(key);
220
+ else if (setnxKeys.has(key)) this.setnx(key, value);
187
221
  else this.set(key, value);
188
- this.#db.exec("COMMIT;");
222
+ this.#db.exec("COMMIT");
189
223
  } catch (error) {
190
- this.#db.exec("ROLLBACK;");
224
+ this.#db.exec("ROLLBACK");
191
225
  throw error;
192
226
  }
193
227
  return {
@@ -202,20 +236,46 @@ var KVSync = class {
202
236
  };
203
237
  }
204
238
  /**
205
- * Open the database
239
+ * Check if a key exists
240
+ * @param key Key name
241
+ * @returns Boolean representing whether a key exists
206
242
  */
243
+ exists(key) {
244
+ if (!this.#db.isOpen) throw new KVError("exists", "Database is not open");
245
+ if (!key || typeof key !== "string") throw new KVError("exists", "Key must be provided and be a non-empty string.");
246
+ return this.#db.prepare(`SELECT 1 FROM ${this.tableName} WHERE key = ?`).get(key) !== void 0;
247
+ }
248
+ /** Get total number of entries in the database */
249
+ size() {
250
+ if (!this.#db.isOpen) throw new KVError("size", "Database is not open");
251
+ return this.#db.prepare(`SELECT COUNT(*) as count FROM ${this.tableName}`).get().count;
252
+ }
253
+ /** Get all keys in the database */
254
+ keys() {
255
+ if (!this.#db.isOpen) throw new KVError("keys", "Database is not open");
256
+ return this.#db.prepare(`SELECT key FROM ${this.tableName}`).all().map((row) => row.key);
257
+ }
258
+ /** Get all values in the database */
259
+ values() {
260
+ if (!this.#db.isOpen) throw new KVError("values", "Database is not open");
261
+ return this.#db.prepare(`SELECT value FROM ${this.tableName}`).all().map((row) => (0, node_v8.deserialize)(row.value));
262
+ }
263
+ /** Open the database */
207
264
  open() {
208
- if (this.#db.isOpen) throw new KVError("open", "Database is open");
265
+ if (this.#db.isOpen) throw new KVError("open", "Database is already open");
266
+ this.#db.open();
267
+ this.#db.exec(`CREATE TABLE IF NOT EXISTS ${this.tableName} (key TEXT PRIMARY KEY NOT NULL, value BLOB NOT NULL) STRICT`);
268
+ return this;
209
269
  }
210
- /**
211
- * Close the database
212
- */
270
+ /** Close the database */
213
271
  close() {
214
272
  if (!this.#db.isOpen) throw new KVError("close", "Database is not open");
215
273
  this.#db.close();
274
+ return this;
216
275
  }
217
276
  };
218
277
 
219
278
  //#endregion
220
- exports.KVSync = KVSync;
221
- exports.journalModes = journalModes;
279
+ exports.JournalModes = JournalModes;
280
+ exports.KVError = KVError;
281
+ exports.KVSync = KVSync;
package/dist/index.d.cts CHANGED
@@ -1,46 +1,128 @@
1
- //#region src/utils/index.d.ts
2
- declare const journalModes: {
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 Off: "OFF";
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
- type JournalMode = (typeof journalModes)[keyof typeof journalModes];
13
- interface KVSyncOptions {
14
- path?: SQLitePath;
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/KVSync.d.ts
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
- constructor(options?: KVSyncOptions);
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
- get<K = T>(key: string): K | null;
59
+ /**
60
+ * Set a key only if it doesn't already exist
61
+ * @param key Key name
62
+ * @param value Key value
63
+ * @returns True if key was set, false if already existed
64
+ */
65
+ setnx<K = T>(key: string, value: K): boolean;
66
+ /**
67
+ * Get a value from the database
68
+ * @param key Key name
69
+ * @returns Value or undefined
70
+ */
71
+ get<K = T>(key: string): K | undefined;
72
+ /**
73
+ * Delete a key from the database
74
+ * @param key Key name
75
+ * @returns KVSync instance
76
+ */
25
77
  delete(key: string): KVSync;
78
+ /**
79
+ * Get all data in the database
80
+ * @returns Array of objects containing keys and values
81
+ */
26
82
  all<K = T>(filter?: (key: string, value: K) => boolean): {
27
83
  key: string;
28
84
  value: K;
29
85
  }[];
86
+ /**
87
+ * Remove all entries from the database
88
+ */
30
89
  clear(): KVSync;
90
+ /**
91
+ * Update the journal mode
92
+ * @param mode New journal mode
93
+ */
31
94
  setJournalMode(mode: JournalMode): this;
95
+ /**
96
+ * Perform a transaction
97
+ * @param callback Callback with KVSync instance
98
+ * @returns Object containing oldValues and newValues each containing arrays of keys and values
99
+ */
32
100
  transaction<R>(callback: (kv: KVSync<T>) => R): {
33
101
  oldValues: {
34
102
  key: string;
35
- value: T | null | undefined;
103
+ value: T | undefined;
36
104
  }[];
37
105
  newValues: {
38
106
  key: string;
39
- value: T | null;
107
+ value: T | undefined;
40
108
  }[];
41
109
  };
42
- open(): void;
43
- close(): void;
110
+ /**
111
+ * Check if a key exists
112
+ * @param key Key name
113
+ * @returns Boolean representing whether a key exists
114
+ */
115
+ exists(key: string): boolean;
116
+ /** Get total number of entries in the database */
117
+ size(): number;
118
+ /** Get all keys in the database */
119
+ keys(): string[];
120
+ /** Get all values in the database */
121
+ values<K = T>(): K[];
122
+ /** Open the database */
123
+ open(): KVSync;
124
+ /** Close the database */
125
+ close(): KVSync;
44
126
  }
45
127
  //#endregion
46
- export { JournalMode, KVSync, KVSyncOptions, SQLitePath, journalModes };
128
+ export { JournalMode, JournalModes, KVError, KVSync, KVSyncProps, SQLitePath };
package/package.json CHANGED
@@ -1,47 +1,39 @@
1
1
  {
2
2
  "name": "node-sqlite-kv",
3
3
  "description": "Key-value store with node:sqlite",
4
- "version": "0.4.0",
4
+ "version": "1.1.0",
5
+ "license": "UNLICENSED",
5
6
  "repository": {
6
- "url": "https://github.com/e60m5ss/node-sqlite-kv"
7
+ "url": "https://github.com/andrewdku/node-sqlite-kv"
7
8
  },
8
9
  "author": {
9
- "name": "e60m5ss",
10
- "url": "https://github.com/e60m5ss"
10
+ "name": "andrewdku",
11
+ "url": "https://github.com/andrewdku"
11
12
  },
12
- "main": "./dist/index.mjs",
13
- "module": "./dist/index.mjs",
14
- "types": "./dist/index.d.mts",
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
- "node": ">=22.5.0"
25
- },
26
- "prettier": {
27
- "jsxSingleQuote": false,
28
- "printWidth": 80,
29
- "semi": true,
30
- "singleQuote": false,
31
- "tabWidth": 4,
32
- "trailingComma": "es5",
33
- "useTabs": false
19
+ "node": ">=22"
34
20
  },
21
+ "files": [
22
+ "dist",
23
+ "LICENSE",
24
+ "package.json",
25
+ "README.md"
26
+ ],
35
27
  "devDependencies": {
36
- "@types/node": "^25.2.0",
28
+ "@types/node": "^25.5.0",
37
29
  "prettier": "^3.8.1",
38
- "tsdown": "^0.20.1",
30
+ "tsdown": "^0.21.4",
39
31
  "typescript": "^5.9.3"
40
32
  },
41
33
  "scripts": {
42
- "build": "tsdown",
43
- "format": "prettier ./src --write --ignore-path=.prettierignore",
34
+ "build": "tsdown -c tsdown.config.mjs",
35
+ "format": "prettier --write . --config .prettierrc.json",
44
36
  "lint": "tsc --noEmit; prettier ./src --check --ignore-path=.prettierignore",
45
- "prepublish": "tsdown"
37
+ "prepublish": "tsdown -c tsdown.config.mjs"
46
38
  }
47
39
  }
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 };