@mark1russell7/docker-sqlite 0.0.0-stage → 0.1.1
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/CHANGELOG.md +34 -0
- package/LICENSE +21 -0
- package/README.md +1063 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/connect.d.ts +51 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +220 -0
- package/dist/connect.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/migrate.d.ts +42 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +254 -0
- package/dist/migrate.js.map +1 -0
- package/dist/query.d.ts +52 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +120 -0
- package/dist/query.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/docker-compose.yml +31 -0
- package/package.json +77 -3
- package/schema/logs.sql +21 -0
- package/src/connect.ts +245 -0
- package/src/index.ts +61 -0
- package/src/migrate.ts +293 -0
- package/src/query.ts +146 -0
- package/src/types.ts +62 -0
package/dist/migrate.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration utilities for SQLite
|
|
3
|
+
*/
|
|
4
|
+
import { query, execute, execMultiple } from "./query.js";
|
|
5
|
+
/**
|
|
6
|
+
* Schema for tracking migrations
|
|
7
|
+
*/
|
|
8
|
+
const MIGRATIONS_TABLE_SQL = `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
10
|
+
version INTEGER PRIMARY KEY,
|
|
11
|
+
description TEXT NOT NULL,
|
|
12
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
13
|
+
)
|
|
14
|
+
`;
|
|
15
|
+
/**
|
|
16
|
+
* Ensure the migrations table exists
|
|
17
|
+
*/
|
|
18
|
+
function ensureMigrationsTable(db) {
|
|
19
|
+
execMultiple(db, MIGRATIONS_TABLE_SQL);
|
|
20
|
+
}
|
|
21
|
+
/** The name of the savepoint that holds one migration and its record. */
|
|
22
|
+
const SAVEPOINT = "docker_sqlite_migration";
|
|
23
|
+
/**
|
|
24
|
+
* The first words of the statements that control transactions, cannot run in a transaction
|
|
25
|
+
* (VACUUM, ATTACH) or can do nothing there (PRAGMA foreign_keys).
|
|
26
|
+
*/
|
|
27
|
+
const OUTSIDE_A_TRANSACTION = new Set([
|
|
28
|
+
"BEGIN",
|
|
29
|
+
"COMMIT",
|
|
30
|
+
"END",
|
|
31
|
+
"ROLLBACK",
|
|
32
|
+
"SAVEPOINT",
|
|
33
|
+
"RELEASE",
|
|
34
|
+
"VACUUM",
|
|
35
|
+
"ATTACH",
|
|
36
|
+
"DETACH",
|
|
37
|
+
"PRAGMA",
|
|
38
|
+
]);
|
|
39
|
+
/**
|
|
40
|
+
* The tokens of SQL text: the words in upper case, ";" for each semicolon, and "?" for each
|
|
41
|
+
* other token. Comments are skipped. A string or a quoted name is one "?" token, so a word in it
|
|
42
|
+
* is not a keyword.
|
|
43
|
+
*/
|
|
44
|
+
function* tokens(sql) {
|
|
45
|
+
const word = /[\p{L}_][\p{L}\p{N}_$]*/uy;
|
|
46
|
+
let i = 0;
|
|
47
|
+
while (i < sql.length) {
|
|
48
|
+
const c = sql[i];
|
|
49
|
+
if (/\s/.test(c)) {
|
|
50
|
+
i++;
|
|
51
|
+
}
|
|
52
|
+
else if (c === "-" && sql[i + 1] === "-") {
|
|
53
|
+
const end = sql.indexOf("\n", i);
|
|
54
|
+
i = end < 0 ? sql.length : end + 1;
|
|
55
|
+
}
|
|
56
|
+
else if (c === "/" && sql[i + 1] === "*") {
|
|
57
|
+
const end = sql.indexOf("*/", i + 2);
|
|
58
|
+
i = end < 0 ? sql.length : end + 2;
|
|
59
|
+
}
|
|
60
|
+
else if (c === "'" || c === '"' || c === "`" || c === "[") {
|
|
61
|
+
// A quote in the text is two quotes. A name in brackets cannot hold "]".
|
|
62
|
+
const close = c === "[" ? "]" : c;
|
|
63
|
+
let end = sql.indexOf(close, i + 1);
|
|
64
|
+
while (end >= 0 && close !== "]" && sql[end + 1] === close) {
|
|
65
|
+
end = sql.indexOf(close, end + 2);
|
|
66
|
+
}
|
|
67
|
+
i = end < 0 ? sql.length : end + 1;
|
|
68
|
+
yield "?";
|
|
69
|
+
}
|
|
70
|
+
else if (c === ";") {
|
|
71
|
+
i++;
|
|
72
|
+
yield ";";
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
word.lastIndex = i;
|
|
76
|
+
const match = word.exec(sql);
|
|
77
|
+
if (match) {
|
|
78
|
+
i += match[0].length;
|
|
79
|
+
yield match[0].toUpperCase();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
i++;
|
|
83
|
+
yield "?";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The first word of each statement of `sql`, in upper case. The statements in the body of a
|
|
90
|
+
* trigger are part of the trigger: like sqlite3_complete(), the trigger ends at "; END ;".
|
|
91
|
+
*/
|
|
92
|
+
function statementKeywords(sql) {
|
|
93
|
+
const keywords = [];
|
|
94
|
+
let words = [];
|
|
95
|
+
let trigger = false;
|
|
96
|
+
let afterSemicolon = false;
|
|
97
|
+
let afterEnd = false;
|
|
98
|
+
for (const token of tokens(sql)) {
|
|
99
|
+
if (token === ";") {
|
|
100
|
+
if (!trigger || afterEnd) {
|
|
101
|
+
words = [];
|
|
102
|
+
trigger = false;
|
|
103
|
+
afterEnd = false;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
afterSemicolon = true;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (words.length === 0) {
|
|
111
|
+
keywords.push(token);
|
|
112
|
+
}
|
|
113
|
+
if (words.length < 3) {
|
|
114
|
+
words.push(token);
|
|
115
|
+
const [first, second, third] = words;
|
|
116
|
+
trigger ||=
|
|
117
|
+
first === "CREATE" &&
|
|
118
|
+
(second === "TRIGGER" || ((second === "TEMP" || second === "TEMPORARY") && third === "TRIGGER"));
|
|
119
|
+
}
|
|
120
|
+
afterEnd = trigger && afterSemicolon && token === "END";
|
|
121
|
+
afterSemicolon = false;
|
|
122
|
+
}
|
|
123
|
+
return keywords;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Run `work` in a savepoint: its changes stay only when it succeeds. Before, a migration that
|
|
127
|
+
* failed after its first statement kept the changes of those statements, and was not recorded:
|
|
128
|
+
* the next run ran it again and failed ("table already exists").
|
|
129
|
+
*
|
|
130
|
+
* A savepoint also works in a transaction of the caller, and keeps that transaction open. SQL
|
|
131
|
+
* that controls transactions itself (BEGIN, COMMIT, VACUUM, PRAGMA and the others in
|
|
132
|
+
* OUTSIDE_A_TRANSACTION) runs without a savepoint, as before: in a transaction it fails or does
|
|
133
|
+
* nothing.
|
|
134
|
+
*/
|
|
135
|
+
function atomically(db, sql, work) {
|
|
136
|
+
if (statementKeywords(sql).some((keyword) => OUTSIDE_A_TRANSACTION.has(keyword))) {
|
|
137
|
+
work();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
db.exec(`SAVEPOINT ${SAVEPOINT}`);
|
|
141
|
+
try {
|
|
142
|
+
work();
|
|
143
|
+
db.exec(`RELEASE ${SAVEPOINT}`);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
try {
|
|
147
|
+
db.exec(`ROLLBACK TO ${SAVEPOINT}`);
|
|
148
|
+
db.exec(`RELEASE ${SAVEPOINT}`);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Some errors end the transaction (ON CONFLICT ROLLBACK): then the savepoint is gone
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get the current migration status
|
|
158
|
+
*/
|
|
159
|
+
export function getMigrationStatus(db, migrations) {
|
|
160
|
+
ensureMigrationsTable(db);
|
|
161
|
+
const applied = query(db, "SELECT version FROM _migrations ORDER BY version");
|
|
162
|
+
const appliedVersions = new Set(applied.rows.map((r) => r.version));
|
|
163
|
+
const appliedMigrations = [...appliedVersions].sort((a, b) => a - b);
|
|
164
|
+
const currentVersion = appliedMigrations.length > 0
|
|
165
|
+
? appliedMigrations[appliedMigrations.length - 1]
|
|
166
|
+
: 0;
|
|
167
|
+
const pendingMigrations = migrations
|
|
168
|
+
.filter((m) => !appliedVersions.has(m.version))
|
|
169
|
+
.sort((a, b) => a.version - b.version);
|
|
170
|
+
return {
|
|
171
|
+
currentVersion,
|
|
172
|
+
appliedMigrations,
|
|
173
|
+
pendingMigrations,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Run all pending migrations
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* const migrations: Migration[] = [
|
|
182
|
+
* {
|
|
183
|
+
* version: 1,
|
|
184
|
+
* description: 'Create users table',
|
|
185
|
+
* up: 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)',
|
|
186
|
+
* down: 'DROP TABLE users'
|
|
187
|
+
* },
|
|
188
|
+
* {
|
|
189
|
+
* version: 2,
|
|
190
|
+
* description: 'Add email column',
|
|
191
|
+
* up: 'ALTER TABLE users ADD COLUMN email TEXT',
|
|
192
|
+
* }
|
|
193
|
+
* ];
|
|
194
|
+
*
|
|
195
|
+
* const applied = runMigrations(db, migrations);
|
|
196
|
+
* console.log(`Applied ${applied.length} migrations`);
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
export function runMigrations(db, migrations) {
|
|
200
|
+
const status = getMigrationStatus(db, migrations);
|
|
201
|
+
const applied = [];
|
|
202
|
+
for (const migration of status.pendingMigrations) {
|
|
203
|
+
try {
|
|
204
|
+
// Run the migration and record it as applied, in one savepoint
|
|
205
|
+
atomically(db, migration.up, () => {
|
|
206
|
+
execMultiple(db, migration.up);
|
|
207
|
+
execute(db, "INSERT INTO _migrations (version, description) VALUES (?, ?)", [migration.version, migration.description]);
|
|
208
|
+
});
|
|
209
|
+
applied.push(migration);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
throw new Error(`Failed to apply migration ${migration.version} (${migration.description}): ${error instanceof Error ? error.message : String(error)}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return applied;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Rollback the last applied migration
|
|
219
|
+
*/
|
|
220
|
+
export function rollbackMigration(db, migrations) {
|
|
221
|
+
const status = getMigrationStatus(db, migrations);
|
|
222
|
+
if (status.currentVersion === 0) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const migration = migrations.find((m) => m.version === status.currentVersion);
|
|
226
|
+
if (!migration) {
|
|
227
|
+
throw new Error(`Migration ${status.currentVersion} not found in migrations list`);
|
|
228
|
+
}
|
|
229
|
+
if (!migration.down) {
|
|
230
|
+
throw new Error(`Migration ${migration.version} (${migration.description}) has no down migration`);
|
|
231
|
+
}
|
|
232
|
+
const down = migration.down;
|
|
233
|
+
try {
|
|
234
|
+
atomically(db, down, () => {
|
|
235
|
+
execMultiple(db, down);
|
|
236
|
+
execute(db, "DELETE FROM _migrations WHERE version = ?", [migration.version]);
|
|
237
|
+
});
|
|
238
|
+
return migration;
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
throw new Error(`Failed to rollback migration ${migration.version}: ${error instanceof Error ? error.message : String(error)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Create a migration from SQL strings
|
|
246
|
+
*/
|
|
247
|
+
export function createMigration(version, description, up, down) {
|
|
248
|
+
const migration = { version, description, up };
|
|
249
|
+
if (down !== undefined) {
|
|
250
|
+
migration.down = down;
|
|
251
|
+
}
|
|
252
|
+
return migration;
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query helper utilities for SQLite
|
|
3
|
+
*/
|
|
4
|
+
import type { Database } from "sql.js";
|
|
5
|
+
import type { QueryResult, ExecuteResult } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Execute a SELECT query and return results
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const result = query(db, 'SELECT * FROM users WHERE age > ?', [21]);
|
|
12
|
+
* console.log(result.columns); // ['id', 'name', 'age']
|
|
13
|
+
* console.log(result.rows); // [{ id: 1, name: 'Alice', age: 25 }, ...]
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function query<T = Record<string, unknown>>(db: Database, sql: string, params?: unknown[]): QueryResult<T>;
|
|
17
|
+
/**
|
|
18
|
+
* Execute an INSERT, UPDATE, DELETE, or other statement
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const result = execute(db, 'INSERT INTO users (name, age) VALUES (?, ?)', ['Bob', 30]);
|
|
23
|
+
* console.log(result.changes); // 1
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function execute(db: Database, sql: string, params?: unknown[]): ExecuteResult;
|
|
27
|
+
/**
|
|
28
|
+
* True when `sql` holds more than one statement. Nothing runs: the function only prepares the
|
|
29
|
+
* first statement and examines the SQL after it.
|
|
30
|
+
*/
|
|
31
|
+
export declare function hasSeveralStatements(db: Database, sql: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Execute multiple statements (for schema setup, etc.)
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* execMultiple(db, `
|
|
38
|
+
* CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT);
|
|
39
|
+
* CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY, user_id INTEGER);
|
|
40
|
+
* `);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function execMultiple(db: Database, sql: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get the last inserted row ID
|
|
46
|
+
*/
|
|
47
|
+
export declare function lastInsertRowId(db: Database): number;
|
|
48
|
+
/**
|
|
49
|
+
* Check if a table exists
|
|
50
|
+
*/
|
|
51
|
+
export declare function tableExists(db: Database, tableName: string): boolean;
|
|
52
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE7D;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/C,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,GACrB,WAAW,CAAC,CAAC,CAAC,CAmBhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CACrB,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,GACrB,aAAa,CAiBf;AAiBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAiBvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAGpD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAOpE"}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query helper utilities for SQLite
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Execute a SELECT query and return results
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const result = query(db, 'SELECT * FROM users WHERE age > ?', [21]);
|
|
10
|
+
* console.log(result.columns); // ['id', 'name', 'age']
|
|
11
|
+
* console.log(result.rows); // [{ id: 1, name: 'Alice', age: 25 }, ...]
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function query(db, sql, params = []) {
|
|
15
|
+
const stmt = db.prepare(sql);
|
|
16
|
+
try {
|
|
17
|
+
stmt.bind(params);
|
|
18
|
+
// The statement knows its columns before the first step. Before, the columns came from the
|
|
19
|
+
// first row, so a result with no rows had no columns.
|
|
20
|
+
const columns = stmt.getColumnNames();
|
|
21
|
+
const rows = [];
|
|
22
|
+
while (stmt.step()) {
|
|
23
|
+
rows.push(stmt.getAsObject());
|
|
24
|
+
}
|
|
25
|
+
return { columns, rows };
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
stmt.free();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Execute an INSERT, UPDATE, DELETE, or other statement
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const result = execute(db, 'INSERT INTO users (name, age) VALUES (?, ?)', ['Bob', 30]);
|
|
37
|
+
* console.log(result.changes); // 1
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function execute(db, sql, params = []) {
|
|
41
|
+
// With parameters, sql.js runs only the first statement. Before, the other statements were
|
|
42
|
+
// ignored and the call reported success. Now several statements run with no parameters, and
|
|
43
|
+
// are an error with parameters.
|
|
44
|
+
if (hasSeveralStatements(db, sql)) {
|
|
45
|
+
if (params.length > 0) {
|
|
46
|
+
throw new Error("execute() binds parameters to one statement, and the SQL has several. Send one statement per call.");
|
|
47
|
+
}
|
|
48
|
+
const before = totalChanges(db);
|
|
49
|
+
db.exec(sql);
|
|
50
|
+
return { changes: totalChanges(db) - before };
|
|
51
|
+
}
|
|
52
|
+
db.run(sql, params);
|
|
53
|
+
const changes = db.getRowsModified();
|
|
54
|
+
return { changes };
|
|
55
|
+
}
|
|
56
|
+
/** The number of rows that the statements of this connection changed. */
|
|
57
|
+
function totalChanges(db) {
|
|
58
|
+
const result = db.exec("SELECT total_changes()");
|
|
59
|
+
return Number(result[0]?.values[0]?.[0] ?? 0);
|
|
60
|
+
}
|
|
61
|
+
/** True when the text holds SQL other than spaces, ";" and comments. */
|
|
62
|
+
function holdsStatement(sql) {
|
|
63
|
+
return sql
|
|
64
|
+
.replace(/--[^\n]*/g, "")
|
|
65
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
66
|
+
.replace(/[\s;]/g, "")
|
|
67
|
+
.length > 0;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* True when `sql` holds more than one statement. Nothing runs: the function only prepares the
|
|
71
|
+
* first statement and examines the SQL after it.
|
|
72
|
+
*/
|
|
73
|
+
export function hasSeveralStatements(db, sql) {
|
|
74
|
+
const iterator = db.iterateStatements(sql);
|
|
75
|
+
const first = iterator.next();
|
|
76
|
+
if (first.done) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const rest = iterator.getRemainingSQL();
|
|
80
|
+
// Read the iterator to its end: this frees the statement and the copy of the SQL. A later
|
|
81
|
+
// statement can fail to prepare (its table does not exist yet), and that also ends it.
|
|
82
|
+
try {
|
|
83
|
+
while (!iterator.next().done) {
|
|
84
|
+
// Prepare only
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// The iterator ended
|
|
89
|
+
}
|
|
90
|
+
return holdsStatement(rest);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Execute multiple statements (for schema setup, etc.)
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* execMultiple(db, `
|
|
98
|
+
* CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT);
|
|
99
|
+
* CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY, user_id INTEGER);
|
|
100
|
+
* `);
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export function execMultiple(db, sql) {
|
|
104
|
+
db.exec(sql);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get the last inserted row ID
|
|
108
|
+
*/
|
|
109
|
+
export function lastInsertRowId(db) {
|
|
110
|
+
const result = query(db, "SELECT last_insert_rowid() as id");
|
|
111
|
+
return result.rows[0]?.id ?? 0;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if a table exists
|
|
115
|
+
*/
|
|
116
|
+
export function tableExists(db, tableName) {
|
|
117
|
+
const result = query(db, "SELECT name FROM sqlite_master WHERE type='table' AND name=?", [tableName]);
|
|
118
|
+
return result.rows.length > 0;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for docker-sqlite
|
|
3
|
+
*/
|
|
4
|
+
import type { Database } from "sql.js";
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for SQLite connection
|
|
7
|
+
*/
|
|
8
|
+
export interface ConnectionConfig {
|
|
9
|
+
/** Path to the SQLite database file. Use ":memory:" for in-memory database */
|
|
10
|
+
dbPath: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Callback function for withConnection
|
|
14
|
+
*/
|
|
15
|
+
export type ConnectionCallback<T> = (db: Database) => T | Promise<T>;
|
|
16
|
+
/**
|
|
17
|
+
* Result of a query operation
|
|
18
|
+
*/
|
|
19
|
+
export interface QueryResult<T = Record<string, unknown>> {
|
|
20
|
+
/** Column names */
|
|
21
|
+
columns: string[];
|
|
22
|
+
/** Row values */
|
|
23
|
+
rows: T[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Result of an execute operation (INSERT, UPDATE, DELETE)
|
|
27
|
+
*/
|
|
28
|
+
export interface ExecuteResult {
|
|
29
|
+
/** Number of rows affected */
|
|
30
|
+
changes: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Migration definition
|
|
34
|
+
*/
|
|
35
|
+
export interface Migration {
|
|
36
|
+
/** Migration version (must be unique and sequential) */
|
|
37
|
+
version: number;
|
|
38
|
+
/** Description of the migration */
|
|
39
|
+
description: string;
|
|
40
|
+
/** SQL to apply the migration */
|
|
41
|
+
up: string;
|
|
42
|
+
/** SQL to revert the migration (optional) */
|
|
43
|
+
down?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Migration status
|
|
47
|
+
*/
|
|
48
|
+
export interface MigrationStatus {
|
|
49
|
+
/** Current version (0 if no migrations applied) */
|
|
50
|
+
currentVersion: number;
|
|
51
|
+
/** List of applied migrations */
|
|
52
|
+
appliedMigrations: number[];
|
|
53
|
+
/** List of pending migrations */
|
|
54
|
+
pendingMigrations: Migration[];
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtD,mBAAmB;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB;IACjB,IAAI,EAAE,CAAC,EAAE,CAAC;CACX;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,iCAAiC;IACjC,iBAAiB,EAAE,SAAS,EAAE,CAAC;CAChC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# SQLite Docker Compose Configuration
|
|
2
|
+
#
|
|
3
|
+
# Provides a web UI for browsing SQLite databases.
|
|
4
|
+
#
|
|
5
|
+
# Environment variables (with defaults):
|
|
6
|
+
# SQLITE_CONTAINER_NAME - Container name (default: sqlite-web)
|
|
7
|
+
# SQLITE_PORT - Host port for web UI (default: 8080)
|
|
8
|
+
# SQLITE_DB_PATH - Path to database directory (default: ./data)
|
|
9
|
+
# SQLITE_DB_NAME - Database filename (default: cli.db)
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# docker compose up -d
|
|
13
|
+
# docker compose down
|
|
14
|
+
# Open http://localhost:8080 to browse the database
|
|
15
|
+
|
|
16
|
+
services:
|
|
17
|
+
sqlite-web:
|
|
18
|
+
image: coleifer/sqlite-web
|
|
19
|
+
container_name: ${SQLITE_CONTAINER_NAME:-sqlite-web}
|
|
20
|
+
ports:
|
|
21
|
+
# Loopback only: the sqlite-web UI has full read/write access and no authentication
|
|
22
|
+
- "127.0.0.1:${SQLITE_PORT:-8080}:8080"
|
|
23
|
+
volumes:
|
|
24
|
+
- ${SQLITE_DB_PATH:-./data}:/data:rw
|
|
25
|
+
command: sqlite_web -H 0.0.0.0 /data/${SQLITE_DB_NAME:-cli.db}
|
|
26
|
+
restart: unless-stopped
|
|
27
|
+
healthcheck:
|
|
28
|
+
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/"]
|
|
29
|
+
interval: 30s
|
|
30
|
+
timeout: 10s
|
|
31
|
+
retries: 3
|
package/package.json
CHANGED
|
@@ -1,6 +1,80 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package",
|
|
2
3
|
"name": "@mark1russell7/docker-sqlite",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
4
|
+
"version": "0.1.1",
|
|
5
|
+
"description": "SQLite for Node.js with no native build: sql.js (SQLite in WebAssembly) with safe file access (one call at a time per file, atomic saves, no write on read), queries, migrations and an optional Docker Compose web UI.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"sqlite",
|
|
8
|
+
"sqlite3",
|
|
9
|
+
"sql.js",
|
|
10
|
+
"wasm",
|
|
11
|
+
"webassembly",
|
|
12
|
+
"database",
|
|
13
|
+
"migrations",
|
|
14
|
+
"schema-migrations",
|
|
15
|
+
"query",
|
|
16
|
+
"embedded-database",
|
|
17
|
+
"docker",
|
|
18
|
+
"docker-compose",
|
|
19
|
+
"sqlite-web",
|
|
20
|
+
"typescript",
|
|
21
|
+
"node"
|
|
22
|
+
],
|
|
23
|
+
"homepage": "https://github.com/mark1russell7/docker-sqlite#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/mark1russell7/docker-sqlite/issues"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/mark1russell7/docker-sqlite.git"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"author": "Mark Russell (https://github.com/mark1russell7)",
|
|
33
|
+
"type": "module",
|
|
34
|
+
"main": "./dist/index.js",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"import": "./dist/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./docker-compose.yml": "./docker-compose.yml",
|
|
42
|
+
"./schema/*": "./schema/*",
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"src",
|
|
48
|
+
"!dist/**/*.test.*",
|
|
49
|
+
"!src/**/*.test.ts",
|
|
50
|
+
"schema",
|
|
51
|
+
"docker-compose.yml",
|
|
52
|
+
"README.md",
|
|
53
|
+
"CHANGELOG.md",
|
|
54
|
+
"LICENSE"
|
|
55
|
+
],
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@types/sql.js": "^1.4.11",
|
|
58
|
+
"sql.js": "^1.11.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@mark1russell7/cue": "github:mark1russell7/cue#main",
|
|
62
|
+
"@types/node": "^22.0.0",
|
|
63
|
+
"typescript": "^5.9.3",
|
|
64
|
+
"vitest": "^4.1.11"
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=22"
|
|
68
|
+
},
|
|
69
|
+
"sideEffects": false,
|
|
70
|
+
"publishConfig": {
|
|
71
|
+
"access": "public",
|
|
72
|
+
"provenance": true
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "tsc -b",
|
|
76
|
+
"typecheck": "tsc --noEmit",
|
|
77
|
+
"clean": "rm -rf dist .tsbuildinfo",
|
|
78
|
+
"test": "vitest run"
|
|
79
|
+
}
|
|
6
80
|
}
|
package/schema/logs.sql
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Log table schema for CLI logging
|
|
2
|
+
-- This is a reference schema, not applied automatically
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
|
7
|
+
level TEXT NOT NULL CHECK (level IN ('trace', 'debug', 'info', 'warn', 'error')),
|
|
8
|
+
message TEXT NOT NULL,
|
|
9
|
+
data TEXT, -- JSON serialized additional data
|
|
10
|
+
session_id TEXT, -- Groups logs by CLI session
|
|
11
|
+
command TEXT, -- CLI command that generated the log (e.g., 'lib.refresh')
|
|
12
|
+
context TEXT, -- Additional context (e.g., package name)
|
|
13
|
+
error_stack TEXT, -- Stack trace if error
|
|
14
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- Indexes for common queries
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_logs_session ON logs(session_id);
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level);
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON logs(timestamp);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_logs_command ON logs(command);
|