tina4-nodejs 3.8.4 → 3.8.5
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tina4-nodejs",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "This is not a framework. Tina4 for Node.js/TypeScript — zero deps, 38 built-in features.",
|
|
6
6
|
"keywords": ["tina4", "framework", "web", "api", "orm", "graphql", "websocket", "typescript"],
|
|
@@ -49,9 +49,7 @@
|
|
|
49
49
|
"engines": {
|
|
50
50
|
"node": ">=20.0.0"
|
|
51
51
|
},
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"better-sqlite3": "^11.0.0"
|
|
54
|
-
},
|
|
52
|
+
"dependencies": {},
|
|
55
53
|
"devDependencies": {
|
|
56
54
|
"typescript": "^5.7.0",
|
|
57
55
|
"tsx": "^4.19.0",
|
|
@@ -39,18 +39,9 @@ export class DatabaseSessionHandler implements SessionHandler {
|
|
|
39
39
|
constructor(config?: DatabaseSessionConfig) {
|
|
40
40
|
const dbPath = config?.dbPath ?? this.resolveDbPath();
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} catch {
|
|
46
|
-
throw new Error(
|
|
47
|
-
"DatabaseSessionHandler requires 'better-sqlite3'. " +
|
|
48
|
-
"Install it with: npm install better-sqlite3"
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
this.db = new Database(dbPath);
|
|
53
|
-
this.db.pragma("journal_mode = WAL");
|
|
42
|
+
const { DatabaseSync } = require("node:sqlite");
|
|
43
|
+
this.db = new DatabaseSync(dbPath);
|
|
44
|
+
this.db.exec("PRAGMA journal_mode = WAL");
|
|
54
45
|
}
|
|
55
46
|
|
|
56
47
|
/**
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
2
|
import { mkdirSync } from "node:fs";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
5
5
|
|
|
6
6
|
export class SQLiteAdapter implements DatabaseAdapter {
|
|
7
|
-
private db:
|
|
7
|
+
private db: DatabaseSync;
|
|
8
8
|
private _lastInsertId: number | bigint | null = null;
|
|
9
9
|
|
|
10
10
|
constructor(dbPath: string) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
this.db = new
|
|
15
|
-
this.db.
|
|
16
|
-
this.db.
|
|
11
|
+
if (dbPath !== ":memory:") {
|
|
12
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
this.db = new DatabaseSync(dbPath);
|
|
15
|
+
this.db.exec("PRAGMA journal_mode = WAL");
|
|
16
|
+
this.db.exec("PRAGMA foreign_keys = ON");
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
execute(sql: string, params?: unknown[]): unknown {
|
|
@@ -30,8 +30,9 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
30
30
|
let totalAffected = 0;
|
|
31
31
|
let lastId: number | bigint | undefined;
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
this.db.exec("BEGIN TRANSACTION");
|
|
34
|
+
try {
|
|
35
|
+
for (const params of paramsList) {
|
|
35
36
|
const result = stmt.run(...params);
|
|
36
37
|
totalAffected += result.changes;
|
|
37
38
|
if (result.lastInsertRowid) {
|
|
@@ -39,9 +40,12 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
39
40
|
this._lastInsertId = result.lastInsertRowid;
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
+
this.db.exec("COMMIT");
|
|
44
|
+
} catch (e) {
|
|
45
|
+
this.db.exec("ROLLBACK");
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
43
48
|
|
|
44
|
-
runMany(paramsList);
|
|
45
49
|
return { totalAffected, lastInsertId: lastId };
|
|
46
50
|
}
|
|
47
51
|
|
|
@@ -68,7 +72,6 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult {
|
|
71
|
-
// Handle list of rows — batch insert
|
|
72
75
|
if (Array.isArray(data)) {
|
|
73
76
|
if (data.length === 0) return { success: true, rowsAffected: 0 };
|
|
74
77
|
const keys = Object.keys(data[0]);
|
|
@@ -87,17 +90,9 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
87
90
|
try {
|
|
88
91
|
const result = this.db.prepare(sql).run(...values);
|
|
89
92
|
this._lastInsertId = result.lastInsertRowid;
|
|
90
|
-
return {
|
|
91
|
-
success: true,
|
|
92
|
-
rowsAffected: result.changes,
|
|
93
|
-
lastInsertId: result.lastInsertRowid,
|
|
94
|
-
};
|
|
93
|
+
return { success: true, rowsAffected: result.changes, lastInsertId: result.lastInsertRowid };
|
|
95
94
|
} catch (e) {
|
|
96
|
-
return {
|
|
97
|
-
success: false,
|
|
98
|
-
rowsAffected: 0,
|
|
99
|
-
error: (e as Error).message,
|
|
100
|
-
};
|
|
95
|
+
return { success: false, rowsAffected: 0, error: (e as Error).message };
|
|
101
96
|
}
|
|
102
97
|
}
|
|
103
98
|
|
|
@@ -116,7 +111,6 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
116
111
|
}
|
|
117
112
|
|
|
118
113
|
delete(table: string, filter: Record<string, unknown> | string | Record<string, unknown>[]): DatabaseResult {
|
|
119
|
-
// Array of objects — delete each row
|
|
120
114
|
if (Array.isArray(filter)) {
|
|
121
115
|
let totalAffected = 0;
|
|
122
116
|
for (const row of filter) {
|
|
@@ -126,7 +120,6 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
126
120
|
return { success: true, rowsAffected: totalAffected };
|
|
127
121
|
}
|
|
128
122
|
|
|
129
|
-
// String filter — raw WHERE clause
|
|
130
123
|
if (typeof filter === "string") {
|
|
131
124
|
const sql = filter ? `DELETE FROM "${table}" WHERE ${filter}` : `DELETE FROM "${table}"`;
|
|
132
125
|
try {
|
|
@@ -137,7 +130,6 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
137
130
|
}
|
|
138
131
|
}
|
|
139
132
|
|
|
140
|
-
// Object filter — build WHERE from keys
|
|
141
133
|
const whereClauses = Object.keys(filter).map((k) => `"${k}" = ?`).join(" AND ");
|
|
142
134
|
const sql = `DELETE FROM "${table}" WHERE ${whereClauses}`;
|
|
143
135
|
const values = Object.values(filter);
|
|
@@ -150,17 +142,9 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
150
142
|
}
|
|
151
143
|
}
|
|
152
144
|
|
|
153
|
-
startTransaction(): void {
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
commit(): void {
|
|
158
|
-
this.db.exec("COMMIT");
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
rollback(): void {
|
|
162
|
-
this.db.exec("ROLLBACK");
|
|
163
|
-
}
|
|
145
|
+
startTransaction(): void { this.db.exec("BEGIN TRANSACTION"); }
|
|
146
|
+
commit(): void { this.db.exec("COMMIT"); }
|
|
147
|
+
rollback(): void { this.db.exec("ROLLBACK"); }
|
|
164
148
|
|
|
165
149
|
tables(): string[] {
|
|
166
150
|
const rows = this.query<{ name: string }>(
|
|
@@ -171,95 +155,57 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
171
155
|
|
|
172
156
|
columns(table: string): ColumnInfo[] {
|
|
173
157
|
const rows = this.db.prepare(`PRAGMA table_info("${table}")`).all() as Array<{
|
|
174
|
-
name: string;
|
|
175
|
-
type: string;
|
|
176
|
-
notnull: number;
|
|
177
|
-
dflt_value: unknown;
|
|
178
|
-
pk: number;
|
|
158
|
+
name: string; type: string; notnull: number; dflt_value: unknown; pk: number;
|
|
179
159
|
}>;
|
|
180
160
|
return rows.map((r) => ({
|
|
181
|
-
name: r.name,
|
|
182
|
-
type: r.type,
|
|
183
|
-
nullable: r.notnull === 0,
|
|
184
|
-
default: r.dflt_value,
|
|
185
|
-
primaryKey: r.pk === 1,
|
|
161
|
+
name: r.name, type: r.type, nullable: r.notnull === 0, default: r.dflt_value, primaryKey: r.pk === 1,
|
|
186
162
|
}));
|
|
187
163
|
}
|
|
188
164
|
|
|
189
|
-
lastInsertId(): number | bigint | null {
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
close(): void {
|
|
194
|
-
this.db.close();
|
|
195
|
-
}
|
|
165
|
+
lastInsertId(): number | bigint | null { return this._lastInsertId; }
|
|
166
|
+
close(): void { this.db.close(); }
|
|
196
167
|
|
|
197
168
|
tableExists(name: string): boolean {
|
|
198
|
-
const result = this.db
|
|
199
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?")
|
|
200
|
-
.get(name);
|
|
169
|
+
const result = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(name);
|
|
201
170
|
return !!result;
|
|
202
171
|
}
|
|
203
172
|
|
|
204
173
|
createTable(name: string, columns: Record<string, FieldDefinition>): void {
|
|
205
174
|
const colDefs: string[] = [];
|
|
206
|
-
|
|
207
175
|
for (const [colName, def] of Object.entries(columns)) {
|
|
208
176
|
const sqlType = fieldTypeToSQLite(def.type);
|
|
209
177
|
const parts = [`"${colName}" ${sqlType}`];
|
|
210
|
-
|
|
211
178
|
if (def.primaryKey) parts.push("PRIMARY KEY");
|
|
212
179
|
if (def.autoIncrement) parts.push("AUTOINCREMENT");
|
|
213
180
|
if (def.required && !def.primaryKey) parts.push("NOT NULL");
|
|
214
|
-
if (def.default !== undefined && def.default !== "now") {
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
if (def.default === "now") {
|
|
218
|
-
parts.push("DEFAULT CURRENT_TIMESTAMP");
|
|
219
|
-
}
|
|
220
|
-
|
|
181
|
+
if (def.default !== undefined && def.default !== "now") parts.push(`DEFAULT ${sqlDefault(def.default)}`);
|
|
182
|
+
if (def.default === "now") parts.push("DEFAULT CURRENT_TIMESTAMP");
|
|
221
183
|
colDefs.push(parts.join(" "));
|
|
222
184
|
}
|
|
223
|
-
|
|
224
|
-
const sql = `CREATE TABLE IF NOT EXISTS "${name}" (${colDefs.join(", ")})`;
|
|
225
|
-
this.db.exec(sql);
|
|
185
|
+
this.db.exec(`CREATE TABLE IF NOT EXISTS "${name}" (${colDefs.join(", ")})`);
|
|
226
186
|
}
|
|
227
187
|
|
|
228
188
|
getTableColumns(name: string): Array<{ name: string; type: string }> {
|
|
229
|
-
return this.db.prepare(`PRAGMA table_info("${name}")`).all() as Array<{
|
|
230
|
-
name: string;
|
|
231
|
-
type: string;
|
|
232
|
-
}>;
|
|
189
|
+
return this.db.prepare(`PRAGMA table_info("${name}")`).all() as Array<{ name: string; type: string }>;
|
|
233
190
|
}
|
|
234
191
|
|
|
235
192
|
addColumn(table: string, colName: string, def: FieldDefinition): void {
|
|
236
193
|
const sqlType = fieldTypeToSQLite(def.type);
|
|
237
194
|
let sql = `ALTER TABLE "${table}" ADD COLUMN "${colName}" ${sqlType}`;
|
|
238
|
-
if (def.default !== undefined && def.default !== "now") {
|
|
239
|
-
|
|
240
|
-
} else if (def.default === "now") {
|
|
241
|
-
sql += " DEFAULT CURRENT_TIMESTAMP";
|
|
242
|
-
}
|
|
195
|
+
if (def.default !== undefined && def.default !== "now") sql += ` DEFAULT ${sqlDefault(def.default)}`;
|
|
196
|
+
else if (def.default === "now") sql += " DEFAULT CURRENT_TIMESTAMP";
|
|
243
197
|
this.db.exec(sql);
|
|
244
198
|
}
|
|
245
199
|
}
|
|
246
200
|
|
|
247
201
|
function fieldTypeToSQLite(type: string): string {
|
|
248
202
|
switch (type) {
|
|
249
|
-
case "integer":
|
|
250
|
-
|
|
251
|
-
case "
|
|
252
|
-
case "
|
|
253
|
-
|
|
254
|
-
case "
|
|
255
|
-
return "INTEGER";
|
|
256
|
-
case "datetime":
|
|
257
|
-
return "TEXT";
|
|
258
|
-
case "text":
|
|
259
|
-
return "TEXT";
|
|
260
|
-
case "string":
|
|
261
|
-
default:
|
|
262
|
-
return "TEXT";
|
|
203
|
+
case "integer": return "INTEGER";
|
|
204
|
+
case "number": case "numeric": return "REAL";
|
|
205
|
+
case "boolean": return "INTEGER";
|
|
206
|
+
case "datetime": return "TEXT";
|
|
207
|
+
case "text": return "TEXT";
|
|
208
|
+
case "string": default: return "TEXT";
|
|
263
209
|
}
|
|
264
210
|
}
|
|
265
211
|
|