@dangao/bun-server 3.0.0 → 3.0.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/dist/database/connection-manager.d.ts.map +1 -1
- package/dist/database/connection-pool.d.ts.map +1 -1
- package/dist/database/service.d.ts +2 -1
- package/dist/database/service.d.ts.map +1 -1
- package/dist/database/sqlite-adapter.d.ts +2 -2
- package/dist/database/sqlite-adapter.d.ts.map +1 -1
- package/dist/index.js +76 -795
- package/dist/index.node.mjs +63 -17
- package/package.json +10 -4
- package/src/database/connection-manager.ts +19 -12
- package/src/database/connection-pool.ts +12 -3
- package/src/database/service.ts +21 -5
- package/src/database/sqlite-adapter.ts +27 -11
- package/tests/platform/shared/database.cases.ts +9 -9
- package/docs/design/query-interceptor-design.md +0 -381
package/dist/index.node.mjs
CHANGED
|
@@ -10932,8 +10932,14 @@ class ConnectionPool {
|
|
|
10932
10932
|
const { Database } = await import("bun:sqlite");
|
|
10933
10933
|
return new Database(config.path);
|
|
10934
10934
|
}
|
|
10935
|
-
|
|
10936
|
-
|
|
10935
|
+
let sqlite3;
|
|
10936
|
+
try {
|
|
10937
|
+
sqlite3 = __require("@vscode/sqlite3");
|
|
10938
|
+
} catch {
|
|
10939
|
+
throw new Error(`[bun-server] SQLite on Node.js requires @vscode/sqlite3.
|
|
10940
|
+
Install it with: bun add @vscode/sqlite3@5.1.12-vscode`);
|
|
10941
|
+
}
|
|
10942
|
+
return new sqlite3.Database(config.path);
|
|
10937
10943
|
}
|
|
10938
10944
|
async createPostgresConnection(config) {
|
|
10939
10945
|
const url = `postgres://${config.user}:${config.password}@${config.host}:${config.port}/${config.database}`;
|
|
@@ -11099,11 +11105,25 @@ class DatabaseConnectionManager {
|
|
|
11099
11105
|
}
|
|
11100
11106
|
async healthCheckSqlite(connection) {
|
|
11101
11107
|
try {
|
|
11102
|
-
if (connection
|
|
11108
|
+
if (!connection || typeof connection !== "object") {
|
|
11109
|
+
return false;
|
|
11110
|
+
}
|
|
11111
|
+
if ("query" in connection && typeof connection.query === "function" && !(("all" in connection) && ("run" in connection))) {
|
|
11103
11112
|
const db = connection;
|
|
11104
11113
|
db.query("SELECT 1").all();
|
|
11105
11114
|
return true;
|
|
11106
11115
|
}
|
|
11116
|
+
if ("all" in connection && typeof connection.all === "function") {
|
|
11117
|
+
await new Promise((resolve2, reject) => {
|
|
11118
|
+
connection.all("SELECT 1", [], (err) => {
|
|
11119
|
+
if (err)
|
|
11120
|
+
reject(err);
|
|
11121
|
+
else
|
|
11122
|
+
resolve2();
|
|
11123
|
+
});
|
|
11124
|
+
});
|
|
11125
|
+
return true;
|
|
11126
|
+
}
|
|
11107
11127
|
return false;
|
|
11108
11128
|
} catch (_error) {
|
|
11109
11129
|
return false;
|
|
@@ -11213,12 +11233,22 @@ class DatabaseService2 {
|
|
|
11213
11233
|
throw new Error(`Query not supported for database type: ${dbType}`);
|
|
11214
11234
|
}
|
|
11215
11235
|
querySqlite(connection, sql, params) {
|
|
11216
|
-
if (connection && typeof connection === "object" && "query" in connection && typeof connection.query === "function") {
|
|
11236
|
+
if (connection && typeof connection === "object" && "query" in connection && typeof connection.query === "function" && !(("all" in connection) && ("run" in connection))) {
|
|
11217
11237
|
const db = connection;
|
|
11218
11238
|
const statement = db.query(sql);
|
|
11219
11239
|
const result = params && params.length > 0 ? statement.all(...params) : statement.all();
|
|
11220
11240
|
return result;
|
|
11221
11241
|
}
|
|
11242
|
+
if (connection && typeof connection === "object" && "all" in connection && typeof connection.all === "function") {
|
|
11243
|
+
return new Promise((resolve2, reject) => {
|
|
11244
|
+
connection.all(sql, params ?? [], (err, rows) => {
|
|
11245
|
+
if (err)
|
|
11246
|
+
reject(err);
|
|
11247
|
+
else
|
|
11248
|
+
resolve2(rows ?? []);
|
|
11249
|
+
});
|
|
11250
|
+
});
|
|
11251
|
+
}
|
|
11222
11252
|
throw new Error("Invalid SQLite connection");
|
|
11223
11253
|
}
|
|
11224
11254
|
async queryBunSQL(connection, sql, params) {
|
|
@@ -11389,24 +11419,35 @@ class SqliteAdapter {
|
|
|
11389
11419
|
}
|
|
11390
11420
|
this.db = db;
|
|
11391
11421
|
} else {
|
|
11392
|
-
|
|
11393
|
-
|
|
11422
|
+
let sqlite3;
|
|
11423
|
+
try {
|
|
11424
|
+
sqlite3 = __require("@vscode/sqlite3");
|
|
11425
|
+
} catch {
|
|
11426
|
+
throw new Error(`[bun-server] SQLite on Node.js requires @vscode/sqlite3.
|
|
11427
|
+
` + "Install it with: bun add @vscode/sqlite3@5.1.12-vscode");
|
|
11428
|
+
}
|
|
11429
|
+
const db = new sqlite3.Database(config.database);
|
|
11394
11430
|
if (config.wal !== false) {
|
|
11395
|
-
db.
|
|
11431
|
+
db.run("PRAGMA journal_mode = WAL;");
|
|
11396
11432
|
}
|
|
11397
11433
|
this.db = db;
|
|
11398
11434
|
}
|
|
11399
11435
|
this.semaphore = new Semaphore(config.maxWriteConcurrency ?? 1);
|
|
11400
11436
|
}
|
|
11401
|
-
query(sql, params = []) {
|
|
11437
|
+
async query(sql, params = []) {
|
|
11402
11438
|
if (this.isBun) {
|
|
11403
|
-
const
|
|
11404
|
-
const
|
|
11405
|
-
return
|
|
11439
|
+
const db = this.db;
|
|
11440
|
+
const stmt = db.query(sql);
|
|
11441
|
+
return stmt.all(...params);
|
|
11406
11442
|
}
|
|
11407
|
-
|
|
11408
|
-
|
|
11409
|
-
|
|
11443
|
+
return new Promise((resolve2, reject) => {
|
|
11444
|
+
this.db.all(sql, params, (err, rows) => {
|
|
11445
|
+
if (err)
|
|
11446
|
+
reject(err);
|
|
11447
|
+
else
|
|
11448
|
+
resolve2(rows ?? []);
|
|
11449
|
+
});
|
|
11450
|
+
});
|
|
11410
11451
|
}
|
|
11411
11452
|
async execute(sql, params = []) {
|
|
11412
11453
|
if (this.isBun) {
|
|
@@ -11414,9 +11455,14 @@ class SqliteAdapter {
|
|
|
11414
11455
|
const stmt = db.query(sql);
|
|
11415
11456
|
stmt.run(...params);
|
|
11416
11457
|
} else {
|
|
11417
|
-
|
|
11418
|
-
|
|
11419
|
-
|
|
11458
|
+
return new Promise((resolve2, reject) => {
|
|
11459
|
+
this.db.run(sql, params, (err) => {
|
|
11460
|
+
if (err)
|
|
11461
|
+
reject(err);
|
|
11462
|
+
else
|
|
11463
|
+
resolve2();
|
|
11464
|
+
});
|
|
11465
|
+
});
|
|
11420
11466
|
}
|
|
11421
11467
|
}
|
|
11422
11468
|
close() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dangao/bun-server",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"test:platform": "bun run test:bun && bun run test:node",
|
|
51
51
|
"type-check": "tsc --noEmit",
|
|
52
52
|
"clean": "rm -rf dist",
|
|
53
|
-
"bundle": "bun build src/index.ts --target=bun --format=esm --outfile=dist/index.js --external reflect-metadata --external @dangao/logsmith --external @dangao/nacos-client",
|
|
53
|
+
"bundle": "bun build src/index.ts --target=bun --format=esm --outfile=dist/index.js --external reflect-metadata --external @dangao/logsmith --external @dangao/nacos-client --external @vscode/sqlite3",
|
|
54
54
|
"bundle:node": "bun build src/index.ts --target=node --packages=external --outfile=dist/index.node.mjs",
|
|
55
55
|
"dts": "tsc -p tsconfig.build.json",
|
|
56
56
|
"build": "bun run clean && bun run bundle && bun run bundle:node && bun run dts",
|
|
@@ -59,17 +59,23 @@
|
|
|
59
59
|
"publish:package:github": "cp -r ../../README.md ../../LICENSE ../../docs . && bun pm pack --access public && npm publish *.tgz --provenance --access public && rm -rf ./README.md ./LICENSE ./docs"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
63
62
|
"@types/bun": "^1.3.10",
|
|
64
63
|
"@types/mime-types": "^3.0.1",
|
|
65
64
|
"@types/ws": "^8.18.1",
|
|
66
65
|
"typescript": "^5.9.3",
|
|
67
66
|
"vitest": "^4.1.4"
|
|
68
67
|
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"@vscode/sqlite3": "5.1.12-vscode"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"@vscode/sqlite3": {
|
|
73
|
+
"optional": true
|
|
74
|
+
}
|
|
75
|
+
},
|
|
69
76
|
"dependencies": {
|
|
70
77
|
"@dangao/logsmith": "0.2.0",
|
|
71
78
|
"@dangao/nacos-client": "0.1.1",
|
|
72
|
-
"better-sqlite3": "^12.8.0",
|
|
73
79
|
"json5": "^2.2.3",
|
|
74
80
|
"jsonc-parser": "^3.3.1",
|
|
75
81
|
"marked": "^18.0.0",
|
|
@@ -160,21 +160,28 @@ export class DatabaseConnectionManager {
|
|
|
160
160
|
*/
|
|
161
161
|
private async healthCheckSqlite(connection: unknown): Promise<boolean> {
|
|
162
162
|
try {
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
) {
|
|
169
|
-
|
|
170
|
-
const db = connection as {
|
|
171
|
-
query: (sql: string) => {
|
|
172
|
-
all: () => unknown[];
|
|
173
|
-
};
|
|
174
|
-
};
|
|
163
|
+
if (!connection || typeof connection !== 'object') {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// bun:sqlite Database(有 .query() 但没有 callback-based .all())
|
|
168
|
+
if ('query' in connection && typeof (connection as any).query === 'function' && !('all' in connection && 'run' in connection)) {
|
|
169
|
+
const db = connection as { query: (sql: string) => { all: () => unknown[] } };
|
|
175
170
|
db.query('SELECT 1').all();
|
|
176
171
|
return true;
|
|
177
172
|
}
|
|
173
|
+
|
|
174
|
+
// @vscode/sqlite3 Database(callback-based .all())
|
|
175
|
+
if ('all' in connection && typeof (connection as any).all === 'function') {
|
|
176
|
+
await new Promise<void>((resolve, reject) => {
|
|
177
|
+
(connection as any).all('SELECT 1', [], (err: Error | null) => {
|
|
178
|
+
if (err) reject(err);
|
|
179
|
+
else resolve();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
178
185
|
return false;
|
|
179
186
|
} catch (_error) {
|
|
180
187
|
return false;
|
|
@@ -185,9 +185,18 @@ export class ConnectionPool {
|
|
|
185
185
|
const { Database } = await import('bun:sqlite');
|
|
186
186
|
return new Database(config.path);
|
|
187
187
|
}
|
|
188
|
-
// Node.js:使用
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
// Node.js:使用 @vscode/sqlite3(可选对等依赖,需用户自行安装)
|
|
189
|
+
let sqlite3: any;
|
|
190
|
+
try {
|
|
191
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
192
|
+
sqlite3 = require('@vscode/sqlite3');
|
|
193
|
+
} catch {
|
|
194
|
+
throw new Error(
|
|
195
|
+
'[bun-server] SQLite on Node.js requires @vscode/sqlite3.\n' +
|
|
196
|
+
'Install it with: bun add @vscode/sqlite3@5.1.12-vscode',
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return new sqlite3.Database(config.path);
|
|
191
200
|
}
|
|
192
201
|
|
|
193
202
|
/**
|
package/src/database/service.ts
CHANGED
|
@@ -113,7 +113,7 @@ export class DatabaseService {
|
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
115
|
* 执行 SQL 查询
|
|
116
|
-
* SQLite
|
|
116
|
+
* SQLite (bun:sqlite) 返回同步结果;@vscode/sqlite3 / PostgreSQL / MySQL 返回异步结果
|
|
117
117
|
*/
|
|
118
118
|
public query<T = unknown>(sql: string, params?: unknown[]): T[] | Promise<T[]> {
|
|
119
119
|
const session = getCurrentSession();
|
|
@@ -139,18 +139,20 @@ export class DatabaseService {
|
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
141
|
* SQLite 查询实现
|
|
142
|
+
* bun:sqlite 使用同步 .query().all();@vscode/sqlite3 使用异步 callback .all()
|
|
142
143
|
*/
|
|
143
144
|
private querySqlite<T = unknown>(
|
|
144
145
|
connection: unknown,
|
|
145
146
|
sql: string,
|
|
146
147
|
params?: unknown[],
|
|
147
|
-
): T[] {
|
|
148
|
-
//
|
|
148
|
+
): T[] | Promise<T[]> {
|
|
149
|
+
// bun:sqlite Database 对象(有 .query() 方法)
|
|
149
150
|
if (
|
|
150
151
|
connection &&
|
|
151
152
|
typeof connection === 'object' &&
|
|
152
153
|
'query' in connection &&
|
|
153
|
-
typeof connection.query === 'function'
|
|
154
|
+
typeof (connection as any).query === 'function' &&
|
|
155
|
+
!('all' in connection && 'run' in connection)
|
|
154
156
|
) {
|
|
155
157
|
const db = connection as {
|
|
156
158
|
query: (sql: string) => {
|
|
@@ -160,12 +162,26 @@ export class DatabaseService {
|
|
|
160
162
|
};
|
|
161
163
|
|
|
162
164
|
const statement = db.query(sql);
|
|
163
|
-
// Bun SQLite 的 all() 方法接受参数
|
|
164
165
|
const result =
|
|
165
166
|
params && params.length > 0 ? statement.all(...params) : statement.all();
|
|
166
167
|
return result;
|
|
167
168
|
}
|
|
168
169
|
|
|
170
|
+
// @vscode/sqlite3 Database 对象(有 .all() callback 方法)
|
|
171
|
+
if (
|
|
172
|
+
connection &&
|
|
173
|
+
typeof connection === 'object' &&
|
|
174
|
+
'all' in connection &&
|
|
175
|
+
typeof (connection as any).all === 'function'
|
|
176
|
+
) {
|
|
177
|
+
return new Promise<T[]>((resolve, reject) => {
|
|
178
|
+
(connection as any).all(sql, params ?? [], (err: Error | null, rows: T[]) => {
|
|
179
|
+
if (err) reject(err);
|
|
180
|
+
else resolve(rows ?? []);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
169
185
|
throw new Error('Invalid SQLite connection');
|
|
170
186
|
}
|
|
171
187
|
|
|
@@ -40,7 +40,7 @@ export class Semaphore {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* SQLite 适配器(自动感知运行时)
|
|
43
|
-
* Bun 平台下使用 bun:sqlite,Node.js 平台下使用
|
|
43
|
+
* Bun 平台下使用 bun:sqlite,Node.js 平台下使用 @vscode/sqlite3
|
|
44
44
|
*/
|
|
45
45
|
export class SqliteAdapter {
|
|
46
46
|
private readonly db: unknown;
|
|
@@ -58,10 +58,20 @@ export class SqliteAdapter {
|
|
|
58
58
|
}
|
|
59
59
|
this.db = db;
|
|
60
60
|
} else {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
let sqlite3: any;
|
|
62
|
+
try {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
64
|
+
sqlite3 = require('@vscode/sqlite3');
|
|
65
|
+
} catch {
|
|
66
|
+
throw new Error(
|
|
67
|
+
'[bun-server] SQLite on Node.js requires @vscode/sqlite3.\n' +
|
|
68
|
+
'Install it with: bun add @vscode/sqlite3@5.1.12-vscode',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const db: any = new sqlite3.Database(config.database);
|
|
63
72
|
if (config.wal !== false) {
|
|
64
|
-
|
|
73
|
+
// Operations are serialized internally; WAL is queued before any query runs
|
|
74
|
+
db.run('PRAGMA journal_mode = WAL;');
|
|
65
75
|
}
|
|
66
76
|
this.db = db;
|
|
67
77
|
}
|
|
@@ -69,15 +79,18 @@ export class SqliteAdapter {
|
|
|
69
79
|
this.semaphore = new Semaphore(config.maxWriteConcurrency ?? 1);
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
public query<T = unknown>(sql: string, params: unknown[] = []): T[] {
|
|
82
|
+
public async query<T = unknown>(sql: string, params: unknown[] = []): Promise<T[]> {
|
|
73
83
|
if (this.isBun) {
|
|
74
84
|
const db = this.db as import('bun:sqlite').Database;
|
|
75
85
|
const stmt = db.query(sql);
|
|
76
86
|
return stmt.all(...params as Parameters<typeof stmt.all>) as T[];
|
|
77
87
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
return new Promise<T[]>((resolve, reject) => {
|
|
89
|
+
(this.db as any).all(sql, params, (err: Error | null, rows: T[]) => {
|
|
90
|
+
if (err) reject(err);
|
|
91
|
+
else resolve(rows ?? []);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
81
94
|
}
|
|
82
95
|
|
|
83
96
|
public async execute(sql: string, params: unknown[] = []): Promise<void> {
|
|
@@ -86,9 +99,12 @@ export class SqliteAdapter {
|
|
|
86
99
|
const stmt = db.query(sql);
|
|
87
100
|
stmt.run(...params as Parameters<typeof stmt.run>);
|
|
88
101
|
} else {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
return new Promise<void>((resolve, reject) => {
|
|
103
|
+
(this.db as any).run(sql, params, (err: Error | null) => {
|
|
104
|
+
if (err) reject(err);
|
|
105
|
+
else resolve();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
92
108
|
}
|
|
93
109
|
}
|
|
94
110
|
|
|
@@ -7,22 +7,22 @@ import { SqliteManager } from '../../../src/database/sqlite-adapter';
|
|
|
7
7
|
export function runDatabaseCases(suite: TestSuite, engine: PlatformEngine): void {
|
|
8
8
|
const { test, expect } = suite;
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
const
|
|
10
|
+
// @vscode/sqlite3 is a native Node.js addon; skip when Node-platform tests run under Bun runtime
|
|
11
|
+
const skipNodeSqlite = engine === 'node' && typeof (globalThis as any).Bun !== 'undefined';
|
|
12
12
|
|
|
13
|
-
test('SqliteAdapter: create table, insert and query', () => {
|
|
14
|
-
if (
|
|
15
|
-
//
|
|
16
|
-
console.log('[skip]
|
|
13
|
+
test('SqliteAdapter: create table, insert and query', async () => {
|
|
14
|
+
if (skipNodeSqlite) {
|
|
15
|
+
// @vscode/sqlite3 is not available in Bun — this test must run under Node.js
|
|
16
|
+
console.log('[skip] @vscode/sqlite3 not available in Bun runtime; run with vitest for Node platform');
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
const dbPath = join(tmpdir(), `platform-db-test-${engine}-${Date.now()}.db`);
|
|
20
20
|
const manager = new SqliteManager();
|
|
21
21
|
const adapter = manager.getOrCreate('test', { database: dbPath, wal: false });
|
|
22
22
|
|
|
23
|
-
adapter.query('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');
|
|
24
|
-
adapter.query("INSERT INTO users (name) VALUES ('Alice')");
|
|
25
|
-
const rows = adapter.query<{ id: number; name: string }>('SELECT * FROM users');
|
|
23
|
+
await adapter.query('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');
|
|
24
|
+
await adapter.query("INSERT INTO users (name) VALUES ('Alice')");
|
|
25
|
+
const rows = await adapter.query<{ id: number; name: string }>('SELECT * FROM users');
|
|
26
26
|
|
|
27
27
|
expect(rows.length).toBeGreaterThanOrEqual(1);
|
|
28
28
|
expect(rows[0]!.name).toBe('Alice');
|