tina4-nodejs 3.11.11 → 3.11.12
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/package.json
CHANGED
|
@@ -1,17 +1,48 @@
|
|
|
1
1
|
import { DatabaseSync } from "node:sqlite";
|
|
2
2
|
import { mkdirSync } from "node:fs";
|
|
3
|
-
import { dirname } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Resolve a SQLite path argument against the project root (cwd).
|
|
8
|
+
*
|
|
9
|
+
* Matches the tina4-python + tina4-php convention:
|
|
10
|
+
* ":memory:" → passthrough
|
|
11
|
+
* "data/app.db" → {cwd}/data/app.db (auto-mkdir under cwd)
|
|
12
|
+
* "/abs/app.db" → /abs/app.db (NO auto-mkdir; user's responsibility)
|
|
13
|
+
* "C:/Users/app.db" → C:/Users/app.db (NO auto-mkdir)
|
|
14
|
+
*
|
|
15
|
+
* Never mkdir a directory that isn't a descendant of cwd — that was the
|
|
16
|
+
* root cause of the `EROFS: read-only file system, mkdir '/data'` crash
|
|
17
|
+
* reported on macOS.
|
|
18
|
+
*/
|
|
19
|
+
function resolveSqlitePath(dbPath: string): string {
|
|
20
|
+
if (dbPath === ":memory:") return dbPath;
|
|
21
|
+
|
|
22
|
+
let path = dbPath;
|
|
23
|
+
if (!isAbsolute(path)) {
|
|
24
|
+
path = join(process.cwd(), path);
|
|
25
|
+
// Auto-mkdir is safe here — we know the parent is under cwd
|
|
26
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
27
|
+
} else {
|
|
28
|
+
// Absolute path. Only auto-mkdir if it's a descendant of cwd.
|
|
29
|
+
const cwd = resolve(process.cwd());
|
|
30
|
+
const abs = resolve(path);
|
|
31
|
+
if (abs.startsWith(cwd + "/") || abs === cwd) {
|
|
32
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
// Otherwise, trust the user — don't touch the filesystem.
|
|
35
|
+
}
|
|
36
|
+
return path;
|
|
37
|
+
}
|
|
38
|
+
|
|
6
39
|
export class SQLiteAdapter implements DatabaseAdapter {
|
|
7
40
|
private db: DatabaseSync;
|
|
8
41
|
private _lastInsertId: number | bigint | null = null;
|
|
9
42
|
|
|
10
43
|
constructor(dbPath: string) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
this.db = new DatabaseSync(dbPath);
|
|
44
|
+
const resolved = resolveSqlitePath(dbPath);
|
|
45
|
+
this.db = new DatabaseSync(resolved);
|
|
15
46
|
this.db.exec("PRAGMA journal_mode = WAL");
|
|
16
47
|
this.db.exec("PRAGMA foreign_keys = ON");
|
|
17
48
|
}
|
|
@@ -92,18 +92,31 @@ export interface ParsedDatabaseUrl {
|
|
|
92
92
|
export function parseDatabaseUrl(url: string, username?: string, password?: string): ParsedDatabaseUrl {
|
|
93
93
|
let result: ParsedDatabaseUrl;
|
|
94
94
|
|
|
95
|
-
// Handle sqlite:// separately because URL class mangles the path
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
// Handle sqlite:// separately because URL class mangles the path.
|
|
96
|
+
//
|
|
97
|
+
// Convention (matches tina4-python, tina4-php, and the docs):
|
|
98
|
+
// sqlite::memory: → in-memory
|
|
99
|
+
// sqlite:///:memory: → in-memory (URL form)
|
|
100
|
+
// sqlite:///app.db → ./app.db (relative to cwd)
|
|
101
|
+
// sqlite:///data/app.db → ./data/app.db (relative)
|
|
102
|
+
// sqlite:////absolute/app.db → /absolute/app.db (absolute)
|
|
103
|
+
// sqlite:///C:/Users/app.db → C:/Users/app.db (Windows absolute)
|
|
104
|
+
if (url === "sqlite::memory:" || url === "sqlite:///:memory:") {
|
|
105
|
+
result = { type: "sqlite", path: ":memory:" };
|
|
106
|
+
} else if (url.startsWith("sqlite:///")) {
|
|
107
|
+
// Strip the "sqlite://" prefix (leaving one "/" + path)
|
|
108
|
+
let rest = url.slice("sqlite://".length); // e.g. "/data/app.db" or "//abs/app.db" or "/C:/Users/..."
|
|
109
|
+
// Drop exactly one leading "/"
|
|
110
|
+
if (rest.startsWith("/")) rest = rest.slice(1);
|
|
111
|
+
// Windows absolute: C:/Users/app.db or C:\...
|
|
112
|
+
const isWindowsAbs = /^[A-Za-z]:[\/\\]/.test(rest);
|
|
113
|
+
// Unix absolute: still starts with "/" after the strip (four-slash URL form)
|
|
114
|
+
const isUnixAbs = rest.startsWith("/");
|
|
115
|
+
result = { type: "sqlite", path: isWindowsAbs || isUnixAbs ? rest : rest };
|
|
116
|
+
// Relative paths are resolved against cwd by the SQLite adapter at connect time;
|
|
117
|
+
// keep the string as-is here so tests can inspect the raw form.
|
|
105
118
|
} else if (url.startsWith("sqlite://")) {
|
|
106
|
-
// sqlite://./relative or sqlite://relative
|
|
119
|
+
// sqlite://./relative or sqlite://relative — legacy two-slash form
|
|
107
120
|
const path = url.slice("sqlite://".length);
|
|
108
121
|
result = { type: "sqlite", path };
|
|
109
122
|
} else if (url.startsWith("mssql://") || url.startsWith("sqlserver://")) {
|