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.
- package/.cargo/config.toml +2 -0
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +170 -0
- package/package.json +53 -0
- package/rust/plugin/Cargo.lock +847 -0
- package/rust/plugin/Cargo.toml +19 -0
- package/rust/plugin/rustfmt.toml +3 -0
- package/rust/plugin/src/cli.rs +161 -0
- package/rust/plugin/src/db.rs +392 -0
- package/rust/plugin/src/main.rs +309 -0
- package/rust/plugin/src/rpc.rs +420 -0
- package/rust/sqlite-test-extensions/fibonacci-extension/Cargo.toml +7 -0
- package/rust/sqlite-test-extensions/fibonacci-extension/src/lib.rs +139 -0
- package/scripts/postinstall.js +62 -0
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.
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|