@bungres/kit 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +3473 -3084
- package/dist/commands/drop.d.ts.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/refresh.d.ts.map +1 -1
- package/dist/commands/rollback.d.ts.map +1 -1
- package/dist/commands/seed.d.ts.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/studio.d.ts.map +1 -1
- package/dist/commands/template.d.ts.map +1 -1
- package/dist/commands/tusky.d.ts.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/index.js +340 -190
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -168,14 +168,25 @@ var CONFIG_FILES = [
|
|
|
168
168
|
"bungres.config.mts",
|
|
169
169
|
"bungres.config.mjs"
|
|
170
170
|
];
|
|
171
|
-
async function loadConfig(cwd = process.cwd()) {
|
|
171
|
+
async function loadConfig(cwd = process.cwd(), explicitConfigPath) {
|
|
172
172
|
let userConfig = {};
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
if (await Bun.file(
|
|
176
|
-
const mod = await import(
|
|
173
|
+
if (explicitConfigPath) {
|
|
174
|
+
const fullPath = resolve(cwd, explicitConfigPath);
|
|
175
|
+
if (await Bun.file(fullPath).exists()) {
|
|
176
|
+
const mod = await import(fullPath);
|
|
177
177
|
userConfig = mod.default ?? mod;
|
|
178
|
-
|
|
178
|
+
} else {
|
|
179
|
+
console.error(`bungres: Config file not found at ${fullPath}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
for (const file of CONFIG_FILES) {
|
|
184
|
+
const configPath = join(cwd, file);
|
|
185
|
+
if (await Bun.file(configPath).exists()) {
|
|
186
|
+
const mod = await import(resolve(configPath));
|
|
187
|
+
userConfig = mod.default ?? mod;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
179
190
|
}
|
|
180
191
|
}
|
|
181
192
|
const dbUrl = userConfig.dbCredentials?.url ?? Bun.env.DATABASE_URL ?? Bun.env.POSTGRES_URL ?? "";
|
|
@@ -268,10 +279,10 @@ function createTableFactory(casing) {
|
|
|
268
279
|
return Object.assign(tableObj, columnConfigs);
|
|
269
280
|
};
|
|
270
281
|
}
|
|
271
|
-
var
|
|
272
|
-
var snakeCase = {
|
|
273
|
-
var camelCase = {
|
|
274
|
-
var noCasing = {
|
|
282
|
+
var pgTable = createTableFactory("snake");
|
|
283
|
+
var snakeCase = { pgTable: createTableFactory("snake") };
|
|
284
|
+
var camelCase = { pgTable: createTableFactory("camel") };
|
|
285
|
+
var noCasing = { pgTable: createTableFactory("none") };
|
|
275
286
|
function buildColumn(dataType, nameOrOpts, opts) {
|
|
276
287
|
let name = "";
|
|
277
288
|
let options = opts;
|
|
@@ -298,8 +309,8 @@ function buildColumn(dataType, nameOrOpts, opts) {
|
|
|
298
309
|
...options?.check !== undefined ? { check: options.check } : {}
|
|
299
310
|
};
|
|
300
311
|
return Object.assign(config, {
|
|
301
|
-
as(
|
|
302
|
-
return Object.assign({}, this, { alias });
|
|
312
|
+
as(alias2) {
|
|
313
|
+
return Object.assign({}, this, { alias: alias2 });
|
|
303
314
|
},
|
|
304
315
|
array() {
|
|
305
316
|
return Object.assign({}, this, { dataType: `${this.dataType}[]` });
|
|
@@ -428,18 +439,26 @@ var ilike = (column, pattern) => sql`${rawSql(colName(column))} ILIKE ${pattern}
|
|
|
428
439
|
var isNull = (column) => rawSql(`${colName(column)} IS NULL`);
|
|
429
440
|
var isNotNull = (column) => rawSql(`${colName(column)} IS NOT NULL`);
|
|
430
441
|
var inArray = (column, values) => {
|
|
431
|
-
if (values
|
|
442
|
+
if (typeof values === "object" && values !== null && "toSQL" in values) {
|
|
443
|
+
return sql`${rawSql(colName(column))} IN (${values.toSQL()})`;
|
|
444
|
+
}
|
|
445
|
+
const arr = values;
|
|
446
|
+
if (arr.length === 0)
|
|
432
447
|
return rawSql("FALSE");
|
|
433
|
-
const params =
|
|
434
|
-
const placeholders =
|
|
435
|
-
return { sql: `${colName(column)}
|
|
448
|
+
const params = arr;
|
|
449
|
+
const placeholders = arr.map((_, i) => `$${i + 1}`).join(", ");
|
|
450
|
+
return { sql: `${colName(column)} IN (${placeholders})`, params };
|
|
436
451
|
};
|
|
437
452
|
var notInArray = (column, values) => {
|
|
438
|
-
if (values
|
|
453
|
+
if (typeof values === "object" && values !== null && "toSQL" in values) {
|
|
454
|
+
return sql`${rawSql(colName(column))} NOT IN (${values.toSQL()})`;
|
|
455
|
+
}
|
|
456
|
+
const arr = values;
|
|
457
|
+
if (arr.length === 0)
|
|
439
458
|
return rawSql("TRUE");
|
|
440
|
-
const params =
|
|
441
|
-
const placeholders =
|
|
442
|
-
return { sql: `${colName(column)}
|
|
459
|
+
const params = arr;
|
|
460
|
+
const placeholders = arr.map((_, i) => `$${i + 1}`).join(", ");
|
|
461
|
+
return { sql: `${colName(column)} NOT IN (${placeholders})`, params };
|
|
443
462
|
};
|
|
444
463
|
var between = (column, min, max) => sql`${rawSql(colName(column))} BETWEEN ${min} AND ${max}`;
|
|
445
464
|
var and = (...conditions) => sqlJoin(conditions, " AND ");
|
|
@@ -538,8 +557,8 @@ class DeleteBuilder {
|
|
|
538
557
|
_returning;
|
|
539
558
|
_comment;
|
|
540
559
|
_with = [];
|
|
541
|
-
constructor(
|
|
542
|
-
this._table =
|
|
560
|
+
constructor(table, executor) {
|
|
561
|
+
this._table = table;
|
|
543
562
|
this._executor = executor;
|
|
544
563
|
}
|
|
545
564
|
then(onfulfilled, onrejected) {
|
|
@@ -607,8 +626,8 @@ class InsertBuilder {
|
|
|
607
626
|
_returning;
|
|
608
627
|
_comment;
|
|
609
628
|
_with = [];
|
|
610
|
-
constructor(
|
|
611
|
-
this._table =
|
|
629
|
+
constructor(table, executor) {
|
|
630
|
+
this._table = table;
|
|
612
631
|
this._executor = executor;
|
|
613
632
|
}
|
|
614
633
|
then(onfulfilled, onrejected) {
|
|
@@ -789,22 +808,29 @@ class SelectBuilder {
|
|
|
789
808
|
_select;
|
|
790
809
|
_selection;
|
|
791
810
|
_joins = [];
|
|
811
|
+
_isNestedOutput = false;
|
|
792
812
|
_with = [];
|
|
793
813
|
_setOperations = [];
|
|
794
814
|
_comment;
|
|
795
|
-
constructor(
|
|
796
|
-
this._table =
|
|
815
|
+
constructor(table, executor, selection) {
|
|
816
|
+
this._table = table;
|
|
797
817
|
this._executor = executor;
|
|
798
818
|
this._selection = selection;
|
|
799
819
|
}
|
|
800
820
|
then(onfulfilled, onrejected) {
|
|
801
|
-
return this._executor.execute(this).then(
|
|
821
|
+
return this._executor.execute(this).then((rows) => {
|
|
822
|
+
if (this._isNestedOutput) {
|
|
823
|
+
rows = rows.map((r) => typeof r._nested_data === "string" ? JSON.parse(r._nested_data) : r._nested_data);
|
|
824
|
+
}
|
|
825
|
+
return onfulfilled ? onfulfilled(rows) : rows;
|
|
826
|
+
}, onrejected);
|
|
802
827
|
}
|
|
803
828
|
async single() {
|
|
804
829
|
if (this._limit === undefined) {
|
|
805
830
|
this.limit(1);
|
|
806
831
|
}
|
|
807
|
-
|
|
832
|
+
const rows = await this.then();
|
|
833
|
+
return rows[0] ?? null;
|
|
808
834
|
}
|
|
809
835
|
select(...columns) {
|
|
810
836
|
this._select = columns;
|
|
@@ -866,40 +892,82 @@ class SelectBuilder {
|
|
|
866
892
|
this._offset = n;
|
|
867
893
|
return this;
|
|
868
894
|
}
|
|
895
|
+
as(aliasName) {
|
|
896
|
+
const columns = {};
|
|
897
|
+
let fields = [];
|
|
898
|
+
if (this._selection) {
|
|
899
|
+
fields = Object.keys(this._selection);
|
|
900
|
+
} else if (this._select && this._select.length > 0) {
|
|
901
|
+
fields = this._select;
|
|
902
|
+
} else if (!(("alias" in this._table) && ("query" in this._table))) {
|
|
903
|
+
fields = Object.keys(getTableConfig(this._table).columns);
|
|
904
|
+
}
|
|
905
|
+
for (const f of fields) {
|
|
906
|
+
const key = typeof f === "string" ? f : f.name || f.alias;
|
|
907
|
+
const dataType = typeof f !== "string" && f.dataType ? f.dataType : "any";
|
|
908
|
+
if (key)
|
|
909
|
+
columns[key] = { name: key, tableName: aliasName, dataType };
|
|
910
|
+
}
|
|
911
|
+
const sqObj = { ...columns };
|
|
912
|
+
const TableConfigSymbol2 = Symbol.for("BungresTableConfig");
|
|
913
|
+
sqObj[TableConfigSymbol2] = {
|
|
914
|
+
name: aliasName,
|
|
915
|
+
qualifiedName: `"${aliasName}"`,
|
|
916
|
+
columns,
|
|
917
|
+
isSubquery: true,
|
|
918
|
+
builder: this
|
|
919
|
+
};
|
|
920
|
+
return sqObj;
|
|
921
|
+
}
|
|
869
922
|
join(rawClause) {
|
|
870
|
-
this._joins.push(rawClause);
|
|
923
|
+
this._joins.push({ chunk: rawSql(rawClause) });
|
|
871
924
|
return this;
|
|
872
925
|
}
|
|
873
|
-
|
|
874
|
-
|
|
926
|
+
_buildJoin(type, table, condition) {
|
|
927
|
+
const TableConfigSymbol2 = Symbol.for("BungresTableConfig");
|
|
928
|
+
const cfg = table[TableConfigSymbol2];
|
|
929
|
+
if (cfg && cfg.isSubquery) {
|
|
930
|
+
const sqChunk = cfg.builder.toSQL();
|
|
931
|
+
return {
|
|
932
|
+
table,
|
|
933
|
+
chunk: sql`${rawSql(type)} JOIN (${sqChunk}) AS "${rawSql(cfg.name)}" ON ${condition}`
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
return {
|
|
937
|
+
table,
|
|
938
|
+
chunk: sql`${rawSql(type)} JOIN ${rawSql(cfg.qualifiedName)} ON ${condition}`
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
innerJoin(table, condition) {
|
|
942
|
+
this._joins.push(this._buildJoin("INNER", table, condition));
|
|
875
943
|
return this;
|
|
876
944
|
}
|
|
877
|
-
leftJoin(
|
|
878
|
-
this._joins.push(
|
|
945
|
+
leftJoin(table, condition) {
|
|
946
|
+
this._joins.push(this._buildJoin("LEFT", table, condition));
|
|
879
947
|
return this;
|
|
880
948
|
}
|
|
881
|
-
rightJoin(
|
|
882
|
-
this._joins.push(
|
|
949
|
+
rightJoin(table, condition) {
|
|
950
|
+
this._joins.push(this._buildJoin("RIGHT", table, condition));
|
|
883
951
|
return this;
|
|
884
952
|
}
|
|
885
|
-
fullJoin(
|
|
886
|
-
this._joins.push(
|
|
953
|
+
fullJoin(table, condition) {
|
|
954
|
+
this._joins.push(this._buildJoin("FULL", table, condition));
|
|
887
955
|
return this;
|
|
888
956
|
}
|
|
889
957
|
_buildSelection(selection, params) {
|
|
890
958
|
const parts = [];
|
|
891
|
-
for (const [
|
|
959
|
+
for (const [alias2, col2] of Object.entries(selection)) {
|
|
892
960
|
if (typeof col2 === "object" && col2 !== null) {
|
|
893
961
|
if ("sql" in col2 && "params" in col2) {
|
|
894
962
|
const chunk = col2;
|
|
895
963
|
const offset = params.length;
|
|
896
964
|
params.push(...chunk.params);
|
|
897
|
-
parts.push(`'${
|
|
965
|
+
parts.push(`'${alias2}', ${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)}`);
|
|
898
966
|
} else if ("name" in col2 && "dataType" in col2) {
|
|
899
967
|
const c = col2;
|
|
900
|
-
parts.push(`'${
|
|
968
|
+
parts.push(`'${alias2}', ${c.tableName ? c.tableName + "." : ""}"${c.name}"`);
|
|
901
969
|
} else {
|
|
902
|
-
parts.push(`'${
|
|
970
|
+
parts.push(`'${alias2}', json_build_object(${this._buildSelection(col2, params)})`);
|
|
903
971
|
}
|
|
904
972
|
}
|
|
905
973
|
}
|
|
@@ -908,35 +976,59 @@ class SelectBuilder {
|
|
|
908
976
|
toSQL() {
|
|
909
977
|
let cols = "";
|
|
910
978
|
const params = [];
|
|
979
|
+
const isCte = "alias" in this._table && "query" in this._table;
|
|
980
|
+
const qName = isCte ? `"${this._table.alias}"` : getTableConfig(this._table).qualifiedName;
|
|
911
981
|
if (this._selection) {
|
|
912
|
-
cols = Object.entries(this._selection).map(([
|
|
982
|
+
cols = Object.entries(this._selection).map(([alias2, col2]) => {
|
|
913
983
|
if (typeof col2 === "object" && col2 !== null && "sql" in col2 && "params" in col2) {
|
|
914
984
|
const chunk = col2;
|
|
915
985
|
const offset = params.length;
|
|
916
986
|
params.push(...chunk.params);
|
|
917
|
-
return `${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)} AS "${
|
|
987
|
+
return `${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)} AS "${alias2}"`;
|
|
918
988
|
} else if (typeof col2 === "object" && col2 !== null && "name" in col2 && "dataType" in col2) {
|
|
919
989
|
const c = col2;
|
|
920
|
-
return `${c.tableName ? c.tableName + "." : ""}"${c.name}" AS "${
|
|
990
|
+
return `${c.tableName ? c.tableName + "." : ""}"${c.name}" AS "${alias2}"`;
|
|
921
991
|
} else {
|
|
922
|
-
return `json_build_object(${this._buildSelection(col2, params)}) AS "${
|
|
992
|
+
return `json_build_object(${this._buildSelection(col2, params)}) AS "${alias2}"`;
|
|
923
993
|
}
|
|
924
994
|
}).join(", ");
|
|
925
995
|
} else if (this._select && this._select.length > 0) {
|
|
926
|
-
const qName = getTableConfig(this._table).qualifiedName;
|
|
927
996
|
cols = this._select.map((c) => {
|
|
928
997
|
if (typeof c === "string") {
|
|
929
|
-
|
|
998
|
+
const colName2 = isCte ? c : getTableConfig(this._table).columns[c]?.name ?? c;
|
|
999
|
+
return `${qName}."${colName2}" AS "${c}"`;
|
|
930
1000
|
}
|
|
931
1001
|
return `${c.tableName ? c.tableName + "." : ""}"${c.name}" AS "${c.alias || c.name}"`;
|
|
932
1002
|
}).join(", ");
|
|
933
1003
|
} else {
|
|
934
|
-
|
|
935
|
-
const colsKeys = Object.keys(getTableConfig(this._table).columns);
|
|
936
|
-
if (colsKeys.length === 0) {
|
|
1004
|
+
if (isCte) {
|
|
937
1005
|
cols = "*";
|
|
938
1006
|
} else {
|
|
939
|
-
|
|
1007
|
+
const hasJoinedTables = this._joins.some((j) => j.table);
|
|
1008
|
+
if (hasJoinedTables) {
|
|
1009
|
+
this._isNestedOutput = true;
|
|
1010
|
+
const rootCfg = getTableConfig(this._table);
|
|
1011
|
+
const tablesToSelect = [{ alias: rootCfg.name, config: rootCfg }];
|
|
1012
|
+
for (const join2 of this._joins) {
|
|
1013
|
+
if (join2.table) {
|
|
1014
|
+
const cfg = getTableConfig(join2.table);
|
|
1015
|
+
tablesToSelect.push({ alias: cfg.name, config: cfg });
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
cols = `json_build_object(${tablesToSelect.map((t) => {
|
|
1019
|
+
const innerFields = Object.keys(t.config.columns).map((c) => {
|
|
1020
|
+
return `'${c}', "${t.config.name}"."${t.config.columns[c].name}"`;
|
|
1021
|
+
}).join(", ");
|
|
1022
|
+
return `'${t.alias}', json_build_object(${innerFields})`;
|
|
1023
|
+
}).join(", ")}) AS _nested_data`;
|
|
1024
|
+
} else {
|
|
1025
|
+
const colsKeys = Object.keys(getTableConfig(this._table).columns);
|
|
1026
|
+
if (colsKeys.length === 0) {
|
|
1027
|
+
cols = "*";
|
|
1028
|
+
} else {
|
|
1029
|
+
cols = colsKeys.map((c) => `${qName}."${getTableConfig(this._table).columns[c].name}" AS "${c}"`).join(", ");
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
940
1032
|
}
|
|
941
1033
|
}
|
|
942
1034
|
let prefix = "";
|
|
@@ -950,9 +1042,9 @@ class SelectBuilder {
|
|
|
950
1042
|
}
|
|
951
1043
|
prefix = `WITH ${cteStrs.join(", ")} `;
|
|
952
1044
|
}
|
|
953
|
-
let query = `SELECT ${cols} FROM ${
|
|
1045
|
+
let query = `SELECT ${cols} FROM ${qName}`;
|
|
954
1046
|
if (this._joins.length > 0) {
|
|
955
|
-
const joinChunks = this._joins.map((j) =>
|
|
1047
|
+
const joinChunks = this._joins.map((j) => j.chunk);
|
|
956
1048
|
const combinedJoins = sqlJoin(joinChunks, " ");
|
|
957
1049
|
const offset = params.length;
|
|
958
1050
|
query += " " + combinedJoins.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`);
|
|
@@ -965,11 +1057,11 @@ class SelectBuilder {
|
|
|
965
1057
|
params.push(...combined.params);
|
|
966
1058
|
}
|
|
967
1059
|
if (this._groupBy.length > 0) {
|
|
968
|
-
const
|
|
1060
|
+
const qName2 = getTableConfig(this._table).qualifiedName;
|
|
969
1061
|
query += " GROUP BY " + this._groupBy.map((c) => {
|
|
970
1062
|
if (typeof c === "string") {
|
|
971
|
-
const dbCol = getTableConfig(this._table).columns[c]?.name ?? c;
|
|
972
|
-
return `${
|
|
1063
|
+
const dbCol = isCte ? c : getTableConfig(this._table).columns[c]?.name ?? c;
|
|
1064
|
+
return `${qName2}."${dbCol}"`;
|
|
973
1065
|
} else if ("sql" in c && "params" in c) {
|
|
974
1066
|
const offset = params.length;
|
|
975
1067
|
params.push(...c.params);
|
|
@@ -986,11 +1078,11 @@ class SelectBuilder {
|
|
|
986
1078
|
params.push(...combined.params);
|
|
987
1079
|
}
|
|
988
1080
|
if (this._orderBy.length > 0) {
|
|
989
|
-
const
|
|
1081
|
+
const qName2 = getTableConfig(this._table).qualifiedName;
|
|
990
1082
|
query += " ORDER BY " + this._orderBy.map((o) => {
|
|
991
1083
|
if (typeof o.column === "string") {
|
|
992
|
-
const dbCol = getTableConfig(this._table).columns[o.column]?.name ?? o.column;
|
|
993
|
-
return `${
|
|
1084
|
+
const dbCol = isCte ? o.column : getTableConfig(this._table).columns[o.column]?.name ?? o.column;
|
|
1085
|
+
return `${qName2}."${dbCol}" ${o.dir.toUpperCase()}`;
|
|
994
1086
|
} else if ("sql" in o.column && "params" in o.column) {
|
|
995
1087
|
const offset = params.length;
|
|
996
1088
|
params.push(...o.column.params);
|
|
@@ -1030,8 +1122,8 @@ class SelectBuilderIntermediate {
|
|
|
1030
1122
|
this._executor = _executor;
|
|
1031
1123
|
this._selection = _selection;
|
|
1032
1124
|
}
|
|
1033
|
-
from(
|
|
1034
|
-
return new SelectBuilder(
|
|
1125
|
+
from(table) {
|
|
1126
|
+
return new SelectBuilder(table, this._executor, this._selection);
|
|
1035
1127
|
}
|
|
1036
1128
|
}
|
|
1037
1129
|
|
|
@@ -1043,8 +1135,8 @@ class UpdateBuilder {
|
|
|
1043
1135
|
_returning;
|
|
1044
1136
|
_comment;
|
|
1045
1137
|
_with = [];
|
|
1046
|
-
constructor(
|
|
1047
|
-
this._table =
|
|
1138
|
+
constructor(table, executor) {
|
|
1139
|
+
this._table = table;
|
|
1048
1140
|
this._executor = executor;
|
|
1049
1141
|
}
|
|
1050
1142
|
then(onfulfilled, onrejected) {
|
|
@@ -1226,8 +1318,8 @@ class RelationalQueryBuilder {
|
|
|
1226
1318
|
let cached = schemaCache.get(tableName);
|
|
1227
1319
|
if (cached)
|
|
1228
1320
|
return cached;
|
|
1229
|
-
const
|
|
1230
|
-
const tConfig =
|
|
1321
|
+
const table = this._schema[tableName];
|
|
1322
|
+
const tConfig = table[TableConfigSymbol];
|
|
1231
1323
|
const ones = {};
|
|
1232
1324
|
const manys = {};
|
|
1233
1325
|
const manyToManys = {};
|
|
@@ -1240,6 +1332,8 @@ class RelationalQueryBuilder {
|
|
|
1240
1332
|
}
|
|
1241
1333
|
for (const [otherName, otherTable] of Object.entries(this._schema)) {
|
|
1242
1334
|
const otherConfig = otherTable[TableConfigSymbol];
|
|
1335
|
+
if (!otherConfig)
|
|
1336
|
+
continue;
|
|
1243
1337
|
for (const [colName2, col2] of Object.entries(otherConfig.columns)) {
|
|
1244
1338
|
if (col2.references && col2.references.table === tableName) {
|
|
1245
1339
|
const ref = col2.references;
|
|
@@ -1250,6 +1344,8 @@ class RelationalQueryBuilder {
|
|
|
1250
1344
|
}
|
|
1251
1345
|
for (const [junctionName, junctionTable] of Object.entries(this._schema)) {
|
|
1252
1346
|
const junctionConfig = junctionTable[TableConfigSymbol];
|
|
1347
|
+
if (!junctionConfig)
|
|
1348
|
+
continue;
|
|
1253
1349
|
const refs = Object.entries(junctionConfig.columns).filter(([_, c]) => c.references);
|
|
1254
1350
|
const toThis = refs.find(([_, c]) => c.references.table === tableName);
|
|
1255
1351
|
if (toThis) {
|
|
@@ -1272,7 +1368,7 @@ class RelationalQueryBuilder {
|
|
|
1272
1368
|
schemaCache.set(tableName, result);
|
|
1273
1369
|
return result;
|
|
1274
1370
|
}
|
|
1275
|
-
_buildSelectJson(tableName, args,
|
|
1371
|
+
_buildSelectJson(tableName, args, alias2, params, parentAlias, joinCondition, extraJoin) {
|
|
1276
1372
|
const tableConfig = this._schema[tableName][TableConfigSymbol];
|
|
1277
1373
|
const relations = this._getRuntimeRelations(tableName);
|
|
1278
1374
|
const withArgs = args.with || {};
|
|
@@ -1290,26 +1386,26 @@ class RelationalQueryBuilder {
|
|
|
1290
1386
|
continue;
|
|
1291
1387
|
}
|
|
1292
1388
|
}
|
|
1293
|
-
jsonFields.push(`'${colKey}', "${
|
|
1389
|
+
jsonFields.push(`'${colKey}', "${alias2}"."${colConfig.name}"`);
|
|
1294
1390
|
}
|
|
1295
1391
|
for (const [relKey, relArgs] of Object.entries(withArgs)) {
|
|
1296
1392
|
const isTrue = relArgs === true;
|
|
1297
1393
|
const rArgs = isTrue ? {} : relArgs;
|
|
1298
1394
|
if (relations.ones[relKey]) {
|
|
1299
1395
|
const rel = relations.ones[relKey];
|
|
1300
|
-
const subAlias = `${
|
|
1396
|
+
const subAlias = `${alias2}_${relKey}`;
|
|
1301
1397
|
const targetConfig = this._schema[rel.targetTable][TableConfigSymbol];
|
|
1302
1398
|
const targetPk = getPkColumn(targetConfig);
|
|
1303
|
-
const joinCond = `"${subAlias}"."${targetPk}" = "${
|
|
1304
|
-
const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params,
|
|
1399
|
+
const joinCond = `"${subAlias}"."${targetPk}" = "${alias2}"."${rel.sourceColumn}"`;
|
|
1400
|
+
const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias2, joinCond);
|
|
1305
1401
|
const subSql = `SELECT ${subQuery.sql} ${subQuery.from}`;
|
|
1306
1402
|
jsonFields.push(`'${relKey}', (${subSql})`);
|
|
1307
1403
|
} else if (relations.manys[relKey]) {
|
|
1308
1404
|
const rel = relations.manys[relKey];
|
|
1309
|
-
const subAlias = `${
|
|
1405
|
+
const subAlias = `${alias2}_${relKey}`;
|
|
1310
1406
|
const thisPk = getPkColumn(tableConfig);
|
|
1311
|
-
const joinCond = `"${subAlias}"."${rel.targetColumn}" = "${
|
|
1312
|
-
const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params,
|
|
1407
|
+
const joinCond = `"${subAlias}"."${rel.targetColumn}" = "${alias2}"."${thisPk}"`;
|
|
1408
|
+
const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias2, joinCond);
|
|
1313
1409
|
const aggAlias = `${subAlias}_agg`;
|
|
1314
1410
|
const aggSql = `
|
|
1315
1411
|
SELECT COALESCE(json_agg(_sub.obj), '[]'::json)
|
|
@@ -1321,16 +1417,16 @@ class RelationalQueryBuilder {
|
|
|
1321
1417
|
jsonFields.push(`'${relKey}', (${aggSql})`);
|
|
1322
1418
|
} else if (relations.manyToManys[relKey]) {
|
|
1323
1419
|
const rel = relations.manyToManys[relKey];
|
|
1324
|
-
const subAlias = `${
|
|
1325
|
-
const junctionAlias = `${
|
|
1420
|
+
const subAlias = `${alias2}_${relKey}`;
|
|
1421
|
+
const junctionAlias = `${alias2}_j_${relKey}`;
|
|
1326
1422
|
const targetTableConfig = this._schema[rel.targetTable][TableConfigSymbol];
|
|
1327
1423
|
const junctionTableConfig = this._schema[rel.junctionTable][TableConfigSymbol];
|
|
1328
1424
|
const fromExtra = `
|
|
1329
1425
|
INNER JOIN ${junctionTableConfig.qualifiedName} AS "${junctionAlias}"
|
|
1330
1426
|
ON "${junctionAlias}"."${rel.joinTargetColumn}" = "${subAlias}"."${getPkColumn(targetTableConfig)}"
|
|
1331
1427
|
`;
|
|
1332
|
-
const whereExtra = `"${junctionAlias}"."${rel.joinSourceColumn}" = "${
|
|
1333
|
-
const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params,
|
|
1428
|
+
const whereExtra = `"${junctionAlias}"."${rel.joinSourceColumn}" = "${alias2}"."${getPkColumn(tableConfig)}"`;
|
|
1429
|
+
const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias2, whereExtra, fromExtra);
|
|
1334
1430
|
const aggSql = `
|
|
1335
1431
|
SELECT COALESCE(json_agg(_sub.obj), '[]'::json)
|
|
1336
1432
|
FROM (
|
|
@@ -1342,7 +1438,7 @@ class RelationalQueryBuilder {
|
|
|
1342
1438
|
}
|
|
1343
1439
|
}
|
|
1344
1440
|
let selectSql = `json_build_object(${jsonFields.join(", ")})`;
|
|
1345
|
-
let fromSql = `FROM ${tableConfig.qualifiedName} AS "${
|
|
1441
|
+
let fromSql = `FROM ${tableConfig.qualifiedName} AS "${alias2}"`;
|
|
1346
1442
|
if (extraJoin) {
|
|
1347
1443
|
fromSql += ` ${extraJoin}`;
|
|
1348
1444
|
}
|
|
@@ -1390,7 +1486,7 @@ class RelationalQueryBuilder {
|
|
|
1390
1486
|
}
|
|
1391
1487
|
buildSQL(args) {
|
|
1392
1488
|
const params = [];
|
|
1393
|
-
const rootAlias =
|
|
1489
|
+
const rootAlias = this._tableName;
|
|
1394
1490
|
const subQuery = this._buildSelectJson(this._tableName, args, rootAlias, params);
|
|
1395
1491
|
const sql2 = `SELECT ${subQuery.sql} AS _data ${subQuery.from}`;
|
|
1396
1492
|
return { sql: sql2, params };
|
|
@@ -1466,14 +1562,14 @@ class BungresDB {
|
|
|
1466
1562
|
}
|
|
1467
1563
|
return new SelectBuilderIntermediate(this);
|
|
1468
1564
|
}
|
|
1469
|
-
insert(
|
|
1470
|
-
return new InsertBuilder(
|
|
1565
|
+
insert(table) {
|
|
1566
|
+
return new InsertBuilder(table, this);
|
|
1471
1567
|
}
|
|
1472
|
-
update(
|
|
1473
|
-
return new UpdateBuilder(
|
|
1568
|
+
update(table) {
|
|
1569
|
+
return new UpdateBuilder(table, this);
|
|
1474
1570
|
}
|
|
1475
|
-
delete(
|
|
1476
|
-
return new DeleteBuilder(
|
|
1571
|
+
delete(table) {
|
|
1572
|
+
return new DeleteBuilder(table, this);
|
|
1477
1573
|
}
|
|
1478
1574
|
async execute(builder) {
|
|
1479
1575
|
await this.ready();
|
|
@@ -1517,14 +1613,14 @@ class BungresTransaction {
|
|
|
1517
1613
|
}
|
|
1518
1614
|
return new SelectBuilderIntermediate(this);
|
|
1519
1615
|
}
|
|
1520
|
-
insert(
|
|
1521
|
-
return new InsertBuilder(
|
|
1616
|
+
insert(table) {
|
|
1617
|
+
return new InsertBuilder(table, this);
|
|
1522
1618
|
}
|
|
1523
|
-
update(
|
|
1524
|
-
return new UpdateBuilder(
|
|
1619
|
+
update(table) {
|
|
1620
|
+
return new UpdateBuilder(table, this);
|
|
1525
1621
|
}
|
|
1526
|
-
delete(
|
|
1527
|
-
return new DeleteBuilder(
|
|
1622
|
+
delete(table) {
|
|
1623
|
+
return new DeleteBuilder(table, this);
|
|
1528
1624
|
}
|
|
1529
1625
|
async execute(builder) {
|
|
1530
1626
|
const chunk = "toSQL" in builder ? builder.toSQL() : builder;
|
|
@@ -1685,13 +1781,13 @@ function formatDefaultValue(value, dataType) {
|
|
|
1685
1781
|
return String(value);
|
|
1686
1782
|
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
|
1687
1783
|
}
|
|
1688
|
-
function buildIndex(
|
|
1689
|
-
const tableName =
|
|
1784
|
+
function buildIndex(table, idx) {
|
|
1785
|
+
const tableName = table.schema ? `"${table.schema}"."${table.name}"` : `"${table.name}"`;
|
|
1690
1786
|
const unique2 = idx.unique ? "UNIQUE " : "";
|
|
1691
1787
|
const using = idx.using ? ` USING ${idx.using.toUpperCase()}` : "";
|
|
1692
1788
|
const cols = idx.columns.map((c) => `"${c}"`).join(", ");
|
|
1693
1789
|
const where = idx.where ? ` WHERE ${idx.where}` : "";
|
|
1694
|
-
const idxName = idx.name ?? `idx_${
|
|
1790
|
+
const idxName = idx.name ?? `idx_${table.name}_${idx.columns.join("_")}`;
|
|
1695
1791
|
return `CREATE ${unique2}INDEX IF NOT EXISTS "${idxName}" ON ${tableName}${using} (${cols})${where};`;
|
|
1696
1792
|
}
|
|
1697
1793
|
function generateAddColumn(tableName, schema, key, col2) {
|
|
@@ -1726,7 +1822,14 @@ function inlineParams(chunk) {
|
|
|
1726
1822
|
}
|
|
1727
1823
|
function generateCreateView(view) {
|
|
1728
1824
|
const kind = view.materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
1729
|
-
|
|
1825
|
+
let inlineSql = "";
|
|
1826
|
+
if (view.sql) {
|
|
1827
|
+
inlineSql = view.sql;
|
|
1828
|
+
} else if (view.query && typeof view.query.toSQL === "function") {
|
|
1829
|
+
inlineSql = inlineParams(view.query.toSQL());
|
|
1830
|
+
} else {
|
|
1831
|
+
inlineSql = "SELECT * FROM (VALUES ('LEGACY_SNAPSHOT_MISSING_SQL')) AS t(c)";
|
|
1832
|
+
}
|
|
1730
1833
|
return `CREATE ${kind} "${view.name}" AS ${inlineSql};`;
|
|
1731
1834
|
}
|
|
1732
1835
|
function generateDropView(view, ifExists = true) {
|
|
@@ -1961,8 +2064,8 @@ function topoSortConfigs(tables) {
|
|
|
1961
2064
|
visit(dep);
|
|
1962
2065
|
result.push(config);
|
|
1963
2066
|
}
|
|
1964
|
-
for (const
|
|
1965
|
-
visit(
|
|
2067
|
+
for (const table of tables)
|
|
2068
|
+
visit(table.name);
|
|
1966
2069
|
return result;
|
|
1967
2070
|
}
|
|
1968
2071
|
function diffColumn(prev, next) {
|
|
@@ -3120,6 +3223,30 @@ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
|
|
|
3120
3223
|
});
|
|
3121
3224
|
}
|
|
3122
3225
|
}
|
|
3226
|
+
class n extends V {
|
|
3227
|
+
get userInputWithCursor() {
|
|
3228
|
+
if (this.state === "submit")
|
|
3229
|
+
return this.userInput;
|
|
3230
|
+
const t2 = this.userInput;
|
|
3231
|
+
if (this.cursor >= t2.length)
|
|
3232
|
+
return `${this.userInput}\u2588`;
|
|
3233
|
+
const r2 = t2.slice(0, this.cursor), s = t2.slice(this.cursor, this.cursor + 1), e = t2.slice(this.cursor + 1);
|
|
3234
|
+
return `${r2}${styleText("inverse", s)}${e}`;
|
|
3235
|
+
}
|
|
3236
|
+
get cursor() {
|
|
3237
|
+
return this._cursor;
|
|
3238
|
+
}
|
|
3239
|
+
constructor(t2) {
|
|
3240
|
+
super({
|
|
3241
|
+
...t2,
|
|
3242
|
+
initialUserInput: t2.initialUserInput ?? t2.initialValue
|
|
3243
|
+
}), this.on("userInput", (r2) => {
|
|
3244
|
+
this._setValue(r2);
|
|
3245
|
+
}), this.on("finalize", () => {
|
|
3246
|
+
this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3123
3250
|
|
|
3124
3251
|
// ../../node_modules/.bun/@clack+prompts@1.7.0/node_modules/@clack/prompts/dist/index.mjs
|
|
3125
3252
|
import { styleText as styleText2, stripVTControlCharacters } from "util";
|
|
@@ -3380,24 +3507,62 @@ var SELECT_INSTRUCTIONS = [
|
|
|
3380
3507
|
`${styleText2("dim", "Enter:")} confirm`
|
|
3381
3508
|
];
|
|
3382
3509
|
var i = `${styleText2("gray", S_BAR)} `;
|
|
3510
|
+
var text = (e) => new n({
|
|
3511
|
+
validate: e.validate,
|
|
3512
|
+
placeholder: e.placeholder,
|
|
3513
|
+
defaultValue: e.defaultValue,
|
|
3514
|
+
initialValue: e.initialValue,
|
|
3515
|
+
output: e.output,
|
|
3516
|
+
signal: e.signal,
|
|
3517
|
+
input: e.input,
|
|
3518
|
+
render() {
|
|
3519
|
+
const i2 = e?.withGuide ?? settings.withGuide, s = `${`${i2 ? `${styleText2("gray", S_BAR)}
|
|
3520
|
+
` : ""}${symbol(this.state)} `}${e.message}
|
|
3521
|
+
`, c2 = e.placeholder && e.placeholder.length > 0 ? styleText2("inverse", e.placeholder[0]) + styleText2("dim", e.placeholder.slice(1)) : styleText2(["inverse", "hidden"], "_"), o2 = this.userInput ? this.userInputWithCursor : c2, l2 = this.value ?? "";
|
|
3522
|
+
switch (this.state) {
|
|
3523
|
+
case "error": {
|
|
3524
|
+
const n2 = this.error ? ` ${styleText2("yellow", this.error)}` : "", r2 = i2 ? `${styleText2("yellow", S_BAR)} ` : "", d = i2 ? styleText2("yellow", S_BAR_END) : "";
|
|
3525
|
+
return `${s.trim()}
|
|
3526
|
+
${r2}${o2}
|
|
3527
|
+
${d}${n2}
|
|
3528
|
+
`;
|
|
3529
|
+
}
|
|
3530
|
+
case "submit": {
|
|
3531
|
+
const n2 = l2 ? ` ${styleText2("dim", l2)}` : "", r2 = i2 ? styleText2("gray", S_BAR) : "";
|
|
3532
|
+
return `${s}${r2}${n2}`;
|
|
3533
|
+
}
|
|
3534
|
+
case "cancel": {
|
|
3535
|
+
const n2 = l2 ? ` ${styleText2(["strikethrough", "dim"], l2)}` : "", r2 = i2 ? styleText2("gray", S_BAR) : "";
|
|
3536
|
+
return `${s}${r2}${n2}${l2.trim() ? `
|
|
3537
|
+
${r2}` : ""}`;
|
|
3538
|
+
}
|
|
3539
|
+
default: {
|
|
3540
|
+
const n2 = i2 ? `${styleText2("cyan", S_BAR)} ` : "", r2 = i2 ? styleText2("cyan", S_BAR_END) : "";
|
|
3541
|
+
return `${s}${n2}${o2}
|
|
3542
|
+
${r2}
|
|
3543
|
+
`;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
}).prompt();
|
|
3383
3548
|
|
|
3384
3549
|
// src/commands/push.ts
|
|
3385
3550
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
3386
3551
|
async function runPush(config, opts = {}) {
|
|
3387
3552
|
intro(import_picocolors.default.bgCyan(import_picocolors.default.black(" @bungres/kit push ")));
|
|
3388
|
-
|
|
3389
|
-
|
|
3553
|
+
let activeSpinner = spinner();
|
|
3554
|
+
activeSpinner.start("Loading schemas...");
|
|
3390
3555
|
const schemas = await loadSchemas(config.schema);
|
|
3391
3556
|
if (schemas.length === 0) {
|
|
3392
|
-
|
|
3557
|
+
activeSpinner.stop("No schemas found.");
|
|
3393
3558
|
log.warn(import_picocolors.default.yellow("No table definitions found. Check your schema glob pattern."));
|
|
3394
3559
|
outro("Failed.");
|
|
3395
3560
|
return;
|
|
3396
3561
|
}
|
|
3397
|
-
|
|
3562
|
+
activeSpinner.stop(`Loaded ${schemas.length} schemas.`);
|
|
3398
3563
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
3399
|
-
|
|
3400
|
-
|
|
3564
|
+
activeSpinner = spinner();
|
|
3565
|
+
activeSpinner.start("Computing diff from database...");
|
|
3401
3566
|
try {
|
|
3402
3567
|
await sql2.unsafe(`CREATE SCHEMA IF NOT EXISTS "${config.migrationsSchema}";`);
|
|
3403
3568
|
const qualifiedPush = `"${config.migrationsSchema}"."__bungres_push"`;
|
|
@@ -3422,23 +3587,27 @@ async function runPush(config, opts = {}) {
|
|
|
3422
3587
|
}
|
|
3423
3588
|
}
|
|
3424
3589
|
const currentSnapshot = { tables: {}, enums: {}, views: {} };
|
|
3425
|
-
for (const
|
|
3426
|
-
if (
|
|
3427
|
-
currentSnapshot.tables[
|
|
3428
|
-
} else if (
|
|
3429
|
-
currentSnapshot.enums[
|
|
3430
|
-
} else if (
|
|
3431
|
-
currentSnapshot.views[
|
|
3590
|
+
for (const s of schemas) {
|
|
3591
|
+
if (s.type === "table") {
|
|
3592
|
+
currentSnapshot.tables[s.config.name] = s.config;
|
|
3593
|
+
} else if (s.type === "enum") {
|
|
3594
|
+
currentSnapshot.enums[s.enumName] = { enumName: s.enumName, enumValues: s.enumValues };
|
|
3595
|
+
} else if (s.type === "view") {
|
|
3596
|
+
currentSnapshot.views[s.config.name] = {
|
|
3597
|
+
name: s.config.name,
|
|
3598
|
+
materialized: s.config.materialized,
|
|
3599
|
+
sql: typeof s.config.query?.toSQL === "function" ? inlineParams(s.config.query.toSQL()) : s.config.sql
|
|
3600
|
+
};
|
|
3432
3601
|
}
|
|
3433
3602
|
}
|
|
3434
3603
|
const diff = diffSchemas(prevSnapshot, currentSnapshot);
|
|
3435
3604
|
if (diff.statements.length === 0) {
|
|
3436
|
-
|
|
3605
|
+
activeSpinner.stop("Up to date.");
|
|
3437
3606
|
log.success(import_picocolors.default.green("No schema changes detected. Database is up to date."));
|
|
3438
3607
|
outro("Done.");
|
|
3439
3608
|
return;
|
|
3440
3609
|
}
|
|
3441
|
-
|
|
3610
|
+
activeSpinner.stop(`Computed ${diff.statements.length} statements.`);
|
|
3442
3611
|
log.message(import_picocolors.default.bold("Changes to apply:"));
|
|
3443
3612
|
for (const change of diff.summary) {
|
|
3444
3613
|
if (change.startsWith("CREATE") || change.startsWith("ALTER TABLE") && change.includes("ADD")) {
|
|
@@ -3465,8 +3634,8 @@ async function runPush(config, opts = {}) {
|
|
|
3465
3634
|
return;
|
|
3466
3635
|
}
|
|
3467
3636
|
}
|
|
3468
|
-
|
|
3469
|
-
|
|
3637
|
+
activeSpinner = spinner();
|
|
3638
|
+
activeSpinner.start("Pushing changes...");
|
|
3470
3639
|
for (const stmt of diff.statements) {
|
|
3471
3640
|
if (config.verbose) {
|
|
3472
3641
|
log.info(import_picocolors.default.gray(`-- ${stmt}`));
|
|
@@ -3474,9 +3643,10 @@ async function runPush(config, opts = {}) {
|
|
|
3474
3643
|
await sql2.unsafe(stmt);
|
|
3475
3644
|
}
|
|
3476
3645
|
await sql2.unsafe(`INSERT INTO ${qualifiedPush} (snapshot) VALUES ($1);`, [JSON.stringify(currentSnapshot)]);
|
|
3477
|
-
|
|
3646
|
+
activeSpinner.stop(import_picocolors.default.green("Changes applied successfully."));
|
|
3478
3647
|
outro(import_picocolors.default.cyan("\u2728 Push complete."));
|
|
3479
3648
|
} catch (err) {
|
|
3649
|
+
activeSpinner.stop("Failed.");
|
|
3480
3650
|
log.error(import_picocolors.default.red(`Push failed: ${err.message}`));
|
|
3481
3651
|
outro("Failed.");
|
|
3482
3652
|
} finally {
|
|
@@ -3510,7 +3680,11 @@ async function runGenerate(config, name) {
|
|
|
3510
3680
|
} else if (s2.type === "enum") {
|
|
3511
3681
|
currentSnapshot.enums[s2.enumName] = { enumName: s2.enumName, enumValues: s2.enumValues };
|
|
3512
3682
|
} else if (s2.type === "view") {
|
|
3513
|
-
currentSnapshot.views[s2.config.name] =
|
|
3683
|
+
currentSnapshot.views[s2.config.name] = {
|
|
3684
|
+
name: s2.config.name,
|
|
3685
|
+
materialized: s2.config.materialized,
|
|
3686
|
+
sql: typeof s2.config.query?.toSQL === "function" ? inlineParams(s2.config.query.toSQL()) : s2.config.sql
|
|
3687
|
+
};
|
|
3514
3688
|
}
|
|
3515
3689
|
}
|
|
3516
3690
|
let prevSnapshot = { tables: {}, enums: {}, views: {} };
|
|
@@ -3675,9 +3849,9 @@ async function runMigrate(config) {
|
|
|
3675
3849
|
intro(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" @bungres/kit migrate ")));
|
|
3676
3850
|
const migrationsDir = resolve4(config.out);
|
|
3677
3851
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
3678
|
-
const
|
|
3852
|
+
const table = config.migrationsTable;
|
|
3679
3853
|
const schema = config.migrationsSchema;
|
|
3680
|
-
const qualifiedTable = `"${schema}"."${
|
|
3854
|
+
const qualifiedTable = `"${schema}"."${table}"`;
|
|
3681
3855
|
const createSchema = `CREATE SCHEMA IF NOT EXISTS "${schema}";`;
|
|
3682
3856
|
const createMigrationsTable = `
|
|
3683
3857
|
CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
@@ -3685,7 +3859,7 @@ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
|
3685
3859
|
name TEXT NOT NULL UNIQUE,
|
|
3686
3860
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
3687
3861
|
);`.trim();
|
|
3688
|
-
|
|
3862
|
+
let s = spinner();
|
|
3689
3863
|
s.start("Checking pending migrations...");
|
|
3690
3864
|
try {
|
|
3691
3865
|
await sql2.unsafe(createSchema);
|
|
@@ -3714,8 +3888,8 @@ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
|
3714
3888
|
}
|
|
3715
3889
|
s.stop(`Found ${pending.length} pending migration(s).`);
|
|
3716
3890
|
for (const file of pending) {
|
|
3717
|
-
|
|
3718
|
-
|
|
3891
|
+
s = spinner();
|
|
3892
|
+
s.start(`Applying ${file}...`);
|
|
3719
3893
|
const content = await Bun.file(join4(migrationsDir, file)).text();
|
|
3720
3894
|
let upContent = content;
|
|
3721
3895
|
const upMatch = content.match(/-- ==== UP ====([\s\S]*?)(?:-- ==== DOWN ====|$)/i);
|
|
@@ -3734,10 +3908,11 @@ ${upContent}
|
|
|
3734
3908
|
}
|
|
3735
3909
|
await txSql.unsafe(`INSERT INTO ${qualifiedTable} (name) VALUES ($1)`, [file]);
|
|
3736
3910
|
});
|
|
3737
|
-
|
|
3911
|
+
s.stop(import_picocolors3.default.green(`\u2713 Applied ${file}`));
|
|
3738
3912
|
}
|
|
3739
3913
|
outro(import_picocolors3.default.cyan("\u2728 All migrations applied successfully."));
|
|
3740
3914
|
} catch (err) {
|
|
3915
|
+
s.stop("Failed.");
|
|
3741
3916
|
log.error(import_picocolors3.default.red(`Migration failed: ${err.message}`));
|
|
3742
3917
|
outro("Failed.");
|
|
3743
3918
|
} finally {
|
|
@@ -3851,10 +4026,10 @@ function generateSchemaTS(tableMap, dbSchema) {
|
|
|
3851
4026
|
`} from "@bungres/orm";`,
|
|
3852
4027
|
``
|
|
3853
4028
|
];
|
|
3854
|
-
for (const [,
|
|
3855
|
-
const varName = toCamelCase(
|
|
3856
|
-
lines.push(`export const ${varName} = pgTable("${
|
|
3857
|
-
for (const col2 of
|
|
4029
|
+
for (const [, table] of tableMap) {
|
|
4030
|
+
const varName = toCamelCase(table.tableName);
|
|
4031
|
+
lines.push(`export const ${varName} = pgTable("${table.tableName}", {`);
|
|
4032
|
+
for (const col2 of table.columns) {
|
|
3858
4033
|
const colExpr = buildColumnExpression(col2);
|
|
3859
4034
|
lines.push(` ${toCamelCase(col2.name)}: ${colExpr},`);
|
|
3860
4035
|
}
|
|
@@ -3862,9 +4037,9 @@ function generateSchemaTS(tableMap, dbSchema) {
|
|
|
3862
4037
|
if (dbSchema !== "public") {
|
|
3863
4038
|
options.push(`schema: "${dbSchema}"`);
|
|
3864
4039
|
}
|
|
3865
|
-
if (
|
|
4040
|
+
if (table.indexes.length > 0) {
|
|
3866
4041
|
const idxLines = [];
|
|
3867
|
-
for (const idx of
|
|
4042
|
+
for (const idx of table.indexes) {
|
|
3868
4043
|
const m2 = idx.indexdef.match(/CREATE (UNIQUE )?INDEX (.+) ON (.+) USING (\w+) \((.+)\)(?: WHERE (.+))?/i);
|
|
3869
4044
|
if (m2) {
|
|
3870
4045
|
const isUnique = !!m2[1];
|
|
@@ -4007,16 +4182,16 @@ async function runStatus(config) {
|
|
|
4007
4182
|
intro(import_picocolors4.default.bgCyan(import_picocolors4.default.black(" @bungres/kit status ")));
|
|
4008
4183
|
const migrationsDir = resolve6(config.out);
|
|
4009
4184
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
4010
|
-
const
|
|
4185
|
+
const table = config.migrationsTable;
|
|
4011
4186
|
const schema = config.migrationsSchema;
|
|
4012
|
-
const qualifiedTable = `"${schema}"."${
|
|
4187
|
+
const qualifiedTable = `"${schema}"."${table}"`;
|
|
4013
4188
|
const s = spinner();
|
|
4014
4189
|
s.start("Checking migration status...");
|
|
4015
4190
|
try {
|
|
4016
4191
|
const tableCheck = await sql2.unsafe(`SELECT EXISTS (
|
|
4017
4192
|
SELECT 1 FROM information_schema.tables
|
|
4018
4193
|
WHERE table_schema = $1 AND table_name = $2
|
|
4019
|
-
) AS exists`, [schema,
|
|
4194
|
+
) AS exists`, [schema, table]);
|
|
4020
4195
|
const trackingExists = tableCheck[0]?.exists ?? false;
|
|
4021
4196
|
const glob = new Bun.Glob("*.sql");
|
|
4022
4197
|
const files = [];
|
|
@@ -4051,6 +4226,7 @@ async function runStatus(config) {
|
|
|
4051
4226
|
log.info(`${import_picocolors4.default.green(appliedSet.size.toString())} applied, ${import_picocolors4.default.yellow(pendingCount.toString())} pending.`);
|
|
4052
4227
|
outro("Done.");
|
|
4053
4228
|
} catch (err) {
|
|
4229
|
+
s.stop("Failed.");
|
|
4054
4230
|
log.error(import_picocolors4.default.red(`Status check failed: ${err.message}`));
|
|
4055
4231
|
outro("Failed.");
|
|
4056
4232
|
} finally {
|
|
@@ -4061,26 +4237,14 @@ async function runStatus(config) {
|
|
|
4061
4237
|
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
4062
4238
|
async function runDrop(config, opts = {}) {
|
|
4063
4239
|
intro(import_picocolors5.default.bgCyan(import_picocolors5.default.black(" @bungres/kit drop ")));
|
|
4064
|
-
const s = spinner();
|
|
4065
|
-
s.start("Loading schemas...");
|
|
4066
|
-
const schemas = await loadSchemas(config.schema);
|
|
4067
|
-
const tableSchemas = schemas.filter((s2) => s2.type === "table");
|
|
4068
|
-
const enumSchemas = schemas.filter((s2) => s2.type === "enum");
|
|
4069
|
-
const viewSchemas = schemas.filter((s2) => s2.type === "view");
|
|
4070
|
-
if (tableSchemas.length === 0 && enumSchemas.length === 0 && viewSchemas.length === 0) {
|
|
4071
|
-
s.stop("No schemas found.");
|
|
4072
|
-
log.warn(import_picocolors5.default.yellow("No definitions found in schema files."));
|
|
4073
|
-
outro("Failed.");
|
|
4074
|
-
return;
|
|
4075
|
-
}
|
|
4076
|
-
s.stop(`Loaded ${schemas.length} schemas.`);
|
|
4077
4240
|
const ms = spinner();
|
|
4078
|
-
ms.start("
|
|
4241
|
+
ms.start("Scanning database for objects to drop...");
|
|
4079
4242
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
4080
4243
|
try {
|
|
4081
|
-
const userSchema = config.dbSchema;
|
|
4082
|
-
const
|
|
4083
|
-
const
|
|
4244
|
+
const userSchema = config.dbSchema || "public";
|
|
4245
|
+
const existingViews = await sql2.unsafe(`SELECT table_name FROM information_schema.views WHERE table_schema = $1`, [userSchema]);
|
|
4246
|
+
const existingTables = await sql2.unsafe(`SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE'`, [userSchema]);
|
|
4247
|
+
const existingTypes = await sql2.unsafe(`SELECT t.typname FROM pg_type t JOIN pg_namespace n ON n.oid = t.typnamespace WHERE n.nspname = $1 AND t.typtype = 'e'`, [userSchema]);
|
|
4084
4248
|
const migTableCheck = await sql2.unsafe(`SELECT EXISTS (
|
|
4085
4249
|
SELECT 1 FROM information_schema.tables
|
|
4086
4250
|
WHERE table_schema = $1 AND table_name = $2
|
|
@@ -4091,35 +4255,20 @@ async function runDrop(config, opts = {}) {
|
|
|
4091
4255
|
WHERE table_schema = $1 AND table_name = $2
|
|
4092
4256
|
) AS exists`, [config.migrationsSchema, "__bungres_push"]);
|
|
4093
4257
|
const pushTableExists = pushTableCheck[0]?.exists ?? false;
|
|
4094
|
-
const
|
|
4095
|
-
if (
|
|
4258
|
+
const totalObjects = existingViews.length + existingTables.length + existingTypes.length + (migrationTableExists ? 1 : 0) + (pushTableExists ? 1 : 0);
|
|
4259
|
+
if (totalObjects === 0) {
|
|
4096
4260
|
ms.stop("Nothing to do.");
|
|
4097
|
-
log.success(import_picocolors5.default.green("No items to drop (
|
|
4261
|
+
log.success(import_picocolors5.default.green("No items to drop (schema is empty)."));
|
|
4098
4262
|
outro("Done.");
|
|
4099
4263
|
return;
|
|
4100
4264
|
}
|
|
4101
|
-
ms.stop("Database
|
|
4102
|
-
const namesToPrint = tablesToDrop.map((sch) => (sch.config.schema ? sch.config.schema + "." : "") + sch.config.name + " (table)");
|
|
4103
|
-
for (const v of viewSchemas) {
|
|
4104
|
-
namesToPrint.push(`${v.config.name} (view)`);
|
|
4105
|
-
}
|
|
4106
|
-
for (const e of enumSchemas) {
|
|
4107
|
-
namesToPrint.push(`${e.enumName} (enum)`);
|
|
4108
|
-
}
|
|
4109
|
-
if (migrationTableExists) {
|
|
4110
|
-
namesToPrint.push(`${config.migrationsSchema}.${config.migrationsTable} (table)`);
|
|
4111
|
-
}
|
|
4112
|
-
if (pushTableExists) {
|
|
4113
|
-
namesToPrint.push(`${config.migrationsSchema}.__bungres_push (table)`);
|
|
4114
|
-
}
|
|
4265
|
+
ms.stop("Database scan complete.");
|
|
4115
4266
|
if (!opts.force) {
|
|
4116
4267
|
log.warn(import_picocolors5.default.bgRed(import_picocolors5.default.white(" \u26A0\uFE0F WARNING ")));
|
|
4117
|
-
log.message(import_picocolors5.default.bold(import_picocolors5.default.red("This will drop
|
|
4118
|
-
|
|
4119
|
-
log.info(import_picocolors5.default.yellow(` - ${name}`));
|
|
4120
|
-
}
|
|
4268
|
+
log.message(import_picocolors5.default.bold(import_picocolors5.default.red("This will GLOBALLY drop all tables, views, and types in the database schema!")));
|
|
4269
|
+
log.info(import_picocolors5.default.yellow(`Found ${existingViews.length} views, ${existingTables.length} tables, ${existingTypes.length} enums.`));
|
|
4121
4270
|
const confirm2 = await confirm({
|
|
4122
|
-
message: "Are you sure you want to
|
|
4271
|
+
message: "Are you absolutely sure you want to drop ALL data?",
|
|
4123
4272
|
initialValue: false
|
|
4124
4273
|
});
|
|
4125
4274
|
if (isCancel(confirm2) || !confirm2) {
|
|
@@ -4128,35 +4277,36 @@ async function runDrop(config, opts = {}) {
|
|
|
4128
4277
|
}
|
|
4129
4278
|
}
|
|
4130
4279
|
const exSpinner = spinner();
|
|
4131
|
-
exSpinner.start("Dropping
|
|
4132
|
-
for (const
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
log.step(import_picocolors5.default.gray(`Dropped ${entry.enumName}`));
|
|
4280
|
+
exSpinner.start("Dropping objects...");
|
|
4281
|
+
for (const v of existingViews) {
|
|
4282
|
+
await sql2.unsafe(`DROP VIEW IF EXISTS "${userSchema}"."${v.table_name}" CASCADE;`);
|
|
4283
|
+
if (opts.force !== true)
|
|
4284
|
+
log.step(import_picocolors5.default.gray(`Dropped view ${v.table_name}`));
|
|
4285
|
+
}
|
|
4286
|
+
for (const t2 of existingTables) {
|
|
4287
|
+
await sql2.unsafe(`DROP TABLE IF EXISTS "${userSchema}"."${t2.table_name}" CASCADE;`);
|
|
4288
|
+
if (opts.force !== true)
|
|
4289
|
+
log.step(import_picocolors5.default.gray(`Dropped table ${t2.table_name}`));
|
|
4290
|
+
}
|
|
4291
|
+
for (const type of existingTypes) {
|
|
4292
|
+
await sql2.unsafe(`DROP TYPE IF EXISTS "${userSchema}"."${type.typname}" CASCADE;`);
|
|
4293
|
+
if (opts.force !== true)
|
|
4294
|
+
log.step(import_picocolors5.default.gray(`Dropped enum ${type.typname}`));
|
|
4147
4295
|
}
|
|
4148
4296
|
if (migrationTableExists) {
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4297
|
+
await sql2.unsafe(`DROP TABLE IF EXISTS "${config.migrationsSchema}"."${config.migrationsTable}" CASCADE`);
|
|
4298
|
+
if (opts.force !== true)
|
|
4299
|
+
log.step(import_picocolors5.default.gray(`Dropped ${config.migrationsSchema}.${config.migrationsTable}`));
|
|
4152
4300
|
}
|
|
4153
4301
|
if (pushTableExists) {
|
|
4154
4302
|
await sql2.unsafe(`DROP TABLE IF EXISTS "${config.migrationsSchema}"."__bungres_push" CASCADE`);
|
|
4155
|
-
|
|
4303
|
+
if (opts.force !== true)
|
|
4304
|
+
log.step(import_picocolors5.default.gray(`Dropped ${config.migrationsSchema}.__bungres_push`));
|
|
4156
4305
|
}
|
|
4157
4306
|
exSpinner.stop("Items dropped.");
|
|
4158
4307
|
outro(import_picocolors5.default.cyan("\u2728 Drop complete."));
|
|
4159
4308
|
} catch (err) {
|
|
4309
|
+
ms.stop("Scan failed.");
|
|
4160
4310
|
log.error(import_picocolors5.default.red(`Drop failed: ${err.message}`));
|
|
4161
4311
|
outro("Failed.");
|
|
4162
4312
|
} finally {
|