neutralinojs-ext-sqlite3 0.0.1-alpha.2

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,2 @@
1
+ [build]
2
+ target-dir = "./build/rust"
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Raman Piatrou <rpiatrou.github@proton.me>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # neutralinojs-ext-sqlite3
2
+
3
+ A Neutralino extension that provides SQLite access for desktop applications.
4
+
5
+ It runs SQLite in a separate native process and communicates with your app through Neutralino’s extension IPC layer.
6
+
7
+ The package also includes a JavaScript client API compatible with the sqlite3 interface,
8
+ supporting both callbacks and Promises.
9
+
10
+ ---
11
+
12
+ ## Motivation & Intent
13
+
14
+ Desktop applications require persistent local storage with support for queries and complex data structures.
15
+ This is commonly provided by SQLite or similar database engines.
16
+
17
+ However, browser-based environments cannot access local SQLite database files due to sandbox restrictions.
18
+
19
+ This extension bridges that gap by running SQLite in a separate native process
20
+ and providing a client API compatible with the sqlite3 interface, as well as ORM packages built on top of it.
21
+
22
+ Design goals:
23
+
24
+ * JS-first development
25
+ * Generic, not app-specific, API
26
+ * Minimal native surface and small binary footprint
27
+
28
+ ---
29
+
30
+ ## Architecture Overview
31
+
32
+ ```
33
+ Application Code (JavaScript / TypeScript)
34
+
35
+ │ calls SQLite client API
36
+
37
+ Extension Client API (JS wrapper)
38
+ (runs inside WebView, managed by Neutralino)
39
+
40
+ │ Neutralino.extensions.dispatch (IPC)
41
+
42
+ Neutralino Runtime (native neutralino process)
43
+
44
+ │ WebSocket-based extension IPC
45
+
46
+ SQLite Extension Process (Rust native binary)
47
+
48
+ │ direct SQLite C API
49
+
50
+ SQLite Engine
51
+
52
+
53
+ Database File (.db) on Filesystem
54
+ ```
55
+
56
+ Key points:
57
+
58
+ * The extension runs as a separate process managed by Neutralino
59
+ * All database operations are asynchronous and do not block the UI.
60
+
61
+ ---
62
+
63
+ ## When to Use This Extension
64
+
65
+ Use neutralinojs-ext-sqlite3 if you need:
66
+
67
+ * Local SQL storage
68
+ * A small, fast desktop database
69
+ * Optional SQLite extensions (loaded at startup)
70
+
71
+ ---
72
+
73
+ ## Installation
74
+
75
+ ```
76
+ npm install neutralinojs-ext-sqlite3
77
+ ```
78
+
79
+ It will automatically copy appropriate binary into your Neutralino extensions/ directory:
80
+
81
+ ```
82
+ extensions/sqlite/
83
+ ```
84
+
85
+ Then register it in neutralino.config.json.
86
+
87
+ Example:
88
+
89
+ ```json
90
+ {
91
+ "enableExtensions": true,
92
+ "extensions": [
93
+ {
94
+ "id": "sqlite3",
95
+ "command": "./extensions/sqlite3/neutralinojs-ext-sqlite3 --run"
96
+ }
97
+ ]
98
+ }
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Usage Example
104
+
105
+ The neutralino runtime should be initialized first.
106
+
107
+ ```js
108
+ Neutralino.init();
109
+ ```
110
+
111
+ Use the provided JS wrapper:
112
+
113
+ ```js
114
+ import * as sqlite from "neutralinojs-ext-sqlite3";
115
+
116
+ const db = await sqlite.open({
117
+ path: "app.db"
118
+ });
119
+
120
+ await db.exec("CREATE TABLE messages(id TEXT, content TEXT)");
121
+
122
+ await db.close();
123
+ ```
124
+
125
+ The JS wrapper API handles IPC communication with the native extension.
126
+
127
+ ---
128
+
129
+ ## Runtime Configuration
130
+
131
+ The extension runs as a Neutralino extension process and communicates using Neutralino’s extension IPC protocol.
132
+
133
+ Startup parameters:
134
+
135
+ * `--run` (required)
136
+ * `--extension <PATH>` for standard SQLite extensions (exports `sqlite3_extension_init`)
137
+ * `--extension-symbol <PATH> <SYMBOL>` for extensions with a custom init symbol
138
+
139
+ Example:
140
+
141
+ ```
142
+ neutralinojs-ext-sqlite3 --run
143
+ ```
144
+
145
+ ```
146
+ neutralinojs-ext-sqlite3 --run --extension ./my_ext.so
147
+ ```
148
+
149
+ ```
150
+ neutralinojs-ext-sqlite3 --run --extension-symbol ./custom_ext.so my_custom_init
151
+ ```
152
+
153
+ Neutralino provides WebSocket connection parameters to the extension at startup,
154
+ and all runtime communication occurs over Neutralino’s extension messaging layer.
155
+
156
+ ---
157
+
158
+ ## Development
159
+
160
+ ### Build the native extension
161
+
162
+ ```
163
+ npm run build
164
+ ```
165
+
166
+ The extension links SQLite via the Rust libsqlite3-sys crate with the bundled SQLite source enabled.
167
+ SQLite is compiled automatically during the build process,
168
+ so no system SQLite libraries or development headers are required.
169
+
170
+ Platform binaries are published into `dist/` folder.
171
+
172
+ ### Run tests
173
+
174
+ ```
175
+ npm run test
176
+ ```
177
+ Vitest requires Node.js 16+ (18+ recommended).
178
+ The test suite also covers SQLite extension loading with multiple extension types;
179
+ it is not tied to any specific extension library.
180
+
181
+ ---
182
+
183
+ ## Release Notes
184
+
185
+ See `RELEASES.md` for binary coverage expectations.
186
+
187
+ The SQLite engine version is determined by the Rust SQLite crate (libsqlite3-sys) used by this project. SQLite is bundled and compiled as part of the extension build process and included directly in the extension binary. No system SQLite installation is required at runtime.
188
+
189
+ ---
190
+
191
+ ## License
192
+
193
+ MIT License © 2026 Raman Piatrou.
194
+
195
+ See `LICENSE` for details.
@@ -0,0 +1,82 @@
1
+ /** Result of run() - matches sqlite3 RunResult */
2
+ export interface RunResult {
3
+ lastID: number;
4
+ changes: number;
5
+ /** Alias for lastID (SQLite native name) */
6
+ lastInsertRowid: number;
7
+ }
8
+ /** Options for open() */
9
+ export interface OpenOptions {
10
+ path?: string;
11
+ filename?: string;
12
+ readonly?: boolean;
13
+ create?: boolean;
14
+ wal?: boolean;
15
+ pragmas?: string[];
16
+ [key: string]: unknown;
17
+ }
18
+ /**
19
+ * Database instance - classic sqlite3 promises interface.
20
+ * Use open() to obtain a Database.
21
+ */
22
+ export declare class Database {
23
+ #private;
24
+ constructor(connection: string);
25
+ /**
26
+ * Run a SQL statement (INSERT/UPDATE/DELETE). Returns lastID and changes.
27
+ * @example db.run("INSERT INTO t VALUES (?)", "foo")
28
+ * @example db.run("UPDATE t SET x = ? WHERE id = ?", "bar", 1)
29
+ */
30
+ run(sql: string, ...params: unknown[]): Promise<RunResult>;
31
+ /**
32
+ * Get a single row. Returns undefined if no row.
33
+ * @example db.get("SELECT * FROM t WHERE id = ?", 1)
34
+ */
35
+ get<T = Record<string, unknown>>(sql: string, ...params: unknown[]): Promise<T | undefined>;
36
+ /**
37
+ * Get all rows.
38
+ * @example db.all("SELECT * FROM t")
39
+ */
40
+ all<T = Record<string, unknown>>(sql: string, ...params: unknown[]): Promise<T[]>;
41
+ /**
42
+ * Execute SQL without params (DDL, PRAGMAs, multiple statements).
43
+ * @example db.exec("CREATE TABLE t(id INTEGER PRIMARY KEY)")
44
+ */
45
+ exec(sql: string): Promise<void>;
46
+ /**
47
+ * Close the database connection.
48
+ */
49
+ close(): Promise<void>;
50
+ /**
51
+ * Run multiple statements in a transaction. Rolls back on error.
52
+ */
53
+ transaction(ops: Array<{
54
+ sql: string;
55
+ params?: unknown[];
56
+ }>): Promise<{
57
+ changes: number;
58
+ }>;
59
+ }
60
+ /**
61
+ * Open a database. Returns a Database instance (classic sqlite3 interface).
62
+ * @param pathOrOptions - Path string (e.g. ":memory:" or "/path/to/db.sqlite") or options object
63
+ * @param options - Optional options when path is a string
64
+ * @example const db = await open(":memory:")
65
+ * @example const db = await open({ path: "/tmp/app.db", readonly: false })
66
+ */
67
+ export declare function open(pathOrOptions?: string | {
68
+ path?: string;
69
+ filename?: string;
70
+ options?: Record<string, unknown>;
71
+ }, options?: Record<string, unknown>): Promise<Database>;
72
+ /**
73
+ * Shutdown the SQLite extension process. Call after closing all databases.
74
+ */
75
+ export declare function shutdown(): Promise<void>;
76
+ declare const _default: {
77
+ open: typeof open;
78
+ Database: typeof Database;
79
+ shutdown: typeof shutdown;
80
+ };
81
+ export default _default;
82
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiFA,kDAAkD;AAClD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,yBAAyB;AACzB,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,qBAAa,QAAQ;;gBAGP,UAAU,EAAE,MAAM;IAI9B;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC;IAc1D;;;OAGG;IACH,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAU3F;;;OAGG;IACH,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKjF;;;OAGG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhC;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB;;OAEG;IACH,WAAW,CACT,GAAG,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,GAC9C,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAMhC;AAED;;;;;;GAMG;AACH,wBAAsB,IAAI,CACxB,aAAa,CAAC,EACV,MAAM,GACN;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC3E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,QAAQ,CAAC,CAInB;AAED;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAE9C;;;;;;AAED,wBAIE"}
package/dist/index.js ADDED
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ /// <reference path="../neutralino.d.ts" />
3
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
4
+ if (kind === "m") throw new TypeError("Private method is not writable");
5
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
6
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
7
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
8
+ };
9
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
10
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
11
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
12
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
13
+ };
14
+ var _Database_connection;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.Database = void 0;
17
+ exports.open = open;
18
+ exports.shutdown = shutdown;
19
+ function assertNeutralino() {
20
+ if (typeof Neutralino === "undefined" || !Neutralino.extensions) {
21
+ throw new Error("Neutralino.extensions is not available in this runtime");
22
+ }
23
+ }
24
+ function generateId() {
25
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
26
+ const r = Math.random() * 16 | 0;
27
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
28
+ return v.toString(16);
29
+ });
30
+ }
31
+ async function invoke(method, params) {
32
+ assertNeutralino();
33
+ const id = generateId();
34
+ return new Promise((resolve, reject) => {
35
+ const handler = (evt) => {
36
+ const data = evt.detail || evt;
37
+ if (data.id !== id)
38
+ return;
39
+ Neutralino.events.off("sqlite3", handler);
40
+ const rpcData = data;
41
+ if (rpcData.error) {
42
+ const err = new Error(rpcData.error.message || "RPC error");
43
+ err.code = rpcData.error.code;
44
+ err.details = rpcData.error.details;
45
+ reject(err);
46
+ }
47
+ else {
48
+ resolve(rpcData.result);
49
+ }
50
+ };
51
+ Neutralino.events.on("sqlite3", handler);
52
+ Neutralino.extensions.dispatch("sqlite3", "sqlite3", { id, method, params });
53
+ });
54
+ }
55
+ /**
56
+ * Normalize params for run/get/all: (sql, x) | (sql, x, y) | (sql, [x, y])
57
+ */
58
+ function normalizeParams(sql, ...args) {
59
+ if (args.length === 0) {
60
+ return { sql, params: [] };
61
+ }
62
+ if (args.length === 1 && Array.isArray(args[0])) {
63
+ return { sql, params: args[0] };
64
+ }
65
+ return { sql, params: args };
66
+ }
67
+ function normalizeOpenArg(arg1, arg2) {
68
+ if (arg1 === undefined)
69
+ return {};
70
+ if (typeof arg1 === "string") {
71
+ return { path: arg1, options: arg2 };
72
+ }
73
+ if (typeof arg1 === "object" && arg1 !== null && !Array.isArray(arg1)) {
74
+ const path = arg1.path ?? arg1.filename;
75
+ return path !== undefined ? { path, options: arg1.options ?? arg2 } : arg1;
76
+ }
77
+ return { path: arg1, options: arg2 };
78
+ }
79
+ /**
80
+ * Database instance - classic sqlite3 promises interface.
81
+ * Use open() to obtain a Database.
82
+ */
83
+ class Database {
84
+ constructor(connection) {
85
+ _Database_connection.set(this, void 0);
86
+ __classPrivateFieldSet(this, _Database_connection, connection, "f");
87
+ }
88
+ /**
89
+ * Run a SQL statement (INSERT/UPDATE/DELETE). Returns lastID and changes.
90
+ * @example db.run("INSERT INTO t VALUES (?)", "foo")
91
+ * @example db.run("UPDATE t SET x = ? WHERE id = ?", "bar", 1)
92
+ */
93
+ run(sql, ...params) {
94
+ const { sql: s, params: p } = normalizeParams(sql, ...params);
95
+ return invoke("run", { connection: __classPrivateFieldGet(this, _Database_connection, "f"), sql: s, params: p }).then((r) => {
96
+ const res = r;
97
+ return {
98
+ lastID: res.lastInsertRowid,
99
+ changes: res.changes,
100
+ lastInsertRowid: res.lastInsertRowid,
101
+ };
102
+ });
103
+ }
104
+ /**
105
+ * Get a single row. Returns undefined if no row.
106
+ * @example db.get("SELECT * FROM t WHERE id = ?", 1)
107
+ */
108
+ get(sql, ...params) {
109
+ const { sql: s, params: p } = normalizeParams(sql, ...params);
110
+ return invoke("query", { connection: __classPrivateFieldGet(this, _Database_connection, "f"), sql: s, params: p }).then((rows) => {
111
+ const arr = rows;
112
+ return arr.length > 0 ? arr[0] : undefined;
113
+ });
114
+ }
115
+ /**
116
+ * Get all rows.
117
+ * @example db.all("SELECT * FROM t")
118
+ */
119
+ all(sql, ...params) {
120
+ const { sql: s, params: p } = normalizeParams(sql, ...params);
121
+ return invoke("query", { connection: __classPrivateFieldGet(this, _Database_connection, "f"), sql: s, params: p });
122
+ }
123
+ /**
124
+ * Execute SQL without params (DDL, PRAGMAs, multiple statements).
125
+ * @example db.exec("CREATE TABLE t(id INTEGER PRIMARY KEY)")
126
+ */
127
+ exec(sql) {
128
+ return invoke("exec", { connection: __classPrivateFieldGet(this, _Database_connection, "f"), sql });
129
+ }
130
+ /**
131
+ * Close the database connection.
132
+ */
133
+ close() {
134
+ return invoke("close", { connection: __classPrivateFieldGet(this, _Database_connection, "f") });
135
+ }
136
+ /**
137
+ * Run multiple statements in a transaction. Rolls back on error.
138
+ */
139
+ transaction(ops) {
140
+ return invoke("transaction", {
141
+ connection: __classPrivateFieldGet(this, _Database_connection, "f"),
142
+ ops,
143
+ });
144
+ }
145
+ }
146
+ exports.Database = Database;
147
+ _Database_connection = new WeakMap();
148
+ /**
149
+ * Open a database. Returns a Database instance (classic sqlite3 interface).
150
+ * @param pathOrOptions - Path string (e.g. ":memory:" or "/path/to/db.sqlite") or options object
151
+ * @param options - Optional options when path is a string
152
+ * @example const db = await open(":memory:")
153
+ * @example const db = await open({ path: "/tmp/app.db", readonly: false })
154
+ */
155
+ async function open(pathOrOptions, options) {
156
+ const params = normalizeOpenArg(pathOrOptions, options);
157
+ const connection = (await invoke("open", params));
158
+ return new Database(connection);
159
+ }
160
+ /**
161
+ * Shutdown the SQLite extension process. Call after closing all databases.
162
+ */
163
+ async function shutdown() {
164
+ return invoke("shutdown", {});
165
+ }
166
+ exports.default = {
167
+ open,
168
+ Database,
169
+ shutdown,
170
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "neutralinojs-ext-sqlite3",
3
+ "version": "0.0.1-alpha.2",
4
+ "description": "Neutralino extension providing SQLite with vector indexing",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "scripts/postinstall.js",
10
+ "rust",
11
+ ".cargo",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "optionalDependencies": {
16
+ "neutralinojs-ext-sqlite3-darwin-arm64": "0.0.1-alpha.2",
17
+ "neutralinojs-ext-sqlite3-darwin-x64": "0.0.1-alpha.2",
18
+ "neutralinojs-ext-sqlite3-linux-arm64": "0.0.1-alpha.2",
19
+ "neutralinojs-ext-sqlite3-linux-x64": "0.0.1-alpha.2",
20
+ "neutralinojs-ext-sqlite3-win32-x64": "0.0.1-alpha.2"
21
+ },
22
+ "scripts": {
23
+ "clean": "rimraf dist build/rust tests/integration/tmp tests/e2e/app/bin tests/e2e/app/dist tests/e2e/app/extensions tests/e2e/app/resources/js tests/e2e/app/tmp tests/e2e/app/.storage tests/e2e/app/*.log",
24
+ "typecheck": "npx tsc --noEmit -p tsconfig.build.json",
25
+ "build": "npm run clean && npm run typecheck && npm run build:ts && npm run build:native",
26
+ "build:ts": "npx tsc -p tsconfig.build.json",
27
+ "build:native": "cargo build --release",
28
+ "package:platform": "node scripts/package.js",
29
+ "prepublishOnly": "npm run build",
30
+ "postinstall": "node scripts/postinstall.js",
31
+ "test": "vitest run tests/integration",
32
+ "test:e2e": "vitest run tests/e2e --testTimeout=90000",
33
+ "version:all": "node scripts/version-all.js",
34
+ "setup:config": "node scripts/postinstall.js"
35
+ },
36
+ "license": "MIT",
37
+ "devDependencies": {
38
+ "@types/adm-zip": "^0.5.7",
39
+ "@types/sqlite3": "^3.1.11",
40
+ "@types/ws": "^8.18.1",
41
+ "adm-zip": "^0.5.16",
42
+ "rimraf": "^6.1.3",
43
+ "sqlite3": "^5.1.7",
44
+ "tar": "^7.5.7",
45
+ "tsx": "^4.19.2",
46
+ "typescript": "^5.9.3",
47
+ "vitest": "^0.34.6",
48
+ "ws": "^8.19.0"
49
+ },
50
+ "dependencies": {
51
+ "jsonc-parser": "^3.3.1"
52
+ }
53
+ }