@getstrata/core 2.2.33 → 2.2.35

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.
@@ -0,0 +1,923 @@
1
+ // @bun
2
+ var __esm = (fn, res, err) => () => {
3
+ if (fn)
4
+ try {
5
+ res = fn(fn = 0);
6
+ } catch (e) {
7
+ err = [e];
8
+ }
9
+ if (err)
10
+ throw err[0];
11
+ return res;
12
+ };
13
+
14
+ // ../../src/core/database/schema/driver.ts
15
+ function normalizeConnectionName(connection) {
16
+ const normalized = connection.trim().toLowerCase();
17
+ if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
18
+ return "pgsql";
19
+ }
20
+ if (normalized === "mysql" || normalized === "mariadb") {
21
+ return "mysql";
22
+ }
23
+ if (normalized === "sqlite") {
24
+ return "sqlite";
25
+ }
26
+ throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
27
+ }
28
+ function resolveDriverFromUrl(url) {
29
+ const normalized = url.trim().toLowerCase();
30
+ if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
31
+ return "pgsql";
32
+ }
33
+ if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
34
+ return "mysql";
35
+ }
36
+ if (normalized.startsWith("sqlite:")) {
37
+ return "sqlite";
38
+ }
39
+ return null;
40
+ }
41
+ function resolveDatabaseDriver(options = {}) {
42
+ const connection = options.connection ?? process.env.DB_CONNECTION;
43
+ if (connection) {
44
+ return normalizeConnectionName(connection);
45
+ }
46
+ const url = options.url ?? process.env.DATABASE_URL ?? "";
47
+ const fromUrl = resolveDriverFromUrl(url);
48
+ if (fromUrl) {
49
+ return fromUrl;
50
+ }
51
+ return "pgsql";
52
+ }
53
+ var init_driver = () => {};
54
+
55
+ // ../../src/core/runtime/asyncContextStore.ts
56
+ import { AsyncLocalStorage } from "async_hooks";
57
+ function createAsyncContextStore(key) {
58
+ const symbol = Symbol.for(key);
59
+ const globalRecord = globalThis;
60
+ const existing = globalRecord[symbol];
61
+ if (existing) {
62
+ return existing;
63
+ }
64
+ const store = new AsyncLocalStorage;
65
+ globalRecord[symbol] = store;
66
+ return store;
67
+ }
68
+ var init_asyncContextStore = () => {};
69
+
70
+ // ../../src/core/database/dialect.ts
71
+ function assertSafeIdentifier(identifier) {
72
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) {
73
+ throw new Error(`Invalid SQL identifier: ${identifier}`);
74
+ }
75
+ return identifier;
76
+ }
77
+ function quoteIdentifierFor(dialect, column) {
78
+ return dialect.quoteIdentifier(column);
79
+ }
80
+ function dialectFor(driver) {
81
+ return dialects[driver];
82
+ }
83
+ function currentSqlDialect() {
84
+ return dialectContext.getStore() ?? dialectOverride ?? dialectFor(resolveDatabaseDriver());
85
+ }
86
+ var postgresDialect, mysqlDialect, sqliteDialect, dialects, dialectContext, dialectOverride = null;
87
+ var init_dialect = __esm(() => {
88
+ init_asyncContextStore();
89
+ init_driver();
90
+ postgresDialect = {
91
+ driver: "pgsql",
92
+ placeholder(index) {
93
+ return `$${index}`;
94
+ },
95
+ quoteIdentifier(identifier) {
96
+ return `"${assertSafeIdentifier(identifier)}"`;
97
+ },
98
+ nowExpression() {
99
+ return "NOW()";
100
+ },
101
+ epochMillisecondsExpression() {
102
+ return "CAST(EXTRACT(EPOCH FROM clock_timestamp()) * 1000 AS BIGINT)";
103
+ },
104
+ rowLockClause(skipLocked = false) {
105
+ return ` FOR UPDATE${skipLocked ? " SKIP LOCKED" : ""}`;
106
+ },
107
+ timestampValue(value) {
108
+ return value.toISOString();
109
+ },
110
+ returningClause(columns) {
111
+ return ` RETURNING ${columns}`;
112
+ },
113
+ ilikeOperator() {
114
+ return "ILIKE";
115
+ },
116
+ nullsLastSuffix() {
117
+ return " NULLS LAST";
118
+ },
119
+ castToText(expression) {
120
+ return `${expression}::text`;
121
+ },
122
+ upsertSuffix(conflictColumns, updateColumns) {
123
+ const target = conflictColumns.map((column) => quoteIdentifierFor(this, column)).join(", ");
124
+ if (updateColumns.length === 0) {
125
+ return ` ON CONFLICT (${target}) DO NOTHING`;
126
+ }
127
+ const assignments = updateColumns.map((column) => {
128
+ const quoted = quoteIdentifierFor(this, column);
129
+ return `${quoted} = excluded.${quoted}`;
130
+ }).join(", ");
131
+ return ` ON CONFLICT (${target}) DO UPDATE SET ${assignments}`;
132
+ }
133
+ };
134
+ mysqlDialect = {
135
+ driver: "mysql",
136
+ placeholder() {
137
+ return "?";
138
+ },
139
+ quoteIdentifier(identifier) {
140
+ return `\`${assertSafeIdentifier(identifier)}\``;
141
+ },
142
+ nowExpression() {
143
+ return "CURRENT_TIMESTAMP";
144
+ },
145
+ epochMillisecondsExpression() {
146
+ return "CAST(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000 AS UNSIGNED)";
147
+ },
148
+ rowLockClause(skipLocked = false) {
149
+ return ` FOR UPDATE${skipLocked ? " SKIP LOCKED" : ""}`;
150
+ },
151
+ timestampValue(value) {
152
+ return value.toISOString().slice(0, 19).replace("T", " ");
153
+ },
154
+ returningClause() {
155
+ return "";
156
+ },
157
+ ilikeOperator() {
158
+ return "LIKE";
159
+ },
160
+ nullsLastSuffix() {
161
+ return "";
162
+ },
163
+ castToText(expression) {
164
+ return `CAST(${expression} AS CHAR)`;
165
+ },
166
+ upsertSuffix(conflictColumns, updateColumns) {
167
+ if (updateColumns.length === 0) {
168
+ const anchor = quoteIdentifierFor(this, conflictColumns[0] ?? "");
169
+ return ` ON DUPLICATE KEY UPDATE ${anchor} = ${anchor}`;
170
+ }
171
+ const assignments = updateColumns.map((column) => {
172
+ const quoted = quoteIdentifierFor(this, column);
173
+ return `${quoted} = VALUES(${quoted})`;
174
+ }).join(", ");
175
+ return ` ON DUPLICATE KEY UPDATE ${assignments}`;
176
+ }
177
+ };
178
+ sqliteDialect = {
179
+ driver: "sqlite",
180
+ placeholder() {
181
+ return "?";
182
+ },
183
+ quoteIdentifier(identifier) {
184
+ return `"${assertSafeIdentifier(identifier)}"`;
185
+ },
186
+ nowExpression() {
187
+ return "strftime('%Y-%m-%dT%H:%M:%fZ', 'now')";
188
+ },
189
+ epochMillisecondsExpression() {
190
+ return "CAST((julianday('now') - 2440587.5) * 86400000 AS INTEGER)";
191
+ },
192
+ rowLockClause() {
193
+ return "";
194
+ },
195
+ timestampValue(value) {
196
+ return value.toISOString();
197
+ },
198
+ returningClause(columns) {
199
+ return ` RETURNING ${columns}`;
200
+ },
201
+ ilikeOperator() {
202
+ return "LIKE";
203
+ },
204
+ nullsLastSuffix() {
205
+ return "";
206
+ },
207
+ castToText(expression) {
208
+ return `CAST(${expression} AS TEXT)`;
209
+ },
210
+ upsertSuffix(conflictColumns, updateColumns) {
211
+ const target = conflictColumns.map((column) => quoteIdentifierFor(this, column)).join(", ");
212
+ if (updateColumns.length === 0) {
213
+ return ` ON CONFLICT (${target}) DO NOTHING`;
214
+ }
215
+ const assignments = updateColumns.map((column) => {
216
+ const quoted = quoteIdentifierFor(this, column);
217
+ return `${quoted} = excluded.${quoted}`;
218
+ }).join(", ");
219
+ return ` ON CONFLICT (${target}) DO UPDATE SET ${assignments}`;
220
+ }
221
+ };
222
+ dialects = {
223
+ pgsql: postgresDialect,
224
+ mysql: mysqlDialect,
225
+ sqlite: sqliteDialect
226
+ };
227
+ dialectContext = createAsyncContextStore("@getstrata/sqlDialect");
228
+ });
229
+
230
+ // ../../src/core/database/migrations/rollbackGuard.ts
231
+ async function guardMigrationRollback(context, guard) {
232
+ if (context?.operation === "fresh")
233
+ return;
234
+ if (await guard.hasHistory())
235
+ throw new Error(guard.message);
236
+ }
237
+
238
+ // ../../src/core/database/schema/columnDefinition.ts
239
+ class ColumnDefinition {
240
+ name;
241
+ kind;
242
+ length;
243
+ isNullable = false;
244
+ isPrimary = false;
245
+ isUnique = false;
246
+ autoIncrement = false;
247
+ defaultValue;
248
+ checkExpression;
249
+ foreignKey;
250
+ constructor(name, kind) {
251
+ this.name = name;
252
+ this.kind = kind;
253
+ }
254
+ nullable() {
255
+ this.isNullable = true;
256
+ return this;
257
+ }
258
+ notNullable() {
259
+ this.isNullable = false;
260
+ return this;
261
+ }
262
+ default(value) {
263
+ if (typeof value === "boolean") {
264
+ this.defaultValue = value ? "TRUE" : "FALSE";
265
+ return this;
266
+ }
267
+ if (typeof value === "number") {
268
+ this.defaultValue = String(value);
269
+ return this;
270
+ }
271
+ this.defaultValue = `'${value.replace(/'/g, "''")}'`;
272
+ return this;
273
+ }
274
+ defaultRaw(expression) {
275
+ this.defaultValue = expression;
276
+ return this;
277
+ }
278
+ unique() {
279
+ this.isUnique = true;
280
+ return this;
281
+ }
282
+ primary() {
283
+ this.isPrimary = true;
284
+ return this;
285
+ }
286
+ check(expression) {
287
+ this.checkExpression = expression;
288
+ return this;
289
+ }
290
+ }
291
+
292
+ class ForeignIdColumnDefinition extends ColumnDefinition {
293
+ constructor(name) {
294
+ super(name, "foreignId");
295
+ this.notNullable();
296
+ }
297
+ references(table, column = "id") {
298
+ this.foreignKey = {
299
+ referencesTable: table,
300
+ referencesColumn: column
301
+ };
302
+ return this;
303
+ }
304
+ constrained(table) {
305
+ const referencesTable = table ?? inferReferencedTable(this.name);
306
+ return this.references(referencesTable);
307
+ }
308
+ cascadeOnDelete() {
309
+ if (!this.foreignKey) {
310
+ throw new Error(`Foreign key is not defined for column ${this.name}`);
311
+ }
312
+ this.foreignKey.onDelete = "cascade";
313
+ return this;
314
+ }
315
+ nullOnDelete() {
316
+ if (!this.foreignKey) {
317
+ throw new Error(`Foreign key is not defined for column ${this.name}`);
318
+ }
319
+ this.foreignKey.onDelete = "set null";
320
+ return this;
321
+ }
322
+ }
323
+ function inferReferencedTable(columnName) {
324
+ if (!columnName.endsWith("_id")) {
325
+ throw new Error(`Cannot infer referenced table from column ${columnName}`);
326
+ }
327
+ return columnName.slice(0, -3);
328
+ }
329
+
330
+ // ../../src/core/database/schema/blueprint.ts
331
+ class Blueprint {
332
+ table;
333
+ action;
334
+ columns = [];
335
+ indexes = [];
336
+ foreignKeys = [];
337
+ checks = [];
338
+ droppedColumns = [];
339
+ droppedIndexes = [];
340
+ constructor(table, action) {
341
+ this.table = table;
342
+ this.action = action;
343
+ }
344
+ id(name = "id") {
345
+ const column = new ColumnDefinition(name, "id");
346
+ column.primary();
347
+ column.autoIncrement = true;
348
+ this.columns.push(column);
349
+ return column;
350
+ }
351
+ bigId(name = "id") {
352
+ const column = new ColumnDefinition(name, "bigId");
353
+ column.primary();
354
+ column.autoIncrement = true;
355
+ this.columns.push(column);
356
+ return column;
357
+ }
358
+ string(name, length) {
359
+ if (length !== undefined && (!Number.isSafeInteger(length) || length <= 0)) {
360
+ throw new Error("String length must be a positive safe integer.");
361
+ }
362
+ const column = new ColumnDefinition(name, "string");
363
+ column.length = length;
364
+ column.notNullable();
365
+ this.columns.push(column);
366
+ return column;
367
+ }
368
+ text(name) {
369
+ const column = new ColumnDefinition(name, "text");
370
+ column.notNullable();
371
+ this.columns.push(column);
372
+ return column;
373
+ }
374
+ boolean(name) {
375
+ const column = new ColumnDefinition(name, "boolean");
376
+ column.notNullable();
377
+ this.columns.push(column);
378
+ return column;
379
+ }
380
+ integer(name) {
381
+ const column = new ColumnDefinition(name, "integer");
382
+ column.notNullable();
383
+ this.columns.push(column);
384
+ return column;
385
+ }
386
+ bigInteger(name) {
387
+ const column = new ColumnDefinition(name, "bigInteger");
388
+ column.notNullable();
389
+ this.columns.push(column);
390
+ return column;
391
+ }
392
+ timestamp(name) {
393
+ const column = new ColumnDefinition(name, "timestamp");
394
+ column.notNullable();
395
+ this.columns.push(column);
396
+ return column;
397
+ }
398
+ json(name) {
399
+ const column = new ColumnDefinition(name, "json");
400
+ column.notNullable();
401
+ this.columns.push(column);
402
+ return column;
403
+ }
404
+ jsonb(name) {
405
+ const column = new ColumnDefinition(name, "jsonb");
406
+ column.notNullable();
407
+ this.columns.push(column);
408
+ return column;
409
+ }
410
+ foreignId(name) {
411
+ const column = new ForeignIdColumnDefinition(name);
412
+ this.columns.push(column);
413
+ return column;
414
+ }
415
+ timestamps() {
416
+ this.timestamp("created_at").defaultRaw("NOW()");
417
+ this.timestamp("updated_at").defaultRaw("NOW()");
418
+ }
419
+ softDeletes() {
420
+ this.timestamp("deleted_at").nullable();
421
+ }
422
+ dropColumn(name) {
423
+ this.droppedColumns.push(name);
424
+ }
425
+ dropSoftDeletes() {
426
+ this.dropColumn("deleted_at");
427
+ this.dropIndex(`idx_${this.table}_deleted_at`);
428
+ }
429
+ dropIndex(name) {
430
+ this.droppedIndexes.push(name);
431
+ }
432
+ unique(columns, name) {
433
+ this.indexes.push({
434
+ name,
435
+ columns: Array.isArray(columns) ? columns : [columns],
436
+ kind: "unique"
437
+ });
438
+ }
439
+ foreignKey(columns, referencesTable, referencesColumns, options = {}) {
440
+ if (!columns.length || columns.length !== referencesColumns.length) {
441
+ throw new Error("Foreign key columns must be nonempty and match referenced columns.");
442
+ }
443
+ this.foreignKeys.push({
444
+ columns: [...columns],
445
+ referencesTable,
446
+ referencesColumns: [...referencesColumns],
447
+ ...options
448
+ });
449
+ }
450
+ check(expression, name) {
451
+ if (!expression.trim())
452
+ throw new Error("Check expression must not be empty.");
453
+ this.checks.push({ expression, name });
454
+ }
455
+ index(columns, options = {}) {
456
+ this.indexes.push({
457
+ name: options.name,
458
+ columns: Array.isArray(columns) ? columns : [columns],
459
+ kind: "index",
460
+ order: options.order
461
+ });
462
+ }
463
+ partialIndex(columns, where, nameOrOptions) {
464
+ const options = typeof nameOrOptions === "string" ? { name: nameOrOptions } : nameOrOptions ?? {};
465
+ this.indexes.push({
466
+ name: options.name,
467
+ columns: Array.isArray(columns) ? columns : [columns],
468
+ kind: options.unique ? "uniquePartial" : "partial",
469
+ where
470
+ });
471
+ }
472
+ fullText(columns, name) {
473
+ this.indexes.push({
474
+ name,
475
+ columns: Array.isArray(columns) ? columns : [columns],
476
+ kind: "fullText"
477
+ });
478
+ }
479
+ ginIndex(column, name) {
480
+ this.indexes.push({
481
+ name,
482
+ columns: [column],
483
+ kind: "gin"
484
+ });
485
+ }
486
+ }
487
+
488
+ // ../../src/core/database/schema/index.ts
489
+ init_driver();
490
+
491
+ // ../../src/core/database/schema/errors.ts
492
+ class UnsupportedSchemaFeatureError extends Error {
493
+ constructor(feature, driver) {
494
+ super(`${feature} is not supported for the ${driver} driver`);
495
+ this.name = "UnsupportedSchemaFeatureError";
496
+ }
497
+ }
498
+ // ../../src/core/database/schema/grammars/compileStatements.ts
499
+ init_dialect();
500
+
501
+ // ../../src/core/database/schema/grammars/grammar.ts
502
+ function compileColumnType(driver, column) {
503
+ switch (column.kind) {
504
+ case "bigId":
505
+ return driver === "pgsql" ? "BIGSERIAL" : compileIdType(driver);
506
+ case "id":
507
+ return compileIdType(driver);
508
+ case "string":
509
+ return compileStringType(driver, column.length);
510
+ case "text":
511
+ return compileTextType(driver);
512
+ case "boolean":
513
+ return compileBooleanType(driver);
514
+ case "integer":
515
+ case "foreignId":
516
+ return compileIntegerType(driver);
517
+ case "bigInteger":
518
+ return compileBigIntegerType(driver);
519
+ case "timestamp":
520
+ return compileTimestampType(driver);
521
+ case "json":
522
+ return compileJsonType(driver);
523
+ case "jsonb":
524
+ return compileJsonbType(driver);
525
+ default:
526
+ throw new Error(`Unsupported column kind: ${column.kind}`);
527
+ }
528
+ }
529
+ function compileIdType(driver) {
530
+ switch (driver) {
531
+ case "pgsql":
532
+ return "SERIAL";
533
+ case "mysql":
534
+ return "BIGINT UNSIGNED";
535
+ case "sqlite":
536
+ return "INTEGER";
537
+ }
538
+ }
539
+ function compileStringType(driver, length) {
540
+ switch (driver) {
541
+ case "pgsql":
542
+ return length ? `VARCHAR(${length})` : "TEXT";
543
+ case "mysql":
544
+ return length ? `VARCHAR(${length})` : "VARCHAR(255)";
545
+ case "sqlite":
546
+ return "TEXT";
547
+ }
548
+ }
549
+ function compileTextType(driver) {
550
+ switch (driver) {
551
+ case "pgsql":
552
+ case "sqlite":
553
+ return "TEXT";
554
+ case "mysql":
555
+ return "TEXT";
556
+ }
557
+ }
558
+ function compileBooleanType(driver) {
559
+ switch (driver) {
560
+ case "pgsql":
561
+ return "BOOLEAN";
562
+ case "mysql":
563
+ return "BOOLEAN";
564
+ case "sqlite":
565
+ return "INTEGER";
566
+ }
567
+ }
568
+ function compileIntegerType(driver) {
569
+ switch (driver) {
570
+ case "pgsql":
571
+ return "INTEGER";
572
+ case "mysql":
573
+ return "INT";
574
+ case "sqlite":
575
+ return "INTEGER";
576
+ }
577
+ }
578
+ function compileBigIntegerType(driver) {
579
+ switch (driver) {
580
+ case "pgsql":
581
+ return "BIGINT";
582
+ case "mysql":
583
+ return "BIGINT";
584
+ case "sqlite":
585
+ return "INTEGER";
586
+ }
587
+ }
588
+ function compileTimestampType(driver) {
589
+ switch (driver) {
590
+ case "pgsql":
591
+ return "TIMESTAMPTZ";
592
+ case "mysql":
593
+ return "TIMESTAMP";
594
+ case "sqlite":
595
+ return "TEXT";
596
+ }
597
+ }
598
+ function compileJsonType(driver) {
599
+ switch (driver) {
600
+ case "pgsql":
601
+ return "JSONB";
602
+ case "mysql":
603
+ return "JSON";
604
+ case "sqlite":
605
+ return "TEXT";
606
+ }
607
+ }
608
+ function compileJsonbType(driver) {
609
+ switch (driver) {
610
+ case "pgsql":
611
+ return "JSONB";
612
+ case "mysql":
613
+ return "JSON";
614
+ case "sqlite":
615
+ return "TEXT";
616
+ }
617
+ }
618
+
619
+ // ../../src/core/database/schema/grammars/compileStatements.ts
620
+ function compileTableConstraints(driver, blueprint) {
621
+ const quoteIdentifier = dialectFor(driver).quoteIdentifier;
622
+ const named = (name) => name ? `CONSTRAINT ${quoteIdentifier(name)} ` : "";
623
+ const columns = (names) => names.map(quoteIdentifier).join(", ");
624
+ return [
625
+ ...blueprint.foreignKeys.map((key) => {
626
+ const deletion = key.onDelete ? ` ON DELETE ${key.onDelete.toUpperCase()}` : "";
627
+ return `${named(key.name)}FOREIGN KEY (${columns(key.columns)}) REFERENCES ${quoteIdentifier(key.referencesTable)}(${columns(key.referencesColumns)})${deletion}`;
628
+ }),
629
+ ...blueprint.checks.map((check) => `${named(check.name)}CHECK (${check.expression})`)
630
+ ];
631
+ }
632
+ function compileCreateTable(driver, blueprint) {
633
+ const quoteIdentifier = dialectFor(driver).quoteIdentifier;
634
+ const table = quoteIdentifier(blueprint.table);
635
+ const parts = blueprint.columns.map((column) => compileColumn(driver, column, "create"));
636
+ for (const index of blueprint.indexes) {
637
+ if (index.kind === "unique") {
638
+ const columns = index.columns.map((column) => quoteIdentifier(column)).join(", ");
639
+ const name = index.name ? `CONSTRAINT ${quoteIdentifier(index.name)} ` : "";
640
+ parts.push(`${name}UNIQUE (${columns})`);
641
+ }
642
+ }
643
+ parts.push(...compileTableConstraints(driver, blueprint));
644
+ const statements = [`CREATE TABLE IF NOT EXISTS ${table} (
645
+ ${parts.join(`,
646
+ `)}
647
+ )`];
648
+ for (const index of blueprint.indexes) {
649
+ if (index.kind === "unique" && index.columns.length === 1) {
650
+ continue;
651
+ }
652
+ if (index.kind === "index") {
653
+ statements.push(compileIndex(driver, blueprint.table, index));
654
+ } else if (index.kind === "partial" || index.kind === "uniquePartial" || index.kind === "gin" || index.kind === "fullText") {
655
+ statements.push(...compileSpecialIndex(driver, blueprint.table, index));
656
+ }
657
+ }
658
+ return statements;
659
+ }
660
+ function compileAlterTable(driver, blueprint) {
661
+ const quoteIdentifier = dialectFor(driver).quoteIdentifier;
662
+ const statements = [];
663
+ const table = quoteIdentifier(blueprint.table);
664
+ for (const column of blueprint.columns) {
665
+ const addPrefix = driver === "pgsql" ? "ADD COLUMN IF NOT EXISTS" : "ADD COLUMN";
666
+ statements.push(`ALTER TABLE ${table}
667
+ ${addPrefix} ${compileColumn(driver, column, "alter")}`);
668
+ }
669
+ if (blueprint.foreignKeys.length || blueprint.checks.length) {
670
+ if (driver === "sqlite") {
671
+ throw new UnsupportedSchemaFeatureError("adding table constraints; rebuild the SQLite table", driver);
672
+ }
673
+ for (const constraint of compileTableConstraints(driver, blueprint)) {
674
+ statements.push(`ALTER TABLE ${table} ADD ${constraint}`);
675
+ }
676
+ }
677
+ for (const columnName of blueprint.droppedColumns) {
678
+ const dropPrefix = driver === "pgsql" ? "DROP COLUMN IF EXISTS" : "DROP COLUMN";
679
+ statements.push(`ALTER TABLE ${table} ${dropPrefix} ${quoteIdentifier(columnName)}`);
680
+ }
681
+ for (const indexName of blueprint.droppedIndexes) {
682
+ statements.push(driver === "mysql" ? `DROP INDEX ${quoteIdentifier(indexName)} ON ${table}` : `DROP INDEX IF EXISTS ${quoteIdentifier(indexName)}`);
683
+ }
684
+ for (const index of blueprint.indexes) {
685
+ if (index.kind === "index" || index.kind === "unique") {
686
+ statements.push(compileIndex(driver, blueprint.table, index));
687
+ } else {
688
+ statements.push(...compileSpecialIndex(driver, blueprint.table, index));
689
+ }
690
+ }
691
+ return statements;
692
+ }
693
+ function compileDropTable(driver, tableName) {
694
+ const quoteIdentifier = dialectFor(driver).quoteIdentifier;
695
+ const cascade = driver === "pgsql" ? " CASCADE" : "";
696
+ return [`DROP TABLE IF EXISTS ${quoteIdentifier(tableName)}${cascade}`];
697
+ }
698
+ function compileColumn(driver, column, mode) {
699
+ const quoteIdentifier = dialectFor(driver).quoteIdentifier;
700
+ const parts = [quoteIdentifier(column.name), compileColumnType(driver, column)];
701
+ if (column.autoIncrement && driver === "mysql") {
702
+ parts[1] = `${parts[1]} AUTO_INCREMENT`;
703
+ }
704
+ if (column.isPrimary && mode === "create") {
705
+ if (driver === "sqlite" && column.autoIncrement) {
706
+ parts.push("PRIMARY KEY AUTOINCREMENT");
707
+ } else {
708
+ parts.push(driver === "sqlite" && !column.isNullable ? "PRIMARY KEY NOT NULL" : "PRIMARY KEY");
709
+ }
710
+ } else if (!column.isNullable) {
711
+ parts.push("NOT NULL");
712
+ } else if (column.isNullable) {
713
+ parts.push("NULL");
714
+ }
715
+ if (column.defaultValue !== undefined) {
716
+ parts.push(`DEFAULT ${column.defaultValue}`);
717
+ }
718
+ if (column.isUnique) {
719
+ parts.push("UNIQUE");
720
+ }
721
+ if (column.checkExpression) {
722
+ parts.push(`CHECK (${column.checkExpression})`);
723
+ }
724
+ if (column.foreignKey) {
725
+ const { referencesTable, referencesColumn, onDelete } = column.foreignKey;
726
+ const reference = `${quoteIdentifier(referencesTable)}(${quoteIdentifier(referencesColumn)})`;
727
+ let clause = `REFERENCES ${reference}`;
728
+ if (onDelete === "cascade") {
729
+ clause += " ON DELETE CASCADE";
730
+ } else if (onDelete === "set null") {
731
+ clause += " ON DELETE SET NULL";
732
+ }
733
+ parts.push(clause);
734
+ }
735
+ return parts.join(" ");
736
+ }
737
+ function compileIndex(driver, tableName, index) {
738
+ const quoteIdentifier = dialectFor(driver).quoteIdentifier;
739
+ const indexName = index.name ?? defaultIndexName(tableName, index.columns, index.kind === "unique" ? "unique" : "index");
740
+ const columns = index.columns.map((column) => {
741
+ const quoted = quoteIdentifier(column);
742
+ if (index.order === "desc") {
743
+ return `${quoted} DESC`;
744
+ }
745
+ return quoted;
746
+ }).join(", ");
747
+ const unique = index.kind === "unique" ? "UNIQUE " : "";
748
+ const existence = driver === "mysql" ? "" : "IF NOT EXISTS ";
749
+ return `CREATE ${unique}INDEX ${existence}${quoteIdentifier(indexName)} ON ${quoteIdentifier(tableName)}(${columns})`;
750
+ }
751
+ function compileSpecialIndex(driver, tableName, index) {
752
+ const quoteIdentifier = dialectFor(driver).quoteIdentifier;
753
+ const indexName = index.name ?? defaultIndexName(tableName, index.columns, index.kind);
754
+ const columns = index.columns.map((column) => quoteIdentifier(column)).join(", ");
755
+ switch (index.kind) {
756
+ case "partial":
757
+ case "uniquePartial": {
758
+ if (driver !== "pgsql") {
759
+ throw new UnsupportedSchemaFeatureError("partialIndex()", driver);
760
+ }
761
+ const unique = index.kind === "uniquePartial" ? "UNIQUE " : "";
762
+ return [
763
+ `CREATE ${unique}INDEX IF NOT EXISTS ${quoteIdentifier(indexName)} ON ${quoteIdentifier(tableName)}(${columns}) WHERE ${index.where}`
764
+ ];
765
+ }
766
+ case "gin": {
767
+ if (driver !== "pgsql") {
768
+ throw new UnsupportedSchemaFeatureError("ginIndex()", driver);
769
+ }
770
+ return [
771
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(indexName)} ON ${quoteIdentifier(tableName)} USING GIN(${columns})`
772
+ ];
773
+ }
774
+ case "fullText": {
775
+ if (driver === "mysql") {
776
+ return [
777
+ `CREATE FULLTEXT INDEX ${quoteIdentifier(indexName)} ON ${quoteIdentifier(tableName)}(${columns})`
778
+ ];
779
+ }
780
+ if (driver === "pgsql") {
781
+ throw new UnsupportedSchemaFeatureError("fullText(); use ginIndex() with a tsvector column on PostgreSQL", driver);
782
+ }
783
+ throw new UnsupportedSchemaFeatureError("fullText()", driver);
784
+ }
785
+ default:
786
+ return [];
787
+ }
788
+ }
789
+ function defaultIndexName(tableName, columns, kind) {
790
+ return `idx_${tableName}_${columns.join("_")}_${kind}`;
791
+ }
792
+ function compileBlueprint(driver, blueprint) {
793
+ switch (blueprint.action) {
794
+ case "create":
795
+ return compileCreateTable(driver, blueprint);
796
+ case "alter":
797
+ return compileAlterTable(driver, blueprint);
798
+ case "drop":
799
+ return compileDropTable(driver, blueprint.table);
800
+ default:
801
+ throw new Error(`Unsupported blueprint action: ${blueprint.action}`);
802
+ }
803
+ }
804
+ // ../../src/core/database/schema/grammars/createGrammar.ts
805
+ function createGrammar(driver) {
806
+ return {
807
+ driver,
808
+ compile(blueprint) {
809
+ return compileBlueprint(driver, blueprint);
810
+ }
811
+ };
812
+ }
813
+
814
+ // ../../src/core/database/schema/grammars/mysqlGrammar.ts
815
+ var MySqlGrammar = createGrammar("mysql");
816
+
817
+ // ../../src/core/database/schema/grammars/postgresGrammar.ts
818
+ var PostgresGrammar = createGrammar("pgsql");
819
+
820
+ // ../../src/core/database/schema/grammars/sqliteGrammar.ts
821
+ var SqliteGrammar = createGrammar("sqlite");
822
+
823
+ // ../../src/core/database/schema/grammars/index.ts
824
+ function grammarForDriver(driver) {
825
+ switch (driver) {
826
+ case "pgsql":
827
+ return PostgresGrammar;
828
+ case "mysql":
829
+ return MySqlGrammar;
830
+ case "sqlite":
831
+ return SqliteGrammar;
832
+ default:
833
+ throw new Error(`Unsupported database driver: ${driver}`);
834
+ }
835
+ }
836
+ // ../../src/core/database/schema/schema.ts
837
+ init_driver();
838
+ class SchemaBuilder {
839
+ #driver;
840
+ #statements = [];
841
+ constructor(driver) {
842
+ this.#driver = driver;
843
+ }
844
+ create(table, callback) {
845
+ const blueprint = new Blueprint(table, "create");
846
+ callback(blueprint);
847
+ this.#statements.push(...grammarForDriver(this.#driver).compile(blueprint));
848
+ return this;
849
+ }
850
+ table(table, callback) {
851
+ const blueprint = new Blueprint(table, "alter");
852
+ callback(blueprint);
853
+ this.#statements.push(...grammarForDriver(this.#driver).compile(blueprint));
854
+ return this;
855
+ }
856
+ drop(table) {
857
+ const blueprint = new Blueprint(table, "drop");
858
+ this.#statements.push(...grammarForDriver(this.#driver).compile(blueprint));
859
+ return this;
860
+ }
861
+ toSql() {
862
+ return [...this.#statements];
863
+ }
864
+ async execute(db) {
865
+ for (const statement of this.#statements) {
866
+ await db.unsafe(statement);
867
+ }
868
+ }
869
+ }
870
+
871
+ class Schema {
872
+ static builder(driver) {
873
+ return new SchemaBuilder(driver ?? resolveDatabaseDriver());
874
+ }
875
+ static async run(db, driver, callback) {
876
+ const schema = Schema.builder(driver);
877
+ await callback(schema);
878
+ await schema.execute(db);
879
+ }
880
+ }
881
+ function createSchemaBuilder(db, driver) {
882
+ const builder = Schema.builder(driver);
883
+ return Object.assign(builder, {
884
+ async commit() {
885
+ await builder.execute(db);
886
+ }
887
+ });
888
+ }
889
+ // ../../src/core/auth/accountStateMigration.ts
890
+ function createAccountStateMigration(name, driver, options = {}) {
891
+ return {
892
+ name,
893
+ async up(db) {
894
+ await Schema.run(db, driver, (schema) => {
895
+ schema.table("users", (table) => {
896
+ table.boolean("is_active").default(true);
897
+ table.integer("auth_version").default(0).check("auth_version >= 0");
898
+ });
899
+ if (options.sessions)
900
+ schema.table("sessions", (table) => {
901
+ table.integer("auth_version").default(0).check("auth_version >= 0");
902
+ });
903
+ });
904
+ },
905
+ async down(db, context) {
906
+ await guardMigrationRollback(context, {
907
+ hasHistory: () => true,
908
+ message: "Removing account state requires an explicit administrative credential-reset procedure."
909
+ });
910
+ await Schema.run(db, driver, (schema) => {
911
+ schema.table("users", (table) => {
912
+ table.dropColumn("auth_version");
913
+ table.dropColumn("is_active");
914
+ });
915
+ if (options.sessions)
916
+ schema.table("sessions", (table) => table.dropColumn("auth_version"));
917
+ });
918
+ }
919
+ };
920
+ }
921
+ export {
922
+ createAccountStateMigration
923
+ };