@dangao/bun-server 3.0.5 → 3.2.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.
Files changed (42) hide show
  1. package/dist/database/connection-manager.d.ts +2 -2
  2. package/dist/database/connection-manager.d.ts.map +1 -1
  3. package/dist/database/connection-pool.d.ts +4 -4
  4. package/dist/database/connection-pool.d.ts.map +1 -1
  5. package/dist/database/database-module.d.ts.map +1 -1
  6. package/dist/database/db-proxy.d.ts.map +1 -1
  7. package/dist/database/driver.d.ts +83 -0
  8. package/dist/database/driver.d.ts.map +1 -0
  9. package/dist/database/index.d.ts +2 -1
  10. package/dist/database/index.d.ts.map +1 -1
  11. package/dist/database/service.d.ts +0 -10
  12. package/dist/database/service.d.ts.map +1 -1
  13. package/dist/database/sql-manager.d.ts.map +1 -1
  14. package/dist/database/types.d.ts +26 -0
  15. package/dist/database/types.d.ts.map +1 -1
  16. package/dist/di/module-registry.d.ts.map +1 -1
  17. package/dist/di/module.d.ts +9 -1
  18. package/dist/di/module.d.ts.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +5898 -5792
  22. package/dist/index.node.mjs +260 -114
  23. package/docs/database.md +44 -0
  24. package/docs/zh/database.md +44 -0
  25. package/package.json +1 -1
  26. package/src/database/connection-manager.ts +5 -46
  27. package/src/database/connection-pool.ts +26 -49
  28. package/src/database/database-module.ts +6 -0
  29. package/src/database/db-proxy.ts +3 -2
  30. package/src/database/driver.ts +368 -0
  31. package/src/database/index.ts +8 -0
  32. package/src/database/service.ts +3 -74
  33. package/src/database/sql-manager.ts +38 -24
  34. package/src/database/types.ts +27 -2
  35. package/src/di/module-registry.ts +15 -0
  36. package/src/di/module.ts +15 -1
  37. package/src/index.ts +1 -0
  38. package/tests/database/database-module.test.ts +4 -0
  39. package/tests/database/driver-mysql2.test.ts +95 -0
  40. package/tests/database/driver.test.ts +234 -0
  41. package/tests/database/orm.test.ts +4 -0
  42. package/tests/di/module.test.ts +74 -0
@@ -5581,6 +5581,13 @@ class ModuleRegistry {
5581
5581
  });
5582
5582
  continue;
5583
5583
  }
5584
+ if ("useExisting" in provider) {
5585
+ container.register(provider.provide, {
5586
+ lifecycle: provider.lifecycle ?? "singleton" /* Singleton */,
5587
+ factory: () => container.resolve(provider.useExisting)
5588
+ });
5589
+ continue;
5590
+ }
5584
5591
  if ("useClass" in provider) {
5585
5592
  const token = provider.provide ?? provider.useClass;
5586
5593
  container.register(token, {
@@ -5660,6 +5667,12 @@ class ModuleRegistry {
5660
5667
  seen.add(instance);
5661
5668
  instances.push(instance);
5662
5669
  }
5670
+ } else if ("useExisting" in provider) {
5671
+ const instance = ref.container.resolve(provider.provide);
5672
+ if (!seen.has(instance)) {
5673
+ seen.add(instance);
5674
+ instances.push(instance);
5675
+ }
5663
5676
  }
5664
5677
  } catch (_error) {}
5665
5678
  }
@@ -10852,6 +10865,197 @@ init_decorators();
10852
10865
  // src/database/connection-pool.ts
10853
10866
  init_runtime();
10854
10867
 
10868
+ // src/database/driver.ts
10869
+ var DRIVER_TAG = Symbol.for("@dangao/bun-server:database:driver");
10870
+ function resolveDriver(dbType, option, engine) {
10871
+ const driver = option ?? "auto";
10872
+ switch (driver) {
10873
+ case "auto":
10874
+ if (engine === "bun") {
10875
+ return "bun-sql";
10876
+ }
10877
+ return dbType === "mysql" ? "mysql2" : "postgres";
10878
+ case "bun-sql":
10879
+ if (engine !== "bun") {
10880
+ throw new Error(`[bun-server] driver 'bun-sql' requires the Bun runtime, but the current platform engine is '${engine}'. ` + `Use driver 'auto' / 'mysql2' / 'postgres', or run on Bun.`);
10881
+ }
10882
+ return "bun-sql";
10883
+ case "mysql2":
10884
+ if (dbType !== "mysql") {
10885
+ throw new Error(`[bun-server] driver 'mysql2' is only valid for type 'mysql', but got type '${dbType}'.`);
10886
+ }
10887
+ return "mysql2";
10888
+ case "postgres":
10889
+ if (dbType !== "postgres") {
10890
+ throw new Error(`[bun-server] driver 'postgres' is only valid for type 'postgres', but got type '${dbType}'.`);
10891
+ }
10892
+ return "postgres";
10893
+ default:
10894
+ throw new Error(`[bun-server] unknown driver '${String(driver)}'.`);
10895
+ }
10896
+ }
10897
+ function tagConnection(connection, driver) {
10898
+ if (connection && (typeof connection === "object" || typeof connection === "function")) {
10899
+ try {
10900
+ Object.defineProperty(connection, DRIVER_TAG, {
10901
+ value: driver,
10902
+ enumerable: false,
10903
+ configurable: true,
10904
+ writable: true
10905
+ });
10906
+ } catch {}
10907
+ }
10908
+ return connection;
10909
+ }
10910
+ function getConnectionDriver(connection) {
10911
+ if (!connection) {
10912
+ return;
10913
+ }
10914
+ if (typeof connection === "object" || typeof connection === "function") {
10915
+ const tagged = connection[DRIVER_TAG];
10916
+ if (tagged === "bun-sql" || tagged === "mysql2" || tagged === "postgres") {
10917
+ return tagged;
10918
+ }
10919
+ }
10920
+ if (typeof connection === "function") {
10921
+ return "bun-sql";
10922
+ }
10923
+ return;
10924
+ }
10925
+ async function createPostgresConnection(config, driver) {
10926
+ const url = `postgres://${config.user}:${config.password}@${config.host}:${config.port}/${config.database}`;
10927
+ if (driver === "bun-sql") {
10928
+ const { SQL } = await import("bun");
10929
+ return tagConnection(new SQL({
10930
+ adapter: "postgres",
10931
+ hostname: config.host,
10932
+ port: config.port,
10933
+ username: config.user,
10934
+ password: config.password,
10935
+ database: config.database,
10936
+ max: 1,
10937
+ tls: config.ssl ?? false
10938
+ }), "bun-sql");
10939
+ }
10940
+ if (driver === "postgres") {
10941
+ const postgres = loadPostgres();
10942
+ return tagConnection(postgres(url, { max: 1, ssl: config.ssl ? "require" : false }), "postgres");
10943
+ }
10944
+ throw new Error(`[bun-server] driver '${driver}' cannot create a postgres connection.`);
10945
+ }
10946
+ async function createMysqlConnection(config, driver) {
10947
+ if (driver === "bun-sql") {
10948
+ const { SQL } = await import("bun");
10949
+ return tagConnection(new SQL({
10950
+ adapter: "mysql",
10951
+ hostname: config.host,
10952
+ port: config.port,
10953
+ username: config.user,
10954
+ password: config.password,
10955
+ database: config.database,
10956
+ max: 1,
10957
+ ssl: config.ssl ?? false
10958
+ }), "bun-sql");
10959
+ }
10960
+ if (driver === "mysql2") {
10961
+ const mysql2 = loadMysql2();
10962
+ const conn = await mysql2.createConnection({
10963
+ host: config.host,
10964
+ port: config.port,
10965
+ database: config.database,
10966
+ user: config.user,
10967
+ password: config.password
10968
+ });
10969
+ return tagConnection(conn, "mysql2");
10970
+ }
10971
+ throw new Error(`[bun-server] driver '${driver}' cannot create a mysql connection.`);
10972
+ }
10973
+ async function queryViaDriver(connection, sql, params) {
10974
+ const driver = getConnectionDriver(connection);
10975
+ if (driver === "mysql2") {
10976
+ const conn = connection;
10977
+ const [rows] = await conn.query(sql, params ?? []);
10978
+ return rows ?? [];
10979
+ }
10980
+ if (driver === "postgres") {
10981
+ const conn = connection;
10982
+ const rows = await conn.unsafe(sql, params ?? []);
10983
+ return rows ?? [];
10984
+ }
10985
+ if (typeof connection === "function") {
10986
+ const { strings, values } = buildTemplateFromSql(sql, params);
10987
+ const template = Object.assign(strings.slice(), {
10988
+ raw: strings.slice()
10989
+ });
10990
+ const result = await connection(template, ...values);
10991
+ return result;
10992
+ }
10993
+ if (connection && typeof connection === "object" && "query" in connection && typeof connection.query === "function") {
10994
+ const result = await connection.query(sql, params ?? []);
10995
+ return Array.isArray(result) ? result[0] : result;
10996
+ }
10997
+ throw new Error("[bun-server] invalid SQL connection for query.");
10998
+ }
10999
+ async function templateQueryViaDriver(connection, strings, values) {
11000
+ const driver = getConnectionDriver(connection);
11001
+ if (driver === "mysql2") {
11002
+ const sql = strings.join("?");
11003
+ const conn = connection;
11004
+ const [rows] = await conn.query(sql, values);
11005
+ return rows;
11006
+ }
11007
+ return await connection(strings, ...values);
11008
+ }
11009
+ async function healthCheckViaDriver(connection) {
11010
+ try {
11011
+ const rows = await queryViaDriver(connection, "SELECT 1");
11012
+ return Array.isArray(rows);
11013
+ } catch {
11014
+ return false;
11015
+ }
11016
+ }
11017
+ async function closeViaDriver(connection) {
11018
+ if (!connection || typeof connection !== "object" && typeof connection !== "function") {
11019
+ return;
11020
+ }
11021
+ const driver = getConnectionDriver(connection);
11022
+ const conn = connection;
11023
+ if (driver === "mysql2" || driver === "postgres") {
11024
+ if (typeof conn.end === "function") {
11025
+ await conn.end();
11026
+ return;
11027
+ }
11028
+ }
11029
+ if (driver === "bun-sql") {
11030
+ if (typeof conn.close === "function") {
11031
+ await conn.close();
11032
+ return;
11033
+ }
11034
+ }
11035
+ if (typeof conn.close === "function") {
11036
+ await conn.close();
11037
+ } else if (typeof conn.end === "function") {
11038
+ await conn.end();
11039
+ }
11040
+ }
11041
+ function buildTemplateFromSql(sql, params) {
11042
+ if (!params || params.length === 0) {
11043
+ return { strings: [sql], values: [] };
11044
+ }
11045
+ const strings = sql.split("?");
11046
+ if (strings.length !== params.length + 1) {
11047
+ throw new Error("SQL placeholders count does not match parameters count");
11048
+ }
11049
+ return { strings, values: params };
11050
+ }
11051
+ function loadMysql2() {
11052
+ return __require("mysql2/promise");
11053
+ }
11054
+ function loadPostgres() {
11055
+ return __require("postgres");
11056
+ }
11057
+
11058
+ // src/database/connection-pool.ts
10855
11059
  class ConnectionPool {
10856
11060
  config;
10857
11061
  options;
@@ -10966,29 +11170,12 @@ Install it with: bun add @vscode/sqlite3@5.1.12-vscode`);
10966
11170
  return new sqlite3.Database(config.path);
10967
11171
  }
10968
11172
  async createPostgresConnection(config) {
10969
- const url = `postgres://${config.user}:${config.password}@${config.host}:${config.port}/${config.database}`;
10970
- if (getRuntime().engine === "bun") {
10971
- const { SQL } = await import("bun");
10972
- return new SQL(url, { max: 1, tls: config.ssl ?? false });
10973
- }
10974
- const postgres = __require("postgres");
10975
- return postgres(url, { max: 1, ssl: config.ssl ? "require" : false });
11173
+ const driver = resolveDriver("postgres", this.config.type === "postgres" ? this.config.driver : undefined, getRuntime().engine);
11174
+ return await createPostgresConnection(config, driver);
10976
11175
  }
10977
11176
  async createMysqlConnection(config) {
10978
- if (getRuntime().engine === "bun") {
10979
- const url = `mysql://${config.user}:${config.password}@${config.host}:${config.port}/${config.database}`;
10980
- const { SQL } = await import("bun");
10981
- return new SQL(url, { max: 1 });
10982
- }
10983
- const mysql2 = __require("mysql2/promise");
10984
- const conn = await mysql2.createConnection({
10985
- host: config.host,
10986
- port: config.port,
10987
- database: config.database,
10988
- user: config.user,
10989
- password: config.password
10990
- });
10991
- return conn;
11177
+ const driver = resolveDriver("mysql", this.config.type === "mysql" ? this.config.driver : undefined, getRuntime().engine);
11178
+ return await createMysqlConnection(config, driver);
10992
11179
  }
10993
11180
  async closeConnection(connection) {
10994
11181
  const dbType = this.config.type;
@@ -11005,15 +11192,11 @@ Install it with: bun add @vscode/sqlite3@5.1.12-vscode`);
11005
11192
  connection.close();
11006
11193
  }
11007
11194
  }
11008
- async closePostgresConnection(_connection) {
11009
- if (_connection && typeof _connection === "object" && "close" in _connection && typeof _connection.close === "function") {
11010
- _connection.close();
11011
- }
11195
+ async closePostgresConnection(connection) {
11196
+ await closeViaDriver(connection);
11012
11197
  }
11013
- async closeMysqlConnection(_connection) {
11014
- if (_connection && typeof _connection === "object" && "close" in _connection && typeof _connection.close === "function") {
11015
- _connection.close();
11016
- }
11198
+ async closeMysqlConnection(connection) {
11199
+ await closeViaDriver(connection);
11017
11200
  }
11018
11201
  async waitForConnection() {
11019
11202
  const startTime = Date.now();
@@ -11154,34 +11337,10 @@ class DatabaseConnectionManager {
11154
11337
  }
11155
11338
  }
11156
11339
  async healthCheckPostgres(connection) {
11157
- try {
11158
- if (connection && typeof connection === "function") {
11159
- const result = await connection`SELECT 1`;
11160
- return Array.isArray(result) && result.length > 0;
11161
- }
11162
- if (connection && typeof connection === "object" && "query" in connection && typeof connection.query === "function") {
11163
- await connection.query("SELECT 1");
11164
- return true;
11165
- }
11166
- return false;
11167
- } catch (_error) {
11168
- return false;
11169
- }
11340
+ return await healthCheckViaDriver(connection);
11170
11341
  }
11171
11342
  async healthCheckMysql(connection) {
11172
- try {
11173
- if (connection && typeof connection === "function") {
11174
- const result = await connection`SELECT 1`;
11175
- return Array.isArray(result) && result.length > 0;
11176
- }
11177
- if (connection && typeof connection === "object" && "query" in connection && typeof connection.query === "function") {
11178
- await connection.query("SELECT 1");
11179
- return true;
11180
- }
11181
- return false;
11182
- } catch (_error) {
11183
- return false;
11184
- }
11343
+ return await healthCheckViaDriver(connection);
11185
11344
  }
11186
11345
  }
11187
11346
 
@@ -11252,7 +11411,7 @@ class DatabaseService2 {
11252
11411
  if (dbType === "sqlite") {
11253
11412
  return this.querySqlite(connection, sql, params);
11254
11413
  } else if (dbType === "postgres" || dbType === "mysql") {
11255
- return this.queryBunSQL(connection, sql, params);
11414
+ return queryViaDriver(connection, sql, params);
11256
11415
  }
11257
11416
  throw new Error(`Query not supported for database type: ${dbType}`);
11258
11417
  }
@@ -11275,37 +11434,6 @@ class DatabaseService2 {
11275
11434
  }
11276
11435
  throw new Error("Invalid SQLite connection");
11277
11436
  }
11278
- async queryBunSQL(connection, sql, params) {
11279
- if (connection && typeof connection === "function") {
11280
- try {
11281
- const { strings, values } = this.buildTemplateFromSql(sql, params);
11282
- const template = Object.assign(strings, {
11283
- raw: strings
11284
- });
11285
- const result = await connection(template, ...values);
11286
- return result;
11287
- } catch (error) {
11288
- const errorMessage = error instanceof Error ? error.message : String(error);
11289
- throw new Error(`Bun.SQL parameterized queries are not fully supported. Consider using template string queries. Original error: ${errorMessage}`);
11290
- }
11291
- }
11292
- if (connection && typeof connection === "object" && "query" in connection && typeof connection.query === "function") {
11293
- const db = connection;
11294
- const result = await db.query(sql, ...params ?? []);
11295
- return result;
11296
- }
11297
- throw new Error("Invalid Bun.SQL connection");
11298
- }
11299
- buildTemplateFromSql(sql, params) {
11300
- if (!params || params.length === 0) {
11301
- return { strings: [sql], values: [] };
11302
- }
11303
- const strings = sql.split("?");
11304
- if (strings.length !== params.length + 1) {
11305
- throw new Error("SQL placeholders count does not match parameters count");
11306
- }
11307
- return { strings, values: params };
11308
- }
11309
11437
  }
11310
11438
  DatabaseService2 = __legacyDecorateClassTS([
11311
11439
  Injectable(),
@@ -11319,7 +11447,6 @@ var ORM_SERVICE_TOKEN = Symbol("@dangao/bun-server:orm:service");
11319
11447
 
11320
11448
  // src/database/sql-manager.ts
11321
11449
  init_runtime();
11322
-
11323
11450
  class BunSQLManager2 {
11324
11451
  instances = new Map;
11325
11452
  defaultTenantId = "default";
@@ -11329,34 +11456,49 @@ class BunSQLManager2 {
11329
11456
  return existing;
11330
11457
  }
11331
11458
  const pool = config.pool ?? {};
11459
+ const driver = resolveDriver(config.type, config.driver, getRuntime().engine);
11332
11460
  let sql;
11333
- if (getRuntime().engine === "bun") {
11461
+ if (driver === "bun-sql") {
11334
11462
  const { SQL } = __require("bun");
11335
- sql = new SQL(config.url, {
11336
- max: pool.max ?? 10,
11337
- idleTimeout: pool.idleTimeout ?? 30,
11338
- maxLifetime: pool.maxLifetime ?? 0,
11339
- connectionTimeout: pool.connectionTimeout ?? 30000
11340
- });
11341
- } else {
11342
- const url = config.url.toLowerCase();
11343
- if (url.startsWith("mysql://") || url.startsWith("mysql2://")) {
11344
- const mysql2 = __require("mysql2/promise");
11345
- sql = mysql2.createPool({
11346
- uri: config.url,
11347
- connectionLimit: pool.max ?? 10,
11348
- waitForConnections: true
11463
+ if (config.type === "mysql") {
11464
+ const parsed = new URL(config.url);
11465
+ sql = new SQL({
11466
+ adapter: "mysql",
11467
+ hostname: parsed.hostname,
11468
+ port: parsed.port ? Number(parsed.port) : 3306,
11469
+ username: decodeURIComponent(parsed.username),
11470
+ password: decodeURIComponent(parsed.password),
11471
+ database: parsed.pathname.replace(/^\//, ""),
11472
+ max: pool.max ?? 10,
11473
+ idleTimeout: pool.idleTimeout ?? 30,
11474
+ maxLifetime: pool.maxLifetime ?? 0,
11475
+ connectionTimeout: pool.connectionTimeout ?? 30000
11349
11476
  });
11350
11477
  } else {
11351
- const postgres = __require("postgres");
11352
- sql = postgres(config.url, {
11478
+ sql = new SQL(config.url, {
11353
11479
  max: pool.max ?? 10,
11354
- idle_timeout: pool.idleTimeout ?? 30,
11355
- max_lifetime: pool.maxLifetime ?? 0,
11356
- connect_timeout: (pool.connectionTimeout ?? 30000) / 1000
11480
+ idleTimeout: pool.idleTimeout ?? 30,
11481
+ maxLifetime: pool.maxLifetime ?? 0,
11482
+ connectionTimeout: pool.connectionTimeout ?? 30000
11357
11483
  });
11358
11484
  }
11485
+ } else if (driver === "mysql2") {
11486
+ const mysql2 = __require("mysql2/promise");
11487
+ sql = mysql2.createPool({
11488
+ uri: config.url,
11489
+ connectionLimit: pool.max ?? 10,
11490
+ waitForConnections: true
11491
+ });
11492
+ } else {
11493
+ const postgres = __require("postgres");
11494
+ sql = postgres(config.url, {
11495
+ max: pool.max ?? 10,
11496
+ idle_timeout: pool.idleTimeout ?? 30,
11497
+ max_lifetime: pool.maxLifetime ?? 0,
11498
+ connect_timeout: (pool.connectionTimeout ?? 30000) / 1000
11499
+ });
11359
11500
  }
11501
+ tagConnection(sql, driver);
11360
11502
  this.instances.set(tenantId, sql);
11361
11503
  return sql;
11362
11504
  }
@@ -11567,10 +11709,10 @@ var baseDb = async (tenantId, strings, ...values) => {
11567
11709
  if (tenantId) {
11568
11710
  const tenantSql = sqlManager.get(tenantId);
11569
11711
  if (tenantSql) {
11570
- return await tenantSql(strings, ...values);
11712
+ return await templateQueryViaDriver(tenantSql, strings, values);
11571
11713
  }
11572
11714
  }
11573
- return await sqlManager.getDefault()(strings, ...values);
11715
+ return await templateQueryViaDriver(sqlManager.getDefault(), strings, values);
11574
11716
  };
11575
11717
  function createDb(tenantId) {
11576
11718
  const fn = async (strings, ...values) => baseDb(tenantId, strings, ...values);
@@ -11644,7 +11786,8 @@ class DatabaseModule {
11644
11786
  config: {
11645
11787
  type: db2.type,
11646
11788
  url,
11647
- pool: options.bunSqlPool
11789
+ pool: options.bunSqlPool,
11790
+ driver: db2.driver ?? options.driver
11648
11791
  }
11649
11792
  }
11650
11793
  ];
@@ -11669,7 +11812,8 @@ class DatabaseModule {
11669
11812
  config: {
11670
11813
  type: options.type,
11671
11814
  url: options.url,
11672
- pool: options.bunSqlPool
11815
+ pool: options.bunSqlPool,
11816
+ driver: options.driver
11673
11817
  }
11674
11818
  }
11675
11819
  ];
@@ -11683,7 +11827,8 @@ class DatabaseModule {
11683
11827
  config: {
11684
11828
  type: options.type,
11685
11829
  url,
11686
- pool: options.bunSqlPool
11830
+ pool: options.bunSqlPool,
11831
+ driver: options.driver
11687
11832
  }
11688
11833
  }
11689
11834
  ];
@@ -11767,7 +11912,8 @@ class DatabaseModule {
11767
11912
  database: options.databasePath ?? "default",
11768
11913
  user: options.username ?? "root",
11769
11914
  password: options.password ?? ""
11770
- }
11915
+ },
11916
+ driver: normalized[0]?.config?.driver ?? options.driver
11771
11917
  }
11772
11918
  };
11773
11919
  const service = new DatabaseService2(legacyOptions);
package/docs/database.md CHANGED
@@ -25,6 +25,50 @@ DatabaseModule.forRoot({
25
25
  });
26
26
  ```
27
27
 
28
+ ## Driver selection (decoupled from the runtime platform)
29
+
30
+ > Added in v3.2.0.
31
+
32
+ By default the underlying driver for Postgres/MySQL is chosen by the runtime platform: the Bun runtime uses the built-in `Bun.SQL`, while Node.js uses the pure-JS `mysql2` / `postgres` drivers.
33
+
34
+ The optional `driver` field lets you **explicitly pick a pure-JS driver even under the Bun runtime**, decoupled from the platform engine (fs/crypto/http server, etc.):
35
+
36
+ | driver | Behavior |
37
+ | --- | --- |
38
+ | `'auto'` (default) | Bun → `Bun.SQL`; Node → `mysql2` (MySQL) / `postgres` (PostgreSQL). Backward compatible. |
39
+ | `'mysql2'` | Always use `mysql2`, regardless of runtime (only for `type: 'mysql'`). |
40
+ | `'postgres'` | Always use `postgres`, regardless of runtime (only for `type: 'postgres'`). |
41
+ | `'bun-sql'` | Force `Bun.SQL` (valid on Bun only; throws a clear error on Node). |
42
+
43
+ ```ts
44
+ DatabaseModule.forRoot({
45
+ database: {
46
+ type: 'mysql',
47
+ driver: 'mysql2', // uses mysql2 even on the Bun runtime
48
+ config: {
49
+ host: '127.0.0.1',
50
+ port: 3306,
51
+ database: 'app',
52
+ user: 'root',
53
+ password: process.env.DB_PASSWORD!,
54
+ },
55
+ },
56
+ });
57
+
58
+ // The V2 single-tenant form is supported too:
59
+ DatabaseModule.forRoot({
60
+ type: 'mysql',
61
+ url: process.env.DB_URL!,
62
+ driver: 'mysql2',
63
+ });
64
+ ```
65
+
66
+ ### Why? MySQL under `bun build --compile`
67
+
68
+ A single binary produced by `bun build --compile` freezes the Bun runtime at build time, and the built-in `Bun.SQL` MySQL adapter has several known issues. Setting `driver: 'mysql2'` lets the compiled binary connect using the statically bundled pure-JS `mysql2` driver, without falling back to SQLite and without switching the whole platform via `BUN_SERVER_PLATFORM=node` (which would also move the HTTP server to Node).
69
+
70
+ The `driver` switch is orthogonal to `BUN_SERVER_PLATFORM`: the former only selects the SQL driver, while the latter selects the entire platform engine.
71
+
28
72
  ## Route strategy
29
73
 
30
74
  ```ts
@@ -25,6 +25,50 @@ DatabaseModule.forRoot({
25
25
  });
26
26
  ```
27
27
 
28
+ ## 驱动选择(driver,与运行时平台解耦)
29
+
30
+ > v3.2.0 新增。
31
+
32
+ 默认情况下,Postgres/MySQL 连接的底层驱动由运行时平台决定:Bun 运行时使用内建 `Bun.SQL`,Node.js 运行时使用 `mysql2` / `postgres` 纯 JS 驱动。
33
+
34
+ 通过可选的 `driver` 字段,可以**在 Bun 运行时下也显式选用纯 JS 驱动**,与平台引擎(fs/crypto/http server 等)解耦:
35
+
36
+ | driver | 行为 |
37
+ | --- | --- |
38
+ | `'auto'`(默认) | Bun → `Bun.SQL`;Node → `mysql2`(MySQL)/ `postgres`(PostgreSQL)。向后兼容。 |
39
+ | `'mysql2'` | 无论运行时如何,都使用 `mysql2`(仅 `type: 'mysql'`)。 |
40
+ | `'postgres'` | 无论运行时如何,都使用 `postgres`(仅 `type: 'postgres'`)。 |
41
+ | `'bun-sql'` | 强制使用 `Bun.SQL`(仅 Bun 合法,Node 会抛出清晰错误)。 |
42
+
43
+ ```ts
44
+ DatabaseModule.forRoot({
45
+ database: {
46
+ type: 'mysql',
47
+ driver: 'mysql2', // 即使在 Bun 运行时也走 mysql2
48
+ config: {
49
+ host: '127.0.0.1',
50
+ port: 3306,
51
+ database: 'app',
52
+ user: 'root',
53
+ password: process.env.DB_PASSWORD!,
54
+ },
55
+ },
56
+ });
57
+
58
+ // V2 单租户写法同样支持:
59
+ DatabaseModule.forRoot({
60
+ type: 'mysql',
61
+ url: process.env.DB_URL!,
62
+ driver: 'mysql2',
63
+ });
64
+ ```
65
+
66
+ ### 为什么需要它?`bun build --compile` 下的 MySQL
67
+
68
+ `bun build --compile` 产出的单二进制会把当时的 Bun 运行时焊死,其中内建 `Bun.SQL` 的 MySQL 适配带有若干已知问题。显式设置 `driver: 'mysql2'` 即可让编译产物使用静态打包的纯 JS `mysql2` 驱动连库,无需为此回退到 SQLite,也无需用 `BUN_SERVER_PLATFORM=node` 整体切平台(那会把 HTTP server 也切到 Node)。
69
+
70
+ `driver` 开关与 `BUN_SERVER_PLATFORM` 正交:前者只决定 SQL 驱动,后者决定整个平台引擎。
71
+
28
72
  ## 路由策略示例
29
73
 
30
74
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dangao/bun-server",
3
- "version": "3.0.5",
3
+ "version": "3.2.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -7,6 +7,7 @@ import type {
7
7
  } from './types';
8
8
 
9
9
  import { ConnectionPool } from './connection-pool';
10
+ import { healthCheckViaDriver } from './driver';
10
11
 
11
12
  /**
12
13
  * 数据库连接管理器
@@ -189,58 +190,16 @@ export class DatabaseConnectionManager {
189
190
  }
190
191
 
191
192
  /**
192
- * PostgreSQL 健康检查(使用 Bun.SQL)
193
+ * PostgreSQL 健康检查(按连接 driver tag 分流)
193
194
  */
194
195
  private async healthCheckPostgres(connection: unknown): Promise<boolean> {
195
- try {
196
- // Bun.SQL 对象可以作为函数调用(模板字符串)
197
- if (connection && typeof connection === 'function') {
198
- const result = await (connection as (template: TemplateStringsArray, ...values: unknown[]) => Promise<unknown[]>)`SELECT 1`;
199
- return Array.isArray(result) && result.length > 0;
200
- }
201
- // 或者使用 query 方法(如果存在)
202
- if (
203
- connection &&
204
- typeof connection === 'object' &&
205
- 'query' in connection &&
206
- typeof connection.query === 'function'
207
- ) {
208
- await (connection as { query: (sql: string) => Promise<unknown> }).query(
209
- 'SELECT 1',
210
- );
211
- return true;
212
- }
213
- return false;
214
- } catch (_error) {
215
- return false;
216
- }
196
+ return await healthCheckViaDriver(connection);
217
197
  }
218
198
 
219
199
  /**
220
- * MySQL 健康检查(使用 Bun.SQL)
200
+ * MySQL 健康检查(按连接 driver tag 分流)
221
201
  */
222
202
  private async healthCheckMysql(connection: unknown): Promise<boolean> {
223
- try {
224
- // Bun.SQL 对象可以作为函数调用(模板字符串)
225
- if (connection && typeof connection === 'function') {
226
- const result = await (connection as (template: TemplateStringsArray, ...values: unknown[]) => Promise<unknown[]>)`SELECT 1`;
227
- return Array.isArray(result) && result.length > 0;
228
- }
229
- // 或者使用 query 方法(如果存在)
230
- if (
231
- connection &&
232
- typeof connection === 'object' &&
233
- 'query' in connection &&
234
- typeof connection.query === 'function'
235
- ) {
236
- await (connection as { query: (sql: string) => Promise<unknown> }).query(
237
- 'SELECT 1',
238
- );
239
- return true;
240
- }
241
- return false;
242
- } catch (_error) {
243
- return false;
244
- }
203
+ return await healthCheckViaDriver(connection);
245
204
  }
246
205
  }