@bungres/kit 0.7.0 → 1.1.0
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/README.md +3 -1
- package/dist/cli.js +973 -494
- package/dist/commands/drop.d.ts.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/studio.d.ts.map +1 -1
- package/dist/commands/template.d.ts +4 -1
- package/dist/commands/template.d.ts.map +1 -1
- package/dist/differ.d.ts +8 -1
- package/dist/differ.d.ts.map +1 -1
- package/dist/index.js +501 -131
- package/dist/schema-loader.d.ts +16 -1
- package/dist/schema-loader.d.ts.map +1 -1
- package/package.json +5 -4
package/dist/cli.js
CHANGED
|
@@ -56,12 +56,12 @@ var require_src = __commonJS((exports, module) => {
|
|
|
56
56
|
ret += `${CSI2}${y}B`;
|
|
57
57
|
return ret;
|
|
58
58
|
},
|
|
59
|
-
up: (
|
|
60
|
-
down: (
|
|
61
|
-
forward: (
|
|
62
|
-
backward: (
|
|
63
|
-
nextLine: (
|
|
64
|
-
prevLine: (
|
|
59
|
+
up: (count = 1) => `${CSI2}${count}A`,
|
|
60
|
+
down: (count = 1) => `${CSI2}${count}B`,
|
|
61
|
+
forward: (count = 1) => `${CSI2}${count}C`,
|
|
62
|
+
backward: (count = 1) => `${CSI2}${count}D`,
|
|
63
|
+
nextLine: (count = 1) => `${CSI2}E`.repeat(count),
|
|
64
|
+
prevLine: (count = 1) => `${CSI2}F`.repeat(count),
|
|
65
65
|
left: `${CSI2}G`,
|
|
66
66
|
hide: `${CSI2}?25l`,
|
|
67
67
|
show: `${CSI2}?25h`,
|
|
@@ -69,21 +69,21 @@ var require_src = __commonJS((exports, module) => {
|
|
|
69
69
|
restore: `${ESC2}8`
|
|
70
70
|
};
|
|
71
71
|
var scroll = {
|
|
72
|
-
up: (
|
|
73
|
-
down: (
|
|
72
|
+
up: (count = 1) => `${CSI2}S`.repeat(count),
|
|
73
|
+
down: (count = 1) => `${CSI2}T`.repeat(count)
|
|
74
74
|
};
|
|
75
75
|
var erase = {
|
|
76
76
|
screen: `${CSI2}2J`,
|
|
77
|
-
up: (
|
|
78
|
-
down: (
|
|
77
|
+
up: (count = 1) => `${CSI2}1J`.repeat(count),
|
|
78
|
+
down: (count = 1) => `${CSI2}J`.repeat(count),
|
|
79
79
|
line: `${CSI2}2K`,
|
|
80
80
|
lineEnd: `${CSI2}K`,
|
|
81
81
|
lineStart: `${CSI2}1K`,
|
|
82
|
-
lines(
|
|
82
|
+
lines(count) {
|
|
83
83
|
let clear = "";
|
|
84
|
-
for (let i = 0;i <
|
|
85
|
-
clear += this.line + (i <
|
|
86
|
-
if (
|
|
84
|
+
for (let i = 0;i < count; i++)
|
|
85
|
+
clear += this.line + (i < count - 1 ? cursor.up() : "");
|
|
86
|
+
if (count)
|
|
87
87
|
clear += cursor.left;
|
|
88
88
|
return clear;
|
|
89
89
|
}
|
|
@@ -98,16 +98,16 @@ var require_picocolors = __commonJS((exports, module) => {
|
|
|
98
98
|
var env = p2.env || {};
|
|
99
99
|
var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p2.platform === "win32" || (p2.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
100
100
|
var formatter = (open, close, replace = open) => (input2) => {
|
|
101
|
-
let string = "" + input2,
|
|
102
|
-
return ~
|
|
101
|
+
let string = "" + input2, index = string.indexOf(close, open.length);
|
|
102
|
+
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
103
103
|
};
|
|
104
|
-
var replaceClose = (string, close, replace,
|
|
104
|
+
var replaceClose = (string, close, replace, index) => {
|
|
105
105
|
let result2 = "", cursor3 = 0;
|
|
106
106
|
do {
|
|
107
|
-
result2 += string.substring(cursor3,
|
|
108
|
-
cursor3 =
|
|
109
|
-
|
|
110
|
-
} while (~
|
|
107
|
+
result2 += string.substring(cursor3, index) + replace;
|
|
108
|
+
cursor3 = index + close.length;
|
|
109
|
+
index = string.indexOf(close, cursor3);
|
|
110
|
+
} while (~index);
|
|
111
111
|
return result2 + string.substring(cursor3);
|
|
112
112
|
};
|
|
113
113
|
var createColors = (enabled = isColorSupported) => {
|
|
@@ -164,7 +164,7 @@ var require_picocolors = __commonJS((exports, module) => {
|
|
|
164
164
|
// src/schema-loader.ts
|
|
165
165
|
import { resolve, join } from "path";
|
|
166
166
|
|
|
167
|
-
// ../bungres-orm/
|
|
167
|
+
// ../bungres-orm/dist/index.js
|
|
168
168
|
var TableConfigSymbol = Symbol.for("BungresTableConfig");
|
|
169
169
|
function getTableConfig(table) {
|
|
170
170
|
return table[TableConfigSymbol];
|
|
@@ -234,7 +234,6 @@ var table = createTableFactory("snake");
|
|
|
234
234
|
var snakeCase = { table: createTableFactory("snake") };
|
|
235
235
|
var camelCase = { table: createTableFactory("camel") };
|
|
236
236
|
var noCasing = { table: createTableFactory("none") };
|
|
237
|
-
// ../bungres-orm/src/schema/columns.ts
|
|
238
237
|
function buildColumn(dataType, nameOrOpts, opts) {
|
|
239
238
|
let name = "";
|
|
240
239
|
let options = opts;
|
|
@@ -263,6 +262,13 @@ function buildColumn(dataType, nameOrOpts, opts) {
|
|
|
263
262
|
return Object.assign(config2, {
|
|
264
263
|
as(alias) {
|
|
265
264
|
return Object.assign({}, this, { alias });
|
|
265
|
+
},
|
|
266
|
+
array() {
|
|
267
|
+
return Object.assign({}, this, { dataType: `${this.dataType}[]` });
|
|
268
|
+
},
|
|
269
|
+
generatedAlwaysAs(expr) {
|
|
270
|
+
const sqlStr = typeof expr === "string" ? expr : expr.sql;
|
|
271
|
+
return Object.assign({}, this, { generatedAs: sqlStr });
|
|
266
272
|
}
|
|
267
273
|
});
|
|
268
274
|
}
|
|
@@ -292,7 +298,6 @@ var textArray = col("text[]");
|
|
|
292
298
|
var integerArray = col("integer[]");
|
|
293
299
|
var varcharArray = col("varchar[]");
|
|
294
300
|
var uuidArray = col("uuid[]");
|
|
295
|
-
// ../bungres-orm/src/core/sql.ts
|
|
296
301
|
function sql(strings, ...values) {
|
|
297
302
|
let query = "";
|
|
298
303
|
const params = [];
|
|
@@ -335,7 +340,6 @@ function colName(c) {
|
|
|
335
340
|
return c.sql;
|
|
336
341
|
return c.tableName ? `${c.tableName}."${c.name}"` : `"${c.name}"`;
|
|
337
342
|
}
|
|
338
|
-
// ../bungres-orm/src/core/conditions.ts
|
|
339
343
|
function isColumnConfig(val) {
|
|
340
344
|
return val !== null && typeof val === "object" && "name" in val && "dataType" in val;
|
|
341
345
|
}
|
|
@@ -489,14 +493,13 @@ function parseOrderByObject(tableConfig, orderByObj) {
|
|
|
489
493
|
}
|
|
490
494
|
return parts;
|
|
491
495
|
}
|
|
492
|
-
|
|
493
|
-
// ../bungres-orm/src/builders/delete.ts
|
|
494
496
|
class DeleteBuilder {
|
|
495
497
|
_table;
|
|
496
498
|
_executor;
|
|
497
499
|
_where = [];
|
|
498
500
|
_returning;
|
|
499
501
|
_comment;
|
|
502
|
+
_with = [];
|
|
500
503
|
constructor(table2, executor) {
|
|
501
504
|
this._table = table2;
|
|
502
505
|
this._executor = executor;
|
|
@@ -515,6 +518,10 @@ class DeleteBuilder {
|
|
|
515
518
|
}
|
|
516
519
|
return this;
|
|
517
520
|
}
|
|
521
|
+
with(...ctes) {
|
|
522
|
+
this._with.push(...ctes);
|
|
523
|
+
return this;
|
|
524
|
+
}
|
|
518
525
|
returning(...columns) {
|
|
519
526
|
this._returning = columns.length > 0 ? columns : ["*"];
|
|
520
527
|
return this;
|
|
@@ -524,8 +531,20 @@ class DeleteBuilder {
|
|
|
524
531
|
return this;
|
|
525
532
|
}
|
|
526
533
|
toSQL() {
|
|
527
|
-
|
|
534
|
+
const tConfig = getTableConfig(this._table);
|
|
528
535
|
const params = [];
|
|
536
|
+
let prefix = "";
|
|
537
|
+
if (this._with.length > 0) {
|
|
538
|
+
const cteStrs = [];
|
|
539
|
+
for (const cte of this._with) {
|
|
540
|
+
const chunk = cte.query.toSQL();
|
|
541
|
+
const offset = params.length;
|
|
542
|
+
params.push(...chunk.params);
|
|
543
|
+
cteStrs.push(`"${cte.alias}" AS (${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)})`);
|
|
544
|
+
}
|
|
545
|
+
prefix = `WITH ${cteStrs.join(", ")} `;
|
|
546
|
+
}
|
|
547
|
+
let query = `DELETE FROM ${tConfig.qualifiedName}`;
|
|
529
548
|
if (this._where.length > 0) {
|
|
530
549
|
const combined = sqlJoin(this._where, " AND ");
|
|
531
550
|
query += " WHERE " + combined.sql;
|
|
@@ -537,17 +556,19 @@ class DeleteBuilder {
|
|
|
537
556
|
if (this._comment) {
|
|
538
557
|
query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
|
|
539
558
|
}
|
|
540
|
-
return { sql: query, params };
|
|
559
|
+
return { sql: prefix + query, params };
|
|
541
560
|
}
|
|
542
561
|
}
|
|
543
|
-
|
|
562
|
+
|
|
544
563
|
class InsertBuilder {
|
|
545
564
|
_table;
|
|
546
565
|
_executor;
|
|
547
566
|
_values = [];
|
|
548
567
|
_onConflict;
|
|
568
|
+
_onConflictUpdateConfig;
|
|
549
569
|
_returning;
|
|
550
570
|
_comment;
|
|
571
|
+
_with = [];
|
|
551
572
|
constructor(table2, executor) {
|
|
552
573
|
this._table = table2;
|
|
553
574
|
this._executor = executor;
|
|
@@ -574,6 +595,14 @@ class InsertBuilder {
|
|
|
574
595
|
this._onConflict = clause;
|
|
575
596
|
return this;
|
|
576
597
|
}
|
|
598
|
+
onConflictDoUpdate(config2) {
|
|
599
|
+
this._onConflictUpdateConfig = config2;
|
|
600
|
+
return this;
|
|
601
|
+
}
|
|
602
|
+
with(...ctes) {
|
|
603
|
+
this._with.push(...ctes);
|
|
604
|
+
return this;
|
|
605
|
+
}
|
|
577
606
|
returning(...columns) {
|
|
578
607
|
this._returning = columns.length > 0 ? columns : ["*"];
|
|
579
608
|
return this;
|
|
@@ -599,6 +628,17 @@ class InsertBuilder {
|
|
|
599
628
|
}
|
|
600
629
|
const columnsStr = keys.map((k) => `"${tConfig.columns[k]?.name ?? k}"`).join(", ");
|
|
601
630
|
const params = [];
|
|
631
|
+
let prefix = "";
|
|
632
|
+
if (this._with.length > 0) {
|
|
633
|
+
const cteStrs = [];
|
|
634
|
+
for (const cte of this._with) {
|
|
635
|
+
const chunk = cte.query.toSQL();
|
|
636
|
+
const offset = params.length;
|
|
637
|
+
params.push(...chunk.params);
|
|
638
|
+
cteStrs.push(`"${cte.alias}" AS (${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)})`);
|
|
639
|
+
}
|
|
640
|
+
prefix = `WITH ${cteStrs.join(", ")} `;
|
|
641
|
+
}
|
|
602
642
|
const valuesStrs = this._values.map((v) => {
|
|
603
643
|
const vals = keys.map((k) => {
|
|
604
644
|
const val = v[k];
|
|
@@ -642,6 +682,52 @@ class InsertBuilder {
|
|
|
642
682
|
query += " " + this._onConflict.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`);
|
|
643
683
|
params.push(...this._onConflict.params);
|
|
644
684
|
}
|
|
685
|
+
} else if (this._onConflictUpdateConfig) {
|
|
686
|
+
const config2 = this._onConflictUpdateConfig;
|
|
687
|
+
const targets = Array.isArray(config2.target) ? config2.target : [config2.target];
|
|
688
|
+
const targetStrs = [];
|
|
689
|
+
for (const t of targets) {
|
|
690
|
+
if (typeof t === "string") {
|
|
691
|
+
targetStrs.push(`"${tConfig.columns[t]?.name ?? t}"`);
|
|
692
|
+
} else {
|
|
693
|
+
const offset = params.length;
|
|
694
|
+
targetStrs.push(t.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`));
|
|
695
|
+
params.push(...t.params);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const setEntries = Object.entries(config2.set);
|
|
699
|
+
if (setEntries.length === 0)
|
|
700
|
+
throw new Error("InsertBuilder: onConflictDoUpdate requires 'set' fields");
|
|
701
|
+
const setClauses = setEntries.map(([key, value]) => {
|
|
702
|
+
const dbCol = tConfig.columns[key]?.name ?? key;
|
|
703
|
+
if (value && typeof value === "object" && "sql" in value && "params" in value) {
|
|
704
|
+
const chunk = value;
|
|
705
|
+
const offset = params.length;
|
|
706
|
+
params.push(...chunk.params);
|
|
707
|
+
return `"${dbCol}" = ${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)}`;
|
|
708
|
+
}
|
|
709
|
+
if (value && typeof value === "object" && !(value instanceof Date)) {
|
|
710
|
+
const colType = tConfig.columns[key]?.dataType;
|
|
711
|
+
if (colType === "json" || colType === "jsonb") {
|
|
712
|
+
params.push(value);
|
|
713
|
+
} else if (Array.isArray(value)) {
|
|
714
|
+
const pgArray = "{" + value.map((item) => {
|
|
715
|
+
if (item === null || item === undefined)
|
|
716
|
+
return "NULL";
|
|
717
|
+
if (typeof item === "string")
|
|
718
|
+
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
719
|
+
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"") + '"' : String(item);
|
|
720
|
+
}).join(",") + "}";
|
|
721
|
+
params.push(pgArray);
|
|
722
|
+
} else {
|
|
723
|
+
params.push(JSON.stringify(value));
|
|
724
|
+
}
|
|
725
|
+
return `"${dbCol}" = $${params.length}`;
|
|
726
|
+
}
|
|
727
|
+
params.push(value);
|
|
728
|
+
return `"${dbCol}" = $${params.length}`;
|
|
729
|
+
});
|
|
730
|
+
query += ` ON CONFLICT (${targetStrs.join(", ")}) DO UPDATE SET ${setClauses.join(", ")}`;
|
|
645
731
|
}
|
|
646
732
|
if (this._returning) {
|
|
647
733
|
query += " RETURNING " + (this._returning[0] === "*" ? Object.keys(tConfig.columns).map((c) => `"${tConfig.columns[c].name}" AS "${c}"`).join(", ") : this._returning.map((c) => `"${tConfig.columns[c]?.name ?? c}" AS "${c}"`).join(", "));
|
|
@@ -649,10 +735,10 @@ class InsertBuilder {
|
|
|
649
735
|
if (this._comment) {
|
|
650
736
|
query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
|
|
651
737
|
}
|
|
652
|
-
return { sql: query, params };
|
|
738
|
+
return { sql: prefix + query, params };
|
|
653
739
|
}
|
|
654
740
|
}
|
|
655
|
-
|
|
741
|
+
|
|
656
742
|
class SelectBuilder {
|
|
657
743
|
_table;
|
|
658
744
|
_executor;
|
|
@@ -665,6 +751,8 @@ class SelectBuilder {
|
|
|
665
751
|
_select;
|
|
666
752
|
_selection;
|
|
667
753
|
_joins = [];
|
|
754
|
+
_with = [];
|
|
755
|
+
_setOperations = [];
|
|
668
756
|
_comment;
|
|
669
757
|
constructor(table2, executor, selection) {
|
|
670
758
|
this._table = table2;
|
|
@@ -675,12 +763,35 @@ class SelectBuilder {
|
|
|
675
763
|
return this._executor.execute(this).then(onfulfilled, onrejected);
|
|
676
764
|
}
|
|
677
765
|
async single() {
|
|
766
|
+
if (this._limit === undefined) {
|
|
767
|
+
this.limit(1);
|
|
768
|
+
}
|
|
678
769
|
return this._executor.executeSingle(this);
|
|
679
770
|
}
|
|
680
771
|
select(...columns) {
|
|
681
772
|
this._select = columns;
|
|
682
773
|
return this;
|
|
683
774
|
}
|
|
775
|
+
with(...ctes) {
|
|
776
|
+
this._with.push(...ctes);
|
|
777
|
+
return this;
|
|
778
|
+
}
|
|
779
|
+
union(other) {
|
|
780
|
+
this._setOperations.push({ type: "UNION", builder: other });
|
|
781
|
+
return this;
|
|
782
|
+
}
|
|
783
|
+
unionAll(other) {
|
|
784
|
+
this._setOperations.push({ type: "UNION ALL", builder: other });
|
|
785
|
+
return this;
|
|
786
|
+
}
|
|
787
|
+
intersect(other) {
|
|
788
|
+
this._setOperations.push({ type: "INTERSECT", builder: other });
|
|
789
|
+
return this;
|
|
790
|
+
}
|
|
791
|
+
except(other) {
|
|
792
|
+
this._setOperations.push({ type: "EXCEPT", builder: other });
|
|
793
|
+
return this;
|
|
794
|
+
}
|
|
684
795
|
comment(tag) {
|
|
685
796
|
this._comment = tag;
|
|
686
797
|
return this;
|
|
@@ -783,7 +894,23 @@ class SelectBuilder {
|
|
|
783
894
|
}).join(", ");
|
|
784
895
|
} else {
|
|
785
896
|
const qName = getTableConfig(this._table).qualifiedName;
|
|
786
|
-
|
|
897
|
+
const colsKeys = Object.keys(getTableConfig(this._table).columns);
|
|
898
|
+
if (colsKeys.length === 0) {
|
|
899
|
+
cols = "*";
|
|
900
|
+
} else {
|
|
901
|
+
cols = colsKeys.map((c) => `${qName}."${getTableConfig(this._table).columns[c].name}" AS "${c}"`).join(", ");
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
let prefix = "";
|
|
905
|
+
if (this._with.length > 0) {
|
|
906
|
+
const cteStrs = [];
|
|
907
|
+
for (const cte of this._with) {
|
|
908
|
+
const chunk = cte.query.toSQL();
|
|
909
|
+
const offset = params.length;
|
|
910
|
+
params.push(...chunk.params);
|
|
911
|
+
cteStrs.push(`"${cte.alias}" AS (${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)})`);
|
|
912
|
+
}
|
|
913
|
+
prefix = `WITH ${cteStrs.join(", ")} `;
|
|
787
914
|
}
|
|
788
915
|
let query = `SELECT ${cols} FROM ${getTableConfig(this._table).qualifiedName}`;
|
|
789
916
|
if (this._joins.length > 0) {
|
|
@@ -843,10 +970,18 @@ class SelectBuilder {
|
|
|
843
970
|
params.push(this._offset);
|
|
844
971
|
query += ` OFFSET $${params.length}`;
|
|
845
972
|
}
|
|
973
|
+
if (this._setOperations.length > 0) {
|
|
974
|
+
for (const op of this._setOperations) {
|
|
975
|
+
const chunk = op.builder.toSQL();
|
|
976
|
+
const offset = params.length;
|
|
977
|
+
params.push(...chunk.params);
|
|
978
|
+
query += ` ${op.type} ${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)}`;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
846
981
|
if (this._comment) {
|
|
847
982
|
query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
|
|
848
983
|
}
|
|
849
|
-
return { sql: query, params };
|
|
984
|
+
return { sql: prefix + query, params };
|
|
850
985
|
}
|
|
851
986
|
}
|
|
852
987
|
|
|
@@ -861,7 +996,7 @@ class SelectBuilderIntermediate {
|
|
|
861
996
|
return new SelectBuilder(table2, this._executor, this._selection);
|
|
862
997
|
}
|
|
863
998
|
}
|
|
864
|
-
|
|
999
|
+
|
|
865
1000
|
class UpdateBuilder {
|
|
866
1001
|
_table;
|
|
867
1002
|
_executor;
|
|
@@ -869,6 +1004,7 @@ class UpdateBuilder {
|
|
|
869
1004
|
_where = [];
|
|
870
1005
|
_returning;
|
|
871
1006
|
_comment;
|
|
1007
|
+
_with = [];
|
|
872
1008
|
constructor(table2, executor) {
|
|
873
1009
|
this._table = table2;
|
|
874
1010
|
this._executor = executor;
|
|
@@ -891,6 +1027,10 @@ class UpdateBuilder {
|
|
|
891
1027
|
}
|
|
892
1028
|
return this;
|
|
893
1029
|
}
|
|
1030
|
+
with(...ctes) {
|
|
1031
|
+
this._with.push(...ctes);
|
|
1032
|
+
return this;
|
|
1033
|
+
}
|
|
894
1034
|
returning(...columns) {
|
|
895
1035
|
this._returning = columns.length > 0 ? columns : ["*"];
|
|
896
1036
|
return this;
|
|
@@ -905,6 +1045,17 @@ class UpdateBuilder {
|
|
|
905
1045
|
throw new Error("UpdateBuilder: no fields to set");
|
|
906
1046
|
}
|
|
907
1047
|
const params = [];
|
|
1048
|
+
let prefix = "";
|
|
1049
|
+
if (this._with.length > 0) {
|
|
1050
|
+
const cteStrs = [];
|
|
1051
|
+
for (const cte of this._with) {
|
|
1052
|
+
const chunk = cte.query.toSQL();
|
|
1053
|
+
const offset = params.length;
|
|
1054
|
+
params.push(...chunk.params);
|
|
1055
|
+
cteStrs.push(`"${cte.alias}" AS (${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)})`);
|
|
1056
|
+
}
|
|
1057
|
+
prefix = `WITH ${cteStrs.join(", ")} `;
|
|
1058
|
+
}
|
|
908
1059
|
const setClauses = entries.map(([key, value]) => {
|
|
909
1060
|
const dbCol = getTableConfig(this._table).columns[key]?.name ?? key;
|
|
910
1061
|
if (value && typeof value === "object" && "sql" in value && "params" in value) {
|
|
@@ -947,10 +1098,9 @@ class UpdateBuilder {
|
|
|
947
1098
|
if (this._comment) {
|
|
948
1099
|
query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
|
|
949
1100
|
}
|
|
950
|
-
return { sql: query, params };
|
|
1101
|
+
return { sql: prefix + query, params };
|
|
951
1102
|
}
|
|
952
1103
|
}
|
|
953
|
-
// ../bungres-orm/src/builders/relational.ts
|
|
954
1104
|
function getPkColumn(tableConfig) {
|
|
955
1105
|
if (tableConfig.primaryKeys?.length > 0)
|
|
956
1106
|
return tableConfig.primaryKeys[0];
|
|
@@ -1208,8 +1358,6 @@ class RelationalQueryBuilder {
|
|
|
1208
1358
|
return { sql: sql2, params };
|
|
1209
1359
|
}
|
|
1210
1360
|
}
|
|
1211
|
-
|
|
1212
|
-
// ../bungres-orm/src/core/db.ts
|
|
1213
1361
|
function parseDBName(url) {
|
|
1214
1362
|
try {
|
|
1215
1363
|
return new URL(url).pathname.slice(1);
|
|
@@ -1318,6 +1466,7 @@ class BungresDB {
|
|
|
1318
1466
|
|
|
1319
1467
|
class BungresTransaction {
|
|
1320
1468
|
_sql;
|
|
1469
|
+
_savepointCounter = 0;
|
|
1321
1470
|
constructor(sql2) {
|
|
1322
1471
|
this._sql = sql2;
|
|
1323
1472
|
}
|
|
@@ -1352,6 +1501,19 @@ class BungresTransaction {
|
|
|
1352
1501
|
const result2 = await this._sql.unsafe(query, params);
|
|
1353
1502
|
return Array.from(result2);
|
|
1354
1503
|
}
|
|
1504
|
+
async transaction(fn) {
|
|
1505
|
+
this._savepointCounter++;
|
|
1506
|
+
const spName = `sp_${this._savepointCounter}`;
|
|
1507
|
+
await this._sql.unsafe(`SAVEPOINT ${spName}`);
|
|
1508
|
+
try {
|
|
1509
|
+
const result2 = await fn(this);
|
|
1510
|
+
await this._sql.unsafe(`RELEASE SAVEPOINT ${spName}`);
|
|
1511
|
+
return result2;
|
|
1512
|
+
} catch (e) {
|
|
1513
|
+
await this._sql.unsafe(`ROLLBACK TO SAVEPOINT ${spName}`);
|
|
1514
|
+
throw e;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1355
1517
|
}
|
|
1356
1518
|
function bungres(config2) {
|
|
1357
1519
|
const db2 = new BungresDB(config2);
|
|
@@ -1375,7 +1537,6 @@ function bungres(config2) {
|
|
|
1375
1537
|
}
|
|
1376
1538
|
return db2;
|
|
1377
1539
|
}
|
|
1378
|
-
// ../bungres-orm/src/ddl.ts
|
|
1379
1540
|
function generateCreateTable(config2, ifNotExists = true) {
|
|
1380
1541
|
const tableName = config2.schema ? `"${config2.schema}"."${config2.name}"` : `"${config2.name}"`;
|
|
1381
1542
|
const exists = ifNotExists ? " IF NOT EXISTS" : "";
|
|
@@ -1385,8 +1546,8 @@ function generateCreateTable(config2, ifNotExists = true) {
|
|
|
1385
1546
|
columnDefs.push(`PRIMARY KEY (${pkCols})`);
|
|
1386
1547
|
}
|
|
1387
1548
|
if (config2.checks) {
|
|
1388
|
-
for (const
|
|
1389
|
-
columnDefs.push(`CHECK (${
|
|
1549
|
+
for (const check2 of config2.checks) {
|
|
1550
|
+
columnDefs.push(`CHECK (${check2})`);
|
|
1390
1551
|
}
|
|
1391
1552
|
}
|
|
1392
1553
|
let sql2 = `CREATE TABLE${exists} ${tableName} (
|
|
@@ -1424,7 +1585,9 @@ function buildColumnDDL(key, col2, tableName) {
|
|
|
1424
1585
|
if (col2.unique && !col2.primaryKey) {
|
|
1425
1586
|
parts.push("UNIQUE");
|
|
1426
1587
|
}
|
|
1427
|
-
if (col2.
|
|
1588
|
+
if (col2.generatedAs) {
|
|
1589
|
+
parts.push(`GENERATED ALWAYS AS (${col2.generatedAs}) STORED`);
|
|
1590
|
+
} else if (col2.defaultFn) {
|
|
1428
1591
|
parts.push(`DEFAULT ${col2.defaultFn}`);
|
|
1429
1592
|
} else if (col2.defaultValue !== undefined) {
|
|
1430
1593
|
parts.push(`DEFAULT ${formatDefaultValue(col2.defaultValue, col2.dataType)}`);
|
|
@@ -1445,6 +1608,13 @@ function buildColumnDDL(key, col2, tableName) {
|
|
|
1445
1608
|
return parts.join(" ");
|
|
1446
1609
|
}
|
|
1447
1610
|
function buildType(col2) {
|
|
1611
|
+
if (col2.enumConfig) {
|
|
1612
|
+
return `"${col2.enumConfig.enumName}"`;
|
|
1613
|
+
}
|
|
1614
|
+
if (col2.dataType.endsWith("[]")) {
|
|
1615
|
+
const baseType = buildType({ ...col2, dataType: col2.dataType.slice(0, -2) });
|
|
1616
|
+
return `${baseType}[]`;
|
|
1617
|
+
}
|
|
1448
1618
|
switch (col2.dataType) {
|
|
1449
1619
|
case "varchar":
|
|
1450
1620
|
return col2.length ? `VARCHAR(${col2.length})` : "VARCHAR";
|
|
@@ -1479,12 +1649,12 @@ function formatDefaultValue(value, dataType) {
|
|
|
1479
1649
|
}
|
|
1480
1650
|
function buildIndex(table2, idx) {
|
|
1481
1651
|
const tableName = table2.schema ? `"${table2.schema}"."${table2.name}"` : `"${table2.name}"`;
|
|
1482
|
-
const
|
|
1652
|
+
const unique2 = idx.unique ? "UNIQUE " : "";
|
|
1483
1653
|
const using = idx.using ? ` USING ${idx.using.toUpperCase()}` : "";
|
|
1484
1654
|
const cols = idx.columns.map((c) => `"${c}"`).join(", ");
|
|
1485
1655
|
const where = idx.where ? ` WHERE ${idx.where}` : "";
|
|
1486
1656
|
const idxName = idx.name ?? `idx_${table2.name}_${idx.columns.join("_")}`;
|
|
1487
|
-
return `CREATE ${
|
|
1657
|
+
return `CREATE ${unique2}INDEX IF NOT EXISTS "${idxName}" ON ${tableName}${using} (${cols})${where};`;
|
|
1488
1658
|
}
|
|
1489
1659
|
function generateAddColumn(tableName, schema, key, col2) {
|
|
1490
1660
|
const tbl = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
|
|
@@ -1494,6 +1664,38 @@ function generateDropColumn(tableName, schema, columnName) {
|
|
|
1494
1664
|
const tbl = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
|
|
1495
1665
|
return `ALTER TABLE ${tbl} DROP COLUMN IF EXISTS "${columnName}";`;
|
|
1496
1666
|
}
|
|
1667
|
+
function generateCreateEnum(name, values) {
|
|
1668
|
+
const vals = values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
1669
|
+
return `CREATE TYPE "${name}" AS ENUM (${vals});`;
|
|
1670
|
+
}
|
|
1671
|
+
function generateDropEnum(name, ifExists = true) {
|
|
1672
|
+
return `DROP TYPE${ifExists ? " IF EXISTS" : ""} "${name}";`;
|
|
1673
|
+
}
|
|
1674
|
+
function inlineParams(chunk) {
|
|
1675
|
+
let { sql: sql2, params } = chunk;
|
|
1676
|
+
return sql2.replace(/\$(\d+)/g, (_, n) => {
|
|
1677
|
+
const p = params[parseInt(n) - 1];
|
|
1678
|
+
if (typeof p === "string")
|
|
1679
|
+
return `'${p.replace(/'/g, "''")}'`;
|
|
1680
|
+
if (typeof p === "number")
|
|
1681
|
+
return String(p);
|
|
1682
|
+
if (typeof p === "boolean")
|
|
1683
|
+
return p ? "TRUE" : "FALSE";
|
|
1684
|
+
if (p === null)
|
|
1685
|
+
return "NULL";
|
|
1686
|
+
return `'${String(p).replace(/'/g, "''")}'`;
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
function generateCreateView(view) {
|
|
1690
|
+
const kind = view.materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
1691
|
+
const inlineSql = inlineParams(view.query.toSQL());
|
|
1692
|
+
return `CREATE ${kind} "${view.name}" AS ${inlineSql};`;
|
|
1693
|
+
}
|
|
1694
|
+
function generateDropView(view, ifExists = true) {
|
|
1695
|
+
const kind = view.materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
1696
|
+
return `DROP ${kind}${ifExists ? " IF EXISTS" : ""} "${view.name}";`;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1497
1699
|
// src/schema-loader.ts
|
|
1498
1700
|
async function loadSchemas(patterns, cwd = process.cwd()) {
|
|
1499
1701
|
const globs = Array.isArray(patterns) ? patterns : [patterns];
|
|
@@ -1506,11 +1708,27 @@ async function loadSchemas(patterns, cwd = process.cwd()) {
|
|
|
1506
1708
|
for (const [exportName, value] of Object.entries(mod)) {
|
|
1507
1709
|
if (isTable(value)) {
|
|
1508
1710
|
entries.push({
|
|
1711
|
+
type: "table",
|
|
1509
1712
|
exportName,
|
|
1510
1713
|
config: value[TableConfigSymbol],
|
|
1511
1714
|
table: value,
|
|
1512
1715
|
filePath: absPath
|
|
1513
1716
|
});
|
|
1717
|
+
} else if (isEnum(value)) {
|
|
1718
|
+
entries.push({
|
|
1719
|
+
type: "enum",
|
|
1720
|
+
exportName,
|
|
1721
|
+
enumName: value.enumName,
|
|
1722
|
+
enumValues: value.enumValues,
|
|
1723
|
+
filePath: absPath
|
|
1724
|
+
});
|
|
1725
|
+
} else if (isView(value)) {
|
|
1726
|
+
entries.push({
|
|
1727
|
+
type: "view",
|
|
1728
|
+
exportName,
|
|
1729
|
+
config: value,
|
|
1730
|
+
filePath: absPath
|
|
1731
|
+
});
|
|
1514
1732
|
}
|
|
1515
1733
|
}
|
|
1516
1734
|
}
|
|
@@ -1520,6 +1738,12 @@ async function loadSchemas(patterns, cwd = process.cwd()) {
|
|
|
1520
1738
|
function isTable(value) {
|
|
1521
1739
|
return typeof value === "object" && value !== null && TableConfigSymbol in value;
|
|
1522
1740
|
}
|
|
1741
|
+
function isEnum(value) {
|
|
1742
|
+
return typeof value === "function" && "enumName" in value && "enumValues" in value && Array.isArray(value.enumValues);
|
|
1743
|
+
}
|
|
1744
|
+
function isView(value) {
|
|
1745
|
+
return typeof value === "object" && value !== null && "name" in value && "query" in value && typeof value.query === "object" && typeof value.query.toSQL === "function";
|
|
1746
|
+
}
|
|
1523
1747
|
|
|
1524
1748
|
// ../../node_modules/.bun/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
|
|
1525
1749
|
import { styleText } from "util";
|
|
@@ -1575,7 +1799,7 @@ var getStringTruncatedWidth = (input2, truncationOptions = {}, widthOptions = {}
|
|
|
1575
1799
|
[CJKT_WIDE_RE, WIDE_WIDTH]
|
|
1576
1800
|
];
|
|
1577
1801
|
let indexPrev = 0;
|
|
1578
|
-
let
|
|
1802
|
+
let index = 0;
|
|
1579
1803
|
let length = input2.length;
|
|
1580
1804
|
let lengthExtra = 0;
|
|
1581
1805
|
let truncationEnabled = false;
|
|
@@ -1587,8 +1811,8 @@ var getStringTruncatedWidth = (input2, truncationOptions = {}, widthOptions = {}
|
|
|
1587
1811
|
let widthExtra = 0;
|
|
1588
1812
|
outer:
|
|
1589
1813
|
while (true) {
|
|
1590
|
-
if (unmatchedEnd > unmatchedStart ||
|
|
1591
|
-
const unmatched = input2.slice(unmatchedStart, unmatchedEnd) || input2.slice(indexPrev,
|
|
1814
|
+
if (unmatchedEnd > unmatchedStart || index >= length && index > indexPrev) {
|
|
1815
|
+
const unmatched = input2.slice(unmatchedStart, unmatchedEnd) || input2.slice(indexPrev, index);
|
|
1592
1816
|
lengthExtra = 0;
|
|
1593
1817
|
for (const char of unmatched.replaceAll(MODIFIER_RE, "")) {
|
|
1594
1818
|
const codePoint = char.codePointAt(0) || 0;
|
|
@@ -1611,17 +1835,17 @@ var getStringTruncatedWidth = (input2, truncationOptions = {}, widthOptions = {}
|
|
|
1611
1835
|
}
|
|
1612
1836
|
unmatchedStart = unmatchedEnd = 0;
|
|
1613
1837
|
}
|
|
1614
|
-
if (
|
|
1838
|
+
if (index >= length) {
|
|
1615
1839
|
break outer;
|
|
1616
1840
|
}
|
|
1617
1841
|
for (let i = 0, l = PARSE_BLOCKS.length;i < l; i++) {
|
|
1618
1842
|
const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i];
|
|
1619
|
-
BLOCK_RE.lastIndex =
|
|
1843
|
+
BLOCK_RE.lastIndex = index;
|
|
1620
1844
|
if (BLOCK_RE.test(input2)) {
|
|
1621
|
-
lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input2.slice(
|
|
1845
|
+
lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input2.slice(index, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index;
|
|
1622
1846
|
widthExtra = lengthExtra * BLOCK_WIDTH;
|
|
1623
1847
|
if (width + widthExtra > truncationLimit) {
|
|
1624
|
-
truncationIndex = Math.min(truncationIndex,
|
|
1848
|
+
truncationIndex = Math.min(truncationIndex, index + Math.floor((truncationLimit - width) / BLOCK_WIDTH));
|
|
1625
1849
|
}
|
|
1626
1850
|
if (width + widthExtra > LIMIT) {
|
|
1627
1851
|
truncationEnabled = true;
|
|
@@ -1629,12 +1853,12 @@ var getStringTruncatedWidth = (input2, truncationOptions = {}, widthOptions = {}
|
|
|
1629
1853
|
}
|
|
1630
1854
|
width += widthExtra;
|
|
1631
1855
|
unmatchedStart = indexPrev;
|
|
1632
|
-
unmatchedEnd =
|
|
1633
|
-
|
|
1856
|
+
unmatchedEnd = index;
|
|
1857
|
+
index = indexPrev = BLOCK_RE.lastIndex;
|
|
1634
1858
|
continue outer;
|
|
1635
1859
|
}
|
|
1636
1860
|
}
|
|
1637
|
-
|
|
1861
|
+
index += 1;
|
|
1638
1862
|
}
|
|
1639
1863
|
return {
|
|
1640
1864
|
width: truncationEnabled ? truncationLimit : width,
|
|
@@ -1693,7 +1917,7 @@ var getClosingCode = (openingCode) => {
|
|
|
1693
1917
|
};
|
|
1694
1918
|
var wrapAnsiCode = (code2) => `${ESC}${ANSI_CSI}${code2}${ANSI_SGR_TERMINATOR}`;
|
|
1695
1919
|
var wrapAnsiHyperlink = (url) => `${ESC}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
|
|
1696
|
-
var wrapWord = (rows, word,
|
|
1920
|
+
var wrapWord = (rows, word, columns) => {
|
|
1697
1921
|
const characters = word[Symbol.iterator]();
|
|
1698
1922
|
let isInsideEscape = false;
|
|
1699
1923
|
let isInsideLinkEscape = false;
|
|
@@ -1705,7 +1929,7 @@ var wrapWord = (rows, word, columns2) => {
|
|
|
1705
1929
|
while (!currentCharacter.done) {
|
|
1706
1930
|
const character = currentCharacter.value;
|
|
1707
1931
|
const characterLength = dist_default2(character);
|
|
1708
|
-
if (visible + characterLength <=
|
|
1932
|
+
if (visible + characterLength <= columns) {
|
|
1709
1933
|
rows[rows.length - 1] += character;
|
|
1710
1934
|
} else {
|
|
1711
1935
|
rows.push(character);
|
|
@@ -1726,7 +1950,7 @@ var wrapWord = (rows, word, columns2) => {
|
|
|
1726
1950
|
}
|
|
1727
1951
|
} else {
|
|
1728
1952
|
visible += characterLength;
|
|
1729
|
-
if (visible ===
|
|
1953
|
+
if (visible === columns && !nextCharacter.done) {
|
|
1730
1954
|
rows.push("");
|
|
1731
1955
|
visible = 0;
|
|
1732
1956
|
}
|
|
@@ -1754,7 +1978,7 @@ var stringVisibleTrimSpacesRight = (string) => {
|
|
|
1754
1978
|
}
|
|
1755
1979
|
return words.slice(0, last).join(" ") + words.slice(last).join("");
|
|
1756
1980
|
};
|
|
1757
|
-
var exec = (string,
|
|
1981
|
+
var exec = (string, columns, options = {}) => {
|
|
1758
1982
|
if (options.trim !== false && string.trim() === "") {
|
|
1759
1983
|
return "";
|
|
1760
1984
|
}
|
|
@@ -1764,8 +1988,8 @@ var exec = (string, columns2, options = {}) => {
|
|
|
1764
1988
|
const words = string.split(" ");
|
|
1765
1989
|
let rows = [""];
|
|
1766
1990
|
let rowLength = 0;
|
|
1767
|
-
for (let
|
|
1768
|
-
const word = words[
|
|
1991
|
+
for (let index = 0;index < words.length; index++) {
|
|
1992
|
+
const word = words[index];
|
|
1769
1993
|
if (options.trim !== false) {
|
|
1770
1994
|
const row = rows.at(-1) ?? "";
|
|
1771
1995
|
const trimmed = row.trimStart();
|
|
@@ -1774,8 +1998,8 @@ var exec = (string, columns2, options = {}) => {
|
|
|
1774
1998
|
rowLength = dist_default2(trimmed);
|
|
1775
1999
|
}
|
|
1776
2000
|
}
|
|
1777
|
-
if (
|
|
1778
|
-
if (rowLength >=
|
|
2001
|
+
if (index !== 0) {
|
|
2002
|
+
if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
|
|
1779
2003
|
rows.push("");
|
|
1780
2004
|
rowLength = 0;
|
|
1781
2005
|
}
|
|
@@ -1785,28 +2009,28 @@ var exec = (string, columns2, options = {}) => {
|
|
|
1785
2009
|
}
|
|
1786
2010
|
}
|
|
1787
2011
|
const wordLength = dist_default2(word);
|
|
1788
|
-
if (options.hard && wordLength >
|
|
1789
|
-
const remainingColumns =
|
|
1790
|
-
const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) /
|
|
1791
|
-
const breaksStartingNextLine = Math.floor((wordLength - 1) /
|
|
2012
|
+
if (options.hard && wordLength > columns) {
|
|
2013
|
+
const remainingColumns = columns - rowLength;
|
|
2014
|
+
const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) / columns);
|
|
2015
|
+
const breaksStartingNextLine = Math.floor((wordLength - 1) / columns);
|
|
1792
2016
|
if (breaksStartingNextLine < breaksStartingThisLine) {
|
|
1793
2017
|
rows.push("");
|
|
1794
2018
|
}
|
|
1795
|
-
wrapWord(rows, word,
|
|
2019
|
+
wrapWord(rows, word, columns);
|
|
1796
2020
|
rowLength = dist_default2(rows.at(-1) ?? "");
|
|
1797
2021
|
continue;
|
|
1798
2022
|
}
|
|
1799
|
-
if (rowLength + wordLength >
|
|
1800
|
-
if (options.wordWrap === false && rowLength <
|
|
1801
|
-
wrapWord(rows, word,
|
|
2023
|
+
if (rowLength + wordLength > columns && rowLength && wordLength) {
|
|
2024
|
+
if (options.wordWrap === false && rowLength < columns) {
|
|
2025
|
+
wrapWord(rows, word, columns);
|
|
1802
2026
|
rowLength = dist_default2(rows.at(-1) ?? "");
|
|
1803
2027
|
continue;
|
|
1804
2028
|
}
|
|
1805
2029
|
rows.push("");
|
|
1806
2030
|
rowLength = 0;
|
|
1807
2031
|
}
|
|
1808
|
-
if (rowLength + wordLength >
|
|
1809
|
-
wrapWord(rows, word,
|
|
2032
|
+
if (rowLength + wordLength > columns && options.wordWrap === false) {
|
|
2033
|
+
wrapWord(rows, word, columns);
|
|
1810
2034
|
rowLength = dist_default2(rows.at(-1) ?? "");
|
|
1811
2035
|
continue;
|
|
1812
2036
|
}
|
|
@@ -1863,8 +2087,8 @@ var exec = (string, columns2, options = {}) => {
|
|
|
1863
2087
|
return returnValue;
|
|
1864
2088
|
};
|
|
1865
2089
|
var CRLF_OR_LF = /\r?\n/;
|
|
1866
|
-
function wrapAnsi(string,
|
|
1867
|
-
return String(string).normalize().split(CRLF_OR_LF).map((line2) => exec(line2,
|
|
2090
|
+
function wrapAnsi(string, columns, options) {
|
|
2091
|
+
return String(string).normalize().split(CRLF_OR_LF).map((line2) => exec(line2, columns, options)).join(`
|
|
1868
2092
|
`);
|
|
1869
2093
|
}
|
|
1870
2094
|
|
|
@@ -2887,9 +3111,12 @@ async function runDrop(config2, opts = {}) {
|
|
|
2887
3111
|
const s = spinner();
|
|
2888
3112
|
s.start("Loading schemas...");
|
|
2889
3113
|
const schemas2 = await loadSchemas(config2.schema);
|
|
2890
|
-
|
|
3114
|
+
const tableSchemas = schemas2.filter((s2) => s2.type === "table");
|
|
3115
|
+
const enumSchemas = schemas2.filter((s2) => s2.type === "enum");
|
|
3116
|
+
const viewSchemas = schemas2.filter((s2) => s2.type === "view");
|
|
3117
|
+
if (tableSchemas.length === 0 && enumSchemas.length === 0 && viewSchemas.length === 0) {
|
|
2891
3118
|
s.stop("No schemas found.");
|
|
2892
|
-
log.warn(import_picocolors.default.yellow("No
|
|
3119
|
+
log.warn(import_picocolors.default.yellow("No definitions found in schema files."));
|
|
2893
3120
|
outro("Failed.");
|
|
2894
3121
|
return;
|
|
2895
3122
|
}
|
|
@@ -2911,25 +3138,31 @@ async function runDrop(config2, opts = {}) {
|
|
|
2911
3138
|
WHERE table_schema = $1 AND table_name = $2
|
|
2912
3139
|
) AS exists`, [config2.migrationsSchema, "__bungres_push"]);
|
|
2913
3140
|
const pushTableExists = pushTableCheck[0]?.exists ?? false;
|
|
2914
|
-
const tablesToDrop =
|
|
2915
|
-
if (tablesToDrop.length === 0 && !migrationTableExists && !pushTableExists) {
|
|
3141
|
+
const tablesToDrop = tableSchemas.filter((sch) => existingTableNames.has(sch.config.name));
|
|
3142
|
+
if (tablesToDrop.length === 0 && enumSchemas.length === 0 && viewSchemas.length === 0 && !migrationTableExists && !pushTableExists) {
|
|
2916
3143
|
ms.stop("Nothing to do.");
|
|
2917
|
-
log.success(import_picocolors.default.green("No
|
|
3144
|
+
log.success(import_picocolors.default.green("No items to drop (they either don't exist or were already dropped)."));
|
|
2918
3145
|
outro("Done.");
|
|
2919
3146
|
return;
|
|
2920
3147
|
}
|
|
2921
3148
|
ms.stop("Database check complete.");
|
|
2922
|
-
const
|
|
3149
|
+
const namesToPrint = tablesToDrop.map((sch) => (sch.config.schema ? sch.config.schema + "." : "") + sch.config.name + " (table)");
|
|
3150
|
+
for (const v of viewSchemas) {
|
|
3151
|
+
namesToPrint.push(`${v.config.name} (view)`);
|
|
3152
|
+
}
|
|
3153
|
+
for (const e of enumSchemas) {
|
|
3154
|
+
namesToPrint.push(`${e.enumName} (enum)`);
|
|
3155
|
+
}
|
|
2923
3156
|
if (migrationTableExists) {
|
|
2924
|
-
|
|
3157
|
+
namesToPrint.push(`${config2.migrationsSchema}.${config2.migrationsTable} (table)`);
|
|
2925
3158
|
}
|
|
2926
3159
|
if (pushTableExists) {
|
|
2927
|
-
|
|
3160
|
+
namesToPrint.push(`${config2.migrationsSchema}.__bungres_push (table)`);
|
|
2928
3161
|
}
|
|
2929
3162
|
if (!opts.force) {
|
|
2930
3163
|
log.warn(import_picocolors.default.bgRed(import_picocolors.default.white(" \u26A0\uFE0F WARNING ")));
|
|
2931
|
-
log.message(import_picocolors.default.bold(import_picocolors.default.red("This will drop the following
|
|
2932
|
-
for (const name of
|
|
3164
|
+
log.message(import_picocolors.default.bold(import_picocolors.default.red("This will drop the following items and ALL their data:")));
|
|
3165
|
+
for (const name of namesToPrint) {
|
|
2933
3166
|
log.info(import_picocolors.default.yellow(` - ${name}`));
|
|
2934
3167
|
}
|
|
2935
3168
|
const confirm2 = await confirm({
|
|
@@ -2943,11 +3176,22 @@ async function runDrop(config2, opts = {}) {
|
|
|
2943
3176
|
}
|
|
2944
3177
|
const exSpinner = spinner();
|
|
2945
3178
|
exSpinner.start("Dropping tables...");
|
|
3179
|
+
for (const entry of viewSchemas) {
|
|
3180
|
+
const isMat = entry.config.materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
3181
|
+
const ddl = `DROP ${isMat} IF EXISTS "${entry.config.name}" CASCADE;`;
|
|
3182
|
+
await sql2.unsafe(ddl);
|
|
3183
|
+
log.step(import_picocolors.default.gray(`Dropped ${entry.config.name}`));
|
|
3184
|
+
}
|
|
2946
3185
|
for (const entry of tablesToDrop) {
|
|
2947
3186
|
const ddl = `DROP TABLE IF EXISTS "${entry.config.name}" CASCADE;`;
|
|
2948
3187
|
await sql2.unsafe(ddl);
|
|
2949
3188
|
log.step(import_picocolors.default.gray(`Dropped ${entry.config.name}`));
|
|
2950
3189
|
}
|
|
3190
|
+
for (const entry of enumSchemas) {
|
|
3191
|
+
const ddl = `DROP TYPE IF EXISTS "${entry.enumName}" CASCADE;`;
|
|
3192
|
+
await sql2.unsafe(ddl);
|
|
3193
|
+
log.step(import_picocolors.default.gray(`Dropped ${entry.enumName}`));
|
|
3194
|
+
}
|
|
2951
3195
|
if (migrationTableExists) {
|
|
2952
3196
|
const qualifiedMigTable = `"${config2.migrationsSchema}"."${config2.migrationsTable}"`;
|
|
2953
3197
|
await sql2.unsafe(`DROP TABLE IF EXISTS ${qualifiedMigTable} CASCADE`);
|
|
@@ -2957,7 +3201,7 @@ async function runDrop(config2, opts = {}) {
|
|
|
2957
3201
|
await sql2.unsafe(`DROP TABLE IF EXISTS "${config2.migrationsSchema}"."__bungres_push" CASCADE`);
|
|
2958
3202
|
log.step(import_picocolors.default.gray(`Dropped ${config2.migrationsSchema}.__bungres_push`));
|
|
2959
3203
|
}
|
|
2960
|
-
exSpinner.stop("
|
|
3204
|
+
exSpinner.stop("Items dropped.");
|
|
2961
3205
|
outro(import_picocolors.default.cyan("\u2728 Drop complete."));
|
|
2962
3206
|
} catch (err) {
|
|
2963
3207
|
log.error(import_picocolors.default.red(`Drop failed: ${err.message}`));
|
|
@@ -3064,12 +3308,56 @@ function diffSchemas(prev, next) {
|
|
|
3064
3308
|
const statements = [];
|
|
3065
3309
|
const summary = [];
|
|
3066
3310
|
const warnings = [];
|
|
3067
|
-
const
|
|
3068
|
-
const
|
|
3311
|
+
const prevEnums = prev.enums || {};
|
|
3312
|
+
const nextEnums = next.enums || {};
|
|
3313
|
+
const prevEnumNames = new Set(Object.keys(prevEnums));
|
|
3314
|
+
const nextEnumNames = new Set(Object.keys(nextEnums));
|
|
3315
|
+
for (const enumName of nextEnumNames) {
|
|
3316
|
+
if (!prevEnumNames.has(enumName)) {
|
|
3317
|
+
const e = nextEnums[enumName];
|
|
3318
|
+
statements.push(generateCreateEnum(e.enumName, e.enumValues));
|
|
3319
|
+
summary.push(`CREATE TYPE ${e.enumName}`);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
for (const enumName of prevEnumNames) {
|
|
3323
|
+
if (!nextEnumNames.has(enumName)) {
|
|
3324
|
+
const e = prevEnums[enumName];
|
|
3325
|
+
statements.push(generateDropEnum(e.enumName, true));
|
|
3326
|
+
summary.push(`DROP TYPE ${e.enumName}`);
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
for (const enumName of nextEnumNames) {
|
|
3330
|
+
if (prevEnumNames.has(enumName)) {
|
|
3331
|
+
const p2 = prevEnums[enumName];
|
|
3332
|
+
const n2 = nextEnums[enumName];
|
|
3333
|
+
if (JSON.stringify(p2.enumValues) !== JSON.stringify(n2.enumValues)) {
|
|
3334
|
+
warnings.push(`Enum '${n2.enumName}' values have changed. Bungres Kit currently requires manual migration for ALTER TYPE ... ADD VALUE.`);
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
const prevViews = prev.views || {};
|
|
3339
|
+
const nextViews = next.views || {};
|
|
3340
|
+
const prevViewNames = new Set(Object.keys(prevViews));
|
|
3341
|
+
const nextViewNames = new Set(Object.keys(nextViews));
|
|
3342
|
+
for (const viewName of prevViewNames) {
|
|
3343
|
+
if (!nextViewNames.has(viewName)) {
|
|
3344
|
+
statements.push(generateDropView(prevViews[viewName]));
|
|
3345
|
+
summary.push(`DROP VIEW ${viewName}`);
|
|
3346
|
+
} else {
|
|
3347
|
+
const pSql = generateCreateView(prevViews[viewName]);
|
|
3348
|
+
const nSql = generateCreateView(nextViews[viewName]);
|
|
3349
|
+
if (pSql !== nSql) {
|
|
3350
|
+
statements.push(generateDropView(prevViews[viewName]));
|
|
3351
|
+
summary.push(`DROP VIEW ${viewName} (for recreation)`);
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
const prevTables = new Set(Object.keys(prev.tables || {}));
|
|
3356
|
+
const nextTables = new Set(Object.keys(next.tables || {}));
|
|
3069
3357
|
const newTableConfigs = [];
|
|
3070
3358
|
for (const tableName of nextTables) {
|
|
3071
3359
|
if (!prevTables.has(tableName)) {
|
|
3072
|
-
newTableConfigs.push(next[tableName]);
|
|
3360
|
+
newTableConfigs.push(next.tables[tableName]);
|
|
3073
3361
|
}
|
|
3074
3362
|
}
|
|
3075
3363
|
for (const config2 of topoSortConfigs(newTableConfigs)) {
|
|
@@ -3078,7 +3366,7 @@ function diffSchemas(prev, next) {
|
|
|
3078
3366
|
}
|
|
3079
3367
|
for (const tableName of prevTables) {
|
|
3080
3368
|
if (!nextTables.has(tableName)) {
|
|
3081
|
-
const config2 = prev[tableName];
|
|
3369
|
+
const config2 = prev.tables[tableName];
|
|
3082
3370
|
const tbl = config2.schema ? `"${config2.schema}"."${tableName}"` : `"${tableName}"`;
|
|
3083
3371
|
statements.push(`DROP TABLE IF EXISTS ${tbl};`);
|
|
3084
3372
|
summary.push(`DROP TABLE ${tableName}`);
|
|
@@ -3088,8 +3376,8 @@ function diffSchemas(prev, next) {
|
|
|
3088
3376
|
for (const tableName of nextTables) {
|
|
3089
3377
|
if (!prevTables.has(tableName))
|
|
3090
3378
|
continue;
|
|
3091
|
-
const prevConfig = prev[tableName];
|
|
3092
|
-
const nextConfig = next[tableName];
|
|
3379
|
+
const prevConfig = prev.tables[tableName];
|
|
3380
|
+
const nextConfig = next.tables[tableName];
|
|
3093
3381
|
const prevCols = prevConfig.columns;
|
|
3094
3382
|
const nextCols = nextConfig.columns;
|
|
3095
3383
|
const prevColNames = new Set(Object.keys(prevCols));
|
|
@@ -3144,11 +3432,11 @@ function diffSchemas(prev, next) {
|
|
|
3144
3432
|
const idxName = idx.name ?? `idx_${tableName}_${idx.columns.join("_")}`;
|
|
3145
3433
|
if (!prevIdxNames.has(idxName)) {
|
|
3146
3434
|
const tbl = nextConfig.schema ? `"${nextConfig.schema}"."${tableName}"` : `"${tableName}"`;
|
|
3147
|
-
const
|
|
3435
|
+
const unique = idx.unique ? "UNIQUE " : "";
|
|
3148
3436
|
const using = idx.using ? ` USING ${idx.using.toUpperCase()}` : "";
|
|
3149
3437
|
const cols = idx.columns.map((c2) => `"${c2}"`).join(", ");
|
|
3150
3438
|
const where = idx.where ? ` WHERE ${idx.where}` : "";
|
|
3151
|
-
statements.push(`CREATE ${
|
|
3439
|
+
statements.push(`CREATE ${unique}INDEX IF NOT EXISTS "${idxName}" ON ${tbl}${using} (${cols})${where};`);
|
|
3152
3440
|
summary.push(`CREATE INDEX ${idxName} ON ${tableName}`);
|
|
3153
3441
|
}
|
|
3154
3442
|
}
|
|
@@ -3161,6 +3449,19 @@ function diffSchemas(prev, next) {
|
|
|
3161
3449
|
}
|
|
3162
3450
|
}
|
|
3163
3451
|
}
|
|
3452
|
+
for (const viewName of nextViewNames) {
|
|
3453
|
+
if (!prevViewNames.has(viewName)) {
|
|
3454
|
+
statements.push(generateCreateView(nextViews[viewName]));
|
|
3455
|
+
summary.push(`CREATE VIEW ${viewName}`);
|
|
3456
|
+
} else {
|
|
3457
|
+
const pSql = generateCreateView(prevViews[viewName]);
|
|
3458
|
+
const nSql = generateCreateView(nextViews[viewName]);
|
|
3459
|
+
if (pSql !== nSql) {
|
|
3460
|
+
statements.push(generateCreateView(nextViews[viewName]));
|
|
3461
|
+
summary.push(`CREATE VIEW ${viewName} (recreated)`);
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3164
3465
|
return { statements, summary, warnings };
|
|
3165
3466
|
}
|
|
3166
3467
|
function topoSortConfigs(tables) {
|
|
@@ -3188,7 +3489,14 @@ function topoSortConfigs(tables) {
|
|
|
3188
3489
|
function diffColumn(prev, next) {
|
|
3189
3490
|
const changes = [];
|
|
3190
3491
|
const col2 = `"${next.name}"`;
|
|
3492
|
+
const prevDef = prev.defaultFn ?? (prev.defaultValue !== undefined ? String(prev.defaultValue) : undefined);
|
|
3493
|
+
const nextDef = next.defaultFn ?? (next.defaultValue !== undefined ? String(next.defaultValue) : undefined);
|
|
3494
|
+
let typeChanged = false;
|
|
3191
3495
|
if (prev.dataType !== next.dataType) {
|
|
3496
|
+
typeChanged = true;
|
|
3497
|
+
if (prevDef !== undefined) {
|
|
3498
|
+
changes.push(`ALTER COLUMN ${col2} DROP DEFAULT`);
|
|
3499
|
+
}
|
|
3192
3500
|
changes.push(`ALTER COLUMN ${col2} TYPE ${next.dataType.toUpperCase()} USING ${col2}::${next.dataType}`);
|
|
3193
3501
|
} else {
|
|
3194
3502
|
const prevLen = prev.length;
|
|
@@ -3205,9 +3513,12 @@ function diffColumn(prev, next) {
|
|
|
3205
3513
|
if (prev.notNull && !next.notNull) {
|
|
3206
3514
|
changes.push(`ALTER COLUMN ${col2} DROP NOT NULL`);
|
|
3207
3515
|
}
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3516
|
+
if (typeChanged) {
|
|
3517
|
+
if (nextDef !== undefined) {
|
|
3518
|
+
const val = next.defaultFn ?? formatDefault(next.defaultValue, next.dataType);
|
|
3519
|
+
changes.push(`ALTER COLUMN ${col2} SET DEFAULT ${val}`);
|
|
3520
|
+
}
|
|
3521
|
+
} else if (prevDef !== nextDef) {
|
|
3211
3522
|
if (nextDef !== undefined) {
|
|
3212
3523
|
const val = next.defaultFn ?? formatDefault(next.defaultValue, next.dataType);
|
|
3213
3524
|
changes.push(`ALTER COLUMN ${col2} SET DEFAULT ${val}`);
|
|
@@ -3250,19 +3561,46 @@ async function runGenerate(config2, name) {
|
|
|
3250
3561
|
const metaDir = join3(migrationsDir, "meta");
|
|
3251
3562
|
await Bun.$`mkdir -p ${migrationsDir}`.quiet();
|
|
3252
3563
|
await Bun.$`mkdir -p ${metaDir}`.quiet();
|
|
3253
|
-
const currentSnapshot =
|
|
3254
|
-
|
|
3564
|
+
const currentSnapshot = { tables: {}, enums: {}, views: {} };
|
|
3565
|
+
for (const s2 of schemas2) {
|
|
3566
|
+
if (s2.type === "table") {
|
|
3567
|
+
currentSnapshot.tables[s2.config.name] = s2.config;
|
|
3568
|
+
} else if (s2.type === "enum") {
|
|
3569
|
+
currentSnapshot.enums[s2.enumName] = { enumName: s2.enumName, enumValues: s2.enumValues };
|
|
3570
|
+
} else if (s2.type === "view") {
|
|
3571
|
+
currentSnapshot.views[s2.config.name] = s2.config;
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
let prevSnapshot = { tables: {}, enums: {}, views: {} };
|
|
3255
3575
|
let isFirstMigration = true;
|
|
3256
3576
|
try {
|
|
3257
3577
|
const files = readdirSync(metaDir).filter((f2) => f2.endsWith("_snapshot.json")).sort();
|
|
3258
3578
|
if (files.length > 0) {
|
|
3259
3579
|
const latest = files[files.length - 1];
|
|
3260
|
-
|
|
3580
|
+
const parsed = JSON.parse(await Bun.file(join3(metaDir, latest)).text());
|
|
3581
|
+
if (parsed.tables || parsed.enums || parsed.views) {
|
|
3582
|
+
prevSnapshot = {
|
|
3583
|
+
tables: parsed.tables || {},
|
|
3584
|
+
enums: parsed.enums || {},
|
|
3585
|
+
views: parsed.views || {}
|
|
3586
|
+
};
|
|
3587
|
+
} else {
|
|
3588
|
+
prevSnapshot = { tables: parsed, enums: {}, views: {} };
|
|
3589
|
+
}
|
|
3261
3590
|
isFirstMigration = false;
|
|
3262
3591
|
} else {
|
|
3263
3592
|
const legacyFile2 = Bun.file(join3(migrationsDir, ".snapshot.json"));
|
|
3264
3593
|
if (await legacyFile2.exists()) {
|
|
3265
|
-
|
|
3594
|
+
const parsed = JSON.parse(await legacyFile2.text());
|
|
3595
|
+
if (parsed.tables || parsed.enums || parsed.views) {
|
|
3596
|
+
prevSnapshot = {
|
|
3597
|
+
tables: parsed.tables || {},
|
|
3598
|
+
enums: parsed.enums || {},
|
|
3599
|
+
views: parsed.views || {}
|
|
3600
|
+
};
|
|
3601
|
+
} else {
|
|
3602
|
+
prevSnapshot = { tables: parsed, enums: {}, views: {} };
|
|
3603
|
+
}
|
|
3266
3604
|
isFirstMigration = false;
|
|
3267
3605
|
}
|
|
3268
3606
|
}
|
|
@@ -3282,17 +3620,30 @@ async function runGenerate(config2, name) {
|
|
|
3282
3620
|
let summary;
|
|
3283
3621
|
let warnings = [];
|
|
3284
3622
|
if (isFirstMigration) {
|
|
3285
|
-
const
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
]
|
|
3291
|
-
|
|
3623
|
+
const tableSchemas = schemas2.filter((s2) => s2.type === "table");
|
|
3624
|
+
const enumSchemas = schemas2.filter((s2) => s2.type === "enum");
|
|
3625
|
+
const viewSchemas = schemas2.filter((s2) => s2.type === "view");
|
|
3626
|
+
const sorted = topoSort(tableSchemas);
|
|
3627
|
+
upStatements = [];
|
|
3628
|
+
downStatements = [];
|
|
3629
|
+
summary = [];
|
|
3630
|
+
for (const e of enumSchemas) {
|
|
3631
|
+
upStatements.push(`-- ${e.exportName}`, `CREATE TYPE "${e.enumName}" AS ENUM (${e.enumValues.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ")});`, ``);
|
|
3632
|
+
downStatements.unshift(`DROP TYPE IF EXISTS "${e.enumName}";`);
|
|
3633
|
+
summary.push(`CREATE TYPE ${e.enumName}`);
|
|
3634
|
+
}
|
|
3635
|
+
for (const entry of sorted) {
|
|
3636
|
+
upStatements.push(`-- ${entry.exportName}`, generateCreateTable(entry.config, true), ``);
|
|
3292
3637
|
const tbl = entry.config.schema ? `"${entry.config.schema}"."${entry.config.name}"` : `"${entry.config.name}"`;
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3638
|
+
downStatements.unshift(`DROP TABLE IF EXISTS ${tbl};`);
|
|
3639
|
+
summary.push(`CREATE TABLE ${entry.config.name}`);
|
|
3640
|
+
}
|
|
3641
|
+
for (const v of viewSchemas) {
|
|
3642
|
+
upStatements.push(`-- ${v.exportName}`, generateCreateView(v.config), ``);
|
|
3643
|
+
const isMat = v.config.materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
3644
|
+
downStatements.unshift(`DROP ${isMat} IF EXISTS "${v.config.name}";`);
|
|
3645
|
+
summary.push(`CREATE ${isMat} ${v.config.name}`);
|
|
3646
|
+
}
|
|
3296
3647
|
} else {
|
|
3297
3648
|
const upDiff = diffSchemas(prevSnapshot, currentSnapshot);
|
|
3298
3649
|
if (upDiff.statements.length === 0) {
|
|
@@ -3424,12 +3775,12 @@ export default defineConfig({
|
|
|
3424
3775
|
uuid,
|
|
3425
3776
|
varchar,
|
|
3426
3777
|
timestamptz,
|
|
3427
|
-
|
|
3778
|
+
pgTable,
|
|
3428
3779
|
unique,
|
|
3429
3780
|
index
|
|
3430
3781
|
} from "@bungres/orm";
|
|
3431
3782
|
|
|
3432
|
-
export const users =
|
|
3783
|
+
export const users = pgTable("users", {
|
|
3433
3784
|
id: uuid({ primaryKey: true }),
|
|
3434
3785
|
name: varchar({ length: 255, notNull: true }),
|
|
3435
3786
|
email: varchar({ length: 255, notNull: true }),
|
|
@@ -3470,7 +3821,7 @@ export const db = bungres({ url, schema });
|
|
|
3470
3821
|
// src/commands/pull.ts
|
|
3471
3822
|
import { resolve as resolve4, join as join5 } from "path";
|
|
3472
3823
|
async function introspectDb(sql2, dbSchema) {
|
|
3473
|
-
const
|
|
3824
|
+
const columns = await sql2.unsafe(`SELECT
|
|
3474
3825
|
c.table_name,
|
|
3475
3826
|
c.column_name,
|
|
3476
3827
|
c.data_type,
|
|
@@ -3504,7 +3855,7 @@ async function introspectDb(sql2, dbSchema) {
|
|
|
3504
3855
|
const indexes = await sql2.unsafe(`SELECT tablename, indexname, indexdef
|
|
3505
3856
|
FROM pg_indexes
|
|
3506
3857
|
WHERE schemaname = $1`, [dbSchema]);
|
|
3507
|
-
return groupByTable(
|
|
3858
|
+
return groupByTable(columns, constraints, indexes);
|
|
3508
3859
|
}
|
|
3509
3860
|
async function runPull(config2) {
|
|
3510
3861
|
console.log("@bungres/kit pull: introspecting database...");
|
|
@@ -3527,9 +3878,9 @@ async function runPull(config2) {
|
|
|
3527
3878
|
await sql2.end();
|
|
3528
3879
|
}
|
|
3529
3880
|
}
|
|
3530
|
-
function groupByTable(
|
|
3881
|
+
function groupByTable(columns, constraints, indexes) {
|
|
3531
3882
|
const map = new Map;
|
|
3532
|
-
for (const col2 of
|
|
3883
|
+
for (const col2 of columns) {
|
|
3533
3884
|
if (!map.has(col2.table_name)) {
|
|
3534
3885
|
map.set(col2.table_name, {
|
|
3535
3886
|
tableName: col2.table_name,
|
|
@@ -3565,19 +3916,18 @@ function generateSchemaTS(tableMap, dbSchema) {
|
|
|
3565
3916
|
`// Generated at: ${new Date().toISOString()}`,
|
|
3566
3917
|
``,
|
|
3567
3918
|
`import {`,
|
|
3568
|
-
`
|
|
3919
|
+
` pgTable,`,
|
|
3569
3920
|
` text, varchar, char, integer, bigint, smallint,`,
|
|
3570
3921
|
` serial, bigserial, boolean, real, doublePrecision,`,
|
|
3571
3922
|
` numeric, decimal, json, jsonb,`,
|
|
3572
3923
|
` timestamp, timestamptz, date, time, uuid,`,
|
|
3573
3924
|
` bytea, inet,`,
|
|
3574
|
-
` textArray, integerArray, varcharArray, uuidArray,`,
|
|
3575
3925
|
`} from "@bungres/orm";`,
|
|
3576
3926
|
``
|
|
3577
3927
|
];
|
|
3578
3928
|
for (const [, table2] of tableMap) {
|
|
3579
3929
|
const varName = toCamelCase(table2.tableName);
|
|
3580
|
-
lines.push(`export const ${varName} =
|
|
3930
|
+
lines.push(`export const ${varName} = pgTable("${table2.tableName}", {`);
|
|
3581
3931
|
for (const col2 of table2.columns) {
|
|
3582
3932
|
const colExpr = buildColumnExpression(col2);
|
|
3583
3933
|
lines.push(` ${toCamelCase(col2.name)}: ${colExpr},`);
|
|
@@ -3658,11 +4008,13 @@ function buildColumnExpression(col2) {
|
|
|
3658
4008
|
refOpts += `, onUpdate: "${updateRule}"`;
|
|
3659
4009
|
opts.push(`references: { ${refOpts} }`);
|
|
3660
4010
|
}
|
|
3661
|
-
let
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
}
|
|
3665
|
-
|
|
4011
|
+
let builderResult = pgTypeToBungresBuilderName(col2);
|
|
4012
|
+
let builderName = typeof builderResult === "string" ? builderResult : builderResult.base;
|
|
4013
|
+
let isArray = typeof builderResult !== "string" && builderResult.isArray;
|
|
4014
|
+
let expr = opts.length > 0 ? `${builderName}({ ${opts.join(", ")} })` : `${builderName}()`;
|
|
4015
|
+
if (isArray)
|
|
4016
|
+
expr += `.array()`;
|
|
4017
|
+
return expr;
|
|
3666
4018
|
}
|
|
3667
4019
|
function pgTypeToBungresBuilderName(col2) {
|
|
3668
4020
|
const dt = col2.dataType;
|
|
@@ -3708,14 +4060,14 @@ function pgTypeToBungresBuilderName(col2) {
|
|
|
3708
4060
|
return "text";
|
|
3709
4061
|
if (dt === "ARRAY") {
|
|
3710
4062
|
if (col2.udtName === "_text")
|
|
3711
|
-
return "
|
|
4063
|
+
return { base: "text", isArray: true };
|
|
3712
4064
|
if (col2.udtName === "_int4" || col2.udtName === "_int8")
|
|
3713
|
-
return "
|
|
4065
|
+
return { base: "integer", isArray: true };
|
|
3714
4066
|
if (col2.udtName === "_varchar")
|
|
3715
|
-
return "
|
|
4067
|
+
return { base: "varchar", isArray: true };
|
|
3716
4068
|
if (col2.udtName === "_uuid")
|
|
3717
|
-
return "
|
|
3718
|
-
return "
|
|
4069
|
+
return { base: "uuid", isArray: true };
|
|
4070
|
+
return { base: "text", isArray: true };
|
|
3719
4071
|
}
|
|
3720
4072
|
return "text";
|
|
3721
4073
|
}
|
|
@@ -3750,11 +4102,29 @@ async function runPush(config2, opts = {}) {
|
|
|
3750
4102
|
);
|
|
3751
4103
|
`);
|
|
3752
4104
|
const rows = await sql2.unsafe(`SELECT snapshot FROM ${qualifiedPush} ORDER BY id DESC LIMIT 1;`);
|
|
3753
|
-
let prevSnapshot = {};
|
|
4105
|
+
let prevSnapshot = { tables: {}, enums: {}, views: {} };
|
|
3754
4106
|
if (rows.length > 0) {
|
|
3755
|
-
|
|
4107
|
+
const parsed = typeof rows[0].snapshot === "string" ? JSON.parse(rows[0].snapshot) : rows[0].snapshot;
|
|
4108
|
+
if (parsed.tables || parsed.enums || parsed.views) {
|
|
4109
|
+
prevSnapshot = {
|
|
4110
|
+
tables: parsed.tables || {},
|
|
4111
|
+
enums: parsed.enums || {},
|
|
4112
|
+
views: parsed.views || {}
|
|
4113
|
+
};
|
|
4114
|
+
} else {
|
|
4115
|
+
prevSnapshot = { tables: parsed, enums: {}, views: {} };
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
const currentSnapshot = { tables: {}, enums: {}, views: {} };
|
|
4119
|
+
for (const s2 of schemas2) {
|
|
4120
|
+
if (s2.type === "table") {
|
|
4121
|
+
currentSnapshot.tables[s2.config.name] = s2.config;
|
|
4122
|
+
} else if (s2.type === "enum") {
|
|
4123
|
+
currentSnapshot.enums[s2.enumName] = { enumName: s2.enumName, enumValues: s2.enumValues };
|
|
4124
|
+
} else if (s2.type === "view") {
|
|
4125
|
+
currentSnapshot.views[s2.config.name] = s2.config;
|
|
4126
|
+
}
|
|
3756
4127
|
}
|
|
3757
|
-
const currentSnapshot = Object.fromEntries(schemas2.map((schemaEntry) => [schemaEntry.config.name, schemaEntry.config]));
|
|
3758
4128
|
const diff = diffSchemas(prevSnapshot, currentSnapshot);
|
|
3759
4129
|
if (diff.statements.length === 0) {
|
|
3760
4130
|
ms.stop("Up to date.");
|
|
@@ -3811,7 +4181,7 @@ async function runPush(config2, opts = {}) {
|
|
|
3811
4181
|
// src/commands/refresh.ts
|
|
3812
4182
|
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
3813
4183
|
async function runRefresh(config2) {
|
|
3814
|
-
const schemas2 = await loadSchemas(config2.schema);
|
|
4184
|
+
const schemas2 = (await loadSchemas(config2.schema)).filter((s) => s.type === "table");
|
|
3815
4185
|
if (schemas2.length === 0) {
|
|
3816
4186
|
console.warn("No table definitions found in schema files.");
|
|
3817
4187
|
return;
|
|
@@ -3945,7 +4315,7 @@ async function runSeed(config2) {
|
|
|
3945
4315
|
}
|
|
3946
4316
|
const s = spinner();
|
|
3947
4317
|
s.start("Loading schemas for auto-seeding...");
|
|
3948
|
-
const schemas2 = await loadSchemas(config2.schema);
|
|
4318
|
+
const schemas2 = (await loadSchemas(config2.schema)).filter((s2) => s2.type === "table");
|
|
3949
4319
|
if (schemas2.length === 0) {
|
|
3950
4320
|
s.stop("No schemas.");
|
|
3951
4321
|
outro("Nothing to seed.");
|
|
@@ -4077,7 +4447,8 @@ async function runStatus(config2) {
|
|
|
4077
4447
|
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
4078
4448
|
|
|
4079
4449
|
// src/commands/template.ts
|
|
4080
|
-
|
|
4450
|
+
function renderIndexHtml(opts) {
|
|
4451
|
+
return `<!DOCTYPE html>
|
|
4081
4452
|
<html lang="en">
|
|
4082
4453
|
<head>
|
|
4083
4454
|
<meta charset="UTF-8">
|
|
@@ -4086,8 +4457,7 @@ var indexHtml = `<!DOCTYPE html>
|
|
|
4086
4457
|
|
|
4087
4458
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
4088
4459
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
4089
|
-
<link href="https://fonts.googleapis.com/css2?family=
|
|
4090
|
-
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
|
4460
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
4091
4461
|
|
|
4092
4462
|
<script src="https://unpkg.com/htmx.org@2.0.0" defer></script>
|
|
4093
4463
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
@@ -4099,38 +4469,17 @@ var indexHtml = `<!DOCTYPE html>
|
|
|
4099
4469
|
theme: {
|
|
4100
4470
|
extend: {
|
|
4101
4471
|
fontFamily: {
|
|
4102
|
-
sans: ['
|
|
4103
|
-
mono: ['"
|
|
4472
|
+
sans: ['Inter', 'sans-serif'],
|
|
4473
|
+
mono: ['"JetBrains Mono"', 'monospace'],
|
|
4104
4474
|
},
|
|
4105
4475
|
colors: {
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
'primary-foreground': 'var(--primary-foreground)',
|
|
4114
|
-
secondary: 'var(--secondary)',
|
|
4115
|
-
'secondary-foreground': 'var(--secondary-foreground)',
|
|
4116
|
-
muted: 'var(--muted)',
|
|
4117
|
-
'muted-foreground': 'var(--muted-foreground)',
|
|
4118
|
-
accent: 'var(--accent)',
|
|
4119
|
-
'accent-foreground': 'var(--accent-foreground)',
|
|
4120
|
-
destructive: 'var(--destructive)',
|
|
4121
|
-
border: 'var(--border)',
|
|
4122
|
-
input: 'var(--input)',
|
|
4123
|
-
ring: 'var(--ring)',
|
|
4124
|
-
sidebar: {
|
|
4125
|
-
DEFAULT: 'var(--sidebar)',
|
|
4126
|
-
foreground: 'var(--sidebar-foreground)',
|
|
4127
|
-
primary: 'var(--sidebar-primary)',
|
|
4128
|
-
'primary-foreground': 'var(--sidebar-primary-foreground)',
|
|
4129
|
-
accent: 'var(--sidebar-accent)',
|
|
4130
|
-
'accent-foreground': 'var(--sidebar-accent-foreground)',
|
|
4131
|
-
border: 'var(--sidebar-border)',
|
|
4132
|
-
ring: 'var(--sidebar-ring)',
|
|
4133
|
-
}
|
|
4476
|
+
bg: '#161618',
|
|
4477
|
+
panel: '#1c1c1f',
|
|
4478
|
+
border: '#2c2c2f',
|
|
4479
|
+
text: '#ededed',
|
|
4480
|
+
muted: '#8f8f91',
|
|
4481
|
+
accent: '#00e599',
|
|
4482
|
+
hover: '#28282c'
|
|
4134
4483
|
}
|
|
4135
4484
|
}
|
|
4136
4485
|
}
|
|
@@ -4138,265 +4487,319 @@ var indexHtml = `<!DOCTYPE html>
|
|
|
4138
4487
|
</script>
|
|
4139
4488
|
|
|
4140
4489
|
<style>
|
|
4141
|
-
:
|
|
4142
|
-
|
|
4143
|
-
--foreground: oklch(0.148 0.004 228.8);
|
|
4144
|
-
--card: oklch(1 0 0);
|
|
4145
|
-
--card-foreground: oklch(0.148 0.004 228.8);
|
|
4146
|
-
--popover: oklch(1 0 0);
|
|
4147
|
-
--popover-foreground: oklch(0.148 0.004 228.8);
|
|
4148
|
-
--primary: oklch(0.508 0.118 165.612);
|
|
4149
|
-
--primary-foreground: oklch(0.979 0.021 166.113);
|
|
4150
|
-
--secondary: oklch(0.967 0.001 286.375);
|
|
4151
|
-
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
4152
|
-
--muted: oklch(0.963 0.002 197.1);
|
|
4153
|
-
--muted-foreground: oklch(0.56 0.021 213.5);
|
|
4154
|
-
--accent: oklch(0.963 0.002 197.1);
|
|
4155
|
-
--accent-foreground: oklch(0.218 0.008 223.9);
|
|
4156
|
-
--destructive: oklch(0.577 0.245 27.325);
|
|
4157
|
-
--border: oklch(0.925 0.005 214.3);
|
|
4158
|
-
--input: oklch(0.925 0.005 214.3);
|
|
4159
|
-
--ring: oklch(0.723 0.014 214.4);
|
|
4160
|
-
--chart-1: oklch(0.845 0.143 164.978);
|
|
4161
|
-
--chart-2: oklch(0.696 0.17 162.48);
|
|
4162
|
-
--chart-3: oklch(0.596 0.145 163.225);
|
|
4163
|
-
--chart-4: oklch(0.508 0.118 165.612);
|
|
4164
|
-
--chart-5: oklch(0.432 0.095 166.913);
|
|
4165
|
-
--radius: 0.625rem;
|
|
4166
|
-
--sidebar: oklch(0.987 0.002 197.1);
|
|
4167
|
-
--sidebar-foreground: oklch(0.148 0.004 228.8);
|
|
4168
|
-
--sidebar-primary: oklch(0.596 0.145 163.225);
|
|
4169
|
-
--sidebar-primary-foreground: oklch(0.979 0.021 166.113);
|
|
4170
|
-
--sidebar-accent: oklch(0.963 0.002 197.1);
|
|
4171
|
-
--sidebar-accent-foreground: oklch(0.218 0.008 223.9);
|
|
4172
|
-
--sidebar-border: oklch(0.925 0.005 214.3);
|
|
4173
|
-
--sidebar-ring: oklch(0.723 0.014 214.4);
|
|
4174
|
-
}
|
|
4175
|
-
|
|
4176
|
-
.dark {
|
|
4177
|
-
--background: oklch(0.148 0.004 228.8);
|
|
4178
|
-
--foreground: oklch(0.987 0.002 197.1);
|
|
4179
|
-
--card: oklch(0.218 0.008 223.9);
|
|
4180
|
-
--card-foreground: oklch(0.987 0.002 197.1);
|
|
4181
|
-
--popover: oklch(0.218 0.008 223.9);
|
|
4182
|
-
--popover-foreground: oklch(0.987 0.002 197.1);
|
|
4183
|
-
--primary: oklch(0.432 0.095 166.913);
|
|
4184
|
-
--primary-foreground: oklch(0.979 0.021 166.113);
|
|
4185
|
-
--secondary: oklch(0.274 0.006 286.033);
|
|
4186
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
4187
|
-
--muted: oklch(0.275 0.011 216.9);
|
|
4188
|
-
--muted-foreground: oklch(0.723 0.014 214.4);
|
|
4189
|
-
--accent: oklch(0.275 0.011 216.9);
|
|
4190
|
-
--accent-foreground: oklch(0.987 0.002 197.1);
|
|
4191
|
-
--destructive: oklch(0.704 0.191 22.216);
|
|
4192
|
-
--border: oklch(1 0 0 / 10%);
|
|
4193
|
-
--input: oklch(1 0 0 / 15%);
|
|
4194
|
-
--ring: oklch(0.56 0.021 213.5);
|
|
4195
|
-
--chart-1: oklch(0.845 0.143 164.978);
|
|
4196
|
-
--chart-2: oklch(0.696 0.17 162.48);
|
|
4197
|
-
--chart-3: oklch(0.596 0.145 163.225);
|
|
4198
|
-
--chart-4: oklch(0.508 0.118 165.612);
|
|
4199
|
-
--chart-5: oklch(0.432 0.095 166.913);
|
|
4200
|
-
--sidebar: oklch(0.218 0.008 223.9);
|
|
4201
|
-
--sidebar-foreground: oklch(0.987 0.002 197.1);
|
|
4202
|
-
--sidebar-primary: oklch(0.696 0.17 162.48);
|
|
4203
|
-
--sidebar-primary-foreground: oklch(0.262 0.051 172.552);
|
|
4204
|
-
--sidebar-accent: oklch(0.275 0.011 216.9);
|
|
4205
|
-
--sidebar-accent-foreground: oklch(0.987 0.002 197.1);
|
|
4206
|
-
--sidebar-border: oklch(1 0 0 / 10%);
|
|
4207
|
-
--sidebar-ring: oklch(0.56 0.021 213.5);
|
|
4208
|
-
}
|
|
4209
|
-
|
|
4210
|
-
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
4490
|
+
body { background-color: #161618; color: #ededed; }
|
|
4491
|
+
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
4211
4492
|
::-webkit-scrollbar-track { background: transparent; }
|
|
4212
|
-
::-webkit-scrollbar-thumb { background:
|
|
4213
|
-
::-webkit-scrollbar-thumb:hover { background:
|
|
4493
|
+
::-webkit-scrollbar-thumb { background: #3a3a3e; border-radius: 4px; }
|
|
4494
|
+
::-webkit-scrollbar-thumb:hover { background: #4a4a4e; }
|
|
4214
4495
|
::-webkit-scrollbar-corner { background: transparent; }
|
|
4215
4496
|
[x-cloak] { display: none !important; }
|
|
4216
|
-
|
|
4217
|
-
.
|
|
4218
|
-
.
|
|
4497
|
+
|
|
4498
|
+
.tab-active { background-color: #1c1c1f; border-top: 2px solid #00e599; color: #ededed; }
|
|
4499
|
+
.tab-inactive { background-color: #161618; border-top: 2px solid transparent; color: #8f8f91; }
|
|
4500
|
+
|
|
4501
|
+
table { border-spacing: 0; }
|
|
4502
|
+
th { border-bottom: 1px solid #2c2c2f; border-right: 1px solid #2c2c2f; background: #1c1c1f; font-weight: 500; font-size: 13px; color: #8f8f91; }
|
|
4503
|
+
td { border-bottom: 1px solid #2c2c2f; border-right: 1px solid #2c2c2f; font-size: 13px; }
|
|
4504
|
+
tr:hover td { background-color: #28282c; }
|
|
4219
4505
|
</style>
|
|
4220
4506
|
</head>
|
|
4221
|
-
<body class="
|
|
4222
|
-
<div x-data="
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
<
|
|
4229
|
-
|
|
4230
|
-
|
|
4507
|
+
<body class="font-sans h-screen flex overflow-hidden text-sm selection:bg-accent/30">
|
|
4508
|
+
<div x-data="studioState()" class="flex h-screen w-full relative">
|
|
4509
|
+
|
|
4510
|
+
<!-- JSON Modal -->
|
|
4511
|
+
<div x-show="jsonModalOpen" x-cloak class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm transition-opacity" style="display: none;">
|
|
4512
|
+
<div @click.away="jsonModalOpen = false" class="bg-panel border border-border rounded-lg shadow-xl w-full max-w-2xl flex flex-col max-h-[80vh]">
|
|
4513
|
+
<div class="flex items-center justify-between p-3 border-b border-border bg-bg/50">
|
|
4514
|
+
<h3 class="font-medium text-text text-xs flex items-center gap-2">
|
|
4515
|
+
<svg class="w-4 h-4 text-accent" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>
|
|
4516
|
+
Data Viewer
|
|
4517
|
+
</h3>
|
|
4518
|
+
<button @click="jsonModalOpen = false" class="text-muted hover:text-text p-1 rounded hover:bg-hover transition-colors"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></button>
|
|
4519
|
+
</div>
|
|
4520
|
+
<div class="p-4 overflow-auto flex-1">
|
|
4521
|
+
<pre class="font-mono text-xs text-text bg-bg p-4 rounded border border-border whitespace-pre-wrap break-all" x-text="jsonModalData"></pre>
|
|
4522
|
+
</div>
|
|
4523
|
+
</div>
|
|
4524
|
+
</div>
|
|
4525
|
+
|
|
4526
|
+
<!-- Sidebar -->
|
|
4527
|
+
<aside class="w-64 bg-bg border-r border-border flex flex-col z-20 shrink-0">
|
|
4528
|
+
|
|
4529
|
+
<!-- Schema Selector -->
|
|
4530
|
+
<div class="p-3 border-b border-border">
|
|
4531
|
+
<select x-model="currentSchema" @change="loadSidebar()" class="w-full bg-panel border border-border text-text text-sm rounded px-2 py-1.5 focus:outline-none focus:border-muted cursor-pointer font-medium">
|
|
4532
|
+
<template x-for="s in schemas" :key="s">
|
|
4533
|
+
<option :value="s" x-text="s"></option>
|
|
4534
|
+
</template>
|
|
4535
|
+
</select>
|
|
4536
|
+
</div>
|
|
4537
|
+
|
|
4538
|
+
<!-- Search Box -->
|
|
4539
|
+
<div class="p-3 border-b border-border bg-bg">
|
|
4540
|
+
<div class="relative">
|
|
4541
|
+
<svg class="w-4 h-4 text-muted absolute left-2.5 top-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
|
4542
|
+
<input type="text" x-model="searchQuery" placeholder="Filter tables.." class="w-full bg-panel border border-border rounded pl-8 pr-2 py-1.5 text-sm focus:outline-none focus:border-muted text-text placeholder-muted">
|
|
4543
|
+
</div>
|
|
4231
4544
|
</div>
|
|
4232
|
-
|
|
4233
|
-
|
|
4545
|
+
|
|
4546
|
+
<!-- Tables List -->
|
|
4547
|
+
<div class="flex-1 overflow-y-auto pt-2" id="sidebar-content" :hx-get="'/htmx/sidebar?schema=' + currentSchema" hx-trigger="load">
|
|
4548
|
+
<div class="p-4 text-xs text-muted animate-pulse text-center">Loading...</div>
|
|
4234
4549
|
</div>
|
|
4235
4550
|
</aside>
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
<line x1="3" y1="9" x2="21" y2="9"></line>
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
<
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4551
|
+
|
|
4552
|
+
<!-- Main Content -->
|
|
4553
|
+
<main class="flex-1 flex flex-col min-w-0 bg-panel">
|
|
4554
|
+
<!-- Tabs Header -->
|
|
4555
|
+
<header class="h-10 border-b border-border flex items-center bg-bg shrink-0 overflow-x-auto overflow-y-hidden">
|
|
4556
|
+
<template x-for="tab in tabs" :key="tab.id">
|
|
4557
|
+
<div
|
|
4558
|
+
@click="activeTab = tab.id"
|
|
4559
|
+
:class="activeTab === tab.id ? 'tab-active' : 'tab-inactive hover:bg-panel hover:text-text'"
|
|
4560
|
+
class="h-full flex items-center gap-2 pl-4 pr-2 cursor-pointer border-r border-border min-w-max transition-colors group relative select-none"
|
|
4561
|
+
>
|
|
4562
|
+
<!-- Icon -->
|
|
4563
|
+
<template x-if="tab.type === 'table'">
|
|
4564
|
+
<svg class="w-4 h-4 text-muted" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>
|
|
4565
|
+
</template>
|
|
4566
|
+
<template x-if="tab.type === 'query'">
|
|
4567
|
+
<svg class="w-4 h-4 text-accent" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>
|
|
4568
|
+
</template>
|
|
4569
|
+
|
|
4570
|
+
<span x-text="tab.title" class="font-medium text-xs mr-2"></span>
|
|
4571
|
+
|
|
4572
|
+
<!-- Close Button -->
|
|
4573
|
+
<button @click.stop="closeTab(tab.id)" class="opacity-0 group-hover:opacity-100 p-0.5 rounded hover:bg-hover text-muted hover:text-text transition-opacity">
|
|
4574
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
|
4575
|
+
</button>
|
|
4576
|
+
</div>
|
|
4577
|
+
</template>
|
|
4578
|
+
<button @click="openQueryEditor()" class="h-full px-3 text-muted hover:text-text hover:bg-panel flex items-center justify-center transition-colors">
|
|
4579
|
+
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
|
4262
4580
|
</button>
|
|
4263
4581
|
</header>
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
<
|
|
4272
|
-
<p class="text-sm">Choose a table from the sidebar to view its data</p>
|
|
4582
|
+
|
|
4583
|
+
<!-- Active Tab Content Container -->
|
|
4584
|
+
<div class="flex-1 relative overflow-hidden bg-panel">
|
|
4585
|
+
|
|
4586
|
+
<!-- Empty State -->
|
|
4587
|
+
<div x-show="tabs.length === 0" class="absolute inset-0 flex flex-col items-center justify-center text-muted">
|
|
4588
|
+
<div class="w-12 h-12 rounded-lg bg-hover border border-border flex items-center justify-center mb-4">
|
|
4589
|
+
<svg class="w-6 h-6 text-text" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>
|
|
4273
4590
|
</div>
|
|
4591
|
+
<p class="text-sm">Select a table to view</p>
|
|
4274
4592
|
</div>
|
|
4593
|
+
|
|
4594
|
+
<!-- Render Tabs -->
|
|
4595
|
+
<template x-for="tab in tabs" :key="tab.id">
|
|
4596
|
+
<div x-show="activeTab === tab.id" class="absolute inset-0 flex flex-col h-full w-full">
|
|
4597
|
+
|
|
4598
|
+
<!-- Table View -->
|
|
4599
|
+
<template x-if="tab.type === 'table'">
|
|
4600
|
+
<div class="flex flex-col h-full w-full">
|
|
4601
|
+
<!-- Table Header Toolbars -->
|
|
4602
|
+
<div class="h-12 border-b border-border flex items-center justify-between px-3 shrink-0 bg-panel">
|
|
4603
|
+
<div class="flex items-center gap-2">
|
|
4604
|
+
<span class="text-xs text-muted font-medium ml-1 flex items-center gap-2">
|
|
4605
|
+
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>
|
|
4606
|
+
<span x-text="tab.title"></span>
|
|
4607
|
+
</span>
|
|
4608
|
+
</div>
|
|
4609
|
+
<!-- HTMX will swap the pagination controls into this div -->
|
|
4610
|
+
<div :id="'pagination-' + tab.id" class="flex items-center gap-2"></div>
|
|
4611
|
+
</div>
|
|
4612
|
+
|
|
4613
|
+
<!-- Table Data Grid Container -->
|
|
4614
|
+
<div class="flex-1 overflow-auto bg-bg" :id="'data-' + tab.id"
|
|
4615
|
+
x-init="htmx.ajax('GET', '/htmx/tables/' + tab.name + '?schema=' + tab.schema + '&tabId=' + tab.id, {target: '#data-' + tab.id})">
|
|
4616
|
+
<div class="p-8 text-center text-muted">Loading data...</div>
|
|
4617
|
+
</div>
|
|
4618
|
+
</div>
|
|
4619
|
+
</template>
|
|
4620
|
+
|
|
4621
|
+
<!-- Query Editor View -->
|
|
4622
|
+
<template x-if="tab.type === 'query'">
|
|
4623
|
+
<div class="flex flex-col h-full w-full" x-data="{ query: 'SELECT 1;' }">
|
|
4624
|
+
<div class="h-12 border-b border-border flex items-center px-3 shrink-0 gap-2 bg-panel">
|
|
4625
|
+
<button
|
|
4626
|
+
@click="htmx.ajax('POST', '/htmx/query', { target: '#query-result-' + tab.id, values: { query: query } })"
|
|
4627
|
+
class="bg-accent text-bg px-3 py-1.5 rounded text-xs font-semibold hover:opacity-90 flex items-center gap-1.5 transition-opacity"
|
|
4628
|
+
>
|
|
4629
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg> Run
|
|
4630
|
+
</button>
|
|
4631
|
+
<span class="text-muted text-[10px] font-mono ml-2">Press Ctrl+Enter to run</span>
|
|
4632
|
+
</div>
|
|
4633
|
+
<div class="flex-none h-48 border-b border-border relative">
|
|
4634
|
+
<textarea
|
|
4635
|
+
x-model="query"
|
|
4636
|
+
@keydown.ctrl.enter.prevent="htmx.ajax('POST', '/htmx/query', { target: '#query-result-' + tab.id, values: { query: query } })"
|
|
4637
|
+
class="absolute inset-0 w-full h-full bg-bg text-text font-mono text-sm p-4 focus:outline-none resize-none"
|
|
4638
|
+
placeholder="SELECT 1;"
|
|
4639
|
+
></textarea>
|
|
4640
|
+
</div>
|
|
4641
|
+
<div class="flex-1 overflow-auto bg-bg flex flex-col" :id="'query-result-' + tab.id">
|
|
4642
|
+
<div class="h-full flex items-center justify-center text-muted">
|
|
4643
|
+
<div class="flex flex-col items-center">
|
|
4644
|
+
<svg class="w-6 h-6 mb-2 opacity-50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
|
|
4645
|
+
<span class="text-xs">Run a query to see results</span>
|
|
4646
|
+
</div>
|
|
4647
|
+
</div>
|
|
4648
|
+
</div>
|
|
4649
|
+
</div>
|
|
4650
|
+
</template>
|
|
4651
|
+
|
|
4652
|
+
</div>
|
|
4653
|
+
</template>
|
|
4275
4654
|
</div>
|
|
4276
4655
|
</main>
|
|
4277
4656
|
</div>
|
|
4278
4657
|
<script>
|
|
4279
|
-
|
|
4658
|
+
const INITIAL_SCHEMAS = ${JSON.stringify(opts.schemas)};
|
|
4659
|
+
const INITIAL_CURRENT_SCHEMA = ${JSON.stringify(opts.currentSchema)};
|
|
4660
|
+
|
|
4661
|
+
function studioState() {
|
|
4662
|
+
return {
|
|
4663
|
+
schemas: INITIAL_SCHEMAS,
|
|
4664
|
+
currentSchema: INITIAL_CURRENT_SCHEMA,
|
|
4665
|
+
searchQuery: '',
|
|
4666
|
+
tabs: [],
|
|
4667
|
+
activeTab: null,
|
|
4668
|
+
queryEditorCount: 0,
|
|
4669
|
+
jsonModalOpen: false,
|
|
4670
|
+
jsonModalData: '',
|
|
4671
|
+
|
|
4672
|
+
init() {
|
|
4673
|
+
this.$watch('activeTab', (value) => {
|
|
4674
|
+
const tab = this.tabs.find(t => t.id === value);
|
|
4675
|
+
if (tab && tab.schema && tab.schema !== this.currentSchema) {
|
|
4676
|
+
this.currentSchema = tab.schema;
|
|
4677
|
+
this.loadSidebar();
|
|
4678
|
+
}
|
|
4679
|
+
});
|
|
4680
|
+
|
|
4681
|
+
// Listen for table clicks from the sidebar
|
|
4682
|
+
window.addEventListener('open-table', (e) => {
|
|
4683
|
+
const tableName = e.detail.tableName;
|
|
4684
|
+
const schema = this.currentSchema;
|
|
4685
|
+
const tabId = 'table_' + schema + '_' + tableName;
|
|
4686
|
+
|
|
4687
|
+
if (!this.tabs.find(t => t.id === tabId)) {
|
|
4688
|
+
this.tabs.push({ id: tabId, type: 'table', title: tableName, name: tableName, schema: schema });
|
|
4689
|
+
}
|
|
4690
|
+
this.activeTab = tabId;
|
|
4691
|
+
});
|
|
4692
|
+
|
|
4693
|
+
window.addEventListener('open-json-modal', (e) => {
|
|
4694
|
+
try {
|
|
4695
|
+
const str = atob(e.detail);
|
|
4696
|
+
this.jsonModalData = JSON.stringify(JSON.parse(str), null, 2);
|
|
4697
|
+
} catch(err) {
|
|
4698
|
+
this.jsonModalData = 'Invalid JSON data';
|
|
4699
|
+
}
|
|
4700
|
+
this.jsonModalOpen = true;
|
|
4701
|
+
});
|
|
4702
|
+
},
|
|
4703
|
+
|
|
4704
|
+
loadSidebar() {
|
|
4705
|
+
htmx.ajax('GET', '/htmx/sidebar?schema=' + this.currentSchema, {target: '#sidebar-content'});
|
|
4706
|
+
},
|
|
4707
|
+
|
|
4708
|
+
openQueryEditor() {
|
|
4709
|
+
this.queryEditorCount++;
|
|
4710
|
+
const tabId = 'query_' + this.queryEditorCount;
|
|
4711
|
+
this.tabs.push({ id: tabId, type: 'query', title: 'Query Editor ' + (this.queryEditorCount > 1 ? this.queryEditorCount : ''), schema: this.currentSchema });
|
|
4712
|
+
this.activeTab = tabId;
|
|
4713
|
+
},
|
|
4714
|
+
|
|
4715
|
+
closeTab(id) {
|
|
4716
|
+
const idx = this.tabs.findIndex(t => t.id === id);
|
|
4717
|
+
if (idx === -1) return;
|
|
4718
|
+
|
|
4719
|
+
this.tabs.splice(idx, 1);
|
|
4720
|
+
if (this.activeTab === id) {
|
|
4721
|
+
if (this.tabs.length > 0) {
|
|
4722
|
+
this.activeTab = this.tabs[Math.max(0, idx - 1)].id;
|
|
4723
|
+
} else {
|
|
4724
|
+
this.activeTab = null;
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4280
4730
|
</script>
|
|
4281
4731
|
</body>
|
|
4282
4732
|
</html>`;
|
|
4733
|
+
}
|
|
4283
4734
|
|
|
4284
4735
|
// src/commands/studio.ts
|
|
4285
4736
|
async function runStudio(config2) {
|
|
4286
|
-
const schemas2 = await loadSchemas(config2.schema);
|
|
4737
|
+
const schemas2 = (await loadSchemas(config2.schema)).filter((s) => s.type === "table");
|
|
4287
4738
|
if (schemas2.length === 0) {
|
|
4288
|
-
console.warn("No table definitions found in schema files.");
|
|
4289
|
-
return;
|
|
4739
|
+
console.warn("No table definitions found in schema files. Connecting anyway to browse DB...");
|
|
4290
4740
|
}
|
|
4291
4741
|
const schemaObj2 = {};
|
|
4292
4742
|
for (const s of schemas2) {
|
|
4293
4743
|
schemaObj2[s.exportName] = s.table;
|
|
4294
4744
|
}
|
|
4295
4745
|
const db2 = bungres({ url: config2.dbUrl, schema: schemaObj2 });
|
|
4746
|
+
let allSchemas = ["public"];
|
|
4747
|
+
try {
|
|
4748
|
+
const res = await db2.execute(rawSql(`SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg_%' AND schema_name != 'information_schema'`));
|
|
4749
|
+
if (Array.isArray(res) && res.length > 0) {
|
|
4750
|
+
allSchemas = res.map((r2) => r2.schema_name);
|
|
4751
|
+
}
|
|
4752
|
+
} catch (e) {}
|
|
4296
4753
|
const port = Bun.env.PORT ? parseInt(Bun.env.PORT, 10) : 5555;
|
|
4297
4754
|
const server = Bun.serve({
|
|
4298
4755
|
port,
|
|
4299
4756
|
async fetch(req) {
|
|
4300
4757
|
const url = new URL(req.url);
|
|
4301
|
-
if (req.method === "GET" && url.pathname === "/
|
|
4302
|
-
const
|
|
4303
|
-
|
|
4304
|
-
if (s.config.foreignKeys) {
|
|
4305
|
-
s.config.foreignKeys.forEach((fk) => {
|
|
4306
|
-
fk.columns.forEach((col2) => fkColumns.add(col2));
|
|
4307
|
-
});
|
|
4308
|
-
}
|
|
4309
|
-
const columns2 = Object.entries(s.config.columns).map(([key, col2]) => {
|
|
4310
|
-
const fk = col2.references ? {
|
|
4311
|
-
table: col2.references.table,
|
|
4312
|
-
column: col2.references.column
|
|
4313
|
-
} : fkColumns.has(col2.name) ? { isForeignKey: true } : null;
|
|
4314
|
-
return {
|
|
4315
|
-
name: col2.name,
|
|
4316
|
-
type: col2.dataType,
|
|
4317
|
-
primaryKey: col2.primaryKey,
|
|
4318
|
-
foreignKey: fk
|
|
4319
|
-
};
|
|
4320
|
-
});
|
|
4321
|
-
return {
|
|
4322
|
-
name: s.config.name,
|
|
4323
|
-
exportName: s.exportName,
|
|
4324
|
-
columns: columns2,
|
|
4325
|
-
foreignKeys: s.config.foreignKeys || []
|
|
4326
|
-
};
|
|
4327
|
-
});
|
|
4328
|
-
return new Response(JSON.stringify(tables), {
|
|
4329
|
-
headers: { "Content-Type": "application/json" }
|
|
4330
|
-
});
|
|
4331
|
-
}
|
|
4332
|
-
if (req.method === "GET" && url.pathname.startsWith("/api/tables/") && url.pathname.endsWith("/data")) {
|
|
4333
|
-
const tableName = url.pathname.split("/")[3];
|
|
4334
|
-
const schema = schemas2.find((s) => s.config.name === tableName);
|
|
4335
|
-
if (!schema) {
|
|
4336
|
-
return new Response("Table not found", { status: 404 });
|
|
4337
|
-
}
|
|
4338
|
-
try {
|
|
4339
|
-
const page = parseInt(url.searchParams.get("page") || "1", 10);
|
|
4340
|
-
const limit = parseInt(url.searchParams.get("limit") || "50", 10);
|
|
4341
|
-
const offset = (page - 1) * limit;
|
|
4342
|
-
const countResult = await db2.select({ count: schema.table }).from(schema.table);
|
|
4343
|
-
const total = Array.isArray(countResult) ? countResult.length : 0;
|
|
4344
|
-
const data = await db2.select().from(schema.table).limit(limit).offset(offset);
|
|
4345
|
-
return new Response(JSON.stringify({
|
|
4346
|
-
data,
|
|
4347
|
-
total,
|
|
4348
|
-
page,
|
|
4349
|
-
limit,
|
|
4350
|
-
totalPages: Math.ceil(total / limit)
|
|
4351
|
-
}), {
|
|
4352
|
-
headers: { "Content-Type": "application/json" }
|
|
4353
|
-
});
|
|
4354
|
-
} catch (e) {
|
|
4355
|
-
return new Response(JSON.stringify({ error: e.message }), {
|
|
4356
|
-
status: 500,
|
|
4357
|
-
headers: { "Content-Type": "application/json" }
|
|
4358
|
-
});
|
|
4359
|
-
}
|
|
4360
|
-
}
|
|
4361
|
-
if (req.method === "POST" && url.pathname === "/api/editor/open") {
|
|
4758
|
+
if (req.method === "GET" && url.pathname === "/htmx/sidebar") {
|
|
4759
|
+
const currentSchema = url.searchParams.get("schema") || config2.dbSchema || "public";
|
|
4760
|
+
let items = [];
|
|
4362
4761
|
try {
|
|
4363
|
-
const
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
});
|
|
4762
|
+
const query = `
|
|
4763
|
+
SELECT c.relname as name, c.reltuples as count, c.relkind as type
|
|
4764
|
+
FROM pg_class c
|
|
4765
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
4766
|
+
WHERE n.nspname = '${currentSchema}'
|
|
4767
|
+
AND c.relkind IN ('r', 'v', 'm')
|
|
4768
|
+
ORDER BY c.relname
|
|
4769
|
+
`;
|
|
4770
|
+
const res = await db2.execute(rawSql(query));
|
|
4771
|
+
if (Array.isArray(res)) {
|
|
4772
|
+
items = res;
|
|
4375
4773
|
}
|
|
4376
|
-
return new Response("Schema not found", { status: 404 });
|
|
4377
4774
|
} catch (e) {
|
|
4378
|
-
|
|
4775
|
+
items = schemas2.map((s) => ({ name: s.config.name, count: 0, type: "r" }));
|
|
4379
4776
|
}
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4777
|
+
const formatCount = (n2) => {
|
|
4778
|
+
if (n2 >= 1e6)
|
|
4779
|
+
return (n2 / 1e6).toFixed(1) + "M";
|
|
4780
|
+
if (n2 >= 1000)
|
|
4781
|
+
return (n2 / 1000).toFixed(1) + "k";
|
|
4782
|
+
return Math.floor(n2).toString();
|
|
4783
|
+
};
|
|
4784
|
+
let html = '<div class="px-3 py-2 text-[10px] font-semibold text-muted tracking-wider flex justify-between items-center"><div class="flex items-center gap-1.5"><svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg> TABLES</div></div>';
|
|
4785
|
+
html += '<ul class="flex flex-col gap-0.5 p-2 m-0 list-none">';
|
|
4786
|
+
items.forEach((item) => {
|
|
4787
|
+
let icon = '<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>';
|
|
4788
|
+
if (item.type === "v")
|
|
4789
|
+
icon = '<svg class="w-4 h-4 text-purple-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>';
|
|
4790
|
+
if (item.type === "m")
|
|
4791
|
+
icon = '<svg class="w-4 h-4 text-orange-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></svg>';
|
|
4385
4792
|
html += `
|
|
4386
|
-
<li>
|
|
4793
|
+
<li x-show="searchQuery === '' || '${item.name}'.toLowerCase().includes(searchQuery.toLowerCase())">
|
|
4387
4794
|
<button
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
@click="currentTable = '${tableName}'; pageSize = 25"
|
|
4391
|
-
:class="currentTable === '${tableName}' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground'"
|
|
4392
|
-
class="w-full text-left px-3 py-2 rounded-md text-sm font-medium flex items-center gap-2 transition-colors focus:outline-none"
|
|
4795
|
+
@click="$dispatch('open-table', { tableName: '${item.name}' })"
|
|
4796
|
+
class="w-full text-left px-2 py-1 rounded text-sm font-medium flex items-center justify-between transition-colors focus:outline-none hover:bg-hover hover:text-text group text-muted"
|
|
4393
4797
|
>
|
|
4394
|
-
<
|
|
4395
|
-
|
|
4396
|
-
<
|
|
4397
|
-
|
|
4398
|
-
</
|
|
4399
|
-
${tableName}
|
|
4798
|
+
<div class="flex items-center gap-2 truncate">
|
|
4799
|
+
${icon}
|
|
4800
|
+
<span class="truncate">${item.name}</span>
|
|
4801
|
+
</div>
|
|
4802
|
+
<span class="text-[10px] font-mono text-muted/50 group-hover:text-muted">${formatCount(Math.max(0, item.count))}</span>
|
|
4400
4803
|
</button>
|
|
4401
4804
|
</li>
|
|
4402
4805
|
`;
|
|
@@ -4406,140 +4809,215 @@ async function runStudio(config2) {
|
|
|
4406
4809
|
}
|
|
4407
4810
|
if (req.method === "GET" && url.pathname.startsWith("/htmx/tables/")) {
|
|
4408
4811
|
const tableName = url.pathname.split("/")[3];
|
|
4409
|
-
const
|
|
4410
|
-
|
|
4411
|
-
return new Response(`<div class="p-4 text-red-500">Table not found</div>`, { status: 404, headers: { "Content-Type": "text/html" } });
|
|
4412
|
-
}
|
|
4812
|
+
const reqSchema = url.searchParams.get("schema") || config2.dbSchema || "public";
|
|
4813
|
+
const tabId = url.searchParams.get("tabId") || `table_${tableName}`;
|
|
4413
4814
|
try {
|
|
4414
4815
|
const page = parseInt(url.searchParams.get("page") || "1", 10);
|
|
4415
4816
|
const limit = parseInt(url.searchParams.get("limit") || "25", 10);
|
|
4416
4817
|
const offset = (page - 1) * limit;
|
|
4417
|
-
|
|
4418
|
-
|
|
4818
|
+
let countResult;
|
|
4819
|
+
let data = [];
|
|
4820
|
+
const tsSchema = schemas2.find((s) => s.config.name === tableName);
|
|
4821
|
+
let colConfigs = {};
|
|
4822
|
+
let fkColumns = new Set;
|
|
4823
|
+
try {
|
|
4824
|
+
if (tsSchema) {
|
|
4825
|
+
colConfigs = tsSchema.config.columns || {};
|
|
4826
|
+
if (tsSchema.config.foreignKeys) {
|
|
4827
|
+
tsSchema.config.foreignKeys.forEach((fk) => fk.columns.forEach((col2) => fkColumns.add(col2)));
|
|
4828
|
+
}
|
|
4829
|
+
const countRes = await db2.select({ count: tsSchema.table }).from(tsSchema.table);
|
|
4830
|
+
countResult = Array.isArray(countRes) ? [{ count: countRes.length }] : [{ count: 0 }];
|
|
4831
|
+
data = await db2.select().from(tsSchema.table).limit(limit).offset(offset);
|
|
4832
|
+
} else {
|
|
4833
|
+
countResult = await db2.execute(rawSql(`SELECT COUNT(*) as count FROM "${reqSchema}"."${tableName}"`));
|
|
4834
|
+
data = await db2.execute(rawSql(`SELECT * FROM "${reqSchema}"."${tableName}" LIMIT ${limit} OFFSET ${offset}`));
|
|
4835
|
+
const colsRes = await db2.execute(rawSql(`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = '${reqSchema}' AND table_name = '${tableName}'`));
|
|
4836
|
+
if (Array.isArray(colsRes)) {
|
|
4837
|
+
colsRes.forEach((c2) => {
|
|
4838
|
+
colConfigs[c2.column_name] = { dataType: c2.data_type };
|
|
4839
|
+
});
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
} catch (e) {
|
|
4843
|
+
return new Response(`<div class="p-4 text-red-500">Table not found or query error</div>`, { status: 404, headers: { "Content-Type": "text/html" } });
|
|
4844
|
+
}
|
|
4845
|
+
const total = Array.isArray(countResult) && countResult.length > 0 ? parseInt(countResult[0].count, 10) : 0;
|
|
4419
4846
|
const totalPages = Math.ceil(total / limit) || 1;
|
|
4420
|
-
|
|
4847
|
+
if (!Array.isArray(data))
|
|
4848
|
+
data = [];
|
|
4421
4849
|
const formatValue = (val) => {
|
|
4422
4850
|
if (val === null || val === undefined)
|
|
4423
|
-
return '<span class="italic text-muted
|
|
4424
|
-
if (typeof val === "
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
return `<span class="text-foreground font-mono text-xs">${JSON.stringify(val).replace(/&/g, "&").replace(/</g, "<")}</span>`;
|
|
4432
|
-
return `<span class="text-foreground">${String(val).replace(/&/g, "&").replace(/</g, "<")}</span>`;
|
|
4851
|
+
return '<span class="italic text-muted/50">NULL</span>';
|
|
4852
|
+
if (typeof val === "object" && !(val instanceof Date)) {
|
|
4853
|
+
const rawJson = JSON.stringify(val);
|
|
4854
|
+
const b64 = Buffer.from(rawJson).toString("base64");
|
|
4855
|
+
const safeJson = rawJson.replace(/&/g, "&").replace(/</g, "<");
|
|
4856
|
+
return `<button onclick="window.dispatchEvent(new CustomEvent('open-json-modal', { detail: '${b64}' }))" class="text-text font-mono text-xs hover:underline text-left truncate w-full max-w-[250px] block cursor-pointer" title="Click to view formatted data">${safeJson}</button>`;
|
|
4857
|
+
}
|
|
4858
|
+
return `<span class="text-text">${String(val).replace(/&/g, "&").replace(/</g, "<")}</span>`;
|
|
4433
4859
|
};
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4860
|
+
const columns = tsSchema ? Object.keys(colConfigs) : Object.keys(data[0] || {});
|
|
4861
|
+
let html = '<div class="h-full w-full overflow-auto bg-bg">';
|
|
4862
|
+
if (data.length === 0 && columns.length === 0) {
|
|
4863
|
+
html += '<div class="p-8 text-center text-muted">Table is Empty</div></div>';
|
|
4864
|
+
} else {
|
|
4865
|
+
html += '<table class="text-left border-collapse whitespace-nowrap min-w-max">';
|
|
4866
|
+
html += "<thead><tr>";
|
|
4867
|
+
html += `<th class="w-10 px-3 py-2 sticky top-0 z-10 text-center bg-panel"><input type="checkbox" @change="document.querySelectorAll('#data-${tabId} .row-checkbox').forEach(cb => cb.checked = $event.target.checked)" class="w-3.5 h-3.5 rounded border-muted bg-transparent cursor-pointer"></th>`;
|
|
4868
|
+
columns.forEach((col2) => {
|
|
4869
|
+
const colConfig = colConfigs[col2];
|
|
4870
|
+
let typeLabel = colConfig ? colConfig.dataType : "unknown";
|
|
4871
|
+
let badges = "";
|
|
4872
|
+
if (colConfig?.primaryKey) {
|
|
4873
|
+
badges += '<span class="ml-1.5 text-[9px] bg-emerald-500/20 text-emerald-400 px-1 py-0.5 rounded uppercase font-bold" title="Primary Key">PK</span>';
|
|
4874
|
+
}
|
|
4875
|
+
if (tsSchema && tsSchema.config.indexes) {
|
|
4876
|
+
const isIndexed = tsSchema.config.indexes.some((idx) => idx.columns.includes(col2));
|
|
4877
|
+
if (isIndexed)
|
|
4878
|
+
badges += '<span class="ml-1.5 text-[9px] bg-blue-500/20 text-blue-400 px-1 py-0.5 rounded uppercase font-bold" title="Indexed">IDX</span>';
|
|
4879
|
+
}
|
|
4880
|
+
if (colConfig?.unique)
|
|
4881
|
+
badges += '<span class="ml-1.5 text-[9px] bg-amber-500/20 text-amber-400 px-1 py-0.5 rounded uppercase font-bold" title="Unique">UQ</span>';
|
|
4882
|
+
if (fkColumns.has(col2))
|
|
4883
|
+
badges += '<span class="ml-1.5 text-[9px] bg-purple-500/20 text-purple-400 px-1 py-0.5 rounded uppercase font-bold" title="Foreign Key">FK</span>';
|
|
4884
|
+
html += `<th class="px-4 py-2 font-medium sticky top-0 z-10 whitespace-nowrap bg-panel text-muted hover:text-text cursor-pointer transition-colors border-b border-r border-border">
|
|
4885
|
+
<div class="flex items-center gap-2">
|
|
4886
|
+
<span class="flex items-center">${col2}${badges}</span>
|
|
4887
|
+
<span class="text-[10px] font-mono opacity-50">${typeLabel}</span>
|
|
4888
|
+
</div>
|
|
4889
|
+
</th>`;
|
|
4890
|
+
});
|
|
4891
|
+
html += "</tr></thead><tbody>";
|
|
4892
|
+
data.forEach((row) => {
|
|
4893
|
+
html += '<tr class="hover:bg-hover transition-colors">';
|
|
4894
|
+
html += '<td class="px-3 py-1.5 text-center bg-bg border-b border-r border-border"><input type="checkbox" class="row-checkbox w-3.5 h-3.5 rounded border-muted bg-transparent cursor-pointer"></td>';
|
|
4895
|
+
columns.forEach((col2) => {
|
|
4896
|
+
html += `<td class="px-4 py-1.5 max-w-[300px] overflow-hidden text-ellipsis font-mono border-b border-r border-border bg-bg">${formatValue(row[col2])}</td>`;
|
|
4897
|
+
});
|
|
4898
|
+
html += "</tr>";
|
|
4899
|
+
});
|
|
4900
|
+
html += "</tbody></table></div>";
|
|
4901
|
+
}
|
|
4902
|
+
const startRecord = total === 0 ? 0 : (page - 1) * limit + 1;
|
|
4903
|
+
const endRecord = Math.min(page * limit, total);
|
|
4904
|
+
let paginationHtml = `
|
|
4905
|
+
<div id="pagination-${tabId}" hx-swap-oob="true" class="flex items-center gap-4 text-xs text-muted font-mono">
|
|
4906
|
+
<span>${startRecord}-${endRecord} of ${total}</span>
|
|
4907
|
+
|
|
4908
|
+
<div class="flex items-center gap-3">
|
|
4909
|
+
<div class="relative flex items-center border border-border rounded bg-panel overflow-hidden group">
|
|
4910
|
+
<select
|
|
4911
|
+
class="bg-transparent text-text pl-2 pr-6 py-0.5 focus:outline-none cursor-pointer appearance-none text-center relative z-10 w-full"
|
|
4912
|
+
@change="htmx.ajax('GET', '/htmx/tables/${tableName}?schema=${reqSchema}&tabId=${tabId}&page=1&limit=' + $event.target.value, {target: '#data-${tabId}'})"
|
|
4913
|
+
>
|
|
4914
|
+
<option class="bg-bg text-text" style="background-color: #161618; color: #ededed;" value="10" ${limit === 10 ? "selected" : ""}>10</option>
|
|
4915
|
+
<option class="bg-bg text-text" style="background-color: #161618; color: #ededed;" value="25" ${limit === 25 ? "selected" : ""}>25</option>
|
|
4916
|
+
<option class="bg-bg text-text" style="background-color: #161618; color: #ededed;" value="50" ${limit === 50 ? "selected" : ""}>50</option>
|
|
4917
|
+
<option class="bg-bg text-text" style="background-color: #161618; color: #ededed;" value="100" ${limit === 100 ? "selected" : ""}>100</option>
|
|
4918
|
+
</select>
|
|
4919
|
+
<div class="absolute right-1 text-muted pointer-events-none z-0 group-hover:text-text transition-colors">
|
|
4920
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
|
4921
|
+
</div>
|
|
4922
|
+
</div>
|
|
4923
|
+
|
|
4924
|
+
<div class="flex items-center border border-border rounded bg-panel overflow-hidden">
|
|
4925
|
+
<div class="px-2 py-0.5 text-text text-center min-w-[30px]">${page}</div>
|
|
4926
|
+
<div class="px-1 py-0.5 text-muted border-l border-border text-[10px] flex items-center justify-center pointer-events-none bg-panel">
|
|
4927
|
+
of ${totalPages}
|
|
4928
|
+
</div>
|
|
4929
|
+
<button
|
|
4930
|
+
${page <= 1 ? "disabled" : ""}
|
|
4931
|
+
hx-get="/htmx/tables/${tableName}?schema=${reqSchema}&tabId=${tabId}&page=${page - 1}&limit=${limit}"
|
|
4932
|
+
hx-target="#data-${tabId}"
|
|
4933
|
+
class="px-2 py-1 hover:bg-hover hover:text-text disabled:opacity-50 disabled:cursor-not-allowed border-l border-r border-border transition-colors flex items-center justify-center"
|
|
4934
|
+
><svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
|
|
4935
|
+
<button
|
|
4936
|
+
${page >= totalPages ? "disabled" : ""}
|
|
4937
|
+
hx-get="/htmx/tables/${tableName}?schema=${reqSchema}&tabId=${tabId}&page=${page + 1}&limit=${limit}"
|
|
4938
|
+
hx-target="#data-${tabId}"
|
|
4939
|
+
class="px-2 py-1 hover:bg-hover hover:text-text disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center"
|
|
4940
|
+
><svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
|
|
4941
|
+
</div>
|
|
4942
|
+
</div>
|
|
4943
|
+
</div>
|
|
4944
|
+
`;
|
|
4945
|
+
return new Response(html + paginationHtml, { headers: { "Content-Type": "text/html" } });
|
|
4946
|
+
} catch (e) {
|
|
4947
|
+
return new Response(`
|
|
4948
|
+
<div class="p-6">
|
|
4949
|
+
<div class="bg-red-500/10 border border-red-500/20 text-red-400 p-4 rounded-lg">
|
|
4950
|
+
<h3 class="font-semibold mb-1">Error Loading Data</h3>
|
|
4951
|
+
<p class="text-sm opacity-80">${e.message}</p>
|
|
4444
4952
|
</div>
|
|
4445
|
-
|
|
4953
|
+
</div>
|
|
4954
|
+
`, { headers: { "Content-Type": "text/html" } });
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
if (req.method === "POST" && url.pathname === "/htmx/query") {
|
|
4958
|
+
try {
|
|
4959
|
+
const body = await req.formData();
|
|
4960
|
+
const query = body.get("query")?.toString() || "";
|
|
4961
|
+
if (!query.trim()) {
|
|
4962
|
+
return new Response('<div class="p-6 text-muted">No query provided.</div>', { headers: { "Content-Type": "text/html" } });
|
|
4446
4963
|
}
|
|
4447
|
-
const
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
if (
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
indexLabel += '<span class="ml-1 text-[10px] bg-yellow-500/20 text-yellow-500 px-1 rounded uppercase" title="Foreign Key">FK</span>';
|
|
4464
|
-
}
|
|
4465
|
-
if (schema.config.indexes) {
|
|
4466
|
-
const isIndexed = schema.config.indexes.some((idx) => idx.columns.includes(col2));
|
|
4467
|
-
if (isIndexed) {
|
|
4468
|
-
indexLabel += '<span class="ml-1 text-[10px] bg-blue-500/20 text-blue-500 px-1 rounded uppercase" title="Indexed">IDX</span>';
|
|
4469
|
-
}
|
|
4964
|
+
const startMs = Date.now();
|
|
4965
|
+
const res = await db2.execute(rawSql(query));
|
|
4966
|
+
const duration2 = Date.now() - startMs;
|
|
4967
|
+
const data = Array.isArray(res) ? res : [res];
|
|
4968
|
+
if (data.length === 0 || data.length === 1 && typeof data[0] === "object" && Object.keys(data[0]).length === 0) {
|
|
4969
|
+
return new Response(`<div class="p-4 text-accent text-xs font-mono border-b border-border">Query executed successfully in ${duration2}ms. No data returned.</div>`, { headers: { "Content-Type": "text/html" } });
|
|
4970
|
+
}
|
|
4971
|
+
const columns = Object.keys(data[0] || {});
|
|
4972
|
+
const formatValue = (val) => {
|
|
4973
|
+
if (val === null || val === undefined)
|
|
4974
|
+
return '<span class="italic text-muted/50">NULL</span>';
|
|
4975
|
+
if (typeof val === "object" && !(val instanceof Date)) {
|
|
4976
|
+
const rawJson = JSON.stringify(val);
|
|
4977
|
+
const b64 = Buffer.from(rawJson).toString("base64");
|
|
4978
|
+
const safeJson = rawJson.replace(/&/g, "&").replace(/</g, "<");
|
|
4979
|
+
return `<button onclick="window.dispatchEvent(new CustomEvent('open-json-modal', { detail: '${b64}' }))" class="text-text font-mono text-xs hover:underline text-left truncate w-full max-w-[250px] block cursor-pointer" title="Click to view formatted data">${safeJson}</button>`;
|
|
4470
4980
|
}
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4981
|
+
return `<span class="text-text">${String(val).replace(/&/g, "&").replace(/</g, "<")}</span>`;
|
|
4982
|
+
};
|
|
4983
|
+
let html = '<div class="h-full w-full overflow-auto bg-bg">';
|
|
4984
|
+
html += '<table class="text-left border-collapse whitespace-nowrap min-w-max">';
|
|
4985
|
+
html += "<thead><tr>";
|
|
4986
|
+
columns.forEach((col2) => {
|
|
4987
|
+
const row0 = data.length > 0 ? data[0] : null;
|
|
4988
|
+
const typeLabel = row0 && row0[col2] != null ? typeof row0[col2] : "unknown";
|
|
4989
|
+
html += `<th class="px-4 py-2 font-medium sticky top-0 z-10 whitespace-nowrap bg-panel text-muted hover:text-text cursor-pointer transition-colors border-b border-r border-border">
|
|
4990
|
+
<div class="flex items-center gap-2">
|
|
4991
|
+
<span class="text-text">${col2}</span>
|
|
4992
|
+
<span class="text-[10px] font-mono opacity-50">${typeLabel}</span>
|
|
4478
4993
|
</div>
|
|
4479
4994
|
</th>`;
|
|
4480
4995
|
});
|
|
4481
4996
|
html += "</tr></thead><tbody>";
|
|
4482
4997
|
data.forEach((row) => {
|
|
4483
|
-
html += '<tr class="hover:bg-
|
|
4484
|
-
|
|
4485
|
-
html += `<td class="
|
|
4998
|
+
html += '<tr class="hover:bg-hover transition-colors">';
|
|
4999
|
+
columns.forEach((col2) => {
|
|
5000
|
+
html += `<td class="px-4 py-1.5 max-w-[300px] overflow-hidden text-ellipsis font-mono border-b border-r border-border bg-bg">${formatValue(row[col2])}</td>`;
|
|
4486
5001
|
});
|
|
4487
5002
|
html += "</tr>";
|
|
4488
5003
|
});
|
|
4489
5004
|
html += "</tbody></table></div>";
|
|
4490
|
-
|
|
4491
|
-
const endRecord = Math.min(page * limit, total);
|
|
4492
|
-
html += `
|
|
4493
|
-
<div class="flex items-center justify-between px-4 py-3 bg-card border-t border-border text-xs text-muted-foreground shrink-0 sticky bottom-0 z-10 w-full">
|
|
4494
|
-
<div class="flex items-center gap-4">
|
|
4495
|
-
<span>${total} records</span>
|
|
4496
|
-
<span>${startRecord}-${endRecord}</span>
|
|
4497
|
-
</div>
|
|
4498
|
-
<div class="flex items-center gap-2">
|
|
4499
|
-
<select
|
|
4500
|
-
name="limit"
|
|
4501
|
-
class="bg-background border border-border text-foreground px-2 py-1.5 rounded-md focus:outline-none focus:ring-1 focus:ring-ring cursor-pointer"
|
|
4502
|
-
@change="pageSize = $event.target.value; htmx.ajax('GET', '/htmx/tables/${tableName}?page=1&limit=' + pageSize, {target: '#data-container'})"
|
|
4503
|
-
>
|
|
4504
|
-
<option value="10" ${limit === 10 ? "selected" : ""}>10</option>
|
|
4505
|
-
<option value="25" ${limit === 25 ? "selected" : ""}>25</option>
|
|
4506
|
-
<option value="50" ${limit === 50 ? "selected" : ""}>50</option>
|
|
4507
|
-
<option value="100" ${limit === 100 ? "selected" : ""}>100</option>
|
|
4508
|
-
</select>
|
|
4509
|
-
|
|
4510
|
-
<button
|
|
4511
|
-
${page <= 1 ? "disabled" : ""}
|
|
4512
|
-
hx-get="/htmx/tables/${tableName}?page=${page - 1}&limit=${limit}"
|
|
4513
|
-
hx-target="#data-container"
|
|
4514
|
-
class="bg-background border border-border text-foreground px-3 py-1.5 rounded-md hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
4515
|
-
>←</button>
|
|
4516
|
-
|
|
4517
|
-
<span class="min-w-[40px] text-center">${page} / ${totalPages}</span>
|
|
4518
|
-
|
|
4519
|
-
<button
|
|
4520
|
-
${page >= totalPages ? "disabled" : ""}
|
|
4521
|
-
hx-get="/htmx/tables/${tableName}?page=${page + 1}&limit=${limit}"
|
|
4522
|
-
hx-target="#data-container"
|
|
4523
|
-
class="bg-background border border-border text-foreground px-3 py-1.5 rounded-md hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
4524
|
-
>→</button>
|
|
4525
|
-
</div>
|
|
4526
|
-
</div>
|
|
4527
|
-
`;
|
|
4528
|
-
html += "</div>";
|
|
5005
|
+
html += `<div class="px-4 py-2 text-[10px] text-muted border-t border-border font-mono shrink-0 bg-panel sticky bottom-0">${data.length} row(s) returned in ${duration2}ms</div>`;
|
|
4529
5006
|
return new Response(html, { headers: { "Content-Type": "text/html" } });
|
|
4530
5007
|
} catch (e) {
|
|
4531
5008
|
return new Response(`
|
|
4532
5009
|
<div class="p-6">
|
|
4533
5010
|
<div class="bg-red-500/10 border border-red-500/20 text-red-400 p-4 rounded-lg">
|
|
4534
|
-
<h3 class="font-semibold mb-1">Error
|
|
4535
|
-
<p class="text-sm opacity-80">${e.message}</p>
|
|
5011
|
+
<h3 class="font-semibold mb-1">Query Error</h3>
|
|
5012
|
+
<p class="text-sm opacity-80 font-mono break-words whitespace-pre-wrap">${e.message}</p>
|
|
4536
5013
|
</div>
|
|
4537
5014
|
</div>
|
|
4538
5015
|
`, { headers: { "Content-Type": "text/html" } });
|
|
4539
5016
|
}
|
|
4540
5017
|
}
|
|
4541
5018
|
if (req.method === "GET" && url.pathname === "/") {
|
|
4542
|
-
|
|
5019
|
+
const currentSchema = url.searchParams.get("schema") || config2.dbSchema || "public";
|
|
5020
|
+
return new Response(renderIndexHtml({ schemas: allSchemas, currentSchema }), {
|
|
4543
5021
|
headers: { "Content-Type": "text/html" }
|
|
4544
5022
|
});
|
|
4545
5023
|
}
|
|
@@ -4554,7 +5032,7 @@ async function runStudio(config2) {
|
|
|
4554
5032
|
// src/commands/tusky.ts
|
|
4555
5033
|
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
4556
5034
|
async function runTusky(config) {
|
|
4557
|
-
const schemas = await loadSchemas(config.schema);
|
|
5035
|
+
const schemas = (await loadSchemas(config.schema)).filter((s) => s.type === "table");
|
|
4558
5036
|
const schemaObj = {};
|
|
4559
5037
|
for (const s of schemas) {
|
|
4560
5038
|
schemaObj[s.exportName] = s.table;
|
|
@@ -4684,7 +5162,7 @@ async function ensureDatabase2(dbUrl) {
|
|
|
4684
5162
|
// package.json
|
|
4685
5163
|
var package_default = {
|
|
4686
5164
|
name: "@bungres/kit",
|
|
4687
|
-
version: "
|
|
5165
|
+
version: "1.1.0",
|
|
4688
5166
|
description: "CLI toolkit for @bungres/orm \u2014 migrate, push, generate, pull",
|
|
4689
5167
|
license: "MIT",
|
|
4690
5168
|
engines: {
|
|
@@ -4699,9 +5177,10 @@ var package_default = {
|
|
|
4699
5177
|
types: "./dist/index.d.ts",
|
|
4700
5178
|
exports: {
|
|
4701
5179
|
".": {
|
|
4702
|
-
|
|
5180
|
+
types: "./dist/index.d.ts",
|
|
4703
5181
|
import: "./dist/index.js",
|
|
4704
|
-
|
|
5182
|
+
require: "./dist/index.js",
|
|
5183
|
+
default: "./dist/index.js"
|
|
4705
5184
|
}
|
|
4706
5185
|
},
|
|
4707
5186
|
files: [
|
|
@@ -4734,7 +5213,7 @@ var package_default = {
|
|
|
4734
5213
|
test: "bun test"
|
|
4735
5214
|
},
|
|
4736
5215
|
dependencies: {
|
|
4737
|
-
"@bungres/orm": "^0.
|
|
5216
|
+
"@bungres/orm": "^1.0.0",
|
|
4738
5217
|
"@clack/prompts": "^1.7.0",
|
|
4739
5218
|
picocolors: "^1.1.1"
|
|
4740
5219
|
},
|