@procedures/sqlite 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +126 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/module.d.ts +2 -1
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +93 -9
- package/dist/module.js.map +1 -1
- package/dist/procedures/db/backup.d.ts +19 -0
- package/dist/procedures/db/backup.d.ts.map +1 -0
- package/dist/procedures/db/backup.js +61 -0
- package/dist/procedures/db/backup.js.map +1 -0
- package/dist/procedures/db/index.d.ts +6 -0
- package/dist/procedures/db/index.d.ts.map +1 -0
- package/dist/procedures/db/index.js +6 -0
- package/dist/procedures/db/index.js.map +1 -0
- package/dist/procedures/db/migrate.d.ts +24 -0
- package/dist/procedures/db/migrate.d.ts.map +1 -0
- package/dist/procedures/db/migrate.js +110 -0
- package/dist/procedures/db/migrate.js.map +1 -0
- package/dist/procedures/db/schema.d.ts +13 -0
- package/dist/procedures/db/schema.d.ts.map +1 -0
- package/dist/procedures/db/schema.js +33 -0
- package/dist/procedures/db/schema.js.map +1 -0
- package/dist/procedures/db/shared.d.ts +25 -0
- package/dist/procedures/db/shared.d.ts.map +1 -0
- package/dist/procedures/db/shared.js +105 -0
- package/dist/procedures/db/shared.js.map +1 -0
- package/dist/procedures/db/tables.d.ts +12 -0
- package/dist/procedures/db/tables.d.ts.map +1 -0
- package/dist/procedures/db/tables.js +22 -0
- package/dist/procedures/db/tables.js.map +1 -0
- package/dist/procedures/db/transaction.d.ts +14 -0
- package/dist/procedures/db/transaction.d.ts.map +1 -0
- package/dist/procedures/db/transaction.js +44 -0
- package/dist/procedures/db/transaction.js.map +1 -0
- package/dist/storage.d.ts +123 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +525 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +109 -0
- package/dist/types.js.map +1 -1
- package/package.json +9 -3
- package/src/index.ts +26 -0
- package/src/module.ts +120 -9
- package/src/procedures/db/backup.ts +62 -0
- package/src/procedures/db/index.ts +5 -0
- package/src/procedures/db/migrate.ts +121 -0
- package/src/procedures/db/schema.ts +71 -0
- package/src/procedures/db/shared.ts +109 -0
- package/src/procedures/db/tables.ts +27 -0
- package/src/procedures/db/transaction.ts +43 -0
- package/src/storage.ts +591 -0
- package/src/types.ts +268 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The helpers of the db.* procedures of roadmap R44.
|
|
3
|
+
*/
|
|
4
|
+
import type { Database } from "@mark1russell7/docker-sqlite";
|
|
5
|
+
/** The database file of an input: `dbPath`, or the CLI log database. */
|
|
6
|
+
export declare function databasePath(dbPath: string | undefined): string;
|
|
7
|
+
/**
|
|
8
|
+
* This function throws ENOENT for a database file that does not exist. A procedure that only
|
|
9
|
+
* reads uses it: sql.js opens a missing file as an empty database, so a wrong path gave no tables.
|
|
10
|
+
*/
|
|
11
|
+
export declare function requireDatabaseFile(procedure: string, path: string): void;
|
|
12
|
+
/** True for a statement that starts, ends or names a transaction (BEGIN, COMMIT, SAVEPOINT, ...). */
|
|
13
|
+
export declare function controlsTransaction(sql: string): boolean;
|
|
14
|
+
/** The result of one statement: the rows that it changed, and its rows when it gives rows. */
|
|
15
|
+
export interface StatementRun {
|
|
16
|
+
changes: number;
|
|
17
|
+
rows?: Record<string, unknown>[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* This function runs the statements of one SQL text in order, and gives one result for the text.
|
|
21
|
+
* The params bind to the first statement, so a text with params must hold one statement. A
|
|
22
|
+
* statement that controls a transaction is refused: the caller owns the transaction.
|
|
23
|
+
*/
|
|
24
|
+
export declare function runStatements(db: Database, sql: string, params: unknown[], label: string): StatementRun;
|
|
25
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/procedures/db/shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAG7D,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAKzE;AAsBD,qGAAqG;AACrG,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,8FAA8F;AAC9F,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CAClC;AAMD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CA4CvG"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The helpers of the db.* procedures of roadmap R44.
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { DEFAULT_DB_PATH } from "../../types.js";
|
|
6
|
+
/** The database file of an input: `dbPath`, or the CLI log database. */
|
|
7
|
+
export function databasePath(dbPath) {
|
|
8
|
+
return dbPath ?? DEFAULT_DB_PATH;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* This function throws ENOENT for a database file that does not exist. A procedure that only
|
|
12
|
+
* reads uses it: sql.js opens a missing file as an empty database, so a wrong path gave no tables.
|
|
13
|
+
*/
|
|
14
|
+
export function requireDatabaseFile(procedure, path) {
|
|
15
|
+
if (path === ":memory:" || existsSync(path))
|
|
16
|
+
return;
|
|
17
|
+
const error = new Error(`${procedure}: the database file does not exist: ${path}`);
|
|
18
|
+
error.code = "ENOENT";
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
/** The statements that control a transaction, which the procedures run themselves. */
|
|
22
|
+
const TRANSACTION_CONTROL = /^(BEGIN|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i;
|
|
23
|
+
/** The SQL of a statement without its leading spaces and comments. */
|
|
24
|
+
function withoutLeadingComments(sql) {
|
|
25
|
+
let rest = sql;
|
|
26
|
+
for (;;) {
|
|
27
|
+
const trimmed = rest.replace(/^\s+/, "");
|
|
28
|
+
if (trimmed.startsWith("--")) {
|
|
29
|
+
const end = trimmed.indexOf("\n");
|
|
30
|
+
rest = end === -1 ? "" : trimmed.slice(end + 1);
|
|
31
|
+
}
|
|
32
|
+
else if (trimmed.startsWith("/*")) {
|
|
33
|
+
const end = trimmed.indexOf("*/");
|
|
34
|
+
rest = end === -1 ? "" : trimmed.slice(end + 2);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return trimmed;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** True for a statement that starts, ends or names a transaction (BEGIN, COMMIT, SAVEPOINT, ...). */
|
|
42
|
+
export function controlsTransaction(sql) {
|
|
43
|
+
return TRANSACTION_CONTROL.test(withoutLeadingComments(sql));
|
|
44
|
+
}
|
|
45
|
+
function totalChanges(db) {
|
|
46
|
+
return Number(db.exec("SELECT total_changes()")[0]?.values[0]?.[0] ?? 0);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* This function runs the statements of one SQL text in order, and gives one result for the text.
|
|
50
|
+
* The params bind to the first statement, so a text with params must hold one statement. A
|
|
51
|
+
* statement that controls a transaction is refused: the caller owns the transaction.
|
|
52
|
+
*/
|
|
53
|
+
export function runStatements(db, sql, params, label) {
|
|
54
|
+
const before = totalChanges(db);
|
|
55
|
+
const result = { changes: 0 };
|
|
56
|
+
let count = 0;
|
|
57
|
+
const iterator = db.iterateStatements(sql);
|
|
58
|
+
try {
|
|
59
|
+
for (let step = iterator.next(); !step.done; step = iterator.next()) {
|
|
60
|
+
const statement = step.value;
|
|
61
|
+
try {
|
|
62
|
+
count++;
|
|
63
|
+
if (count > 1 && params.length > 0) {
|
|
64
|
+
throw new Error(`${label}: the params bind to one statement, and the SQL has several`);
|
|
65
|
+
}
|
|
66
|
+
if (controlsTransaction(statement.getSQL())) {
|
|
67
|
+
throw new Error(`${label}: the procedure runs the transaction: BEGIN, COMMIT, ROLLBACK, SAVEPOINT and RELEASE are refused`);
|
|
68
|
+
}
|
|
69
|
+
if (count === 1 && params.length > 0)
|
|
70
|
+
statement.bind(params);
|
|
71
|
+
if (statement.getColumnNames().length > 0) {
|
|
72
|
+
const rows = [];
|
|
73
|
+
while (statement.step())
|
|
74
|
+
rows.push(statement.getAsObject());
|
|
75
|
+
result.rows = rows;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
while (statement.step()) {
|
|
79
|
+
// A statement without columns gives no rows: one step runs it
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
statement.free();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
// Read the iterator to its end: that frees its copy of the SQL (it only prepares the rest)
|
|
90
|
+
try {
|
|
91
|
+
while (!iterator.next().done) {
|
|
92
|
+
// Prepare only
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// A later statement did not prepare: the iterator ended
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
if (count === 0)
|
|
101
|
+
throw new Error(`${label}: the SQL has no statement`);
|
|
102
|
+
result.changes = totalChanges(db) - before;
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/procedures/db/shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* db.tables procedure (roadmap R44)
|
|
3
|
+
*
|
|
4
|
+
* The tables and the views of a database, without the internal `sqlite_` tables. The connection
|
|
5
|
+
* is read only (PRAGMA query_only), and a missing file is an error: it does not make one.
|
|
6
|
+
*/
|
|
7
|
+
import type { DbTablesInput, DbTablesOutput } from "../../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* The tables and views of a database
|
|
10
|
+
*/
|
|
11
|
+
export declare function dbTables(input: DbTablesInput): Promise<DbTablesOutput>;
|
|
12
|
+
//# sourceMappingURL=tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../src/procedures/db/tables.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAe,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGjF;;GAEG;AACH,wBAAsB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAY5E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* db.tables procedure (roadmap R44)
|
|
3
|
+
*
|
|
4
|
+
* The tables and the views of a database, without the internal `sqlite_` tables. The connection
|
|
5
|
+
* is read only (PRAGMA query_only), and a missing file is an error: it does not make one.
|
|
6
|
+
*/
|
|
7
|
+
import { query, withConnection } from "@mark1russell7/docker-sqlite";
|
|
8
|
+
import { databasePath, requireDatabaseFile } from "./shared.js";
|
|
9
|
+
/**
|
|
10
|
+
* The tables and views of a database
|
|
11
|
+
*/
|
|
12
|
+
export async function dbTables(input) {
|
|
13
|
+
const path = databasePath(input.dbPath);
|
|
14
|
+
requireDatabaseFile("db.tables", path);
|
|
15
|
+
const types = input.views === false ? "('table')" : "('table', 'view')";
|
|
16
|
+
return withConnection({ dbPath: path }, (db) => {
|
|
17
|
+
db.run("PRAGMA query_only = 1");
|
|
18
|
+
const result = query(db, `SELECT name, type, sql FROM sqlite_master WHERE type IN ${types} AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\' ORDER BY name`);
|
|
19
|
+
return { tables: result.rows.map((row) => ({ name: row.name, type: row.type, sql: row.sql ?? null })) };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../../../src/procedures/db/tables.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEhE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAoB;IACjD,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,mBAAmB,CAAC;IACxE,OAAO,cAAc,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;QAC7C,EAAE,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,KAAK,CAClB,EAAE,EACF,2DAA2D,KAAK,2DAA2D,CAC5H,CAAC;QACF,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IAC1G,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* db.transaction procedure (roadmap R44)
|
|
3
|
+
*
|
|
4
|
+
* The statements of the input run in order in one transaction. When one fails, the procedure
|
|
5
|
+
* rolls back the transaction and does not save the file: the database stays as it was. A
|
|
6
|
+
* statement that gives rows (SELECT, or RETURNING) gives them in its result, so a later
|
|
7
|
+
* statement can follow an earlier one in the same transaction.
|
|
8
|
+
*/
|
|
9
|
+
import type { DbTransactionInput, DbTransactionOutput } from "../../types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Run statements in one transaction
|
|
12
|
+
*/
|
|
13
|
+
export declare function dbTransaction(input: DbTransactionInput): Promise<DbTransactionOutput>;
|
|
14
|
+
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../../src/procedures/db/transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAqB,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGjG;;GAEG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA0B3F"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* db.transaction procedure (roadmap R44)
|
|
3
|
+
*
|
|
4
|
+
* The statements of the input run in order in one transaction. When one fails, the procedure
|
|
5
|
+
* rolls back the transaction and does not save the file: the database stays as it was. A
|
|
6
|
+
* statement that gives rows (SELECT, or RETURNING) gives them in its result, so a later
|
|
7
|
+
* statement can follow an earlier one in the same transaction.
|
|
8
|
+
*/
|
|
9
|
+
import { withConnection } from "@mark1russell7/docker-sqlite";
|
|
10
|
+
import { databasePath, runStatements } from "./shared.js";
|
|
11
|
+
/**
|
|
12
|
+
* Run statements in one transaction
|
|
13
|
+
*/
|
|
14
|
+
export async function dbTransaction(input) {
|
|
15
|
+
return withConnection({ dbPath: databasePath(input.dbPath) }, (db) => {
|
|
16
|
+
db.run("BEGIN");
|
|
17
|
+
const results = [];
|
|
18
|
+
try {
|
|
19
|
+
input.statements.forEach((statement, index) => {
|
|
20
|
+
const label = `db.transaction: statements[${index}]`;
|
|
21
|
+
try {
|
|
22
|
+
results.push(runStatements(db, statement.sql, statement.params ?? [], label));
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
26
|
+
throw new Error(message.startsWith(label) ? message : `${label}: ${message}`);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
db.run("COMMIT");
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
try {
|
|
33
|
+
db.run("ROLLBACK");
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// SQLite ended the transaction already (an ON CONFLICT ROLLBACK)
|
|
37
|
+
}
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
throw new Error(`${message}. The transaction was rolled back.`);
|
|
40
|
+
}
|
|
41
|
+
return { results, changes: results.reduce((sum, result) => sum + result.changes, 0) };
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../src/procedures/db/transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAyB;IAC3D,OAAO,cAAc,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;QACnE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChB,MAAM,OAAO,GAAwB,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE;gBAC5C,MAAM,KAAK,GAAG,8BAA8B,KAAK,GAAG,CAAC;gBACrD,IAAI,CAAC;oBACH,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,MAAM,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;gBAChF,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACvE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC;gBAChF,CAAC;YACH,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,iEAAiE;YACnE,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,oCAAoC,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;IACxF,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A collection storage in SQLite (roadmap R45).
|
|
3
|
+
*
|
|
4
|
+
* `SqliteStorage` keeps the items of one collection in one table of a SQLite database: a file,
|
|
5
|
+
* or `:memory:`. It implements `CollectionStorage` of `@procedures/client`, so a
|
|
6
|
+
* `ProcedureServer`, an `ApiStorage` server and a `HybridStorage` can use it. The engine is
|
|
7
|
+
* sql.js (SQLite in WebAssembly), through `@mark1russell7/docker-sqlite`: no native build.
|
|
8
|
+
*
|
|
9
|
+
* - The table of a collection has the columns `id` (the key) and `value` (the JSON text of the
|
|
10
|
+
* item). A value goes through `JSON.stringify` and `JSON.parse`.
|
|
11
|
+
* - A change log gives `getChangesSince()`. The table `_storage_log` has the last change of each
|
|
12
|
+
* id, and `_storage_versions` has the version of each collection: `<epoch>:<seq>`, as
|
|
13
|
+
* `InMemoryStorage` has. The epoch stays in the file, so a version stays valid after a restart.
|
|
14
|
+
* - Each write runs in a transaction. A batch that fails changes nothing.
|
|
15
|
+
* - The storage keeps the database in memory and writes the file after each write. A write ends
|
|
16
|
+
* after its file write. One file write can hold the changes of several writes.
|
|
17
|
+
* - The storages of one file in one process share one database, so the storages of two tables in
|
|
18
|
+
* one file do not write over the changes of each other.
|
|
19
|
+
*/
|
|
20
|
+
import type { CollectionStorage, StorageChanges, StorageMetadata } from "@procedures/client";
|
|
21
|
+
/** The path of a database that is only in memory. */
|
|
22
|
+
export declare const MEMORY_PATH = ":memory:";
|
|
23
|
+
/** The options of a `SqliteStorage`. */
|
|
24
|
+
export interface SqliteStorageOptions {
|
|
25
|
+
/**
|
|
26
|
+
* The database file, or `:memory:` for a database that lives as long as the storage. The
|
|
27
|
+
* storage makes the file, and its folder, at the first write.
|
|
28
|
+
*/
|
|
29
|
+
path: string;
|
|
30
|
+
/**
|
|
31
|
+
* The table of the collection. The name starts with a letter and has only letters, digits and
|
|
32
|
+
* underscores, 64 characters or fewer. A name that starts with `sqlite_` is not permitted.
|
|
33
|
+
*/
|
|
34
|
+
table: string;
|
|
35
|
+
/**
|
|
36
|
+
* The number of deletes that the change log keeps. After more deletes, the log drops the
|
|
37
|
+
* oldest ones, and `getChangesSince` of an older version gives all items.
|
|
38
|
+
* @default 10000
|
|
39
|
+
*/
|
|
40
|
+
maxTombstones?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* This function gives the name when it is a permitted table name. Else it throws a `TypeError`. The name goes
|
|
44
|
+
* into the SQL text, so the check is strict.
|
|
45
|
+
*/
|
|
46
|
+
export declare function checkTableName(name: unknown): string;
|
|
47
|
+
/**
|
|
48
|
+
* A collection storage in a table of a SQLite database (roadmap R45).
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const users = new SqliteStorage<User>({ path: "./data/app.db", table: "users" });
|
|
53
|
+
* await users.set("123", { id: "123", name: "John" });
|
|
54
|
+
* const user = await users.get("123"); // { id: "123", name: "John" }
|
|
55
|
+
* const changes = await users.getChangesSince(); // { version, full: true, changed, deleted: [] }
|
|
56
|
+
* await users.close();
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare class SqliteStorage<T> implements CollectionStorage<T> {
|
|
60
|
+
/** The database file, or `:memory:`. */
|
|
61
|
+
readonly path: string;
|
|
62
|
+
/** The table of the items. */
|
|
63
|
+
readonly table: string;
|
|
64
|
+
private readonly maxTombstones;
|
|
65
|
+
/** The quoted table name, for the SQL text. */
|
|
66
|
+
private readonly quoted;
|
|
67
|
+
private readonly opening;
|
|
68
|
+
private closed;
|
|
69
|
+
constructor(options: SqliteStorageOptions);
|
|
70
|
+
get(id: string): Promise<T | undefined>;
|
|
71
|
+
getAll(): Promise<T[]>;
|
|
72
|
+
/** Every item with its id, in the order of the first write of each id. */
|
|
73
|
+
getEntries(): Promise<Array<[string, T]>>;
|
|
74
|
+
/**
|
|
75
|
+
* The changes after a version (roadmap R28). A version of another storage, of another database
|
|
76
|
+
* file, or older than the dropped deletes gives all items (`full: true`). A version stays valid
|
|
77
|
+
* after a restart, because the change log is in the file.
|
|
78
|
+
*/
|
|
79
|
+
getChangesSince(since?: string): Promise<StorageChanges<T>>;
|
|
80
|
+
find(predicate: (item: T) => boolean): Promise<T[]>;
|
|
81
|
+
has(id: string): Promise<boolean>;
|
|
82
|
+
size(): Promise<number>;
|
|
83
|
+
set(id: string, value: T): Promise<void>;
|
|
84
|
+
delete(id: string): Promise<boolean>;
|
|
85
|
+
clear(): Promise<void>;
|
|
86
|
+
/** This method sets several items in one transaction. When one value is not JSON, nothing changes. */
|
|
87
|
+
setBatch(items: Array<[string, T]>): Promise<void>;
|
|
88
|
+
/** This method deletes several items in one transaction. */
|
|
89
|
+
deleteBatch(ids: string[]): Promise<number>;
|
|
90
|
+
getBatch(ids: string[]): Promise<Map<string, T>>;
|
|
91
|
+
/**
|
|
92
|
+
* This method writes the database file. Each write ends after its file write already, so a
|
|
93
|
+
* caller needs it only after a failed file write.
|
|
94
|
+
*/
|
|
95
|
+
flush(): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* This method writes the file and releases the database. The items stay in the file. A new
|
|
98
|
+
* storage of the same file and table reads them, and the versions of this storage stay valid.
|
|
99
|
+
* After `close()`, each operation throws.
|
|
100
|
+
*/
|
|
101
|
+
close(): Promise<void>;
|
|
102
|
+
getMetadata(): Promise<StorageMetadata>;
|
|
103
|
+
/** This method opens the database, and makes the tables of the collection and of the change log. */
|
|
104
|
+
private open;
|
|
105
|
+
/** The open database. It throws after `close()`. */
|
|
106
|
+
private database;
|
|
107
|
+
private checkOpen;
|
|
108
|
+
private entries;
|
|
109
|
+
private counter;
|
|
110
|
+
/**
|
|
111
|
+
* One write: the change in a transaction with the change log, then the file write. `next` gives
|
|
112
|
+
* the next number of the change log. A write that changes nothing writes no file.
|
|
113
|
+
*/
|
|
114
|
+
private write;
|
|
115
|
+
/** This method writes an item. An existing id keeps its place in the order. */
|
|
116
|
+
private put;
|
|
117
|
+
/** This method deletes an item. True when it existed. */
|
|
118
|
+
private remove;
|
|
119
|
+
private logChange;
|
|
120
|
+
/** This method drops the oldest deletes above the limit, and moves the floor of the change log. */
|
|
121
|
+
private trimTombstones;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|