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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "arrowParens": "always",
3
+ "jsxSingleQuote": false,
4
+ "printWidth": 90,
5
+ "quoteProps": "as-needed",
6
+ "semi": false,
7
+ "singleQuote": false,
8
+ "tabWidth": 4,
9
+ "trailingComma": "es5",
10
+ "useTabs": false
11
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "prettier.configPath": ".prettierrc.json",
3
+ "prettier.prettierPath": "node_modules/prettier"
4
+ }
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,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("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
  /**
98
101
  * Get a value from the database
99
102
  * @param key Key name
100
- * @returns Value or null
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("SELECT value FROM kv WHERE key = ?;").get(key);
106
- return row ? (0, node_v8.deserialize)(row.value) : null;
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("DELETE FROM kv WHERE key = ?;").run(key);
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("DELETE FROM kv;");
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(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};`);
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 === null ? void 0 : oldValue);
173
+ oldMap.set(key, oldValue);
171
174
  }
172
175
  newMap.set(key, value);
173
- return value ?? null;
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 === null ? void 0 : oldValue);
181
+ oldMap.set(key, oldValue);
179
182
  }
180
- newMap.set(key, null);
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 === null) this.delete(key);
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
- * Open the database
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.KVSync = KVSync;
221
- exports.journalModes = journalModes;
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/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
+ * 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 | null | undefined;
96
+ value: T | undefined;
36
97
  }[];
37
98
  newValues: {
38
99
  key: string;
39
- value: T | null;
100
+ value: T | undefined;
40
101
  }[];
41
102
  };
42
- open(): void;
43
- close(): void;
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, KVSyncOptions, SQLitePath, journalModes };
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.0",
4
+ "version": "1.0.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
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.2.0",
22
+ "@types/node": "^25.5.0",
37
23
  "prettier": "^3.8.1",
38
- "tsdown": "^0.20.1",
24
+ "tsdown": "^0.21.4",
39
25
  "typescript": "^5.9.3"
40
26
  },
41
27
  "scripts": {
42
- "build": "tsdown",
43
- "format": "prettier ./src --write --ignore-path=.prettierignore",
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 };