@chidchanun/bcp 0.2.2 → 0.2.3

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.
@@ -1,15 +1,29 @@
1
- // packages/client/src/database.ts
1
+ // packages/client/src/database-mysql.ts
2
2
  var MYSQL_MODULE_SPECIFIER = "mysql2/promise";
3
- var BcpDatabase = class {
3
+ function createMysqlDatabaseAdapter(options) {
4
+ return new MysqlDatabaseAdapter(
5
+ options
6
+ );
7
+ }
8
+ var MysqlDatabaseAdapter = class {
9
+ driver = "mysql";
4
10
  options;
5
11
  poolPromise = null;
6
- constructor(options = {}) {
7
- this.options = {
8
- ...options
9
- };
12
+ constructor(options) {
13
+ this.options = options;
14
+ }
15
+ async connect() {
16
+ if (!this.poolPromise) {
17
+ this.poolPromise = createMysqlPool(
18
+ this.options
19
+ );
20
+ }
21
+ await this.poolPromise;
10
22
  }
11
23
  async query(sql, parameters) {
12
- assertSql(sql);
24
+ assertSql(
25
+ sql
26
+ );
13
27
  const pool = await this.getPool();
14
28
  const [rows] = await pool.query(
15
29
  sql,
@@ -18,7 +32,9 @@ var BcpDatabase = class {
18
32
  return rows;
19
33
  }
20
34
  async execute(sql, parameters) {
21
- assertSql(sql);
35
+ assertSql(
36
+ sql
37
+ );
22
38
  const pool = await this.getPool();
23
39
  const [result] = await pool.execute(
24
40
  sql,
@@ -27,17 +43,17 @@ var BcpDatabase = class {
27
43
  return result;
28
44
  }
29
45
  async transaction(callback) {
30
- if (typeof callback !== "function") {
31
- throw new TypeError(
32
- "BCP Database: transaction callback must be a function."
33
- );
34
- }
46
+ assertTransactionCallback(
47
+ callback
48
+ );
35
49
  const pool = await this.getPool();
36
50
  const connection = await pool.getConnection();
37
51
  await connection.beginTransaction();
38
52
  const transactionDatabase = {
39
53
  query: async (sql, parameters) => {
40
- assertSql(sql);
54
+ assertSql(
55
+ sql
56
+ );
41
57
  const [rows] = await connection.query(
42
58
  sql,
43
59
  parameters
@@ -45,7 +61,9 @@ var BcpDatabase = class {
45
61
  return rows;
46
62
  },
47
63
  execute: async (sql, parameters) => {
48
- assertSql(sql);
64
+ assertSql(
65
+ sql
66
+ );
49
67
  const [result] = await connection.execute(
50
68
  sql,
51
69
  parameters
@@ -69,62 +87,612 @@ var BcpDatabase = class {
69
87
  connection.release();
70
88
  }
71
89
  }
72
- async close() {
90
+ async disconnect() {
73
91
  const pendingPool = this.poolPromise;
74
92
  this.poolPromise = null;
75
93
  if (!pendingPool) {
76
94
  return;
77
95
  }
78
- const pool = await pendingPool;
96
+ let pool;
97
+ try {
98
+ pool = await pendingPool;
99
+ } catch {
100
+ return;
101
+ }
79
102
  await pool.end();
80
103
  }
81
- getPool() {
104
+ async getPool() {
105
+ await this.connect();
106
+ return this.poolPromise;
107
+ }
108
+ };
109
+ async function createMysqlPool(options) {
110
+ let mysqlModule;
111
+ try {
112
+ mysqlModule = await import(MYSQL_MODULE_SPECIFIER);
113
+ } catch (error) {
114
+ throw new Error(
115
+ "BCP Database: MySQL requires the optional dependency mysql2. Install it with `npm install mysql2` or create the app with the MySQL preset.",
116
+ {
117
+ cause: error
118
+ }
119
+ );
120
+ }
121
+ return mysqlModule.createPool({
122
+ host: options.host,
123
+ port: options.port,
124
+ user: options.user,
125
+ password: options.password,
126
+ database: options.database,
127
+ waitForConnections: options.waitForConnections,
128
+ connectionLimit: options.connectionLimit,
129
+ queueLimit: options.queueLimit,
130
+ charset: options.charset
131
+ });
132
+ }
133
+ function assertSql(sql) {
134
+ if (typeof sql !== "string" || sql.trim() === "") {
135
+ throw new TypeError(
136
+ "BCP Database: SQL must be a non-empty string."
137
+ );
138
+ }
139
+ }
140
+ function assertTransactionCallback(callback) {
141
+ if (typeof callback !== "function") {
142
+ throw new TypeError(
143
+ "BCP Database: transaction callback must be a function."
144
+ );
145
+ }
146
+ }
147
+
148
+ // packages/client/src/database-postgresql.ts
149
+ var POSTGRESQL_MODULE_SPECIFIER = "pg";
150
+ function createPostgresqlDatabaseAdapter(options) {
151
+ return new PostgresqlDatabaseAdapter(
152
+ options
153
+ );
154
+ }
155
+ var PostgresqlDatabaseAdapter = class {
156
+ driver = "postgresql";
157
+ options;
158
+ poolPromise = null;
159
+ constructor(options) {
160
+ this.options = options;
161
+ }
162
+ async connect() {
82
163
  if (!this.poolPromise) {
83
- this.poolPromise = createMysqlPool(
84
- resolveDatabaseOptions(
85
- this.options
86
- )
164
+ this.poolPromise = createPostgresqlPool(
165
+ this.options
166
+ );
167
+ }
168
+ await this.poolPromise;
169
+ }
170
+ async query(sql, parameters) {
171
+ assertSql2(
172
+ sql
173
+ );
174
+ const pool = await this.getPool();
175
+ const result = await pool.query(
176
+ sql,
177
+ normalizePostgresqlParameters(
178
+ parameters
179
+ )
180
+ );
181
+ return result.rows;
182
+ }
183
+ async execute(sql, parameters) {
184
+ assertSql2(
185
+ sql
186
+ );
187
+ const pool = await this.getPool();
188
+ const result = await pool.query(
189
+ sql,
190
+ normalizePostgresqlParameters(
191
+ parameters
192
+ )
193
+ );
194
+ return result;
195
+ }
196
+ async transaction(callback) {
197
+ assertTransactionCallback2(
198
+ callback
199
+ );
200
+ const pool = await this.getPool();
201
+ const connection = await pool.connect();
202
+ await connection.query(
203
+ "BEGIN"
204
+ );
205
+ const transactionDatabase = {
206
+ query: async (sql, parameters) => {
207
+ assertSql2(
208
+ sql
209
+ );
210
+ const result = await connection.query(
211
+ sql,
212
+ normalizePostgresqlParameters(
213
+ parameters
214
+ )
215
+ );
216
+ return result.rows;
217
+ },
218
+ execute: async (sql, parameters) => {
219
+ assertSql2(
220
+ sql
221
+ );
222
+ const result = await connection.query(
223
+ sql,
224
+ normalizePostgresqlParameters(
225
+ parameters
226
+ )
227
+ );
228
+ return result;
229
+ }
230
+ };
231
+ try {
232
+ const result = await callback(
233
+ transactionDatabase
234
+ );
235
+ await connection.query(
236
+ "COMMIT"
87
237
  );
238
+ return result;
239
+ } catch (error) {
240
+ try {
241
+ await connection.query(
242
+ "ROLLBACK"
243
+ );
244
+ } catch {
245
+ }
246
+ throw error;
247
+ } finally {
248
+ connection.release();
249
+ }
250
+ }
251
+ async disconnect() {
252
+ const pendingPool = this.poolPromise;
253
+ this.poolPromise = null;
254
+ if (!pendingPool) {
255
+ return;
256
+ }
257
+ let pool;
258
+ try {
259
+ pool = await pendingPool;
260
+ } catch {
261
+ return;
88
262
  }
263
+ await pool.end();
264
+ }
265
+ async getPool() {
266
+ await this.connect();
89
267
  return this.poolPromise;
90
268
  }
91
269
  };
270
+ async function createPostgresqlPool(options) {
271
+ let postgresqlModule;
272
+ try {
273
+ postgresqlModule = await import(POSTGRESQL_MODULE_SPECIFIER);
274
+ } catch (error) {
275
+ throw new Error(
276
+ "BCP Database: PostgreSQL requires the optional dependency pg. Install it with `npm install pg` or create the app with the PostgreSQL preset.",
277
+ {
278
+ cause: error
279
+ }
280
+ );
281
+ }
282
+ const Pool = postgresqlModule.Pool ?? postgresqlModule.default?.Pool;
283
+ if (typeof Pool !== "function") {
284
+ throw new Error(
285
+ "BCP Database: pg did not expose a compatible Pool constructor."
286
+ );
287
+ }
288
+ const poolOptions = options.url ? {
289
+ connectionString: options.url,
290
+ max: options.connectionLimit
291
+ } : {
292
+ host: options.host,
293
+ port: options.port,
294
+ user: options.user,
295
+ password: options.password,
296
+ database: options.database,
297
+ max: options.connectionLimit
298
+ };
299
+ return new Pool(
300
+ poolOptions
301
+ );
302
+ }
303
+ function normalizePostgresqlParameters(parameters) {
304
+ if (parameters === void 0) {
305
+ return void 0;
306
+ }
307
+ if (Array.isArray(
308
+ parameters
309
+ )) {
310
+ return parameters;
311
+ }
312
+ throw new TypeError(
313
+ "BCP Database: PostgreSQL parameters must be an array. Use $1, $2, ... placeholders."
314
+ );
315
+ }
316
+ function assertSql2(sql) {
317
+ if (typeof sql !== "string" || sql.trim() === "") {
318
+ throw new TypeError(
319
+ "BCP Database: SQL must be a non-empty string."
320
+ );
321
+ }
322
+ }
323
+ function assertTransactionCallback2(callback) {
324
+ if (typeof callback !== "function") {
325
+ throw new TypeError(
326
+ "BCP Database: transaction callback must be a function."
327
+ );
328
+ }
329
+ }
330
+
331
+ // packages/client/src/database-sqlite.ts
332
+ import fs from "node:fs";
333
+ import path from "node:path";
334
+ import {
335
+ fileURLToPath
336
+ } from "node:url";
337
+ var SQLITE_MODULE_SPECIFIER = "better-sqlite3";
338
+ function createSqliteDatabaseAdapter(options) {
339
+ return new SqliteDatabaseAdapter(
340
+ options
341
+ );
342
+ }
343
+ var SqliteDatabaseAdapter = class {
344
+ driver = "sqlite";
345
+ options;
346
+ databasePromise = null;
347
+ operationTail = Promise.resolve();
348
+ constructor(options) {
349
+ this.options = options;
350
+ }
351
+ async connect() {
352
+ await this.getDatabase();
353
+ }
354
+ async query(sql, parameters) {
355
+ return this.runExclusive(
356
+ async () => {
357
+ const database = await this.getDatabase();
358
+ return runSqliteQuery(
359
+ database,
360
+ sql,
361
+ parameters
362
+ );
363
+ }
364
+ );
365
+ }
366
+ async execute(sql, parameters) {
367
+ return this.runExclusive(
368
+ async () => {
369
+ const database = await this.getDatabase();
370
+ return runSqliteExecute(
371
+ database,
372
+ sql,
373
+ parameters
374
+ );
375
+ }
376
+ );
377
+ }
378
+ async transaction(callback) {
379
+ return this.runExclusive(
380
+ async () => {
381
+ const database = await this.getDatabase();
382
+ database.exec(
383
+ "BEGIN"
384
+ );
385
+ const transactionDatabase = {
386
+ query: async (sql, parameters) => runSqliteQuery(
387
+ database,
388
+ sql,
389
+ parameters
390
+ ),
391
+ execute: async (sql, parameters) => runSqliteExecute(
392
+ database,
393
+ sql,
394
+ parameters
395
+ )
396
+ };
397
+ try {
398
+ const result = await callback(
399
+ transactionDatabase
400
+ );
401
+ database.exec(
402
+ "COMMIT"
403
+ );
404
+ return result;
405
+ } catch (error) {
406
+ try {
407
+ database.exec(
408
+ "ROLLBACK"
409
+ );
410
+ } catch {
411
+ }
412
+ throw error;
413
+ }
414
+ }
415
+ );
416
+ }
417
+ async disconnect() {
418
+ await this.runExclusive(
419
+ async () => {
420
+ const pendingDatabase = this.databasePromise;
421
+ this.databasePromise = null;
422
+ if (!pendingDatabase) {
423
+ return;
424
+ }
425
+ let database;
426
+ try {
427
+ database = await pendingDatabase;
428
+ } catch {
429
+ return;
430
+ }
431
+ database.close();
432
+ }
433
+ );
434
+ }
435
+ getDatabase() {
436
+ if (!this.databasePromise) {
437
+ this.databasePromise = createSqliteDatabase(
438
+ this.options
439
+ );
440
+ }
441
+ return this.databasePromise;
442
+ }
443
+ runExclusive(operation) {
444
+ const result = this.operationTail.then(
445
+ operation,
446
+ operation
447
+ );
448
+ this.operationTail = result.then(
449
+ () => void 0,
450
+ () => void 0
451
+ );
452
+ return result;
453
+ }
454
+ };
455
+ async function createSqliteDatabase(options) {
456
+ let sqliteModule;
457
+ try {
458
+ sqliteModule = await import(SQLITE_MODULE_SPECIFIER);
459
+ } catch (error) {
460
+ throw new Error(
461
+ "BCP Database: SQLite requires the optional dependency better-sqlite3. Install it with `npm install better-sqlite3` or create the app with the SQLite preset.",
462
+ {
463
+ cause: error
464
+ }
465
+ );
466
+ }
467
+ const Database = sqliteModule.default;
468
+ if (typeof Database !== "function") {
469
+ throw new Error(
470
+ "BCP Database: better-sqlite3 did not expose a compatible database constructor."
471
+ );
472
+ }
473
+ const filename = resolveSqliteFilename(
474
+ options.url ?? options.database
475
+ );
476
+ if (filename !== ":memory:") {
477
+ const resolvedFilename = path.resolve(
478
+ filename
479
+ );
480
+ fs.mkdirSync(
481
+ path.dirname(
482
+ resolvedFilename
483
+ ),
484
+ {
485
+ recursive: true
486
+ }
487
+ );
488
+ return new Database(
489
+ resolvedFilename
490
+ );
491
+ }
492
+ return new Database(
493
+ filename
494
+ );
495
+ }
496
+ function runSqliteQuery(database, sql, parameters) {
497
+ assertSql3(
498
+ sql
499
+ );
500
+ const statement = database.prepare(
501
+ sql
502
+ );
503
+ return callStatement(
504
+ statement.all.bind(
505
+ statement
506
+ ),
507
+ parameters
508
+ );
509
+ }
510
+ function runSqliteExecute(database, sql, parameters) {
511
+ assertSql3(
512
+ sql
513
+ );
514
+ const statement = database.prepare(
515
+ sql
516
+ );
517
+ return callStatement(
518
+ statement.run.bind(
519
+ statement
520
+ ),
521
+ parameters
522
+ );
523
+ }
524
+ function callStatement(operation, parameters) {
525
+ if (parameters === void 0) {
526
+ return operation();
527
+ }
528
+ if (isParameterArray(
529
+ parameters
530
+ )) {
531
+ return operation(
532
+ ...parameters
533
+ );
534
+ }
535
+ return operation(
536
+ parameters
537
+ );
538
+ }
539
+ function isParameterArray(value) {
540
+ return Array.isArray(
541
+ value
542
+ );
543
+ }
544
+ function resolveSqliteFilename(value) {
545
+ const normalized = value.trim();
546
+ if (normalized === "" || normalized === ":memory:") {
547
+ return normalized || ":memory:";
548
+ }
549
+ if (normalized.startsWith(
550
+ "file:"
551
+ )) {
552
+ return fileURLToPath(
553
+ normalized
554
+ );
555
+ }
556
+ if (normalized.startsWith(
557
+ "sqlite://"
558
+ )) {
559
+ return normalized.slice(
560
+ "sqlite://".length
561
+ );
562
+ }
563
+ if (normalized.startsWith(
564
+ "sqlite:"
565
+ )) {
566
+ return normalized.slice(
567
+ "sqlite:".length
568
+ );
569
+ }
570
+ return normalized;
571
+ }
572
+ function assertSql3(sql) {
573
+ if (typeof sql !== "string" || sql.trim() === "") {
574
+ throw new TypeError(
575
+ "BCP Database: SQL must be a non-empty string."
576
+ );
577
+ }
578
+ }
579
+
580
+ // packages/client/src/database.ts
581
+ var BcpDatabase = class {
582
+ options;
583
+ adapterPromise = null;
584
+ constructor(options = {}) {
585
+ this.options = {
586
+ ...options
587
+ };
588
+ }
589
+ async connect() {
590
+ await this.getAdapter();
591
+ }
592
+ async query(sql, parameters) {
593
+ assertSql4(
594
+ sql
595
+ );
596
+ const adapter = await this.getAdapter();
597
+ return adapter.query(
598
+ sql,
599
+ parameters
600
+ );
601
+ }
602
+ async execute(sql, parameters) {
603
+ assertSql4(
604
+ sql
605
+ );
606
+ const adapter = await this.getAdapter();
607
+ return adapter.execute(
608
+ sql,
609
+ parameters
610
+ );
611
+ }
612
+ async transaction(callback) {
613
+ assertTransactionCallback3(
614
+ callback
615
+ );
616
+ const adapter = await this.getAdapter();
617
+ return adapter.transaction(
618
+ callback
619
+ );
620
+ }
621
+ async disconnect() {
622
+ await this.close();
623
+ }
624
+ async close() {
625
+ const pendingAdapter = this.adapterPromise;
626
+ this.adapterPromise = null;
627
+ if (!pendingAdapter) {
628
+ return;
629
+ }
630
+ let adapter;
631
+ try {
632
+ adapter = await pendingAdapter;
633
+ } catch {
634
+ return;
635
+ }
636
+ await adapter.disconnect();
637
+ }
638
+ async getAdapter() {
639
+ if (!this.adapterPromise) {
640
+ this.adapterPromise = initializeDatabaseAdapter(
641
+ this.options
642
+ );
643
+ }
644
+ try {
645
+ return await this.adapterPromise;
646
+ } catch (error) {
647
+ this.adapterPromise = null;
648
+ throw error;
649
+ }
650
+ }
651
+ };
92
652
  function createDatabase(options = {}) {
93
653
  return new BcpDatabase(
94
654
  options
95
655
  );
96
656
  }
97
657
  function resolveDatabaseOptions(options = {}, environment = process.env) {
98
- const driver = options.driver ?? normalizeDriver(
99
- environment.DB_DRIVER ?? "mysql"
658
+ const url = nonEmpty(
659
+ options.url ?? environment.DATABASE_URL
660
+ ) ?? void 0;
661
+ const driver = normalizeDriver(
662
+ String(
663
+ options.driver ?? environment.DB_DRIVER ?? inferDriverFromDatabaseUrl(
664
+ url
665
+ ) ?? "mysql"
666
+ )
667
+ );
668
+ const defaults = getDriverDefaults(
669
+ driver
100
670
  );
101
- if (driver !== "mysql") {
102
- throw new Error(
103
- `BCP Database: unsupported database driver "${driver}".`
104
- );
105
- }
106
671
  return {
107
672
  driver,
673
+ ...url ? {
674
+ url
675
+ } : {},
108
676
  host: nonEmpty(
109
677
  options.host ?? environment.DB_HOST
110
- ) ?? "localhost",
678
+ ) ?? defaults.host,
111
679
  port: resolvePositiveInteger(
112
680
  options.port ?? environment.DB_PORT,
113
- 3306,
681
+ defaults.port,
114
682
  "DB_PORT"
115
683
  ),
116
684
  user: nonEmpty(
117
685
  options.user ?? environment.DB_USER
118
- ) ?? "root",
686
+ ) ?? defaults.user,
119
687
  password: String(
120
688
  options.password ?? environment.DB_PASSWORD ?? ""
121
689
  ),
122
690
  database: nonEmpty(
123
691
  options.database ?? environment.DB_NAME
124
- ) ?? "bcp_app",
692
+ ) ?? defaults.database,
125
693
  connectionLimit: resolvePositiveInteger(
126
694
  options.connectionLimit ?? environment.DB_CONNECTION_LIMIT,
127
- 10,
695
+ defaults.connectionLimit,
128
696
  "DB_CONNECTION_LIMIT"
129
697
  ),
130
698
  waitForConnections: options.waitForConnections ?? resolveBoolean(
@@ -139,43 +707,113 @@ function resolveDatabaseOptions(options = {}, environment = process.env) {
139
707
  ),
140
708
  charset: nonEmpty(
141
709
  options.charset ?? environment.DB_CHARSET
142
- ) ?? "utf8mb4"
710
+ ) ?? defaults.charset
143
711
  };
144
712
  }
145
713
  var db = createDatabase();
146
- async function createMysqlPool(options) {
147
- let mysqlModule;
148
- try {
149
- mysqlModule = await import(MYSQL_MODULE_SPECIFIER);
150
- } catch (error) {
151
- throw new Error(
152
- "BCP Database: MySQL requires the optional dependency mysql2. Install it with `npm install mysql2` or create the app with the MySQL preset.",
153
- {
154
- cause: error
155
- }
714
+ async function initializeDatabaseAdapter(options) {
715
+ let adapter;
716
+ if (options.adapter) {
717
+ adapter = typeof options.adapter === "function" ? await options.adapter() : options.adapter;
718
+ } else {
719
+ const resolvedOptions = resolveDatabaseOptions(
720
+ options
156
721
  );
722
+ switch (resolvedOptions.driver) {
723
+ case "postgresql": {
724
+ adapter = createPostgresqlDatabaseAdapter(
725
+ resolvedOptions
726
+ );
727
+ break;
728
+ }
729
+ case "sqlite": {
730
+ adapter = createSqliteDatabaseAdapter(
731
+ resolvedOptions
732
+ );
733
+ break;
734
+ }
735
+ default: {
736
+ adapter = createMysqlDatabaseAdapter(
737
+ resolvedOptions
738
+ );
739
+ break;
740
+ }
741
+ }
157
742
  }
158
- return mysqlModule.createPool({
159
- host: options.host,
160
- port: options.port,
161
- user: options.user,
162
- password: options.password,
163
- database: options.database,
164
- waitForConnections: options.waitForConnections,
165
- connectionLimit: options.connectionLimit,
166
- queueLimit: options.queueLimit,
167
- charset: options.charset
168
- });
743
+ assertDatabaseAdapter(
744
+ adapter
745
+ );
746
+ await adapter.connect();
747
+ return adapter;
169
748
  }
170
749
  function normalizeDriver(value) {
171
750
  const normalized = value.trim().toLowerCase();
172
751
  if (normalized === "mysql") {
173
752
  return "mysql";
174
753
  }
754
+ if (normalized === "postgresql" || normalized === "postgres" || normalized === "pg") {
755
+ return "postgresql";
756
+ }
757
+ if (normalized === "sqlite" || normalized === "sqlite3") {
758
+ return "sqlite";
759
+ }
175
760
  throw new Error(
176
761
  `BCP Database: unsupported database driver "${value}".`
177
762
  );
178
763
  }
764
+ function inferDriverFromDatabaseUrl(value) {
765
+ if (!value) {
766
+ return null;
767
+ }
768
+ if (/^postgres(?:ql)?:\/\//i.test(
769
+ value
770
+ )) {
771
+ return "postgresql";
772
+ }
773
+ if (/^mysql:\/\//i.test(
774
+ value
775
+ )) {
776
+ return "mysql";
777
+ }
778
+ if (value === ":memory:" || /^(?:sqlite|file):/i.test(
779
+ value
780
+ ) || /\.(?:sqlite|sqlite3|db)$/i.test(
781
+ value
782
+ )) {
783
+ return "sqlite";
784
+ }
785
+ return null;
786
+ }
787
+ function getDriverDefaults(driver) {
788
+ if (driver === "postgresql") {
789
+ return {
790
+ host: "localhost",
791
+ port: 5432,
792
+ user: "postgres",
793
+ database: "bcp_app",
794
+ connectionLimit: 10,
795
+ charset: "utf8"
796
+ };
797
+ }
798
+ if (driver === "sqlite") {
799
+ return {
800
+ host: "",
801
+ port: 0,
802
+ user: "",
803
+ database: "./data/bcp.sqlite",
804
+ connectionLimit: 1,
805
+ charset: "utf8"
806
+ };
807
+ }
808
+ return {
809
+ host: "localhost",
810
+ port: 3306,
811
+ user: "root",
812
+ database: "bcp_app",
813
+ connectionLimit: 10,
814
+ charset: "utf8mb4"
815
+ };
816
+ }
179
817
  function nonEmpty(value) {
180
818
  if (typeof value !== "string") {
181
819
  return null;
@@ -221,13 +859,27 @@ function resolveBoolean(value, fallback, label) {
221
859
  `BCP Database: ${label} must be true/false or 1/0.`
222
860
  );
223
861
  }
224
- function assertSql(sql) {
862
+ function assertSql4(sql) {
225
863
  if (typeof sql !== "string" || sql.trim() === "") {
226
864
  throw new TypeError(
227
865
  "BCP Database: SQL must be a non-empty string."
228
866
  );
229
867
  }
230
868
  }
869
+ function assertTransactionCallback3(callback) {
870
+ if (typeof callback !== "function") {
871
+ throw new TypeError(
872
+ "BCP Database: transaction callback must be a function."
873
+ );
874
+ }
875
+ }
876
+ function assertDatabaseAdapter(adapter) {
877
+ if (!adapter || typeof adapter !== "object" || typeof adapter.driver !== "string" || adapter.driver.trim() === "" || typeof adapter.connect !== "function" || typeof adapter.query !== "function" || typeof adapter.execute !== "function" || typeof adapter.transaction !== "function" || typeof adapter.disconnect !== "function") {
878
+ throw new TypeError(
879
+ "BCP Database: adapter must implement driver, connect, query, execute, transaction and disconnect."
880
+ );
881
+ }
882
+ }
231
883
  export {
232
884
  BcpDatabase,
233
885
  createDatabase,