@iskra-bun/db-kit 0.2.0 → 0.3.1
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/CHANGELOG.md +62 -0
- package/README.md +9 -3
- package/dist/index.d.ts +67 -13
- package/dist/index.js +222 -129
- package/dist/index.js.map +1 -1
- package/package.json +9 -6
- package/src/cli.ts +11 -2
- package/src/driver.ts +132 -41
- package/src/drizzle.config.template.ts +16 -5
- package/src/errors.ts +1 -1
- package/src/index.ts +1 -1
- package/src/migrations.ts +51 -14
- package/src/secrets.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { drizzle as drizzleMysql } from "drizzle-orm/mysql2";
|
|
|
5
5
|
import postgres from "postgres";
|
|
6
6
|
import mysql from "mysql2/promise";
|
|
7
7
|
import { sql } from "drizzle-orm";
|
|
8
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
8
9
|
|
|
9
10
|
// src/errors.ts
|
|
10
11
|
import { IskraError, ErrorCodes } from "@iskra-bun/core";
|
|
@@ -27,107 +28,8 @@ var MigrationError = class extends IskraError {
|
|
|
27
28
|
}
|
|
28
29
|
};
|
|
29
30
|
|
|
30
|
-
// src/
|
|
31
|
-
|
|
32
|
-
return text.replace(/(\/\/)[^/\s:@]+:[^/\s@]+@/g, "$1***:***@");
|
|
33
|
-
}
|
|
34
|
-
var MigrationHelper = class {
|
|
35
|
-
config;
|
|
36
|
-
app;
|
|
37
|
-
constructor(config, app) {
|
|
38
|
-
this.config = config;
|
|
39
|
-
this.app = app;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Genera archivos de migración basados en los cambios del schema.
|
|
43
|
-
* drizzle-kit generate soporta --schema y --out, así que ambos se reenvían
|
|
44
|
-
* desde la config (antes se ignoraban silenciosamente).
|
|
45
|
-
*/
|
|
46
|
-
async generate(name) {
|
|
47
|
-
const args = ["drizzle-kit", "generate"];
|
|
48
|
-
if (this.config.schemaPath) args.push("--schema", this.config.schemaPath);
|
|
49
|
-
if (this.config.migrationsDir) args.push("--out", this.config.migrationsDir);
|
|
50
|
-
if (name) args.push("--name", name);
|
|
51
|
-
if (this.config.configPath) args.push("--config", this.config.configPath);
|
|
52
|
-
await this.exec(args, "generate");
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Aplica las migraciones pendientes a la base de datos.
|
|
56
|
-
* `migrate` sólo acepta --config; schema y out no son flags válidos en este
|
|
57
|
-
* comando, por eso únicamente reenviamos configPath cuando está presente.
|
|
58
|
-
*/
|
|
59
|
-
async migrate() {
|
|
60
|
-
const args = ["drizzle-kit", "migrate"];
|
|
61
|
-
if (this.config.configPath) args.push("--config", this.config.configPath);
|
|
62
|
-
await this.exec(args, "migrate");
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Empuja el schema directamente a la base de datos (sin generar archivos de migración).
|
|
66
|
-
* Útil para desarrollo rápido. `push` acepta --schema pero no --out.
|
|
67
|
-
*/
|
|
68
|
-
async push() {
|
|
69
|
-
const args = ["drizzle-kit", "push"];
|
|
70
|
-
if (this.config.schemaPath) args.push("--schema", this.config.schemaPath);
|
|
71
|
-
if (this.config.configPath) args.push("--config", this.config.configPath);
|
|
72
|
-
await this.exec(args, "push");
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Elimina todas las tablas de la base de datos.
|
|
76
|
-
* `drop` acepta --out (dónde viven las migraciones) pero no --schema.
|
|
77
|
-
*/
|
|
78
|
-
async drop() {
|
|
79
|
-
const args = ["drizzle-kit", "drop"];
|
|
80
|
-
if (this.config.migrationsDir) args.push("--out", this.config.migrationsDir);
|
|
81
|
-
if (this.config.configPath) args.push("--config", this.config.configPath);
|
|
82
|
-
await this.exec(args, "drop");
|
|
83
|
-
}
|
|
84
|
-
async exec(args, operation) {
|
|
85
|
-
const env = {
|
|
86
|
-
...process.env,
|
|
87
|
-
DATABASE_URL: this.config.dbUrl
|
|
88
|
-
};
|
|
89
|
-
this.app?.logger.info(`Running migration: ${operation}`);
|
|
90
|
-
try {
|
|
91
|
-
const proc = Bun.spawn(["bunx", ...args], {
|
|
92
|
-
cwd: process.cwd(),
|
|
93
|
-
env,
|
|
94
|
-
stdout: "pipe",
|
|
95
|
-
stderr: "pipe"
|
|
96
|
-
});
|
|
97
|
-
const exitCode = await proc.exited;
|
|
98
|
-
const stdout = await new Response(proc.stdout).text();
|
|
99
|
-
const stderr = await new Response(proc.stderr).text();
|
|
100
|
-
if (stdout) this.app?.logger.info(stdout.trim());
|
|
101
|
-
if (exitCode !== 0) {
|
|
102
|
-
throw new MigrationError(`Migration ${operation} failed with exit code ${exitCode}`, {
|
|
103
|
-
context: { operation, exitCode, stderr: scrubCredentials(stderr.trim()) }
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
this.app?.logger.info(`Migration ${operation} completed successfully`);
|
|
107
|
-
} catch (error) {
|
|
108
|
-
if (error instanceof MigrationError) throw error;
|
|
109
|
-
throw new MigrationError(`Migration ${operation} failed`, {
|
|
110
|
-
cause: error instanceof Error ? error : new Error(String(error)),
|
|
111
|
-
context: { operation }
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
function mapDialect(driver) {
|
|
117
|
-
switch (driver) {
|
|
118
|
-
case "postgres":
|
|
119
|
-
return "postgresql";
|
|
120
|
-
case "mysql":
|
|
121
|
-
return "mysql";
|
|
122
|
-
case "sqlite":
|
|
123
|
-
case "libsql":
|
|
124
|
-
return "sqlite";
|
|
125
|
-
default:
|
|
126
|
-
throw new MigrationError(`Cannot map driver "${driver}" to a Drizzle dialect`, {
|
|
127
|
-
context: { driver }
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
31
|
+
// src/secrets.ts
|
|
32
|
+
var SENSITIVE_URL_PARAM = /pass|token|secret|key|auth|credential|signature/i;
|
|
131
33
|
|
|
132
34
|
// src/driver.ts
|
|
133
35
|
function scrubUrl(url) {
|
|
@@ -135,6 +37,9 @@ function scrubUrl(url) {
|
|
|
135
37
|
const parsed = new URL(url);
|
|
136
38
|
if (parsed.username) parsed.username = "***";
|
|
137
39
|
if (parsed.password) parsed.password = "***";
|
|
40
|
+
for (const key of [...new Set(parsed.searchParams.keys())]) {
|
|
41
|
+
if (SENSITIVE_URL_PARAM.test(key)) parsed.searchParams.set(key, "***");
|
|
42
|
+
}
|
|
138
43
|
return parsed.toString();
|
|
139
44
|
} catch {
|
|
140
45
|
return void 0;
|
|
@@ -143,6 +48,9 @@ function scrubUrl(url) {
|
|
|
143
48
|
var DbDriver = class {
|
|
144
49
|
name = "db";
|
|
145
50
|
client;
|
|
51
|
+
/** Serializes transactions on the single bun:sqlite connection. */
|
|
52
|
+
sqliteTxQueue = Promise.resolve();
|
|
53
|
+
inSqliteTx = new AsyncLocalStorage();
|
|
146
54
|
db;
|
|
147
55
|
app;
|
|
148
56
|
onQuery;
|
|
@@ -220,9 +128,11 @@ var DbDriver = class {
|
|
|
220
128
|
context: { driver: config.driver }
|
|
221
129
|
});
|
|
222
130
|
}
|
|
131
|
+
await this.roundTrip();
|
|
223
132
|
this.app.logger.info("DB connected successfully.");
|
|
224
133
|
} catch (error) {
|
|
225
134
|
if (error instanceof DriverError) throw error;
|
|
135
|
+
await this.stop();
|
|
226
136
|
this.app.logger.error({ error }, "Failed to connect to DB");
|
|
227
137
|
const safeUrl = scrubUrl(config.url);
|
|
228
138
|
throw new ConnectionError("Failed to connect to DB", {
|
|
@@ -235,32 +145,62 @@ var DbDriver = class {
|
|
|
235
145
|
}
|
|
236
146
|
}
|
|
237
147
|
/**
|
|
238
|
-
*
|
|
148
|
+
* Applies the pending migrations in `migrationsDir` (generated with
|
|
149
|
+
* `drizzle-kit generate`) over the live connection, using Drizzle's
|
|
150
|
+
* migrator for the configured dialect. Requires `start()`.
|
|
151
|
+
*
|
|
152
|
+
* It used to shell out to `drizzle-kit migrate`, which ignored both
|
|
153
|
+
* arguments and failed without a drizzle.config.ts. `schemaPath` is kept for
|
|
154
|
+
* compatibility; applying migrations does not need the schema.
|
|
239
155
|
*/
|
|
240
|
-
async runMigrations(
|
|
241
|
-
|
|
156
|
+
async runMigrations(_schemaPath, migrationsDir = "./drizzle") {
|
|
157
|
+
const config = this.app?.config.db;
|
|
158
|
+
if (!config) {
|
|
242
159
|
throw new DriverError("Cannot run migrations: no DB configuration found", {
|
|
243
160
|
code: "DRIVER_START_FAILED"
|
|
244
161
|
});
|
|
245
162
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
163
|
+
if (!this.db) {
|
|
164
|
+
throw new DriverError("Cannot run migrations: DB is not started", {
|
|
165
|
+
code: "DRIVER_START_FAILED",
|
|
166
|
+
context: { driver: config.driver }
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const options = { migrationsFolder: migrationsDir };
|
|
170
|
+
const db = this.db;
|
|
171
|
+
try {
|
|
172
|
+
switch (config.driver) {
|
|
173
|
+
case "postgres":
|
|
174
|
+
await (await import("drizzle-orm/postgres-js/migrator")).migrate(db, options);
|
|
175
|
+
break;
|
|
176
|
+
case "mysql":
|
|
177
|
+
await (await import("drizzle-orm/mysql2/migrator")).migrate(db, options);
|
|
178
|
+
break;
|
|
179
|
+
case "sqlite":
|
|
180
|
+
(await import("drizzle-orm/bun-sqlite/migrator")).migrate(db, options);
|
|
181
|
+
break;
|
|
182
|
+
case "libsql":
|
|
183
|
+
await (await import("drizzle-orm/libsql/migrator")).migrate(db, options);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
this.app.logger.info({ migrationsDir }, "Migrations applied");
|
|
187
|
+
} catch (error) {
|
|
188
|
+
throw new MigrationError("Failed to apply migrations", {
|
|
189
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
190
|
+
context: { driver: config.driver, migrationsDir }
|
|
191
|
+
});
|
|
192
|
+
}
|
|
257
193
|
}
|
|
258
194
|
/**
|
|
259
195
|
* Run `fn` inside a database transaction, delegating to Drizzle's
|
|
260
196
|
* `db.transaction`. Callers receive the transaction-scoped db handle instead
|
|
261
197
|
* of reaching into the raw `db`. The dialect union means `tx` is typed as
|
|
262
198
|
* {@link IskraDrizzleTx}; narrow by dialect if you need dialect-specific APIs.
|
|
263
|
-
* Failures are wrapped in {@link QueryError}
|
|
199
|
+
* Failures are wrapped in {@link QueryError}; the transaction is rolled back.
|
|
200
|
+
*
|
|
201
|
+
* With `sqlite`, transactions run one at a time on the single connection,
|
|
202
|
+
* and a query made outside `tx` while one is open is part of it. A nested
|
|
203
|
+
* `transaction()` call is rejected: it would wait for itself.
|
|
264
204
|
*/
|
|
265
205
|
async transaction(fn) {
|
|
266
206
|
if (!this.db) {
|
|
@@ -269,6 +209,7 @@ var DbDriver = class {
|
|
|
269
209
|
});
|
|
270
210
|
}
|
|
271
211
|
try {
|
|
212
|
+
if (this.app?.config.db?.driver === "sqlite") return await this.sqliteTransaction(fn);
|
|
272
213
|
return await this.db.transaction((tx) => fn(tx));
|
|
273
214
|
} catch (error) {
|
|
274
215
|
if (error instanceof QueryError) throw error;
|
|
@@ -278,6 +219,35 @@ var DbDriver = class {
|
|
|
278
219
|
});
|
|
279
220
|
}
|
|
280
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Drizzle's bun-sqlite `transaction()` is synchronous: it commits as soon
|
|
224
|
+
* as the callback returns its promise, so an async callback that threw
|
|
225
|
+
* afterwards never rolled back. The transaction is opened and closed here
|
|
226
|
+
* around the awaited callback instead.
|
|
227
|
+
*/
|
|
228
|
+
async sqliteTransaction(fn) {
|
|
229
|
+
if (this.inSqliteTx.getStore()) {
|
|
230
|
+
throw new QueryError("Nested transaction() calls are not supported with sqlite", {
|
|
231
|
+
context: { driver: "sqlite" }
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
const db = this.db;
|
|
235
|
+
const run = () => this.inSqliteTx.run(true, async () => {
|
|
236
|
+
const tx = db.transaction((t) => t);
|
|
237
|
+
db.run(sql`begin`);
|
|
238
|
+
try {
|
|
239
|
+
const result2 = await fn(tx);
|
|
240
|
+
db.run(sql`commit`);
|
|
241
|
+
return result2;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
db.run(sql`rollback`);
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
const result = this.sqliteTxQueue.then(run, run);
|
|
248
|
+
this.sqliteTxQueue = result.catch(() => void 0);
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
281
251
|
/**
|
|
282
252
|
* Liveness probe for readiness checks (e.g. web-kit's addReadinessCheck /
|
|
283
253
|
* k8s readiness). Runs a trivial `SELECT 1` against the active dialect and
|
|
@@ -286,19 +256,23 @@ var DbDriver = class {
|
|
|
286
256
|
async ping() {
|
|
287
257
|
if (!this.db) return false;
|
|
288
258
|
try {
|
|
289
|
-
|
|
290
|
-
if (typeof handle.run === "function") {
|
|
291
|
-
await handle.run(sql`SELECT 1`);
|
|
292
|
-
} else if (typeof handle.execute === "function") {
|
|
293
|
-
await handle.execute(sql`SELECT 1`);
|
|
294
|
-
} else {
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
259
|
+
await this.roundTrip();
|
|
297
260
|
return true;
|
|
298
261
|
} catch {
|
|
299
262
|
return false;
|
|
300
263
|
}
|
|
301
264
|
}
|
|
265
|
+
/** `SELECT 1` on the active connection; throws on failure. */
|
|
266
|
+
async roundTrip() {
|
|
267
|
+
const handle = this.db;
|
|
268
|
+
if (typeof handle.run === "function") {
|
|
269
|
+
await handle.run(sql`SELECT 1`);
|
|
270
|
+
} else if (typeof handle.execute === "function") {
|
|
271
|
+
await handle.execute(sql`SELECT 1`);
|
|
272
|
+
} else {
|
|
273
|
+
throw new Error("DB handle exposes neither run() nor execute()");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
302
276
|
async stop() {
|
|
303
277
|
const client = this.client;
|
|
304
278
|
try {
|
|
@@ -316,21 +290,139 @@ var DbDriver = class {
|
|
|
316
290
|
}
|
|
317
291
|
};
|
|
318
292
|
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
293
|
+
// src/migrations.ts
|
|
294
|
+
import { existsSync } from "fs";
|
|
295
|
+
import path from "path";
|
|
296
|
+
function drizzleKitCommand(args, cwd = process.cwd()) {
|
|
297
|
+
for (let dir = path.resolve(cwd); ; dir = path.dirname(dir)) {
|
|
298
|
+
const bin = path.join(dir, "node_modules", ".bin", "drizzle-kit");
|
|
299
|
+
if (["", ".exe", ".cmd"].some((ext) => existsSync(bin + ext))) {
|
|
300
|
+
return ["bunx", "--no-install", "drizzle-kit", ...args];
|
|
301
|
+
}
|
|
302
|
+
if (path.dirname(dir) === dir) break;
|
|
303
|
+
}
|
|
304
|
+
throw new MigrationError(
|
|
305
|
+
"drizzle-kit is not installed in this project (it is never downloaded at run time): add it with `bun add -d drizzle-kit`",
|
|
306
|
+
{ context: { cwd } }
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
function scrubCredentials(text) {
|
|
310
|
+
return text.replace(/(\/\/)(?:[^\s'"`@]*@)+/g, "$1***:***@").replace(
|
|
311
|
+
new RegExp(`([?&][^=\\s&'"\`]*(?:${SENSITIVE_URL_PARAM.source})[^=\\s&'"\`]*=)[^&\\s'"\`]*`, "gi"),
|
|
312
|
+
"$1***"
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
var MigrationHelper = class {
|
|
316
|
+
config;
|
|
317
|
+
app;
|
|
318
|
+
constructor(config, app) {
|
|
319
|
+
this.config = config;
|
|
320
|
+
this.app = app;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Genera archivos de migración basados en los cambios del schema.
|
|
324
|
+
* drizzle-kit generate soporta --schema y --out, así que ambos se reenvían
|
|
325
|
+
* desde la config (antes se ignoraban silenciosamente).
|
|
326
|
+
*/
|
|
327
|
+
async generate(name) {
|
|
328
|
+
const args = ["generate"];
|
|
329
|
+
if (this.config.schemaPath) args.push("--schema", this.config.schemaPath);
|
|
330
|
+
if (this.config.migrationsDir) args.push("--out", this.config.migrationsDir);
|
|
331
|
+
if (name) args.push("--name", name);
|
|
332
|
+
if (this.config.configPath) args.push("--config", this.config.configPath);
|
|
333
|
+
await this.exec(args, "generate");
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Aplica las migraciones pendientes a la base de datos.
|
|
337
|
+
* `migrate` sólo acepta --config; schema y out no son flags válidos en este
|
|
338
|
+
* comando, por eso únicamente reenviamos configPath cuando está presente.
|
|
339
|
+
*/
|
|
340
|
+
async migrate() {
|
|
341
|
+
const args = ["migrate"];
|
|
342
|
+
if (this.config.configPath) args.push("--config", this.config.configPath);
|
|
343
|
+
await this.exec(args, "migrate");
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Empuja el schema directamente a la base de datos (sin generar archivos de migración).
|
|
347
|
+
* Útil para desarrollo rápido. `push` acepta --schema pero no --out.
|
|
348
|
+
*/
|
|
349
|
+
async push() {
|
|
350
|
+
const args = ["push"];
|
|
351
|
+
if (this.config.schemaPath) args.push("--schema", this.config.schemaPath);
|
|
352
|
+
if (this.config.configPath) args.push("--config", this.config.configPath);
|
|
353
|
+
await this.exec(args, "push");
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Elimina un archivo de migración ya generado (`drizzle-kit drop`, interactivo).
|
|
357
|
+
* No borra tablas ni datos de la base.
|
|
358
|
+
* `drop` acepta --out (dónde viven las migraciones) pero no --schema.
|
|
359
|
+
*/
|
|
360
|
+
async drop() {
|
|
361
|
+
const args = ["drop"];
|
|
362
|
+
if (this.config.migrationsDir) args.push("--out", this.config.migrationsDir);
|
|
363
|
+
if (this.config.configPath) args.push("--config", this.config.configPath);
|
|
364
|
+
await this.exec(args, "drop");
|
|
365
|
+
}
|
|
366
|
+
async exec(args, operation) {
|
|
367
|
+
const env = {
|
|
368
|
+
...process.env,
|
|
369
|
+
DATABASE_URL: this.config.dbUrl
|
|
370
|
+
};
|
|
371
|
+
const cwd = process.cwd();
|
|
372
|
+
const command = drizzleKitCommand(args, cwd);
|
|
373
|
+
this.app?.logger.info(`Running migration: ${operation}`);
|
|
374
|
+
try {
|
|
375
|
+
const proc = Bun.spawn(command, {
|
|
376
|
+
cwd,
|
|
377
|
+
env,
|
|
378
|
+
stdout: "pipe",
|
|
379
|
+
stderr: "pipe"
|
|
380
|
+
});
|
|
381
|
+
const exitCode = await proc.exited;
|
|
382
|
+
const stdout = await new Response(proc.stdout).text();
|
|
383
|
+
const stderr = await new Response(proc.stderr).text();
|
|
384
|
+
if (stdout) this.app?.logger.info(stdout.trim());
|
|
385
|
+
if (exitCode !== 0) {
|
|
386
|
+
throw new MigrationError(`Migration ${operation} failed with exit code ${exitCode}`, {
|
|
387
|
+
context: { operation, exitCode, stderr: scrubCredentials(stderr.trim()) }
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
this.app?.logger.info(`Migration ${operation} completed successfully`);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (error instanceof MigrationError) throw error;
|
|
393
|
+
throw new MigrationError(`Migration ${operation} failed`, {
|
|
394
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
395
|
+
context: { operation }
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
function mapDialect(driver) {
|
|
401
|
+
switch (driver) {
|
|
402
|
+
case "postgres":
|
|
403
|
+
return "postgresql";
|
|
404
|
+
case "mysql":
|
|
405
|
+
return "mysql";
|
|
406
|
+
case "sqlite":
|
|
407
|
+
case "libsql":
|
|
408
|
+
return "sqlite";
|
|
409
|
+
default:
|
|
410
|
+
throw new MigrationError(`Cannot map driver "${driver}" to a Drizzle dialect`, {
|
|
411
|
+
context: { driver }
|
|
412
|
+
});
|
|
413
|
+
}
|
|
322
414
|
}
|
|
323
415
|
|
|
324
416
|
// src/drizzle.config.template.ts
|
|
325
417
|
function createDrizzleConfig(options) {
|
|
326
|
-
return
|
|
418
|
+
return {
|
|
327
419
|
dialect: options.dialect,
|
|
328
420
|
schema: options.schemaPath,
|
|
329
421
|
out: options.migrationsDir || "./drizzle",
|
|
330
422
|
dbCredentials: {
|
|
331
423
|
url: options.dbUrl
|
|
332
424
|
}
|
|
333
|
-
}
|
|
425
|
+
};
|
|
334
426
|
}
|
|
335
427
|
export {
|
|
336
428
|
ConnectionError,
|
|
@@ -339,6 +431,7 @@ export {
|
|
|
339
431
|
MigrationHelper,
|
|
340
432
|
QueryError,
|
|
341
433
|
createDrizzleConfig,
|
|
434
|
+
drizzleKitCommand,
|
|
342
435
|
mapDialect,
|
|
343
436
|
scrubCredentials,
|
|
344
437
|
scrubUrl
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/driver.ts","../src/errors.ts","../src/migrations.ts","../../../node_modules/drizzle-kit/index.mjs","../src/drizzle.config.template.ts"],"sourcesContent":["import { type App, type Driver, type AppConfig, DriverError } from '@iskra-bun/core';\nimport { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { drizzle as drizzleMysql, type MySql2Database } from 'drizzle-orm/mysql2';\nimport type { BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';\nimport type { LibSQLDatabase } from 'drizzle-orm/libsql';\nimport postgres from 'postgres';\nimport mysql from 'mysql2/promise';\nimport { sql } from 'drizzle-orm';\nimport { ConnectionError, QueryError } from './errors';\nimport { MigrationHelper, mapDialect } from './migrations';\n\n/**\n * Observability callback invoked for every SQL statement Drizzle executes.\n * Receives the rendered query and its bound parameters.\n */\nexport type OnQueryHook = (query: string, params: unknown[]) => void;\n\n/**\n * The transaction handle passed to {@link DbDriver.transaction}. Drizzle types\n * the transaction object per dialect, so — like {@link IskraDrizzleDb} — this is\n * the union of the supported dialect databases for the same schema. Callers can\n * narrow by dialect if they need dialect-specific transaction APIs.\n */\nexport type IskraDrizzleTx<TSchema extends Record<string, unknown> = Record<string, never>> =\n IskraDrizzleDb<TSchema>;\n\n/**\n * The Drizzle database handle exposed by {@link DbDriver}, parameterized by the\n * caller's schema. Because the concrete dialect is chosen at runtime, this is a\n * union of the supported dialect databases — all four share the same\n * `TSchema extends Record<string, unknown> = Record<string, never>` parameter,\n * so passing a schema types `db.query.*` for opt-in callers while the default\n * `Record<string, never>` reproduces the historical untyped behavior.\n */\nexport type IskraDrizzleDb<TSchema extends Record<string, unknown> = Record<string, never>> =\n | PostgresJsDatabase<TSchema>\n | MySql2Database<TSchema>\n | BunSQLiteDatabase<TSchema>\n | LibSQLDatabase<TSchema>;\n\n/**\n * Redact username and password from a database URL so it is safe to log.\n * Returns the scrubbed URL string, or undefined if parsing fails.\n *\n * e.g. postgres://user:pass@host:5432/db → postgres://***:***@host:5432/db\n */\nexport function scrubUrl(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n if (parsed.username) parsed.username = '***';\n if (parsed.password) parsed.password = '***';\n return parsed.toString();\n } catch {\n return undefined;\n }\n}\n\n/**\n * The minimal teardown surface {@link DbDriver.stop} probes on the underlying\n * client. postgres-js and mysql2 expose async `end()`; bun:sqlite and libsql\n * expose synchronous `close()`. Typed as optional so either shape satisfies it.\n */\ninterface DbClient {\n end?(): Promise<void>;\n close?(): void;\n}\n\nexport class DbDriver<TSchema extends Record<string, unknown> = Record<string, never>> implements Driver {\n name = 'db';\n private client: DbClient | undefined;\n public db: IskraDrizzleDb<TSchema> | undefined;\n\n private app: App | undefined;\n private onQuery: OnQueryHook | undefined;\n\n /**\n * Register an observability callback that receives every SQL statement (and\n * its bound params) Drizzle executes. Must be called before {@link start},\n * since Drizzle's logger is wired at connection time. A throwing callback is\n * swallowed so observability never breaks a real query.\n */\n setOnQuery(onQuery: OnQueryHook): void {\n this.onQuery = onQuery;\n }\n\n /**\n * Build the Drizzle `logger` option that forwards to {@link onQuery} when a\n * hook is registered, or `undefined` to leave Drizzle's default logging off.\n */\n private buildLogger(): { logQuery(query: string, params: unknown[]): void } | undefined {\n const hook = this.onQuery;\n if (!hook) return undefined;\n return {\n logQuery: (query: string, params: unknown[]) => {\n try {\n hook(query, params);\n } catch {\n // Observability must never break the underlying query.\n }\n },\n };\n }\n\n async init(app: App) {\n this.app = app;\n app.context.set('db', this);\n }\n\n async start() {\n if (!this.app) return;\n const config = this.app.config.db;\n if (!config) {\n this.app!.logger.warn('No DB configuration found. Skipping DB initialization.');\n return;\n }\n\n this.app!.logger.info(`Initializing DB driver: ${config.driver}`);\n\n const logger = this.buildLogger();\n\n try {\n switch (config.driver) {\n case 'postgres': {\n const client = postgres(config.url);\n this.client = client;\n this.db = drizzle<TSchema>(client, { logger });\n break;\n }\n case 'mysql': {\n const client = mysql.createPool(config.url);\n this.client = client;\n this.db = drizzleMysql<TSchema>(client, { logger });\n break;\n }\n case 'sqlite': {\n const { Database } = await import(\"bun:sqlite\");\n const { drizzle: drizzleSqlite } = await import(\"drizzle-orm/bun-sqlite\");\n const client = new Database(config.url);\n this.client = client;\n this.db = drizzleSqlite<TSchema>(client, { logger });\n break;\n }\n case 'libsql': {\n const { createClient } = await import('@libsql/client');\n const { drizzle: drizzleLibsql } = await import('drizzle-orm/libsql');\n const client = createClient({ url: config.url, authToken: config.authToken });\n this.client = client;\n this.db = drizzleLibsql<TSchema>(client, { logger });\n break;\n }\n default:\n throw new DriverError(`Unsupported DB driver: ${config.driver}`, {\n code: 'DRIVER_START_FAILED',\n context: { driver: config.driver },\n });\n }\n this.app!.logger.info('DB connected successfully.');\n } catch (error) {\n if (error instanceof DriverError) throw error;\n this.app!.logger.error({ error }, 'Failed to connect to DB');\n const safeUrl = scrubUrl(config.url);\n throw new ConnectionError('Failed to connect to DB', {\n cause: error instanceof Error ? error : new Error(String(error)),\n context: {\n driver: config.driver,\n ...(safeUrl !== undefined ? { url: safeUrl } : {}),\n },\n });\n }\n }\n\n /**\n * Ejecuta migraciones pendientes usando Drizzle Kit.\n */\n async runMigrations(schemaPath: string, migrationsDir: string = './drizzle'): Promise<void> {\n if (!this.app?.config.db) {\n throw new DriverError('Cannot run migrations: no DB configuration found', {\n code: 'DRIVER_START_FAILED',\n });\n }\n\n const config = this.app.config.db;\n const helper = new MigrationHelper(\n {\n dialect: mapDialect(config.driver),\n dbUrl: config.url,\n schemaPath,\n migrationsDir,\n },\n this.app,\n );\n\n await helper.migrate();\n }\n\n /**\n * Run `fn` inside a database transaction, delegating to Drizzle's\n * `db.transaction`. Callers receive the transaction-scoped db handle instead\n * of reaching into the raw `db`. The dialect union means `tx` is typed as\n * {@link IskraDrizzleTx}; narrow by dialect if you need dialect-specific APIs.\n * Failures are wrapped in {@link QueryError} (Drizzle rolls back on throw).\n */\n async transaction<R>(fn: (tx: IskraDrizzleTx<TSchema>) => Promise<R>): Promise<R> {\n if (!this.db) {\n throw new QueryError('Cannot run transaction: DB is not started', {\n context: { driver: this.app?.config.db?.driver },\n });\n }\n try {\n // The dialect-specific `transaction` overloads do not unify across the\n // union, so we route through the runtime method with a faithful cast\n // of the public handle types.\n return await (this.db as IskraDrizzleDb<TSchema> & {\n transaction(cb: (tx: IskraDrizzleTx<TSchema>) => Promise<R>): Promise<R>;\n }).transaction((tx) => fn(tx));\n } catch (error) {\n if (error instanceof QueryError) throw error;\n throw new QueryError('Transaction failed', {\n cause: error instanceof Error ? error : new Error(String(error)),\n context: { driver: this.app?.config.db?.driver },\n });\n }\n }\n\n /**\n * Liveness probe for readiness checks (e.g. web-kit's addReadinessCheck /\n * k8s readiness). Runs a trivial `SELECT 1` against the active dialect and\n * resolves `true` on success or `false` on any failure — it never rejects.\n */\n async ping(): Promise<boolean> {\n if (!this.db) return false;\n try {\n // bun-sqlite exposes the synchronous `.run()`; postgres-js, mysql2 and\n // libsql expose the async `.execute()`. Prefer whichever exists.\n const handle = this.db as {\n run?(query: unknown): unknown;\n execute?(query: unknown): Promise<unknown>;\n };\n if (typeof handle.run === 'function') {\n await handle.run(sql`SELECT 1`);\n } else if (typeof handle.execute === 'function') {\n await handle.execute(sql`SELECT 1`);\n } else {\n return false;\n }\n return true;\n } catch {\n return false;\n }\n }\n\n async stop() {\n const client = this.client;\n try {\n // postgres-js / mysql2 expose async end(); bun:sqlite / libsql expose\n // synchronous close(). Probe for whichever this client provides.\n if (client?.end) {\n await client.end();\n } else if (client?.close) {\n client.close();\n }\n } catch (error) {\n // A throwing teardown must never abort the orderly shutdown of other\n // drivers; log and continue so the handles below are still cleared.\n this.app?.logger.error({ error }, 'Failed to close DB connection cleanly');\n } finally {\n // Null the handles so a post-stop ping()/transaction() hits the\n // not-started guard instead of an already-closed connection.\n this.client = undefined;\n this.db = undefined;\n }\n }\n}\n","import { IskraError, ErrorCodes, type ErrorCode } from '@iskra-bun/core';\n\n// ─── Connection Error ────────────────────────────────────────────────────────\n\nexport class ConnectionError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.CONNECTION_ERROR, ...options });\n this.name = 'ConnectionError';\n }\n}\n\n// ─── Query Error ─────────────────────────────────────────────────────────────\n\nexport class QueryError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.QUERY_ERROR, ...options });\n this.name = 'QueryError';\n }\n}\n\n// ─── Migration Error ─────────────────────────────────────────────────────────\n\nexport class MigrationError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.MIGRATION_ERROR, ...options });\n this.name = 'MigrationError';\n }\n}\n","import { type App } from '@iskra-bun/core';\nimport { MigrationError } from './errors';\n\n/**\n * Redacta credenciales `//user:pass@host` embebidas en texto arbitrario (p. ej.\n * el stderr de drizzle-kit, que suele imprimir la cadena de conexión completa al\n * fallar). A diferencia de `scrubUrl`, opera sobre texto libre y no requiere que\n * el contenido sea una URL parseable, dejando intacto el resto del diagnóstico.\n *\n * e.g. \"... postgres://user:pass@host:5432/db\" → \"... postgres://***:***@host:5432/db\"\n */\nexport function scrubCredentials(text: string): string {\n return text.replace(/(\\/\\/)[^/\\s:@]+:[^/\\s@]+@/g, '$1***:***@');\n}\n\nexport interface MigrationConfig {\n /** Dialecto de la base de datos */\n dialect: 'postgresql' | 'mysql' | 'sqlite';\n /** URL de conexión a la base de datos */\n dbUrl: string;\n /** Ruta al archivo de schema Drizzle (ej: './src/db/schema.ts') */\n schemaPath: string;\n /** Directorio donde se generan las migraciones (ej: './drizzle') */\n migrationsDir: string;\n /** Ruta opcional a un drizzle.config.ts; cuando se define se pasa como --config. */\n configPath?: string;\n}\n\n/**\n * Helper para ejecutar migraciones de Drizzle Kit.\n * Usa `bunx drizzle-kit` como subproceso para generar y aplicar migraciones.\n */\nexport class MigrationHelper {\n private config: MigrationConfig;\n private app?: App;\n\n constructor(config: MigrationConfig, app?: App) {\n this.config = config;\n this.app = app;\n }\n\n /**\n * Genera archivos de migración basados en los cambios del schema.\n * drizzle-kit generate soporta --schema y --out, así que ambos se reenvían\n * desde la config (antes se ignoraban silenciosamente).\n */\n async generate(name?: string): Promise<void> {\n const args = ['drizzle-kit', 'generate'];\n if (this.config.schemaPath) args.push('--schema', this.config.schemaPath);\n if (this.config.migrationsDir) args.push('--out', this.config.migrationsDir);\n if (name) args.push('--name', name);\n if (this.config.configPath) args.push('--config', this.config.configPath);\n await this.exec(args, 'generate');\n }\n\n /**\n * Aplica las migraciones pendientes a la base de datos.\n * `migrate` sólo acepta --config; schema y out no son flags válidos en este\n * comando, por eso únicamente reenviamos configPath cuando está presente.\n */\n async migrate(): Promise<void> {\n const args = ['drizzle-kit', 'migrate'];\n if (this.config.configPath) args.push('--config', this.config.configPath);\n await this.exec(args, 'migrate');\n }\n\n /**\n * Empuja el schema directamente a la base de datos (sin generar archivos de migración).\n * Útil para desarrollo rápido. `push` acepta --schema pero no --out.\n */\n async push(): Promise<void> {\n const args = ['drizzle-kit', 'push'];\n if (this.config.schemaPath) args.push('--schema', this.config.schemaPath);\n if (this.config.configPath) args.push('--config', this.config.configPath);\n await this.exec(args, 'push');\n }\n\n /**\n * Elimina todas las tablas de la base de datos.\n * `drop` acepta --out (dónde viven las migraciones) pero no --schema.\n */\n async drop(): Promise<void> {\n const args = ['drizzle-kit', 'drop'];\n if (this.config.migrationsDir) args.push('--out', this.config.migrationsDir);\n if (this.config.configPath) args.push('--config', this.config.configPath);\n await this.exec(args, 'drop');\n }\n\n private async exec(args: string[], operation: string): Promise<void> {\n const env: Record<string, string> = {\n ...process.env as Record<string, string>,\n DATABASE_URL: this.config.dbUrl,\n };\n\n this.app?.logger.info(`Running migration: ${operation}`);\n\n try {\n const proc = Bun.spawn(['bunx', ...args], {\n cwd: process.cwd(),\n env,\n stdout: 'pipe',\n stderr: 'pipe',\n });\n\n const exitCode = await proc.exited;\n const stdout = await new Response(proc.stdout).text();\n const stderr = await new Response(proc.stderr).text();\n\n if (stdout) this.app?.logger.info(stdout.trim());\n\n if (exitCode !== 0) {\n throw new MigrationError(`Migration ${operation} failed with exit code ${exitCode}`, {\n context: { operation, exitCode, stderr: scrubCredentials(stderr.trim()) },\n });\n }\n\n this.app?.logger.info(`Migration ${operation} completed successfully`);\n } catch (error) {\n if (error instanceof MigrationError) throw error;\n throw new MigrationError(`Migration ${operation} failed`, {\n cause: error instanceof Error ? error : new Error(String(error)),\n context: { operation },\n });\n }\n }\n}\n\n/**\n * Mapea el driver de Iskra al dialecto de Drizzle Kit.\n */\nexport function mapDialect(driver: string): MigrationConfig['dialect'] {\n switch (driver) {\n case 'postgres':\n return 'postgresql';\n case 'mysql':\n return 'mysql';\n case 'sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new MigrationError(`Cannot map driver \"${driver}\" to a Drizzle dialect`, {\n context: { driver },\n });\n }\n}\n","// src/index.ts\nfunction defineConfig(config) {\n return config;\n}\nexport {\n defineConfig\n};\n","import { defineConfig } from 'drizzle-kit';\n\nexport interface DrizzleConfigOptions {\n /** Dialecto: 'postgresql', 'mysql', 'sqlite' */\n dialect: 'postgresql' | 'mysql' | 'sqlite';\n /** URL de conexión a la base de datos */\n dbUrl: string;\n /** Ruta al archivo de schema (ej: './src/db/schema.ts') */\n schemaPath: string;\n /** Directorio de migraciones (ej: './drizzle') */\n migrationsDir?: string;\n}\n\n/**\n * Crea una configuración de drizzle-kit reutilizable.\n *\n * Uso en tu proyecto:\n * ```ts\n * // drizzle.config.ts\n * import { createDrizzleConfig } from '@iskra-bun/db-kit';\n *\n * export default createDrizzleConfig({\n * dialect: 'sqlite',\n * dbUrl: process.env.DATABASE_URL || 'app.db',\n * schemaPath: './src/db/schema.ts',\n * });\n * ```\n */\nexport function createDrizzleConfig(options: DrizzleConfigOptions) {\n return defineConfig({\n dialect: options.dialect,\n schema: options.schemaPath,\n out: options.migrationsDir || './drizzle',\n dbCredentials: {\n url: options.dbUrl,\n },\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/driver.ts","../src/errors.ts","../src/secrets.ts","../src/migrations.ts","../src/drizzle.config.template.ts"],"sourcesContent":["import { type App, type Driver, DriverError } from '@iskra-bun/core';\nimport { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { drizzle as drizzleMysql, type MySql2Database } from 'drizzle-orm/mysql2';\nimport type { BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';\nimport type { LibSQLDatabase } from 'drizzle-orm/libsql';\nimport postgres from 'postgres';\nimport mysql from 'mysql2/promise';\nimport { sql } from 'drizzle-orm';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { ConnectionError, MigrationError, QueryError } from './errors';\nimport { SENSITIVE_URL_PARAM } from './secrets';\n\n/**\n * Observability callback invoked for every SQL statement Drizzle executes.\n * Receives the rendered query and its bound parameters.\n */\nexport type OnQueryHook = (query: string, params: unknown[]) => void;\n\n/**\n * The transaction handle passed to {@link DbDriver.transaction}. Drizzle types\n * the transaction object per dialect, so — like {@link IskraDrizzleDb} — this is\n * the union of the supported dialect databases for the same schema. Callers can\n * narrow by dialect if they need dialect-specific transaction APIs.\n */\nexport type IskraDrizzleTx<TSchema extends Record<string, unknown> = Record<string, never>> = IskraDrizzleDb<TSchema>;\n\n/**\n * The Drizzle database handle exposed by {@link DbDriver}, parameterized by the\n * caller's schema. Because the concrete dialect is chosen at runtime, this is a\n * union of the supported dialect databases — all four share the same\n * `TSchema extends Record<string, unknown> = Record<string, never>` parameter,\n * so passing a schema types `db.query.*` for opt-in callers while the default\n * `Record<string, never>` reproduces the historical untyped behavior.\n */\nexport type IskraDrizzleDb<TSchema extends Record<string, unknown> = Record<string, never>> =\n | PostgresJsDatabase<TSchema>\n | MySql2Database<TSchema>\n | BunSQLiteDatabase<TSchema>\n | LibSQLDatabase<TSchema>;\n\n/**\n * Redact the username, password and secret query parameters (`authToken`,\n * `password`, `sslpassword`...) from a database URL so it is safe to log.\n * Returns the scrubbed URL string, or undefined if parsing fails.\n *\n * e.g. postgres://user:pass@host:5432/db → postgres://***:***@host:5432/db\n * libsql://db.turso.io?authToken=eyJ… → libsql://db.turso.io?authToken=***\n */\nexport function scrubUrl(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n if (parsed.username) parsed.username = '***';\n if (parsed.password) parsed.password = '***';\n for (const key of [...new Set(parsed.searchParams.keys())]) {\n if (SENSITIVE_URL_PARAM.test(key)) parsed.searchParams.set(key, '***');\n }\n return parsed.toString();\n } catch {\n return undefined;\n }\n}\n\n/**\n * The minimal teardown surface {@link DbDriver.stop} probes on the underlying\n * client. postgres-js and mysql2 expose async `end()`; bun:sqlite and libsql\n * expose synchronous `close()`. Typed as optional so either shape satisfies it.\n */\ninterface DbClient {\n end?(): Promise<void>;\n close?(): void;\n}\n\nexport class DbDriver<TSchema extends Record<string, unknown> = Record<string, never>> implements Driver {\n name = 'db';\n private client: DbClient | undefined;\n /** Serializes transactions on the single bun:sqlite connection. */\n private sqliteTxQueue: Promise<unknown> = Promise.resolve();\n private readonly inSqliteTx = new AsyncLocalStorage<true>();\n public db: IskraDrizzleDb<TSchema> | undefined;\n\n private app: App | undefined;\n private onQuery: OnQueryHook | undefined;\n\n /**\n * Register an observability callback that receives every SQL statement (and\n * its bound params) Drizzle executes. Must be called before {@link start},\n * since Drizzle's logger is wired at connection time. A throwing callback is\n * swallowed so observability never breaks a real query.\n */\n setOnQuery(onQuery: OnQueryHook): void {\n this.onQuery = onQuery;\n }\n\n /**\n * Build the Drizzle `logger` option that forwards to {@link onQuery} when a\n * hook is registered, or `undefined` to leave Drizzle's default logging off.\n */\n private buildLogger(): { logQuery(query: string, params: unknown[]): void } | undefined {\n const hook = this.onQuery;\n if (!hook) return undefined;\n return {\n logQuery: (query: string, params: unknown[]) => {\n try {\n hook(query, params);\n } catch {\n // Observability must never break the underlying query.\n }\n },\n };\n }\n\n async init(app: App) {\n this.app = app;\n app.context.set('db', this);\n }\n\n async start() {\n if (!this.app) return;\n const config = this.app.config.db;\n if (!config) {\n this.app!.logger.warn('No DB configuration found. Skipping DB initialization.');\n return;\n }\n\n this.app!.logger.info(`Initializing DB driver: ${config.driver}`);\n\n const logger = this.buildLogger();\n\n try {\n switch (config.driver) {\n case 'postgres': {\n const client = postgres(config.url);\n this.client = client;\n this.db = drizzle<TSchema>(client, { logger });\n break;\n }\n case 'mysql': {\n const client = mysql.createPool(config.url);\n this.client = client;\n // Name the client type: with only TSchema given, drizzle's\n // TClient defaults to mysql2's callback Pool, not this promise Pool.\n this.db = drizzleMysql<TSchema, typeof client>(client, { logger });\n break;\n }\n case 'sqlite': {\n const { Database } = await import('bun:sqlite');\n const { drizzle: drizzleSqlite } = await import('drizzle-orm/bun-sqlite');\n const client = new Database(config.url);\n this.client = client;\n this.db = drizzleSqlite<TSchema>(client, { logger });\n break;\n }\n case 'libsql': {\n const { createClient } = await import('@libsql/client');\n const { drizzle: drizzleLibsql } = await import('drizzle-orm/libsql');\n const client = createClient({ url: config.url, authToken: config.authToken });\n this.client = client;\n this.db = drizzleLibsql<TSchema>(client, { logger });\n break;\n }\n default:\n throw new DriverError(`Unsupported DB driver: ${config.driver}`, {\n code: 'DRIVER_START_FAILED',\n context: { driver: config.driver },\n });\n }\n // postgres-js and mysql2 pools connect lazily: without a round-trip\n // a wrong host or password only surfaced on the first query.\n await this.roundTrip();\n this.app!.logger.info('DB connected successfully.');\n } catch (error) {\n if (error instanceof DriverError) throw error;\n await this.stop();\n this.app!.logger.error({ error }, 'Failed to connect to DB');\n const safeUrl = scrubUrl(config.url);\n throw new ConnectionError('Failed to connect to DB', {\n cause: error instanceof Error ? error : new Error(String(error)),\n context: {\n driver: config.driver,\n ...(safeUrl !== undefined ? { url: safeUrl } : {}),\n },\n });\n }\n }\n\n /**\n * Applies the pending migrations in `migrationsDir` (generated with\n * `drizzle-kit generate`) over the live connection, using Drizzle's\n * migrator for the configured dialect. Requires `start()`.\n *\n * It used to shell out to `drizzle-kit migrate`, which ignored both\n * arguments and failed without a drizzle.config.ts. `schemaPath` is kept for\n * compatibility; applying migrations does not need the schema.\n */\n async runMigrations(_schemaPath?: string, migrationsDir: string = './drizzle'): Promise<void> {\n const config = this.app?.config.db;\n if (!config) {\n throw new DriverError('Cannot run migrations: no DB configuration found', {\n code: 'DRIVER_START_FAILED',\n });\n }\n if (!this.db) {\n throw new DriverError('Cannot run migrations: DB is not started', {\n code: 'DRIVER_START_FAILED',\n context: { driver: config.driver },\n });\n }\n\n const options = { migrationsFolder: migrationsDir };\n const db = this.db as never;\n try {\n switch (config.driver) {\n case 'postgres':\n await (await import('drizzle-orm/postgres-js/migrator')).migrate(db, options);\n break;\n case 'mysql':\n await (await import('drizzle-orm/mysql2/migrator')).migrate(db, options);\n break;\n case 'sqlite':\n (await import('drizzle-orm/bun-sqlite/migrator')).migrate(db, options);\n break;\n case 'libsql':\n await (await import('drizzle-orm/libsql/migrator')).migrate(db, options);\n break;\n }\n this.app!.logger.info({ migrationsDir }, 'Migrations applied');\n } catch (error) {\n throw new MigrationError('Failed to apply migrations', {\n cause: error instanceof Error ? error : new Error(String(error)),\n context: { driver: config.driver, migrationsDir },\n });\n }\n }\n\n /**\n * Run `fn` inside a database transaction, delegating to Drizzle's\n * `db.transaction`. Callers receive the transaction-scoped db handle instead\n * of reaching into the raw `db`. The dialect union means `tx` is typed as\n * {@link IskraDrizzleTx}; narrow by dialect if you need dialect-specific APIs.\n * Failures are wrapped in {@link QueryError}; the transaction is rolled back.\n *\n * With `sqlite`, transactions run one at a time on the single connection,\n * and a query made outside `tx` while one is open is part of it. A nested\n * `transaction()` call is rejected: it would wait for itself.\n */\n async transaction<R>(fn: (tx: IskraDrizzleTx<TSchema>) => Promise<R>): Promise<R> {\n if (!this.db) {\n throw new QueryError('Cannot run transaction: DB is not started', {\n context: { driver: this.app?.config.db?.driver },\n });\n }\n try {\n if (this.app?.config.db?.driver === 'sqlite') return await this.sqliteTransaction(fn);\n // The dialect-specific `transaction` overloads do not unify across the\n // union, so we route through the runtime method with a faithful cast\n // of the public handle types.\n return await (\n this.db as IskraDrizzleDb<TSchema> & {\n transaction(cb: (tx: IskraDrizzleTx<TSchema>) => Promise<R>): Promise<R>;\n }\n ).transaction((tx) => fn(tx));\n } catch (error) {\n if (error instanceof QueryError) throw error;\n throw new QueryError('Transaction failed', {\n cause: error instanceof Error ? error : new Error(String(error)),\n context: { driver: this.app?.config.db?.driver },\n });\n }\n }\n\n /**\n * Drizzle's bun-sqlite `transaction()` is synchronous: it commits as soon\n * as the callback returns its promise, so an async callback that threw\n * afterwards never rolled back. The transaction is opened and closed here\n * around the awaited callback instead.\n */\n private async sqliteTransaction<R>(fn: (tx: IskraDrizzleTx<TSchema>) => Promise<R>): Promise<R> {\n if (this.inSqliteTx.getStore()) {\n throw new QueryError('Nested transaction() calls are not supported with sqlite', {\n context: { driver: 'sqlite' },\n });\n }\n const db = this.db as BunSQLiteDatabase<TSchema>;\n const run = () =>\n this.inSqliteTx.run(true, async () => {\n // A transaction handle (tx.rollback(), tx.query.*) bound to the\n // connection; the empty native transaction it comes from is done.\n const tx = db.transaction((t) => t);\n db.run(sql`begin`);\n try {\n const result = await fn(tx as unknown as IskraDrizzleTx<TSchema>);\n db.run(sql`commit`);\n return result;\n } catch (error) {\n db.run(sql`rollback`);\n throw error;\n }\n });\n const result = this.sqliteTxQueue.then(run, run);\n this.sqliteTxQueue = result.catch(() => undefined);\n return result;\n }\n\n /**\n * Liveness probe for readiness checks (e.g. web-kit's addReadinessCheck /\n * k8s readiness). Runs a trivial `SELECT 1` against the active dialect and\n * resolves `true` on success or `false` on any failure — it never rejects.\n */\n async ping(): Promise<boolean> {\n if (!this.db) return false;\n try {\n await this.roundTrip();\n return true;\n } catch {\n return false;\n }\n }\n\n /** `SELECT 1` on the active connection; throws on failure. */\n private async roundTrip(): Promise<void> {\n // bun-sqlite exposes the synchronous `.run()`; postgres-js, mysql2 and\n // libsql expose the async `.execute()`. Prefer whichever exists.\n const handle = this.db as {\n run?(query: unknown): unknown;\n execute?(query: unknown): Promise<unknown>;\n };\n if (typeof handle.run === 'function') {\n await handle.run(sql`SELECT 1`);\n } else if (typeof handle.execute === 'function') {\n await handle.execute(sql`SELECT 1`);\n } else {\n throw new Error('DB handle exposes neither run() nor execute()');\n }\n }\n\n async stop() {\n const client = this.client;\n try {\n // postgres-js / mysql2 expose async end(); bun:sqlite / libsql expose\n // synchronous close(). Probe for whichever this client provides.\n if (client?.end) {\n await client.end();\n } else if (client?.close) {\n client.close();\n }\n } catch (error) {\n // A throwing teardown must never abort the orderly shutdown of other\n // drivers; log and continue so the handles below are still cleared.\n this.app?.logger.error({ error }, 'Failed to close DB connection cleanly');\n } finally {\n // Null the handles so a post-stop ping()/transaction() hits the\n // not-started guard instead of an already-closed connection.\n this.client = undefined;\n this.db = undefined;\n }\n }\n}\n\n// `app.context.get('db')` is the DbDriver registered on the app.\ndeclare module '@iskra-bun/core' {\n interface AppContextRegistry {\n db: DbDriver;\n }\n}\n","import { IskraError, ErrorCodes } from '@iskra-bun/core';\n\n// ─── Connection Error ────────────────────────────────────────────────────────\n\nexport class ConnectionError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.CONNECTION_ERROR, ...options });\n this.name = 'ConnectionError';\n }\n}\n\n// ─── Query Error ─────────────────────────────────────────────────────────────\n\nexport class QueryError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.QUERY_ERROR, ...options });\n this.name = 'QueryError';\n }\n}\n\n// ─── Migration Error ─────────────────────────────────────────────────────────\n\nexport class MigrationError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.MIGRATION_ERROR, ...options });\n this.name = 'MigrationError';\n }\n}\n","/** Query parameters that carry secrets, such as libsql's `authToken` or `password`. */\nexport const SENSITIVE_URL_PARAM = /pass|token|secret|key|auth|credential|signature/i;\n","import { type App } from '@iskra-bun/core';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { MigrationError } from './errors';\nimport { SENSITIVE_URL_PARAM } from './secrets';\n\n/**\n * The drizzle-kit command to run `args` with, from the project's own install:\n * `node_modules/.bin` in `cwd` or a parent (workspaces hoist it). drizzle-kit\n * is a devDependency, and `bunx drizzle-kit` downloaded its latest release\n * from npm where it was missing (a production install) and ran it with\n * DATABASE_URL in its environment. `--no-install` keeps bunx from doing so.\n */\nexport function drizzleKitCommand(args: string[], cwd: string = process.cwd()): string[] {\n for (let dir = path.resolve(cwd); ; dir = path.dirname(dir)) {\n const bin = path.join(dir, 'node_modules', '.bin', 'drizzle-kit');\n if (['', '.exe', '.cmd'].some((ext) => existsSync(bin + ext))) {\n return ['bunx', '--no-install', 'drizzle-kit', ...args];\n }\n if (path.dirname(dir) === dir) break;\n }\n throw new MigrationError(\n 'drizzle-kit is not installed in this project (it is never downloaded at run time): ' +\n 'add it with `bun add -d drizzle-kit`',\n { context: { cwd } },\n );\n}\n\n/**\n * Redacta credenciales `//user:pass@host` y parámetros secretos (`authToken`,\n * `password`...) embebidos en texto arbitrario (p. ej. el stderr de drizzle-kit,\n * que suele imprimir la cadena de conexión completa al fallar). A diferencia de\n * `scrubUrl`, opera sobre texto libre y no requiere que el contenido sea una\n * URL parseable, dejando intacto el resto del diagnóstico. Una contraseña con\n * `@` o `/` sin codificar también se redacta: se toma hasta el último `@`.\n *\n * e.g. \"... postgres://user:pass@host:5432/db\" → \"... postgres://***:***@host:5432/db\"\n */\nexport function scrubCredentials(text: string): string {\n return text\n .replace(/(\\/\\/)(?:[^\\s'\"`@]*@)+/g, '$1***:***@')\n .replace(\n new RegExp(`([?&][^=\\\\s&'\"\\`]*(?:${SENSITIVE_URL_PARAM.source})[^=\\\\s&'\"\\`]*=)[^&\\\\s'\"\\`]*`, 'gi'),\n '$1***',\n );\n}\n\nexport interface MigrationConfig {\n /** Dialecto de la base de datos */\n dialect: 'postgresql' | 'mysql' | 'sqlite';\n /** URL de conexión a la base de datos */\n dbUrl: string;\n /** Ruta al archivo de schema Drizzle (ej: './src/db/schema.ts') */\n schemaPath: string;\n /** Directorio donde se generan las migraciones (ej: './drizzle') */\n migrationsDir: string;\n /** Ruta opcional a un drizzle.config.ts; cuando se define se pasa como --config. */\n configPath?: string;\n}\n\n/**\n * Helper para ejecutar migraciones de Drizzle Kit.\n * Usa el drizzle-kit instalado en el proyecto (`bunx --no-install drizzle-kit`)\n * como subproceso para generar y aplicar migraciones; nunca lo descarga.\n */\nexport class MigrationHelper {\n private config: MigrationConfig;\n private app?: App;\n\n constructor(config: MigrationConfig, app?: App) {\n this.config = config;\n this.app = app;\n }\n\n /**\n * Genera archivos de migración basados en los cambios del schema.\n * drizzle-kit generate soporta --schema y --out, así que ambos se reenvían\n * desde la config (antes se ignoraban silenciosamente).\n */\n async generate(name?: string): Promise<void> {\n const args = ['generate'];\n if (this.config.schemaPath) args.push('--schema', this.config.schemaPath);\n if (this.config.migrationsDir) args.push('--out', this.config.migrationsDir);\n if (name) args.push('--name', name);\n if (this.config.configPath) args.push('--config', this.config.configPath);\n await this.exec(args, 'generate');\n }\n\n /**\n * Aplica las migraciones pendientes a la base de datos.\n * `migrate` sólo acepta --config; schema y out no son flags válidos en este\n * comando, por eso únicamente reenviamos configPath cuando está presente.\n */\n async migrate(): Promise<void> {\n const args = ['migrate'];\n if (this.config.configPath) args.push('--config', this.config.configPath);\n await this.exec(args, 'migrate');\n }\n\n /**\n * Empuja el schema directamente a la base de datos (sin generar archivos de migración).\n * Útil para desarrollo rápido. `push` acepta --schema pero no --out.\n */\n async push(): Promise<void> {\n const args = ['push'];\n if (this.config.schemaPath) args.push('--schema', this.config.schemaPath);\n if (this.config.configPath) args.push('--config', this.config.configPath);\n await this.exec(args, 'push');\n }\n\n /**\n * Elimina un archivo de migración ya generado (`drizzle-kit drop`, interactivo).\n * No borra tablas ni datos de la base.\n * `drop` acepta --out (dónde viven las migraciones) pero no --schema.\n */\n async drop(): Promise<void> {\n const args = ['drop'];\n if (this.config.migrationsDir) args.push('--out', this.config.migrationsDir);\n if (this.config.configPath) args.push('--config', this.config.configPath);\n await this.exec(args, 'drop');\n }\n\n private async exec(args: string[], operation: string): Promise<void> {\n const env: Record<string, string> = {\n ...(process.env as Record<string, string>),\n DATABASE_URL: this.config.dbUrl,\n };\n const cwd = process.cwd();\n // Before anything runs with DATABASE_URL: never a downloaded drizzle-kit.\n const command = drizzleKitCommand(args, cwd);\n\n this.app?.logger.info(`Running migration: ${operation}`);\n\n try {\n const proc = Bun.spawn(command, {\n cwd,\n env,\n stdout: 'pipe',\n stderr: 'pipe',\n });\n\n const exitCode = await proc.exited;\n const stdout = await new Response(proc.stdout).text();\n const stderr = await new Response(proc.stderr).text();\n\n if (stdout) this.app?.logger.info(stdout.trim());\n\n if (exitCode !== 0) {\n throw new MigrationError(`Migration ${operation} failed with exit code ${exitCode}`, {\n context: { operation, exitCode, stderr: scrubCredentials(stderr.trim()) },\n });\n }\n\n this.app?.logger.info(`Migration ${operation} completed successfully`);\n } catch (error) {\n if (error instanceof MigrationError) throw error;\n throw new MigrationError(`Migration ${operation} failed`, {\n cause: error instanceof Error ? error : new Error(String(error)),\n context: { operation },\n });\n }\n }\n}\n\n/**\n * Mapea el driver de Iskra al dialecto de Drizzle Kit.\n */\nexport function mapDialect(driver: string): MigrationConfig['dialect'] {\n switch (driver) {\n case 'postgres':\n return 'postgresql';\n case 'mysql':\n return 'mysql';\n case 'sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new MigrationError(`Cannot map driver \"${driver}\" to a Drizzle dialect`, {\n context: { driver },\n });\n }\n}\n","export interface DrizzleConfigOptions {\n /** Dialecto: 'postgresql', 'mysql', 'sqlite' */\n dialect: 'postgresql' | 'mysql' | 'sqlite';\n /** URL de conexión a la base de datos */\n dbUrl: string;\n /** Ruta al archivo de schema (ej: './src/db/schema.ts') */\n schemaPath: string;\n /** Directorio de migraciones (ej: './drizzle') */\n migrationsDir?: string;\n}\n\n/**\n * La configuración que lee drizzle-kit (el `export default` de `drizzle.config.ts`).\n * Se declara aquí para no importar drizzle-kit: es una herramienta de desarrollo,\n * y el entry point de db-kit (que usa Bun vía la condición `bun`) fallaba al\n * importarse donde no estaba instalada, por ejemplo con `--production`.\n */\nexport interface DrizzleKitConfig {\n dialect: DrizzleConfigOptions['dialect'];\n schema: string;\n out: string;\n dbCredentials: { url: string };\n}\n\n/**\n * Crea una configuración de drizzle-kit reutilizable.\n *\n * Uso en tu proyecto:\n * ```ts\n * // drizzle.config.ts\n * import { createDrizzleConfig } from '@iskra-bun/db-kit';\n *\n * export default createDrizzleConfig({\n * dialect: 'sqlite',\n * dbUrl: process.env.DATABASE_URL || 'app.db',\n * schemaPath: './src/db/schema.ts',\n * });\n * ```\n */\nexport function createDrizzleConfig(options: DrizzleConfigOptions): DrizzleKitConfig {\n return {\n dialect: options.dialect,\n schema: options.schemaPath,\n out: options.migrationsDir || './drizzle',\n dbCredentials: {\n url: options.dbUrl,\n },\n };\n}\n"],"mappings":"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","names":["result"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iskra-bun/db-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Base de datos SQL de Iskra con Drizzle ORM (PostgreSQL, MySQL, SQLite, LibSQL).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iskra",
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/fearful/iskra/tree/main/packages/db-kit#readme",
|
|
24
24
|
"bugs": "https://github.com/fearful/iskra/issues",
|
|
25
|
+
"engines": {
|
|
26
|
+
"bun": ">=1.3.0"
|
|
27
|
+
},
|
|
25
28
|
"type": "module",
|
|
26
29
|
"main": "./dist/index.js",
|
|
27
30
|
"module": "./dist/index.js",
|
|
@@ -49,14 +52,14 @@
|
|
|
49
52
|
"build": "tsup --config ../../tsup.config.ts"
|
|
50
53
|
},
|
|
51
54
|
"dependencies": {
|
|
52
|
-
"@iskra-bun/core": "0.
|
|
53
|
-
"drizzle-orm": "^0.
|
|
55
|
+
"@iskra-bun/core": "^0.3.0",
|
|
56
|
+
"drizzle-orm": "^0.45.2",
|
|
54
57
|
"postgres": "^3.4.4",
|
|
55
|
-
"mysql2": "^3.
|
|
56
|
-
"@libsql/client": "^0.
|
|
58
|
+
"mysql2": "^3.24.4",
|
|
59
|
+
"@libsql/client": "^0.18.0"
|
|
57
60
|
},
|
|
58
61
|
"devDependencies": {
|
|
59
62
|
"@types/node": "^22.10.2",
|
|
60
|
-
"drizzle-kit": "^0.
|
|
63
|
+
"drizzle-kit": "^0.31.11"
|
|
61
64
|
}
|
|
62
65
|
}
|