@stacksjs/database 0.70.88 → 0.70.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/auth-tables.d.ts +60 -0
  2. package/dist/auth-tables.js +220 -0
  3. package/dist/class-seeder.d.ts +65 -0
  4. package/dist/class-seeder.js +116 -0
  5. package/dist/column.d.ts +17 -0
  6. package/dist/column.js +26 -0
  7. package/dist/custom/audits.d.ts +16 -0
  8. package/dist/custom/audits.js +57 -0
  9. package/dist/custom/errors.d.ts +1 -0
  10. package/dist/custom/errors.js +48 -0
  11. package/dist/custom/index.d.ts +3 -0
  12. package/dist/custom/index.js +3 -0
  13. package/dist/custom/jobs.d.ts +3 -0
  14. package/dist/custom/jobs.js +449 -0
  15. package/dist/database.d.ts +89 -0
  16. package/dist/database.js +178 -0
  17. package/dist/defaults.d.ts +48 -0
  18. package/dist/defaults.js +48 -0
  19. package/dist/driver-config.d.ts +149 -0
  20. package/dist/driver-config.js +144 -0
  21. package/dist/drivers/defaults/index.d.ts +2 -0
  22. package/dist/drivers/defaults/index.js +2 -0
  23. package/dist/drivers/defaults/passwords.d.ts +4 -0
  24. package/dist/drivers/defaults/passwords.js +106 -0
  25. package/dist/drivers/defaults/traits.d.ts +33 -0
  26. package/dist/drivers/defaults/traits.js +1125 -0
  27. package/dist/drivers/dynamodb.d.ts +200 -0
  28. package/dist/drivers/dynamodb.js +607 -0
  29. package/dist/drivers/helpers.d.ts +35 -0
  30. package/dist/drivers/helpers.js +206 -0
  31. package/dist/drivers/index.d.ts +16 -0
  32. package/dist/drivers/index.js +9 -0
  33. package/dist/drivers/mysql.d.ts +7 -0
  34. package/dist/drivers/mysql.js +322 -0
  35. package/dist/drivers/postgres.d.ts +7 -0
  36. package/dist/drivers/postgres.js +411 -0
  37. package/dist/drivers/sqlite.d.ts +20 -0
  38. package/dist/drivers/sqlite.js +397 -0
  39. package/dist/factory.d.ts +41 -0
  40. package/dist/factory.js +51 -0
  41. package/dist/fk-audit.d.ts +101 -0
  42. package/dist/fk-audit.js +181 -0
  43. package/dist/index.d.ts +149 -0
  44. package/dist/index.js +55 -0
  45. package/dist/migration-lock.d.ts +23 -0
  46. package/dist/migration-lock.js +143 -0
  47. package/dist/migrations.d.ts +76 -0
  48. package/dist/migrations.js +549 -0
  49. package/dist/notification-tables.d.ts +20 -0
  50. package/dist/notification-tables.js +54 -0
  51. package/dist/query-logger.d.ts +26 -0
  52. package/dist/query-logger.js +213 -0
  53. package/dist/query-parser.d.ts +4 -0
  54. package/dist/query-parser.js +93 -0
  55. package/dist/rbac-tables.d.ts +17 -0
  56. package/dist/rbac-tables.js +84 -0
  57. package/dist/safe-migrations.d.ts +72 -0
  58. package/dist/safe-migrations.js +59 -0
  59. package/dist/schema.d.ts +4 -0
  60. package/dist/schema.js +10 -0
  61. package/dist/seed-scaffold.d.ts +34 -0
  62. package/dist/seed-scaffold.js +144 -0
  63. package/dist/seeder.d.ts +116 -0
  64. package/dist/seeder.js +363 -0
  65. package/dist/sql-helpers.d.ts +33 -0
  66. package/dist/sql-helpers.js +24 -0
  67. package/dist/table.d.ts +7 -0
  68. package/dist/table.js +26 -0
  69. package/dist/tools/setup.d.ts +1 -0
  70. package/dist/tools/setup.js +6 -0
  71. package/dist/transaction-context.d.ts +52 -0
  72. package/dist/transaction-context.js +62 -0
  73. package/dist/types.d.ts +151 -0
  74. package/dist/types.js +23 -0
  75. package/dist/unique-audit.d.ts +60 -0
  76. package/dist/unique-audit.js +174 -0
  77. package/dist/utils.d.ts +189 -0
  78. package/dist/utils.js +163 -0
  79. package/dist/uuid-columns.d.ts +22 -0
  80. package/dist/uuid-columns.js +68 -0
  81. package/dist/validators.d.ts +26 -0
  82. package/dist/validators.js +122 -0
  83. package/package.json +11 -11
@@ -0,0 +1,449 @@
1
+ import { err, ok } from "@stacksjs/error-handling";
2
+ import { existsSync, readdirSync, writeFileSync, mkdirSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import process from "node:process";
5
+ import Database from "bun:sqlite";
6
+ import { env as envVars } from "@stacksjs/env";
7
+ function getDriver() {
8
+ return envVars.DB_CONNECTION || "sqlite";
9
+ }
10
+ function getMigrationsPath() {
11
+ return join(process.cwd(), "database", "migrations");
12
+ }
13
+ function hasJobsMigrationBeenCreated() {
14
+ try {
15
+ const migrationsPath = getMigrationsPath();
16
+ if (!existsSync(migrationsPath))
17
+ return !1;
18
+ return readdirSync(migrationsPath).some((file) => file.includes("create-jobs-table"));
19
+ } catch {
20
+ return !1;
21
+ }
22
+ }
23
+ async function jobBatchesTableExists() {
24
+ const sqlitePath = getDatabasePath();
25
+ if (existsSync(sqlitePath))
26
+ try {
27
+ const sqlite = new Database(sqlitePath), result = sqlite.query("SELECT name FROM sqlite_master WHERE type='table' AND name='job_batches'").get();
28
+ sqlite.close();
29
+ return result !== null;
30
+ } catch {}
31
+ if (getDriver() !== "sqlite")
32
+ try {
33
+ const { db } = await import("../utils");
34
+ await db.selectFrom("job_batches").select("id").limit(1).execute();
35
+ return !0;
36
+ } catch {
37
+ return !1;
38
+ }
39
+ return !1;
40
+ }
41
+ function getDatabasePath() {
42
+ const appRoot = envVars.APP_ROOT || process.cwd();
43
+ return join(appRoot, "database", "stacks.sqlite");
44
+ }
45
+ async function jobsTableExists() {
46
+ const driver = getDriver(), sqlitePath = getDatabasePath();
47
+ if (existsSync(sqlitePath))
48
+ try {
49
+ const sqlite = new Database(sqlitePath), result = sqlite.query("SELECT name FROM sqlite_master WHERE type='table' AND name='jobs'").get();
50
+ sqlite.close();
51
+ return result !== null;
52
+ } catch {}
53
+ if (driver !== "sqlite")
54
+ try {
55
+ const { db } = await import("../utils");
56
+ await db.selectFrom("jobs").select("id").limit(1).execute();
57
+ return !0;
58
+ } catch {
59
+ return !1;
60
+ }
61
+ return !1;
62
+ }
63
+ export async function createJobsMigration() {
64
+ try {
65
+ const driver = getDriver();
66
+ if (["sqlite", "mysql", "postgres"].includes(driver)) {
67
+ if (!hasJobsMigrationBeenCreated()) {
68
+ let migrationContent = "";
69
+ if (driver === "sqlite")
70
+ migrationContent = `-- Create jobs table
71
+ CREATE TABLE IF NOT EXISTS jobs (
72
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ queue TEXT NOT NULL DEFAULT 'default',
74
+ payload TEXT NOT NULL,
75
+ attempts INTEGER NOT NULL DEFAULT 0,
76
+ reserved_at INTEGER,
77
+ available_at INTEGER,
78
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
79
+ updated_at DATETIME
80
+ );
81
+
82
+ -- Create failed_jobs table
83
+ CREATE TABLE IF NOT EXISTS failed_jobs (
84
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
85
+ uuid TEXT NOT NULL,
86
+ connection TEXT NOT NULL,
87
+ queue TEXT NOT NULL,
88
+ payload TEXT NOT NULL,
89
+ exception TEXT NOT NULL,
90
+ failed_at DATETIME DEFAULT CURRENT_TIMESTAMP
91
+ );
92
+
93
+ -- Create job_batches table
94
+ -- then_handler / catch_handler / finally_handler columns persist
95
+ -- the terminal handlers across worker restarts
96
+ -- (stacksjs/stacks#1883). Each holds JSON for one
97
+ -- PersistentBatchHandler. Nullable so callers that only use the
98
+ -- in-memory then(fn) API does not need to populate them.
99
+ CREATE TABLE IF NOT EXISTS job_batches (
100
+ id TEXT PRIMARY KEY,
101
+ name TEXT NOT NULL DEFAULT '',
102
+ total_jobs INTEGER NOT NULL DEFAULT 0,
103
+ pending_jobs INTEGER NOT NULL DEFAULT 0,
104
+ failed_jobs INTEGER NOT NULL DEFAULT 0,
105
+ failed_job_ids TEXT NOT NULL DEFAULT '[]',
106
+ options TEXT,
107
+ cancelled_at DATETIME,
108
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
109
+ finished_at DATETIME,
110
+ then_handler TEXT,
111
+ catch_handler TEXT,
112
+ finally_handler TEXT
113
+ );
114
+
115
+ -- Dead-letter queue (stacksjs/stacks#1885). Jobs that have already
116
+ -- been retried and failed again land here instead of cycling back
117
+ -- through the queue. \`reason\` distinguishes "repeat-failure" from
118
+ -- "poison-detected" / "circuit-broken" for operator triage.
119
+ CREATE TABLE IF NOT EXISTS dead_letter_jobs (
120
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
121
+ uuid TEXT NOT NULL,
122
+ connection TEXT NOT NULL,
123
+ queue TEXT NOT NULL,
124
+ payload TEXT NOT NULL,
125
+ exception TEXT NOT NULL,
126
+ reason TEXT NOT NULL,
127
+ total_failures INTEGER NOT NULL DEFAULT 1,
128
+ first_failed_at DATETIME,
129
+ last_failed_at DATETIME,
130
+ dead_lettered_at DATETIME DEFAULT CURRENT_TIMESTAMP
131
+ );
132
+
133
+ -- Poison-message quarantine (stacksjs/stacks#1885). Tracks
134
+ -- failure counts per (jobName + payload-hash) over a rolling
135
+ -- window. When the count breaches the configured threshold, new
136
+ -- dispatches of that exact job+payload combination go directly to
137
+ -- the DLQ instead of the queue.
138
+ CREATE TABLE IF NOT EXISTS job_quarantine (
139
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
140
+ job_name TEXT NOT NULL,
141
+ payload_hash TEXT NOT NULL,
142
+ failure_count INTEGER NOT NULL DEFAULT 0,
143
+ window_start DATETIME DEFAULT CURRENT_TIMESTAMP,
144
+ quarantined_at DATETIME,
145
+ UNIQUE(job_name, payload_hash)
146
+ );
147
+
148
+ -- Per-queue circuit-breaker state (stacksjs/stacks#1885). Tracks
149
+ -- success/failure counts in a rolling window so the worker can
150
+ -- pause a queue with a sustained high failure rate. Reads + writes
151
+ -- are best-effort; a missing row means "not tripped".
152
+ CREATE TABLE IF NOT EXISTS queue_circuit_state (
153
+ queue_name TEXT PRIMARY KEY,
154
+ success_count INTEGER NOT NULL DEFAULT 0,
155
+ failure_count INTEGER NOT NULL DEFAULT 0,
156
+ window_start DATETIME DEFAULT CURRENT_TIMESTAMP,
157
+ paused_at DATETIME,
158
+ resume_at DATETIME
159
+ );
160
+ `;
161
+ else if (driver === "mysql")
162
+ migrationContent = `-- Create jobs table
163
+ CREATE TABLE IF NOT EXISTS jobs (
164
+ id INT AUTO_INCREMENT PRIMARY KEY,
165
+ queue VARCHAR(255) NOT NULL DEFAULT 'default',
166
+ payload LONGTEXT NOT NULL,
167
+ attempts INT NOT NULL DEFAULT 0,
168
+ reserved_at INT UNSIGNED NULL,
169
+ available_at INT UNSIGNED NULL,
170
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
171
+ updated_at TIMESTAMP NULL
172
+ );
173
+
174
+ -- Create failed_jobs table
175
+ CREATE TABLE IF NOT EXISTS failed_jobs (
176
+ id INT AUTO_INCREMENT PRIMARY KEY,
177
+ uuid VARCHAR(255) NOT NULL,
178
+ connection VARCHAR(255) NOT NULL,
179
+ queue VARCHAR(255) NOT NULL,
180
+ payload LONGTEXT NOT NULL,
181
+ exception LONGTEXT NOT NULL,
182
+ failed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
183
+ );
184
+
185
+ -- Create job_batches table
186
+ -- then_handler / catch_handler / finally_handler persist
187
+ -- terminal handlers across worker restarts (stacksjs/stacks#1883).
188
+ CREATE TABLE IF NOT EXISTS job_batches (
189
+ id VARCHAR(255) PRIMARY KEY,
190
+ name VARCHAR(255) NOT NULL DEFAULT '',
191
+ total_jobs INT NOT NULL DEFAULT 0,
192
+ pending_jobs INT NOT NULL DEFAULT 0,
193
+ failed_jobs INT NOT NULL DEFAULT 0,
194
+ failed_job_ids LONGTEXT NOT NULL,
195
+ options LONGTEXT,
196
+ cancelled_at TIMESTAMP NULL,
197
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
198
+ finished_at TIMESTAMP NULL,
199
+ then_handler LONGTEXT,
200
+ catch_handler LONGTEXT,
201
+ finally_handler LONGTEXT
202
+ );
203
+
204
+ -- DLQ + poison-quarantine + circuit-breaker (stacksjs/stacks#1885)
205
+ CREATE TABLE IF NOT EXISTS dead_letter_jobs (
206
+ id INT AUTO_INCREMENT PRIMARY KEY,
207
+ uuid VARCHAR(255) NOT NULL,
208
+ connection VARCHAR(255) NOT NULL,
209
+ queue VARCHAR(255) NOT NULL,
210
+ payload LONGTEXT NOT NULL,
211
+ exception LONGTEXT NOT NULL,
212
+ reason VARCHAR(64) NOT NULL,
213
+ total_failures INT NOT NULL DEFAULT 1,
214
+ first_failed_at TIMESTAMP NULL,
215
+ last_failed_at TIMESTAMP NULL,
216
+ dead_lettered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
217
+ );
218
+
219
+ CREATE TABLE IF NOT EXISTS job_quarantine (
220
+ id INT AUTO_INCREMENT PRIMARY KEY,
221
+ job_name VARCHAR(255) NOT NULL,
222
+ payload_hash VARCHAR(64) NOT NULL,
223
+ failure_count INT NOT NULL DEFAULT 0,
224
+ window_start TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
225
+ quarantined_at TIMESTAMP NULL,
226
+ UNIQUE KEY job_quarantine_unique (job_name, payload_hash)
227
+ );
228
+
229
+ CREATE TABLE IF NOT EXISTS queue_circuit_state (
230
+ queue_name VARCHAR(255) PRIMARY KEY,
231
+ success_count INT NOT NULL DEFAULT 0,
232
+ failure_count INT NOT NULL DEFAULT 0,
233
+ window_start TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
234
+ paused_at TIMESTAMP NULL,
235
+ resume_at TIMESTAMP NULL
236
+ );
237
+ `;
238
+ else if (driver === "postgres")
239
+ migrationContent = `-- Create jobs table
240
+ CREATE TABLE IF NOT EXISTS jobs (
241
+ id SERIAL PRIMARY KEY,
242
+ queue VARCHAR(255) NOT NULL DEFAULT 'default',
243
+ payload TEXT NOT NULL,
244
+ attempts INTEGER NOT NULL DEFAULT 0,
245
+ reserved_at INTEGER,
246
+ available_at INTEGER,
247
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
248
+ updated_at TIMESTAMP
249
+ );
250
+
251
+ -- Create failed_jobs table
252
+ CREATE TABLE IF NOT EXISTS failed_jobs (
253
+ id SERIAL PRIMARY KEY,
254
+ uuid VARCHAR(255) NOT NULL,
255
+ connection VARCHAR(255) NOT NULL,
256
+ queue VARCHAR(255) NOT NULL,
257
+ payload TEXT NOT NULL,
258
+ exception TEXT NOT NULL,
259
+ failed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
260
+ );
261
+
262
+ -- Create job_batches table
263
+ -- then_handler / catch_handler / finally_handler persist
264
+ -- terminal handlers across worker restarts (stacksjs/stacks#1883).
265
+ CREATE TABLE IF NOT EXISTS job_batches (
266
+ id VARCHAR(255) PRIMARY KEY,
267
+ name VARCHAR(255) NOT NULL DEFAULT '',
268
+ total_jobs INTEGER NOT NULL DEFAULT 0,
269
+ pending_jobs INTEGER NOT NULL DEFAULT 0,
270
+ failed_jobs INTEGER NOT NULL DEFAULT 0,
271
+ failed_job_ids TEXT NOT NULL DEFAULT '[]',
272
+ options TEXT,
273
+ cancelled_at TIMESTAMP,
274
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
275
+ finished_at TIMESTAMP,
276
+ then_handler TEXT,
277
+ catch_handler TEXT,
278
+ finally_handler TEXT
279
+ );
280
+
281
+ -- DLQ + poison-quarantine + circuit-breaker (stacksjs/stacks#1885)
282
+ CREATE TABLE IF NOT EXISTS dead_letter_jobs (
283
+ id SERIAL PRIMARY KEY,
284
+ uuid VARCHAR(255) NOT NULL,
285
+ connection VARCHAR(255) NOT NULL,
286
+ queue VARCHAR(255) NOT NULL,
287
+ payload TEXT NOT NULL,
288
+ exception TEXT NOT NULL,
289
+ reason VARCHAR(64) NOT NULL,
290
+ total_failures INTEGER NOT NULL DEFAULT 1,
291
+ first_failed_at TIMESTAMP,
292
+ last_failed_at TIMESTAMP,
293
+ dead_lettered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
294
+ );
295
+
296
+ CREATE TABLE IF NOT EXISTS job_quarantine (
297
+ id SERIAL PRIMARY KEY,
298
+ job_name VARCHAR(255) NOT NULL,
299
+ payload_hash VARCHAR(64) NOT NULL,
300
+ failure_count INTEGER NOT NULL DEFAULT 0,
301
+ window_start TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
302
+ quarantined_at TIMESTAMP,
303
+ UNIQUE (job_name, payload_hash)
304
+ );
305
+
306
+ CREATE TABLE IF NOT EXISTS queue_circuit_state (
307
+ queue_name VARCHAR(255) PRIMARY KEY,
308
+ success_count INTEGER NOT NULL DEFAULT 0,
309
+ failure_count INTEGER NOT NULL DEFAULT 0,
310
+ window_start TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
311
+ paused_at TIMESTAMP,
312
+ resume_at TIMESTAMP
313
+ );
314
+ `;
315
+ const migrationFileName = `${new Date().getTime().toString()}-create-jobs-table.sql`, migrationsPath = getMigrationsPath();
316
+ if (!existsSync(migrationsPath))
317
+ mkdirSync(migrationsPath, { recursive: !0 });
318
+ const migrationFilePath = join(migrationsPath, migrationFileName);
319
+ writeFileSync(migrationFilePath, migrationContent);
320
+ console.log("\u2713 Created jobs migration file");
321
+ } else
322
+ console.log("\u2713 Jobs migration file already exists");
323
+ if (!await jobsTableExists()) {
324
+ console.log(" Running migration...");
325
+ const sqlitePath = getDatabasePath();
326
+ if (driver === "sqlite" || existsSync(sqlitePath)) {
327
+ const sqlite = new Database(sqlitePath);
328
+ sqlite.run(`CREATE TABLE IF NOT EXISTS jobs (
329
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
330
+ queue TEXT NOT NULL DEFAULT 'default',
331
+ payload TEXT NOT NULL,
332
+ attempts INTEGER NOT NULL DEFAULT 0,
333
+ reserved_at INTEGER,
334
+ available_at INTEGER,
335
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
336
+ updated_at DATETIME
337
+ )`);
338
+ sqlite.run(`CREATE TABLE IF NOT EXISTS failed_jobs (
339
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
340
+ uuid TEXT NOT NULL,
341
+ connection TEXT NOT NULL,
342
+ queue TEXT NOT NULL,
343
+ payload TEXT NOT NULL,
344
+ exception TEXT NOT NULL,
345
+ failed_at DATETIME DEFAULT CURRENT_TIMESTAMP
346
+ )`);
347
+ sqlite.close();
348
+ } else if (driver === "mysql") {
349
+ const { db } = await import("../utils");
350
+ await db.unsafe(`CREATE TABLE IF NOT EXISTS jobs (
351
+ id INT AUTO_INCREMENT PRIMARY KEY,
352
+ queue VARCHAR(255) NOT NULL DEFAULT 'default',
353
+ payload LONGTEXT NOT NULL,
354
+ attempts INT NOT NULL DEFAULT 0,
355
+ reserved_at INT UNSIGNED NULL,
356
+ available_at INT UNSIGNED NULL,
357
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
358
+ updated_at TIMESTAMP NULL
359
+ )`).execute();
360
+ await db.unsafe(`CREATE TABLE IF NOT EXISTS failed_jobs (
361
+ id INT AUTO_INCREMENT PRIMARY KEY,
362
+ uuid VARCHAR(255) NOT NULL,
363
+ connection VARCHAR(255) NOT NULL,
364
+ queue VARCHAR(255) NOT NULL,
365
+ payload LONGTEXT NOT NULL,
366
+ exception LONGTEXT NOT NULL,
367
+ failed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
368
+ )`).execute();
369
+ } else if (driver === "postgres") {
370
+ const { db } = await import("../utils");
371
+ await db.unsafe(`CREATE TABLE IF NOT EXISTS jobs (
372
+ id SERIAL PRIMARY KEY,
373
+ queue VARCHAR(255) NOT NULL DEFAULT 'default',
374
+ payload TEXT NOT NULL,
375
+ attempts INTEGER NOT NULL DEFAULT 0,
376
+ reserved_at INTEGER,
377
+ available_at INTEGER,
378
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
379
+ updated_at TIMESTAMP
380
+ )`).execute();
381
+ await db.unsafe(`CREATE TABLE IF NOT EXISTS failed_jobs (
382
+ id SERIAL PRIMARY KEY,
383
+ uuid VARCHAR(255) NOT NULL,
384
+ connection VARCHAR(255) NOT NULL,
385
+ queue VARCHAR(255) NOT NULL,
386
+ payload TEXT NOT NULL,
387
+ exception TEXT NOT NULL,
388
+ failed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
389
+ )`).execute();
390
+ }
391
+ console.log("\u2713 Jobs and failed_jobs tables created");
392
+ } else
393
+ console.log("\u2713 Jobs tables already exist");
394
+ if (!await jobBatchesTableExists()) {
395
+ console.log(" Creating job_batches table...");
396
+ const sqlitePath = getDatabasePath();
397
+ if (driver === "sqlite" || existsSync(sqlitePath)) {
398
+ const sqlite = new Database(sqlitePath);
399
+ sqlite.run(`CREATE TABLE IF NOT EXISTS job_batches (
400
+ id TEXT PRIMARY KEY,
401
+ name TEXT NOT NULL DEFAULT '',
402
+ total_jobs INTEGER NOT NULL DEFAULT 0,
403
+ pending_jobs INTEGER NOT NULL DEFAULT 0,
404
+ failed_jobs INTEGER NOT NULL DEFAULT 0,
405
+ failed_job_ids TEXT NOT NULL DEFAULT '[]',
406
+ options TEXT,
407
+ cancelled_at DATETIME,
408
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
409
+ finished_at DATETIME
410
+ )`);
411
+ sqlite.close();
412
+ } else if (driver === "mysql") {
413
+ const { db } = await import("../utils");
414
+ await db.unsafe(`CREATE TABLE IF NOT EXISTS job_batches (
415
+ id VARCHAR(255) PRIMARY KEY,
416
+ name VARCHAR(255) NOT NULL DEFAULT '',
417
+ total_jobs INT NOT NULL DEFAULT 0,
418
+ pending_jobs INT NOT NULL DEFAULT 0,
419
+ failed_jobs INT NOT NULL DEFAULT 0,
420
+ failed_job_ids LONGTEXT NOT NULL,
421
+ options LONGTEXT,
422
+ cancelled_at TIMESTAMP NULL,
423
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
424
+ finished_at TIMESTAMP NULL
425
+ )`).execute();
426
+ } else if (driver === "postgres") {
427
+ const { db } = await import("../utils");
428
+ await db.unsafe(`CREATE TABLE IF NOT EXISTS job_batches (
429
+ id VARCHAR(255) PRIMARY KEY,
430
+ name VARCHAR(255) NOT NULL DEFAULT '',
431
+ total_jobs INTEGER NOT NULL DEFAULT 0,
432
+ pending_jobs INTEGER NOT NULL DEFAULT 0,
433
+ failed_jobs INTEGER NOT NULL DEFAULT 0,
434
+ failed_job_ids TEXT NOT NULL DEFAULT '[]',
435
+ options TEXT,
436
+ cancelled_at TIMESTAMP,
437
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
438
+ finished_at TIMESTAMP
439
+ )`).execute();
440
+ }
441
+ console.log("\u2713 job_batches table created");
442
+ } else
443
+ console.log("\u2713 job_batches table already exists");
444
+ }
445
+ return ok("Migration created and executed.");
446
+ } catch (error) {
447
+ return err(error instanceof Error ? error : Error(String(error)));
448
+ }
449
+ }
@@ -0,0 +1,89 @@
1
+ import { createQueryBuilder } from '@stacksjs/query-builder';
2
+ import type { QueryBuilderConfig, StacksDialect } from '@stacksjs/query-builder';
3
+ /**
4
+ * Create a database connection with the given options
5
+ */
6
+ export declare function createDatabase(options: DatabaseOptions): Database;
7
+ /**
8
+ * Create a SQLite database connection
9
+ */
10
+ export declare function createSqliteDatabase(database: string, options?: Partial<Omit<DatabaseOptions, 'driver' | 'connection'>>): Database;
11
+ /**
12
+ * Create a PostgreSQL database connection
13
+ */
14
+ export declare function createPostgresDatabase(connection: DatabaseConnectionConfig, options?: Partial<Omit<DatabaseOptions, 'driver' | 'connection'>>): Database;
15
+ /**
16
+ * Create a MySQL database connection
17
+ */
18
+ export declare function createMysqlDatabase(connection: DatabaseConnectionConfig, options?: Partial<Omit<DatabaseOptions, 'driver' | 'connection'>>): Database;
19
+ export declare interface DatabaseConnectionConfig {
20
+ database: string
21
+ host?: string
22
+ port?: number
23
+ username?: string
24
+ password?: string
25
+ url?: string
26
+ }
27
+ export declare interface DatabaseOptions {
28
+ driver: StacksDialect
29
+ connection: DatabaseConnectionConfig
30
+ verbose?: boolean
31
+ timestamps?: {
32
+ createdAt?: string
33
+ updatedAt?: string
34
+ defaultOrderColumn?: string
35
+ }
36
+ softDeletes?: {
37
+ enabled?: boolean
38
+ column?: string
39
+ defaultFilter?: boolean
40
+ }
41
+ hooks?: QueryBuilderConfig['hooks']
42
+ }
43
+ /**
44
+ * Database class for managing database connections
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * // Initialize with SQLite
49
+ * const db = new Database({
50
+ * driver: 'sqlite',
51
+ * connection: { database: 'database/app.sqlite' }
52
+ * })
53
+ *
54
+ * // Initialize with PostgreSQL
55
+ * const db = new Database({
56
+ * driver: 'postgres',
57
+ * connection: {
58
+ * database: 'myapp',
59
+ * host: 'localhost',
60
+ * port: 5432,
61
+ * username: 'postgres',
62
+ * password: 'secret'
63
+ * }
64
+ * })
65
+ *
66
+ * // Use the query builder
67
+ * const users = await db.query.selectFrom('users').where('active', '=', true).get()
68
+ * ```
69
+ */
70
+ export declare class Database {
71
+ constructor(options: DatabaseOptions);
72
+ get driver(): StacksDialect;
73
+ get connection(): DatabaseConnectionConfig;
74
+ get isInitialized(): boolean;
75
+ get query(): ReturnType<typeof createQueryBuilder>;
76
+ initialize(): void;
77
+ switchDriver(driver: StacksDialect, connection: DatabaseConnectionConfig): void;
78
+ close(): Promise<void>;
79
+ static fromConfig(config: {
80
+ default: StacksDialect
81
+ connections: {
82
+ sqlite?: { database: string }
83
+ mysql?: { name: string, host?: string, port?: number, username?: string, password?: string }
84
+ singlestore?: { name: string, host?: string, port?: number, username?: string, password?: string }
85
+ postgres?: { name: string, host?: string, port?: number, username?: string, password?: string }
86
+ }
87
+ }, env?: string): Database;
88
+ static fromEnv(): Database;
89
+ }