@ereo/db-drizzle 0.1.6

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/index.js ADDED
@@ -0,0 +1,653 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // src/adapter.ts
5
+ import {
6
+ dedupQuery,
7
+ clearDedupCache,
8
+ invalidateTables,
9
+ getRequestDedupStats,
10
+ ConnectionError,
11
+ QueryError,
12
+ TransactionError
13
+ } from "@ereo/db";
14
+
15
+ // src/types.ts
16
+ var EDGE_COMPATIBLE_DRIVERS = {
17
+ "postgres-js": false,
18
+ "neon-http": true,
19
+ "neon-websocket": true,
20
+ planetscale: true,
21
+ libsql: true,
22
+ "bun-sqlite": false,
23
+ "better-sqlite3": false,
24
+ d1: true
25
+ };
26
+
27
+ // src/adapter.ts
28
+ class DrizzleAdapter {
29
+ name;
30
+ edgeCompatible;
31
+ client = null;
32
+ rawConnection = null;
33
+ config;
34
+ isConnected = false;
35
+ constructor(config) {
36
+ this.config = config;
37
+ this.name = `drizzle-${config.driver}`;
38
+ this.edgeCompatible = config.edgeCompatible ?? EDGE_COMPATIBLE_DRIVERS[config.driver];
39
+ }
40
+ async connect() {
41
+ if (this.isConnected && this.client) {
42
+ return;
43
+ }
44
+ try {
45
+ const result = await createDrizzleClient(this.config);
46
+ this.client = result.client;
47
+ this.rawConnection = result.connection;
48
+ this.isConnected = true;
49
+ if (this.config.debug) {
50
+ console.log(`[${this.name}] Connected to database`);
51
+ }
52
+ } catch (error) {
53
+ throw new ConnectionError(`Failed to connect to database: ${error instanceof Error ? error.message : error}`, error instanceof Error ? error : undefined);
54
+ }
55
+ }
56
+ getClient() {
57
+ if (!this.client) {
58
+ throw new ConnectionError("Database not connected. Call connect() first or use the plugin which handles this automatically.");
59
+ }
60
+ return this.client;
61
+ }
62
+ getRequestClient(context) {
63
+ const client = this.getClient();
64
+ const adapter = this;
65
+ return {
66
+ get client() {
67
+ return client;
68
+ },
69
+ async query(sql, params) {
70
+ return dedupQuery(context, sql, params, async () => adapter.query(sql, params));
71
+ },
72
+ getDedupStats() {
73
+ return getRequestDedupStats(context);
74
+ },
75
+ clearDedup() {
76
+ clearDedupCache(context);
77
+ },
78
+ invalidate(tables) {
79
+ if (tables) {
80
+ invalidateTables(context, tables);
81
+ } else {
82
+ clearDedupCache(context);
83
+ }
84
+ }
85
+ };
86
+ }
87
+ async query(sql, params) {
88
+ await this.connect();
89
+ try {
90
+ const result = await executeRawQuery(this.rawConnection, this.config.driver, sql, params);
91
+ return {
92
+ rows: result,
93
+ rowCount: Array.isArray(result) ? result.length : 0
94
+ };
95
+ } catch (error) {
96
+ throw new QueryError(`Query failed: ${error instanceof Error ? error.message : error}`, sql, params, error instanceof Error ? error : undefined);
97
+ }
98
+ }
99
+ async execute(sql, params) {
100
+ await this.connect();
101
+ try {
102
+ const result = await executeRawMutation(this.rawConnection, this.config.driver, sql, params);
103
+ return result;
104
+ } catch (error) {
105
+ throw new QueryError(`Execute failed: ${error instanceof Error ? error.message : error}`, sql, params, error instanceof Error ? error : undefined);
106
+ }
107
+ }
108
+ async transaction(fn, _options) {
109
+ await this.connect();
110
+ try {
111
+ const result = await executeTransaction(this.client, this.config.driver, fn);
112
+ return result;
113
+ } catch (error) {
114
+ throw new TransactionError(`Transaction failed: ${error instanceof Error ? error.message : error}`, error instanceof Error ? error : undefined);
115
+ }
116
+ }
117
+ async beginTransaction(_options) {
118
+ await this.connect();
119
+ let isActive = true;
120
+ let resolveTransaction = null;
121
+ let rejectTransaction = null;
122
+ const transactionPromise = new Promise((resolve, reject) => {
123
+ resolveTransaction = resolve;
124
+ rejectTransaction = reject;
125
+ });
126
+ let transactionClient = null;
127
+ executeTransaction(this.client, this.config.driver, async (tx) => {
128
+ transactionClient = tx;
129
+ return transactionPromise;
130
+ }).catch(rejectTransaction);
131
+ return {
132
+ get client() {
133
+ if (!transactionClient) {
134
+ throw new TransactionError("Transaction not yet started");
135
+ }
136
+ return transactionClient;
137
+ },
138
+ async commit() {
139
+ if (!isActive) {
140
+ throw new TransactionError("Transaction is not active");
141
+ }
142
+ isActive = false;
143
+ resolveTransaction?.(undefined);
144
+ },
145
+ async rollback() {
146
+ if (!isActive) {
147
+ throw new TransactionError("Transaction is not active");
148
+ }
149
+ isActive = false;
150
+ rejectTransaction?.(new Error("Transaction rolled back"));
151
+ },
152
+ get isActive() {
153
+ return isActive;
154
+ }
155
+ };
156
+ }
157
+ async healthCheck() {
158
+ const start = Date.now();
159
+ try {
160
+ await this.connect();
161
+ const testQuery = getHealthCheckQuery(this.config.driver);
162
+ await this.query(testQuery);
163
+ return {
164
+ healthy: true,
165
+ latencyMs: Date.now() - start,
166
+ metadata: {
167
+ driver: this.config.driver,
168
+ edgeCompatible: this.edgeCompatible
169
+ }
170
+ };
171
+ } catch (error) {
172
+ return {
173
+ healthy: false,
174
+ latencyMs: Date.now() - start,
175
+ error: error instanceof Error ? error.message : String(error),
176
+ metadata: {
177
+ driver: this.config.driver
178
+ }
179
+ };
180
+ }
181
+ }
182
+ async disconnect() {
183
+ if (!this.isConnected) {
184
+ return;
185
+ }
186
+ try {
187
+ await closeConnection(this.rawConnection, this.config.driver);
188
+ this.client = null;
189
+ this.rawConnection = null;
190
+ this.isConnected = false;
191
+ if (this.config.debug) {
192
+ console.log(`[${this.name}] Disconnected from database`);
193
+ }
194
+ } catch (error) {
195
+ console.error(`[${this.name}] Error during disconnect:`, error);
196
+ }
197
+ }
198
+ }
199
+ function createDrizzleAdapter(config) {
200
+ return new DrizzleAdapter(config);
201
+ }
202
+ async function createDrizzleClient(config) {
203
+ switch (config.driver) {
204
+ case "postgres-js":
205
+ return createPostgresClient(config);
206
+ case "neon-http":
207
+ return createNeonHttpClient(config);
208
+ case "neon-websocket":
209
+ return createNeonWebSocketClient(config);
210
+ case "planetscale":
211
+ return createPlanetScaleClient(config);
212
+ case "libsql":
213
+ return createLibSQLClient(config);
214
+ case "bun-sqlite":
215
+ return createBunSQLiteClient(config);
216
+ case "better-sqlite3":
217
+ return createBetterSQLite3Client(config);
218
+ case "d1":
219
+ return createD1Client(config);
220
+ default:
221
+ throw new Error(`Unsupported driver: ${config.driver}`);
222
+ }
223
+ }
224
+ async function createPostgresClient(config) {
225
+ const postgres = await import("postgres");
226
+ const drizzleModule = await import("drizzle-orm/postgres-js");
227
+ const connection = postgres.default(config.url, {
228
+ ssl: config.connection?.ssl,
229
+ max: config.connection?.max ?? 10,
230
+ idle_timeout: config.connection?.idle_timeout ?? 20,
231
+ connect_timeout: config.connection?.connect_timeout ?? 10,
232
+ prepare: config.connection?.prepare ?? true
233
+ });
234
+ const client = drizzleModule.drizzle(connection, {
235
+ schema: config.schema,
236
+ logger: config.logger
237
+ });
238
+ return { client, connection };
239
+ }
240
+ async function createNeonHttpClient(config) {
241
+ const neonModule = await import("@neondatabase/serverless");
242
+ const drizzleModule = await import("drizzle-orm/neon-http");
243
+ const connection = neonModule.neon(config.url, config.neon);
244
+ const client = drizzleModule.drizzle(connection, {
245
+ schema: config.schema,
246
+ logger: config.logger
247
+ });
248
+ return { client, connection };
249
+ }
250
+ async function createNeonWebSocketClient(config) {
251
+ const neonModule = await import("@neondatabase/serverless");
252
+ const drizzleModule = await import("drizzle-orm/neon-serverless");
253
+ const connection = new neonModule.Pool({ connectionString: config.url });
254
+ const client = drizzleModule.drizzle(connection, {
255
+ schema: config.schema,
256
+ logger: config.logger
257
+ });
258
+ return { client, connection };
259
+ }
260
+ async function createPlanetScaleClient(config) {
261
+ const psModule = await import("@planetscale/database");
262
+ const drizzleModule = await import("drizzle-orm/planetscale-serverless");
263
+ const connection = new psModule.Client({
264
+ url: config.url,
265
+ fetch: config.planetscale?.fetch
266
+ });
267
+ const client = drizzleModule.drizzle(connection, {
268
+ schema: config.schema,
269
+ logger: config.logger
270
+ });
271
+ return { client, connection };
272
+ }
273
+ async function createLibSQLClient(config) {
274
+ const libsqlModule = await import("@libsql/client");
275
+ const drizzleModule = await import("drizzle-orm/libsql");
276
+ const connection = libsqlModule.createClient({
277
+ url: config.url,
278
+ authToken: config.authToken,
279
+ syncUrl: config.syncUrl
280
+ });
281
+ const client = drizzleModule.drizzle(connection, {
282
+ schema: config.schema,
283
+ logger: config.logger
284
+ });
285
+ return { client, connection };
286
+ }
287
+ async function createBunSQLiteClient(config) {
288
+ const sqliteModule = await import("bun:sqlite");
289
+ const drizzleModule = await import("drizzle-orm/bun-sqlite");
290
+ const connection = new sqliteModule.Database(config.url);
291
+ if (config.pragma) {
292
+ const { journal_mode, synchronous, foreign_keys, cache_size } = config.pragma;
293
+ if (journal_mode)
294
+ connection.exec(`PRAGMA journal_mode = ${journal_mode}`);
295
+ if (synchronous)
296
+ connection.exec(`PRAGMA synchronous = ${synchronous}`);
297
+ if (foreign_keys !== undefined)
298
+ connection.exec(`PRAGMA foreign_keys = ${foreign_keys ? "ON" : "OFF"}`);
299
+ if (cache_size)
300
+ connection.exec(`PRAGMA cache_size = ${cache_size}`);
301
+ }
302
+ const client = drizzleModule.drizzle(connection, {
303
+ schema: config.schema,
304
+ logger: config.logger
305
+ });
306
+ return { client, connection };
307
+ }
308
+ async function createBetterSQLite3Client(config) {
309
+ const Database = await import("better-sqlite3");
310
+ const drizzleModule = await import("drizzle-orm/better-sqlite3");
311
+ const connection = new Database.default(config.url, config.options);
312
+ const client = drizzleModule.drizzle(connection, {
313
+ schema: config.schema,
314
+ logger: config.logger
315
+ });
316
+ return { client, connection };
317
+ }
318
+ async function createD1Client(config) {
319
+ const drizzleModule = await import("drizzle-orm/d1");
320
+ if (!config.d1) {
321
+ throw new Error("D1 database binding is required for d1 driver");
322
+ }
323
+ const client = drizzleModule.drizzle(config.d1, {
324
+ schema: config.schema,
325
+ logger: config.logger
326
+ });
327
+ return { client, connection: config.d1 };
328
+ }
329
+ async function executeRawQuery(connection, driver, sql, params) {
330
+ const conn = connection;
331
+ switch (driver) {
332
+ case "postgres-js": {
333
+ return conn.unsafe(sql, params ?? []);
334
+ }
335
+ case "neon-http": {
336
+ return conn(sql, params ?? []);
337
+ }
338
+ case "neon-websocket": {
339
+ const result = await conn.query(sql, params ?? []);
340
+ return result.rows;
341
+ }
342
+ case "planetscale": {
343
+ const result = await conn.execute(sql, params ?? []);
344
+ return result.rows;
345
+ }
346
+ case "libsql": {
347
+ const result = await conn.execute({ sql, args: params ?? [] });
348
+ return result.rows;
349
+ }
350
+ case "bun-sqlite": {
351
+ const stmt = conn.prepare(sql);
352
+ return stmt.all(...params ?? []);
353
+ }
354
+ case "better-sqlite3": {
355
+ const stmt = conn.prepare(sql);
356
+ return stmt.all(...params ?? []);
357
+ }
358
+ case "d1": {
359
+ const stmt = conn.prepare(sql);
360
+ const bound = params ? stmt.bind(...params) : stmt;
361
+ const result = await bound.all();
362
+ return result.results ?? [];
363
+ }
364
+ default:
365
+ throw new Error(`Raw query not supported for driver: ${driver}`);
366
+ }
367
+ }
368
+ async function executeRawMutation(connection, driver, sql, params) {
369
+ const conn = connection;
370
+ switch (driver) {
371
+ case "postgres-js": {
372
+ const result = await conn.unsafe(sql, params ?? []);
373
+ return { rowsAffected: result.count ?? 0 };
374
+ }
375
+ case "neon-http": {
376
+ await conn(sql, params ?? []);
377
+ return { rowsAffected: 0 };
378
+ }
379
+ case "neon-websocket": {
380
+ const result = await conn.query(sql, params ?? []);
381
+ return { rowsAffected: result.rowCount ?? 0 };
382
+ }
383
+ case "planetscale": {
384
+ const result = await conn.execute(sql, params ?? []);
385
+ return {
386
+ rowsAffected: result.rowsAffected ?? 0,
387
+ lastInsertId: result.insertId ? BigInt(result.insertId) : undefined
388
+ };
389
+ }
390
+ case "libsql": {
391
+ const result = await conn.execute({ sql, args: params ?? [] });
392
+ return {
393
+ rowsAffected: result.rowsAffected,
394
+ lastInsertId: result.lastInsertRowid
395
+ };
396
+ }
397
+ case "bun-sqlite": {
398
+ const stmt = conn.prepare(sql);
399
+ const result = stmt.run(...params ?? []);
400
+ return {
401
+ rowsAffected: result.changes,
402
+ lastInsertId: result.lastInsertRowid
403
+ };
404
+ }
405
+ case "better-sqlite3": {
406
+ const stmt = conn.prepare(sql);
407
+ const result = stmt.run(...params ?? []);
408
+ return {
409
+ rowsAffected: result.changes,
410
+ lastInsertId: BigInt(result.lastInsertRowid)
411
+ };
412
+ }
413
+ case "d1": {
414
+ const stmt = conn.prepare(sql);
415
+ const bound = params ? stmt.bind(...params) : stmt;
416
+ const result = await bound.run();
417
+ return {
418
+ rowsAffected: result.meta.changes ?? 0,
419
+ lastInsertId: result.meta.last_row_id
420
+ };
421
+ }
422
+ default:
423
+ throw new Error(`Raw mutation not supported for driver: ${driver}`);
424
+ }
425
+ }
426
+ async function executeTransaction(client, driver, fn) {
427
+ const db = client;
428
+ if (typeof db.transaction === "function") {
429
+ return db.transaction(fn);
430
+ }
431
+ if (driver === "bun-sqlite" || driver === "better-sqlite3") {
432
+ throw new TransactionError(`Transaction support for ${driver} requires using the Drizzle client directly`);
433
+ }
434
+ throw new TransactionError(`Transaction not supported for driver: ${driver}`);
435
+ }
436
+ async function closeConnection(connection, driver) {
437
+ const conn = connection;
438
+ switch (driver) {
439
+ case "postgres-js": {
440
+ await conn.end();
441
+ break;
442
+ }
443
+ case "neon-websocket": {
444
+ await conn.end();
445
+ break;
446
+ }
447
+ case "libsql": {
448
+ conn.close();
449
+ break;
450
+ }
451
+ case "bun-sqlite": {
452
+ conn.close();
453
+ break;
454
+ }
455
+ case "better-sqlite3": {
456
+ conn.close();
457
+ break;
458
+ }
459
+ case "neon-http":
460
+ case "planetscale":
461
+ case "d1":
462
+ break;
463
+ }
464
+ }
465
+ function getHealthCheckQuery(driver) {
466
+ switch (driver) {
467
+ case "postgres-js":
468
+ case "neon-http":
469
+ case "neon-websocket":
470
+ return "SELECT 1";
471
+ case "planetscale":
472
+ return "SELECT 1";
473
+ case "libsql":
474
+ case "bun-sqlite":
475
+ case "better-sqlite3":
476
+ case "d1":
477
+ return "SELECT 1";
478
+ default:
479
+ return "SELECT 1";
480
+ }
481
+ }
482
+ // src/config.ts
483
+ function defineDrizzleConfig(config) {
484
+ return config;
485
+ }
486
+ function definePostgresConfig(config) {
487
+ return {
488
+ driver: "postgres-js",
489
+ connection: {
490
+ ssl: "require",
491
+ max: 10,
492
+ idle_timeout: 20,
493
+ connect_timeout: 10,
494
+ prepare: true
495
+ },
496
+ ...config
497
+ };
498
+ }
499
+ function defineNeonHttpConfig(config) {
500
+ return {
501
+ driver: "neon-http",
502
+ edgeCompatible: true,
503
+ ...config
504
+ };
505
+ }
506
+ function defineNeonWebSocketConfig(config) {
507
+ return {
508
+ driver: "neon-websocket",
509
+ edgeCompatible: true,
510
+ pool: {
511
+ max: 5,
512
+ idleTimeoutMs: 1e4
513
+ },
514
+ ...config
515
+ };
516
+ }
517
+ function definePlanetScaleConfig(config) {
518
+ return {
519
+ driver: "planetscale",
520
+ edgeCompatible: true,
521
+ ...config
522
+ };
523
+ }
524
+ function defineLibSQLConfig(config) {
525
+ return {
526
+ driver: "libsql",
527
+ edgeCompatible: true,
528
+ ...config
529
+ };
530
+ }
531
+ function defineBunSQLiteConfig(config) {
532
+ return {
533
+ driver: "bun-sqlite",
534
+ edgeCompatible: false,
535
+ pragma: {
536
+ journal_mode: "WAL",
537
+ synchronous: "NORMAL",
538
+ foreign_keys: true,
539
+ cache_size: 1e4
540
+ },
541
+ ...config
542
+ };
543
+ }
544
+ function defineBetterSQLite3Config(config) {
545
+ return {
546
+ driver: "better-sqlite3",
547
+ edgeCompatible: false,
548
+ ...config
549
+ };
550
+ }
551
+ function defineD1Config(config) {
552
+ return {
553
+ driver: "d1",
554
+ edgeCompatible: true,
555
+ ...config
556
+ };
557
+ }
558
+ function defineEdgeConfig(options) {
559
+ const { driver, url, schema, authToken, debug } = options;
560
+ const baseConfig = {
561
+ url,
562
+ schema,
563
+ debug,
564
+ edgeCompatible: true
565
+ };
566
+ switch (driver) {
567
+ case "neon-http":
568
+ return defineNeonHttpConfig(baseConfig);
569
+ case "neon-websocket":
570
+ return defineNeonWebSocketConfig(baseConfig);
571
+ case "planetscale":
572
+ return definePlanetScaleConfig(baseConfig);
573
+ case "libsql":
574
+ return defineLibSQLConfig({
575
+ ...baseConfig,
576
+ authToken
577
+ });
578
+ case "d1":
579
+ return defineD1Config(baseConfig);
580
+ default:
581
+ throw new Error(`Unknown edge driver: ${driver}`);
582
+ }
583
+ }
584
+ function detectRuntime() {
585
+ if (typeof globalThis !== "undefined" && "Bun" in globalThis) {
586
+ return "bun";
587
+ }
588
+ if (typeof globalThis !== "undefined" && "Deno" in globalThis) {
589
+ return "deno";
590
+ }
591
+ if (typeof globalThis !== "undefined" && "caches" in globalThis && typeof globalThis.caches?.default !== "undefined") {
592
+ return "cloudflare-workers";
593
+ }
594
+ if (typeof process !== "undefined" && process.env?.VERCEL_EDGE === "1") {
595
+ return "vercel-edge";
596
+ }
597
+ if (typeof process !== "undefined" && process.versions?.node) {
598
+ return "node";
599
+ }
600
+ return "unknown";
601
+ }
602
+ function isEdgeRuntime() {
603
+ const runtime = detectRuntime();
604
+ return runtime === "cloudflare-workers" || runtime === "vercel-edge";
605
+ }
606
+ function suggestDrivers() {
607
+ const runtime = detectRuntime();
608
+ switch (runtime) {
609
+ case "bun":
610
+ return ["bun-sqlite", "postgres-js", "libsql"];
611
+ case "node":
612
+ return ["better-sqlite3", "postgres-js", "libsql"];
613
+ case "cloudflare-workers":
614
+ return ["d1", "neon-http", "planetscale"];
615
+ case "vercel-edge":
616
+ return ["neon-http", "planetscale", "libsql"];
617
+ case "deno":
618
+ return ["postgres-js", "libsql"];
619
+ default:
620
+ return ["neon-http", "postgres-js"];
621
+ }
622
+ }
623
+
624
+ // src/index.ts
625
+ import {
626
+ createDatabasePlugin,
627
+ useDb,
628
+ useAdapter,
629
+ getDb,
630
+ withTransaction
631
+ } from "@ereo/db";
632
+ export {
633
+ withTransaction,
634
+ useDb,
635
+ useAdapter,
636
+ suggestDrivers,
637
+ isEdgeRuntime,
638
+ getDb,
639
+ detectRuntime,
640
+ definePostgresConfig,
641
+ definePlanetScaleConfig,
642
+ defineNeonWebSocketConfig,
643
+ defineNeonHttpConfig,
644
+ defineLibSQLConfig,
645
+ defineEdgeConfig,
646
+ defineDrizzleConfig,
647
+ defineD1Config,
648
+ defineBunSQLiteConfig,
649
+ defineBetterSQLite3Config,
650
+ createDrizzleAdapter,
651
+ createDatabasePlugin,
652
+ EDGE_COMPATIBLE_DRIVERS
653
+ };