@bungres/kit 1.0.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/README.md +3 -1
- package/dist/cli.js +4071 -3374
- 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 +4 -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/differ.d.ts +8 -1
- package/dist/differ.d.ts.map +1 -1
- package/dist/index.js +549 -200
- package/dist/schema-loader.d.ts +16 -1
- package/dist/schema-loader.d.ts.map +1 -1
- package/package.json +4 -3
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) {
|
|
@@ -1702,6 +1798,44 @@ function generateDropColumn(tableName, schema, columnName) {
|
|
|
1702
1798
|
const tbl = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
|
|
1703
1799
|
return `ALTER TABLE ${tbl} DROP COLUMN IF EXISTS "${columnName}";`;
|
|
1704
1800
|
}
|
|
1801
|
+
function generateCreateEnum(name, values) {
|
|
1802
|
+
const vals = values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
1803
|
+
return `CREATE TYPE "${name}" AS ENUM (${vals});`;
|
|
1804
|
+
}
|
|
1805
|
+
function generateDropEnum(name, ifExists = true) {
|
|
1806
|
+
return `DROP TYPE${ifExists ? " IF EXISTS" : ""} "${name}";`;
|
|
1807
|
+
}
|
|
1808
|
+
function inlineParams(chunk) {
|
|
1809
|
+
let { sql: sql2, params } = chunk;
|
|
1810
|
+
return sql2.replace(/\$(\d+)/g, (_, n) => {
|
|
1811
|
+
const p = params[parseInt(n) - 1];
|
|
1812
|
+
if (typeof p === "string")
|
|
1813
|
+
return `'${p.replace(/'/g, "''")}'`;
|
|
1814
|
+
if (typeof p === "number")
|
|
1815
|
+
return String(p);
|
|
1816
|
+
if (typeof p === "boolean")
|
|
1817
|
+
return p ? "TRUE" : "FALSE";
|
|
1818
|
+
if (p === null)
|
|
1819
|
+
return "NULL";
|
|
1820
|
+
return `'${String(p).replace(/'/g, "''")}'`;
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
function generateCreateView(view) {
|
|
1824
|
+
const kind = view.materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
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
|
+
}
|
|
1833
|
+
return `CREATE ${kind} "${view.name}" AS ${inlineSql};`;
|
|
1834
|
+
}
|
|
1835
|
+
function generateDropView(view, ifExists = true) {
|
|
1836
|
+
const kind = view.materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
1837
|
+
return `DROP ${kind}${ifExists ? " IF EXISTS" : ""} "${view.name}";`;
|
|
1838
|
+
}
|
|
1705
1839
|
|
|
1706
1840
|
// src/schema-loader.ts
|
|
1707
1841
|
async function loadSchemas(patterns, cwd = process.cwd()) {
|
|
@@ -1715,11 +1849,27 @@ async function loadSchemas(patterns, cwd = process.cwd()) {
|
|
|
1715
1849
|
for (const [exportName, value] of Object.entries(mod)) {
|
|
1716
1850
|
if (isTable(value)) {
|
|
1717
1851
|
entries.push({
|
|
1852
|
+
type: "table",
|
|
1718
1853
|
exportName,
|
|
1719
1854
|
config: value[TableConfigSymbol],
|
|
1720
1855
|
table: value,
|
|
1721
1856
|
filePath: absPath
|
|
1722
1857
|
});
|
|
1858
|
+
} else if (isEnum(value)) {
|
|
1859
|
+
entries.push({
|
|
1860
|
+
type: "enum",
|
|
1861
|
+
exportName,
|
|
1862
|
+
enumName: value.enumName,
|
|
1863
|
+
enumValues: value.enumValues,
|
|
1864
|
+
filePath: absPath
|
|
1865
|
+
});
|
|
1866
|
+
} else if (isView(value)) {
|
|
1867
|
+
entries.push({
|
|
1868
|
+
type: "view",
|
|
1869
|
+
exportName,
|
|
1870
|
+
config: value,
|
|
1871
|
+
filePath: absPath
|
|
1872
|
+
});
|
|
1723
1873
|
}
|
|
1724
1874
|
}
|
|
1725
1875
|
}
|
|
@@ -1729,17 +1879,67 @@ async function loadSchemas(patterns, cwd = process.cwd()) {
|
|
|
1729
1879
|
function isTable(value) {
|
|
1730
1880
|
return typeof value === "object" && value !== null && TableConfigSymbol in value;
|
|
1731
1881
|
}
|
|
1882
|
+
function isEnum(value) {
|
|
1883
|
+
return typeof value === "function" && "enumName" in value && "enumValues" in value && Array.isArray(value.enumValues);
|
|
1884
|
+
}
|
|
1885
|
+
function isView(value) {
|
|
1886
|
+
return typeof value === "object" && value !== null && "name" in value && "query" in value && typeof value.query === "object" && typeof value.query.toSQL === "function";
|
|
1887
|
+
}
|
|
1732
1888
|
// src/differ.ts
|
|
1733
1889
|
function diffSchemas(prev, next) {
|
|
1734
1890
|
const statements = [];
|
|
1735
1891
|
const summary = [];
|
|
1736
1892
|
const warnings = [];
|
|
1737
|
-
const
|
|
1738
|
-
const
|
|
1893
|
+
const prevEnums = prev.enums || {};
|
|
1894
|
+
const nextEnums = next.enums || {};
|
|
1895
|
+
const prevEnumNames = new Set(Object.keys(prevEnums));
|
|
1896
|
+
const nextEnumNames = new Set(Object.keys(nextEnums));
|
|
1897
|
+
for (const enumName of nextEnumNames) {
|
|
1898
|
+
if (!prevEnumNames.has(enumName)) {
|
|
1899
|
+
const e = nextEnums[enumName];
|
|
1900
|
+
statements.push(generateCreateEnum(e.enumName, e.enumValues));
|
|
1901
|
+
summary.push(`CREATE TYPE ${e.enumName}`);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
for (const enumName of prevEnumNames) {
|
|
1905
|
+
if (!nextEnumNames.has(enumName)) {
|
|
1906
|
+
const e = prevEnums[enumName];
|
|
1907
|
+
statements.push(generateDropEnum(e.enumName, true));
|
|
1908
|
+
summary.push(`DROP TYPE ${e.enumName}`);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
for (const enumName of nextEnumNames) {
|
|
1912
|
+
if (prevEnumNames.has(enumName)) {
|
|
1913
|
+
const p = prevEnums[enumName];
|
|
1914
|
+
const n = nextEnums[enumName];
|
|
1915
|
+
if (JSON.stringify(p.enumValues) !== JSON.stringify(n.enumValues)) {
|
|
1916
|
+
warnings.push(`Enum '${n.enumName}' values have changed. Bungres Kit currently requires manual migration for ALTER TYPE ... ADD VALUE.`);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
const prevViews = prev.views || {};
|
|
1921
|
+
const nextViews = next.views || {};
|
|
1922
|
+
const prevViewNames = new Set(Object.keys(prevViews));
|
|
1923
|
+
const nextViewNames = new Set(Object.keys(nextViews));
|
|
1924
|
+
for (const viewName of prevViewNames) {
|
|
1925
|
+
if (!nextViewNames.has(viewName)) {
|
|
1926
|
+
statements.push(generateDropView(prevViews[viewName]));
|
|
1927
|
+
summary.push(`DROP VIEW ${viewName}`);
|
|
1928
|
+
} else {
|
|
1929
|
+
const pSql = generateCreateView(prevViews[viewName]);
|
|
1930
|
+
const nSql = generateCreateView(nextViews[viewName]);
|
|
1931
|
+
if (pSql !== nSql) {
|
|
1932
|
+
statements.push(generateDropView(prevViews[viewName]));
|
|
1933
|
+
summary.push(`DROP VIEW ${viewName} (for recreation)`);
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
const prevTables = new Set(Object.keys(prev.tables || {}));
|
|
1938
|
+
const nextTables = new Set(Object.keys(next.tables || {}));
|
|
1739
1939
|
const newTableConfigs = [];
|
|
1740
1940
|
for (const tableName of nextTables) {
|
|
1741
1941
|
if (!prevTables.has(tableName)) {
|
|
1742
|
-
newTableConfigs.push(next[tableName]);
|
|
1942
|
+
newTableConfigs.push(next.tables[tableName]);
|
|
1743
1943
|
}
|
|
1744
1944
|
}
|
|
1745
1945
|
for (const config of topoSortConfigs(newTableConfigs)) {
|
|
@@ -1748,7 +1948,7 @@ function diffSchemas(prev, next) {
|
|
|
1748
1948
|
}
|
|
1749
1949
|
for (const tableName of prevTables) {
|
|
1750
1950
|
if (!nextTables.has(tableName)) {
|
|
1751
|
-
const config = prev[tableName];
|
|
1951
|
+
const config = prev.tables[tableName];
|
|
1752
1952
|
const tbl = config.schema ? `"${config.schema}"."${tableName}"` : `"${tableName}"`;
|
|
1753
1953
|
statements.push(`DROP TABLE IF EXISTS ${tbl};`);
|
|
1754
1954
|
summary.push(`DROP TABLE ${tableName}`);
|
|
@@ -1758,8 +1958,8 @@ function diffSchemas(prev, next) {
|
|
|
1758
1958
|
for (const tableName of nextTables) {
|
|
1759
1959
|
if (!prevTables.has(tableName))
|
|
1760
1960
|
continue;
|
|
1761
|
-
const prevConfig = prev[tableName];
|
|
1762
|
-
const nextConfig = next[tableName];
|
|
1961
|
+
const prevConfig = prev.tables[tableName];
|
|
1962
|
+
const nextConfig = next.tables[tableName];
|
|
1763
1963
|
const prevCols = prevConfig.columns;
|
|
1764
1964
|
const nextCols = nextConfig.columns;
|
|
1765
1965
|
const prevColNames = new Set(Object.keys(prevCols));
|
|
@@ -1831,6 +2031,19 @@ function diffSchemas(prev, next) {
|
|
|
1831
2031
|
}
|
|
1832
2032
|
}
|
|
1833
2033
|
}
|
|
2034
|
+
for (const viewName of nextViewNames) {
|
|
2035
|
+
if (!prevViewNames.has(viewName)) {
|
|
2036
|
+
statements.push(generateCreateView(nextViews[viewName]));
|
|
2037
|
+
summary.push(`CREATE VIEW ${viewName}`);
|
|
2038
|
+
} else {
|
|
2039
|
+
const pSql = generateCreateView(prevViews[viewName]);
|
|
2040
|
+
const nSql = generateCreateView(nextViews[viewName]);
|
|
2041
|
+
if (pSql !== nSql) {
|
|
2042
|
+
statements.push(generateCreateView(nextViews[viewName]));
|
|
2043
|
+
summary.push(`CREATE VIEW ${viewName} (recreated)`);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
1834
2047
|
return { statements, summary, warnings };
|
|
1835
2048
|
}
|
|
1836
2049
|
function topoSortConfigs(tables) {
|
|
@@ -1851,14 +2064,21 @@ function topoSortConfigs(tables) {
|
|
|
1851
2064
|
visit(dep);
|
|
1852
2065
|
result.push(config);
|
|
1853
2066
|
}
|
|
1854
|
-
for (const
|
|
1855
|
-
visit(
|
|
2067
|
+
for (const table of tables)
|
|
2068
|
+
visit(table.name);
|
|
1856
2069
|
return result;
|
|
1857
2070
|
}
|
|
1858
2071
|
function diffColumn(prev, next) {
|
|
1859
2072
|
const changes = [];
|
|
1860
2073
|
const col2 = `"${next.name}"`;
|
|
2074
|
+
const prevDef = prev.defaultFn ?? (prev.defaultValue !== undefined ? String(prev.defaultValue) : undefined);
|
|
2075
|
+
const nextDef = next.defaultFn ?? (next.defaultValue !== undefined ? String(next.defaultValue) : undefined);
|
|
2076
|
+
let typeChanged = false;
|
|
1861
2077
|
if (prev.dataType !== next.dataType) {
|
|
2078
|
+
typeChanged = true;
|
|
2079
|
+
if (prevDef !== undefined) {
|
|
2080
|
+
changes.push(`ALTER COLUMN ${col2} DROP DEFAULT`);
|
|
2081
|
+
}
|
|
1862
2082
|
changes.push(`ALTER COLUMN ${col2} TYPE ${next.dataType.toUpperCase()} USING ${col2}::${next.dataType}`);
|
|
1863
2083
|
} else {
|
|
1864
2084
|
const prevLen = prev.length;
|
|
@@ -1875,9 +2095,12 @@ function diffColumn(prev, next) {
|
|
|
1875
2095
|
if (prev.notNull && !next.notNull) {
|
|
1876
2096
|
changes.push(`ALTER COLUMN ${col2} DROP NOT NULL`);
|
|
1877
2097
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
2098
|
+
if (typeChanged) {
|
|
2099
|
+
if (nextDef !== undefined) {
|
|
2100
|
+
const val = next.defaultFn ?? formatDefault(next.defaultValue, next.dataType);
|
|
2101
|
+
changes.push(`ALTER COLUMN ${col2} SET DEFAULT ${val}`);
|
|
2102
|
+
}
|
|
2103
|
+
} else if (prevDef !== nextDef) {
|
|
1881
2104
|
if (nextDef !== undefined) {
|
|
1882
2105
|
const val = next.defaultFn ?? formatDefault(next.defaultValue, next.dataType);
|
|
1883
2106
|
changes.push(`ALTER COLUMN ${col2} SET DEFAULT ${val}`);
|
|
@@ -3000,6 +3223,30 @@ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
|
|
|
3000
3223
|
});
|
|
3001
3224
|
}
|
|
3002
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
|
+
}
|
|
3003
3250
|
|
|
3004
3251
|
// ../../node_modules/.bun/@clack+prompts@1.7.0/node_modules/@clack/prompts/dist/index.mjs
|
|
3005
3252
|
import { styleText as styleText2, stripVTControlCharacters } from "util";
|
|
@@ -3260,24 +3507,62 @@ var SELECT_INSTRUCTIONS = [
|
|
|
3260
3507
|
`${styleText2("dim", "Enter:")} confirm`
|
|
3261
3508
|
];
|
|
3262
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();
|
|
3263
3548
|
|
|
3264
3549
|
// src/commands/push.ts
|
|
3265
3550
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
3266
3551
|
async function runPush(config, opts = {}) {
|
|
3267
3552
|
intro(import_picocolors.default.bgCyan(import_picocolors.default.black(" @bungres/kit push ")));
|
|
3268
|
-
|
|
3269
|
-
|
|
3553
|
+
let activeSpinner = spinner();
|
|
3554
|
+
activeSpinner.start("Loading schemas...");
|
|
3270
3555
|
const schemas = await loadSchemas(config.schema);
|
|
3271
3556
|
if (schemas.length === 0) {
|
|
3272
|
-
|
|
3557
|
+
activeSpinner.stop("No schemas found.");
|
|
3273
3558
|
log.warn(import_picocolors.default.yellow("No table definitions found. Check your schema glob pattern."));
|
|
3274
3559
|
outro("Failed.");
|
|
3275
3560
|
return;
|
|
3276
3561
|
}
|
|
3277
|
-
|
|
3562
|
+
activeSpinner.stop(`Loaded ${schemas.length} schemas.`);
|
|
3278
3563
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
3279
|
-
|
|
3280
|
-
|
|
3564
|
+
activeSpinner = spinner();
|
|
3565
|
+
activeSpinner.start("Computing diff from database...");
|
|
3281
3566
|
try {
|
|
3282
3567
|
await sql2.unsafe(`CREATE SCHEMA IF NOT EXISTS "${config.migrationsSchema}";`);
|
|
3283
3568
|
const qualifiedPush = `"${config.migrationsSchema}"."__bungres_push"`;
|
|
@@ -3288,19 +3573,41 @@ async function runPush(config, opts = {}) {
|
|
|
3288
3573
|
);
|
|
3289
3574
|
`);
|
|
3290
3575
|
const rows = await sql2.unsafe(`SELECT snapshot FROM ${qualifiedPush} ORDER BY id DESC LIMIT 1;`);
|
|
3291
|
-
let prevSnapshot = {};
|
|
3576
|
+
let prevSnapshot = { tables: {}, enums: {}, views: {} };
|
|
3292
3577
|
if (rows.length > 0) {
|
|
3293
|
-
|
|
3578
|
+
const parsed = typeof rows[0].snapshot === "string" ? JSON.parse(rows[0].snapshot) : rows[0].snapshot;
|
|
3579
|
+
if (parsed.tables || parsed.enums || parsed.views) {
|
|
3580
|
+
prevSnapshot = {
|
|
3581
|
+
tables: parsed.tables || {},
|
|
3582
|
+
enums: parsed.enums || {},
|
|
3583
|
+
views: parsed.views || {}
|
|
3584
|
+
};
|
|
3585
|
+
} else {
|
|
3586
|
+
prevSnapshot = { tables: parsed, enums: {}, views: {} };
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
const currentSnapshot = { tables: {}, enums: {}, 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
|
+
};
|
|
3601
|
+
}
|
|
3294
3602
|
}
|
|
3295
|
-
const currentSnapshot = Object.fromEntries(schemas.map((schemaEntry) => [schemaEntry.config.name, schemaEntry.config]));
|
|
3296
3603
|
const diff = diffSchemas(prevSnapshot, currentSnapshot);
|
|
3297
3604
|
if (diff.statements.length === 0) {
|
|
3298
|
-
|
|
3605
|
+
activeSpinner.stop("Up to date.");
|
|
3299
3606
|
log.success(import_picocolors.default.green("No schema changes detected. Database is up to date."));
|
|
3300
3607
|
outro("Done.");
|
|
3301
3608
|
return;
|
|
3302
3609
|
}
|
|
3303
|
-
|
|
3610
|
+
activeSpinner.stop(`Computed ${diff.statements.length} statements.`);
|
|
3304
3611
|
log.message(import_picocolors.default.bold("Changes to apply:"));
|
|
3305
3612
|
for (const change of diff.summary) {
|
|
3306
3613
|
if (change.startsWith("CREATE") || change.startsWith("ALTER TABLE") && change.includes("ADD")) {
|
|
@@ -3327,8 +3634,8 @@ async function runPush(config, opts = {}) {
|
|
|
3327
3634
|
return;
|
|
3328
3635
|
}
|
|
3329
3636
|
}
|
|
3330
|
-
|
|
3331
|
-
|
|
3637
|
+
activeSpinner = spinner();
|
|
3638
|
+
activeSpinner.start("Pushing changes...");
|
|
3332
3639
|
for (const stmt of diff.statements) {
|
|
3333
3640
|
if (config.verbose) {
|
|
3334
3641
|
log.info(import_picocolors.default.gray(`-- ${stmt}`));
|
|
@@ -3336,9 +3643,10 @@ async function runPush(config, opts = {}) {
|
|
|
3336
3643
|
await sql2.unsafe(stmt);
|
|
3337
3644
|
}
|
|
3338
3645
|
await sql2.unsafe(`INSERT INTO ${qualifiedPush} (snapshot) VALUES ($1);`, [JSON.stringify(currentSnapshot)]);
|
|
3339
|
-
|
|
3646
|
+
activeSpinner.stop(import_picocolors.default.green("Changes applied successfully."));
|
|
3340
3647
|
outro(import_picocolors.default.cyan("\u2728 Push complete."));
|
|
3341
3648
|
} catch (err) {
|
|
3649
|
+
activeSpinner.stop("Failed.");
|
|
3342
3650
|
log.error(import_picocolors.default.red(`Push failed: ${err.message}`));
|
|
3343
3651
|
outro("Failed.");
|
|
3344
3652
|
} finally {
|
|
@@ -3365,19 +3673,50 @@ async function runGenerate(config, name) {
|
|
|
3365
3673
|
const metaDir = join3(migrationsDir, "meta");
|
|
3366
3674
|
await Bun.$`mkdir -p ${migrationsDir}`.quiet();
|
|
3367
3675
|
await Bun.$`mkdir -p ${metaDir}`.quiet();
|
|
3368
|
-
const currentSnapshot =
|
|
3369
|
-
|
|
3676
|
+
const currentSnapshot = { tables: {}, enums: {}, views: {} };
|
|
3677
|
+
for (const s2 of schemas) {
|
|
3678
|
+
if (s2.type === "table") {
|
|
3679
|
+
currentSnapshot.tables[s2.config.name] = s2.config;
|
|
3680
|
+
} else if (s2.type === "enum") {
|
|
3681
|
+
currentSnapshot.enums[s2.enumName] = { enumName: s2.enumName, enumValues: s2.enumValues };
|
|
3682
|
+
} else if (s2.type === "view") {
|
|
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
|
+
};
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
let prevSnapshot = { tables: {}, enums: {}, views: {} };
|
|
3370
3691
|
let isFirstMigration = true;
|
|
3371
3692
|
try {
|
|
3372
3693
|
const files = readdirSync(metaDir).filter((f2) => f2.endsWith("_snapshot.json")).sort();
|
|
3373
3694
|
if (files.length > 0) {
|
|
3374
3695
|
const latest = files[files.length - 1];
|
|
3375
|
-
|
|
3696
|
+
const parsed = JSON.parse(await Bun.file(join3(metaDir, latest)).text());
|
|
3697
|
+
if (parsed.tables || parsed.enums || parsed.views) {
|
|
3698
|
+
prevSnapshot = {
|
|
3699
|
+
tables: parsed.tables || {},
|
|
3700
|
+
enums: parsed.enums || {},
|
|
3701
|
+
views: parsed.views || {}
|
|
3702
|
+
};
|
|
3703
|
+
} else {
|
|
3704
|
+
prevSnapshot = { tables: parsed, enums: {}, views: {} };
|
|
3705
|
+
}
|
|
3376
3706
|
isFirstMigration = false;
|
|
3377
3707
|
} else {
|
|
3378
3708
|
const legacyFile2 = Bun.file(join3(migrationsDir, ".snapshot.json"));
|
|
3379
3709
|
if (await legacyFile2.exists()) {
|
|
3380
|
-
|
|
3710
|
+
const parsed = JSON.parse(await legacyFile2.text());
|
|
3711
|
+
if (parsed.tables || parsed.enums || parsed.views) {
|
|
3712
|
+
prevSnapshot = {
|
|
3713
|
+
tables: parsed.tables || {},
|
|
3714
|
+
enums: parsed.enums || {},
|
|
3715
|
+
views: parsed.views || {}
|
|
3716
|
+
};
|
|
3717
|
+
} else {
|
|
3718
|
+
prevSnapshot = { tables: parsed, enums: {}, views: {} };
|
|
3719
|
+
}
|
|
3381
3720
|
isFirstMigration = false;
|
|
3382
3721
|
}
|
|
3383
3722
|
}
|
|
@@ -3397,17 +3736,30 @@ async function runGenerate(config, name) {
|
|
|
3397
3736
|
let summary;
|
|
3398
3737
|
let warnings = [];
|
|
3399
3738
|
if (isFirstMigration) {
|
|
3400
|
-
const
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
]
|
|
3406
|
-
|
|
3739
|
+
const tableSchemas = schemas.filter((s2) => s2.type === "table");
|
|
3740
|
+
const enumSchemas = schemas.filter((s2) => s2.type === "enum");
|
|
3741
|
+
const viewSchemas = schemas.filter((s2) => s2.type === "view");
|
|
3742
|
+
const sorted = topoSort(tableSchemas);
|
|
3743
|
+
upStatements = [];
|
|
3744
|
+
downStatements = [];
|
|
3745
|
+
summary = [];
|
|
3746
|
+
for (const e of enumSchemas) {
|
|
3747
|
+
upStatements.push(`-- ${e.exportName}`, `CREATE TYPE "${e.enumName}" AS ENUM (${e.enumValues.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ")});`, ``);
|
|
3748
|
+
downStatements.unshift(`DROP TYPE IF EXISTS "${e.enumName}";`);
|
|
3749
|
+
summary.push(`CREATE TYPE ${e.enumName}`);
|
|
3750
|
+
}
|
|
3751
|
+
for (const entry of sorted) {
|
|
3752
|
+
upStatements.push(`-- ${entry.exportName}`, generateCreateTable(entry.config, true), ``);
|
|
3407
3753
|
const tbl = entry.config.schema ? `"${entry.config.schema}"."${entry.config.name}"` : `"${entry.config.name}"`;
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3754
|
+
downStatements.unshift(`DROP TABLE IF EXISTS ${tbl};`);
|
|
3755
|
+
summary.push(`CREATE TABLE ${entry.config.name}`);
|
|
3756
|
+
}
|
|
3757
|
+
for (const v of viewSchemas) {
|
|
3758
|
+
upStatements.push(`-- ${v.exportName}`, generateCreateView(v.config), ``);
|
|
3759
|
+
const isMat = v.config.materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
3760
|
+
downStatements.unshift(`DROP ${isMat} IF EXISTS "${v.config.name}";`);
|
|
3761
|
+
summary.push(`CREATE ${isMat} ${v.config.name}`);
|
|
3762
|
+
}
|
|
3411
3763
|
} else {
|
|
3412
3764
|
const upDiff = diffSchemas(prevSnapshot, currentSnapshot);
|
|
3413
3765
|
if (upDiff.statements.length === 0) {
|
|
@@ -3497,9 +3849,9 @@ async function runMigrate(config) {
|
|
|
3497
3849
|
intro(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" @bungres/kit migrate ")));
|
|
3498
3850
|
const migrationsDir = resolve4(config.out);
|
|
3499
3851
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
3500
|
-
const
|
|
3852
|
+
const table = config.migrationsTable;
|
|
3501
3853
|
const schema = config.migrationsSchema;
|
|
3502
|
-
const qualifiedTable = `"${schema}"."${
|
|
3854
|
+
const qualifiedTable = `"${schema}"."${table}"`;
|
|
3503
3855
|
const createSchema = `CREATE SCHEMA IF NOT EXISTS "${schema}";`;
|
|
3504
3856
|
const createMigrationsTable = `
|
|
3505
3857
|
CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
@@ -3507,7 +3859,7 @@ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
|
3507
3859
|
name TEXT NOT NULL UNIQUE,
|
|
3508
3860
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
3509
3861
|
);`.trim();
|
|
3510
|
-
|
|
3862
|
+
let s = spinner();
|
|
3511
3863
|
s.start("Checking pending migrations...");
|
|
3512
3864
|
try {
|
|
3513
3865
|
await sql2.unsafe(createSchema);
|
|
@@ -3536,8 +3888,8 @@ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
|
3536
3888
|
}
|
|
3537
3889
|
s.stop(`Found ${pending.length} pending migration(s).`);
|
|
3538
3890
|
for (const file of pending) {
|
|
3539
|
-
|
|
3540
|
-
|
|
3891
|
+
s = spinner();
|
|
3892
|
+
s.start(`Applying ${file}...`);
|
|
3541
3893
|
const content = await Bun.file(join4(migrationsDir, file)).text();
|
|
3542
3894
|
let upContent = content;
|
|
3543
3895
|
const upMatch = content.match(/-- ==== UP ====([\s\S]*?)(?:-- ==== DOWN ====|$)/i);
|
|
@@ -3556,10 +3908,11 @@ ${upContent}
|
|
|
3556
3908
|
}
|
|
3557
3909
|
await txSql.unsafe(`INSERT INTO ${qualifiedTable} (name) VALUES ($1)`, [file]);
|
|
3558
3910
|
});
|
|
3559
|
-
|
|
3911
|
+
s.stop(import_picocolors3.default.green(`\u2713 Applied ${file}`));
|
|
3560
3912
|
}
|
|
3561
3913
|
outro(import_picocolors3.default.cyan("\u2728 All migrations applied successfully."));
|
|
3562
3914
|
} catch (err) {
|
|
3915
|
+
s.stop("Failed.");
|
|
3563
3916
|
log.error(import_picocolors3.default.red(`Migration failed: ${err.message}`));
|
|
3564
3917
|
outro("Failed.");
|
|
3565
3918
|
} finally {
|
|
@@ -3664,20 +4017,19 @@ function generateSchemaTS(tableMap, dbSchema) {
|
|
|
3664
4017
|
`// Generated at: ${new Date().toISOString()}`,
|
|
3665
4018
|
``,
|
|
3666
4019
|
`import {`,
|
|
3667
|
-
`
|
|
4020
|
+
` pgTable,`,
|
|
3668
4021
|
` text, varchar, char, integer, bigint, smallint,`,
|
|
3669
4022
|
` serial, bigserial, boolean, real, doublePrecision,`,
|
|
3670
4023
|
` numeric, decimal, json, jsonb,`,
|
|
3671
4024
|
` timestamp, timestamptz, date, time, uuid,`,
|
|
3672
4025
|
` bytea, inet,`,
|
|
3673
|
-
` textArray, integerArray, varcharArray, uuidArray,`,
|
|
3674
4026
|
`} from "@bungres/orm";`,
|
|
3675
4027
|
``
|
|
3676
4028
|
];
|
|
3677
|
-
for (const [,
|
|
3678
|
-
const varName = toCamelCase(
|
|
3679
|
-
lines.push(`export const ${varName} =
|
|
3680
|
-
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) {
|
|
3681
4033
|
const colExpr = buildColumnExpression(col2);
|
|
3682
4034
|
lines.push(` ${toCamelCase(col2.name)}: ${colExpr},`);
|
|
3683
4035
|
}
|
|
@@ -3685,9 +4037,9 @@ function generateSchemaTS(tableMap, dbSchema) {
|
|
|
3685
4037
|
if (dbSchema !== "public") {
|
|
3686
4038
|
options.push(`schema: "${dbSchema}"`);
|
|
3687
4039
|
}
|
|
3688
|
-
if (
|
|
4040
|
+
if (table.indexes.length > 0) {
|
|
3689
4041
|
const idxLines = [];
|
|
3690
|
-
for (const idx of
|
|
4042
|
+
for (const idx of table.indexes) {
|
|
3691
4043
|
const m2 = idx.indexdef.match(/CREATE (UNIQUE )?INDEX (.+) ON (.+) USING (\w+) \((.+)\)(?: WHERE (.+))?/i);
|
|
3692
4044
|
if (m2) {
|
|
3693
4045
|
const isUnique = !!m2[1];
|
|
@@ -3757,11 +4109,13 @@ function buildColumnExpression(col2) {
|
|
|
3757
4109
|
refOpts += `, onUpdate: "${updateRule}"`;
|
|
3758
4110
|
opts.push(`references: { ${refOpts} }`);
|
|
3759
4111
|
}
|
|
3760
|
-
let
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
}
|
|
3764
|
-
|
|
4112
|
+
let builderResult = pgTypeToBungresBuilderName(col2);
|
|
4113
|
+
let builderName = typeof builderResult === "string" ? builderResult : builderResult.base;
|
|
4114
|
+
let isArray = typeof builderResult !== "string" && builderResult.isArray;
|
|
4115
|
+
let expr = opts.length > 0 ? `${builderName}({ ${opts.join(", ")} })` : `${builderName}()`;
|
|
4116
|
+
if (isArray)
|
|
4117
|
+
expr += `.array()`;
|
|
4118
|
+
return expr;
|
|
3765
4119
|
}
|
|
3766
4120
|
function pgTypeToBungresBuilderName(col2) {
|
|
3767
4121
|
const dt = col2.dataType;
|
|
@@ -3807,14 +4161,14 @@ function pgTypeToBungresBuilderName(col2) {
|
|
|
3807
4161
|
return "text";
|
|
3808
4162
|
if (dt === "ARRAY") {
|
|
3809
4163
|
if (col2.udtName === "_text")
|
|
3810
|
-
return "
|
|
4164
|
+
return { base: "text", isArray: true };
|
|
3811
4165
|
if (col2.udtName === "_int4" || col2.udtName === "_int8")
|
|
3812
|
-
return "
|
|
4166
|
+
return { base: "integer", isArray: true };
|
|
3813
4167
|
if (col2.udtName === "_varchar")
|
|
3814
|
-
return "
|
|
4168
|
+
return { base: "varchar", isArray: true };
|
|
3815
4169
|
if (col2.udtName === "_uuid")
|
|
3816
|
-
return "
|
|
3817
|
-
return "
|
|
4170
|
+
return { base: "uuid", isArray: true };
|
|
4171
|
+
return { base: "text", isArray: true };
|
|
3818
4172
|
}
|
|
3819
4173
|
return "text";
|
|
3820
4174
|
}
|
|
@@ -3828,16 +4182,16 @@ async function runStatus(config) {
|
|
|
3828
4182
|
intro(import_picocolors4.default.bgCyan(import_picocolors4.default.black(" @bungres/kit status ")));
|
|
3829
4183
|
const migrationsDir = resolve6(config.out);
|
|
3830
4184
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
3831
|
-
const
|
|
4185
|
+
const table = config.migrationsTable;
|
|
3832
4186
|
const schema = config.migrationsSchema;
|
|
3833
|
-
const qualifiedTable = `"${schema}"."${
|
|
4187
|
+
const qualifiedTable = `"${schema}"."${table}"`;
|
|
3834
4188
|
const s = spinner();
|
|
3835
4189
|
s.start("Checking migration status...");
|
|
3836
4190
|
try {
|
|
3837
4191
|
const tableCheck = await sql2.unsafe(`SELECT EXISTS (
|
|
3838
4192
|
SELECT 1 FROM information_schema.tables
|
|
3839
4193
|
WHERE table_schema = $1 AND table_name = $2
|
|
3840
|
-
) AS exists`, [schema,
|
|
4194
|
+
) AS exists`, [schema, table]);
|
|
3841
4195
|
const trackingExists = tableCheck[0]?.exists ?? false;
|
|
3842
4196
|
const glob = new Bun.Glob("*.sql");
|
|
3843
4197
|
const files = [];
|
|
@@ -3872,6 +4226,7 @@ async function runStatus(config) {
|
|
|
3872
4226
|
log.info(`${import_picocolors4.default.green(appliedSet.size.toString())} applied, ${import_picocolors4.default.yellow(pendingCount.toString())} pending.`);
|
|
3873
4227
|
outro("Done.");
|
|
3874
4228
|
} catch (err) {
|
|
4229
|
+
s.stop("Failed.");
|
|
3875
4230
|
log.error(import_picocolors4.default.red(`Status check failed: ${err.message}`));
|
|
3876
4231
|
outro("Failed.");
|
|
3877
4232
|
} finally {
|
|
@@ -3882,23 +4237,14 @@ async function runStatus(config) {
|
|
|
3882
4237
|
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
3883
4238
|
async function runDrop(config, opts = {}) {
|
|
3884
4239
|
intro(import_picocolors5.default.bgCyan(import_picocolors5.default.black(" @bungres/kit drop ")));
|
|
3885
|
-
const s = spinner();
|
|
3886
|
-
s.start("Loading schemas...");
|
|
3887
|
-
const schemas = await loadSchemas(config.schema);
|
|
3888
|
-
if (schemas.length === 0) {
|
|
3889
|
-
s.stop("No schemas found.");
|
|
3890
|
-
log.warn(import_picocolors5.default.yellow("No table definitions found in schema files."));
|
|
3891
|
-
outro("Failed.");
|
|
3892
|
-
return;
|
|
3893
|
-
}
|
|
3894
|
-
s.stop(`Loaded ${schemas.length} schemas.`);
|
|
3895
4240
|
const ms = spinner();
|
|
3896
|
-
ms.start("
|
|
4241
|
+
ms.start("Scanning database for objects to drop...");
|
|
3897
4242
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
3898
4243
|
try {
|
|
3899
|
-
const userSchema = config.dbSchema;
|
|
3900
|
-
const
|
|
3901
|
-
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]);
|
|
3902
4248
|
const migTableCheck = await sql2.unsafe(`SELECT EXISTS (
|
|
3903
4249
|
SELECT 1 FROM information_schema.tables
|
|
3904
4250
|
WHERE table_schema = $1 AND table_name = $2
|
|
@@ -3909,29 +4255,20 @@ async function runDrop(config, opts = {}) {
|
|
|
3909
4255
|
WHERE table_schema = $1 AND table_name = $2
|
|
3910
4256
|
) AS exists`, [config.migrationsSchema, "__bungres_push"]);
|
|
3911
4257
|
const pushTableExists = pushTableCheck[0]?.exists ?? false;
|
|
3912
|
-
const
|
|
3913
|
-
if (
|
|
4258
|
+
const totalObjects = existingViews.length + existingTables.length + existingTypes.length + (migrationTableExists ? 1 : 0) + (pushTableExists ? 1 : 0);
|
|
4259
|
+
if (totalObjects === 0) {
|
|
3914
4260
|
ms.stop("Nothing to do.");
|
|
3915
|
-
log.success(import_picocolors5.default.green("No
|
|
4261
|
+
log.success(import_picocolors5.default.green("No items to drop (schema is empty)."));
|
|
3916
4262
|
outro("Done.");
|
|
3917
4263
|
return;
|
|
3918
4264
|
}
|
|
3919
|
-
ms.stop("Database
|
|
3920
|
-
const tableNamesToPrint = tablesToDrop.map((sch) => (sch.config.schema ? sch.config.schema + "." : "") + sch.config.name);
|
|
3921
|
-
if (migrationTableExists) {
|
|
3922
|
-
tableNamesToPrint.push(`${config.migrationsSchema}.${config.migrationsTable}`);
|
|
3923
|
-
}
|
|
3924
|
-
if (pushTableExists) {
|
|
3925
|
-
tableNamesToPrint.push(`${config.migrationsSchema}.__bungres_push`);
|
|
3926
|
-
}
|
|
4265
|
+
ms.stop("Database scan complete.");
|
|
3927
4266
|
if (!opts.force) {
|
|
3928
4267
|
log.warn(import_picocolors5.default.bgRed(import_picocolors5.default.white(" \u26A0\uFE0F WARNING ")));
|
|
3929
|
-
log.message(import_picocolors5.default.bold(import_picocolors5.default.red("This will drop
|
|
3930
|
-
|
|
3931
|
-
log.info(import_picocolors5.default.yellow(` - ${name}`));
|
|
3932
|
-
}
|
|
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.`));
|
|
3933
4270
|
const confirm2 = await confirm({
|
|
3934
|
-
message: "Are you sure you want to
|
|
4271
|
+
message: "Are you absolutely sure you want to drop ALL data?",
|
|
3935
4272
|
initialValue: false
|
|
3936
4273
|
});
|
|
3937
4274
|
if (isCancel(confirm2) || !confirm2) {
|
|
@@ -3940,24 +4277,36 @@ async function runDrop(config, opts = {}) {
|
|
|
3940
4277
|
}
|
|
3941
4278
|
}
|
|
3942
4279
|
const exSpinner = spinner();
|
|
3943
|
-
exSpinner.start("Dropping
|
|
3944
|
-
for (const
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
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}`));
|
|
3948
4295
|
}
|
|
3949
4296
|
if (migrationTableExists) {
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
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}`));
|
|
3953
4300
|
}
|
|
3954
4301
|
if (pushTableExists) {
|
|
3955
4302
|
await sql2.unsafe(`DROP TABLE IF EXISTS "${config.migrationsSchema}"."__bungres_push" CASCADE`);
|
|
3956
|
-
|
|
4303
|
+
if (opts.force !== true)
|
|
4304
|
+
log.step(import_picocolors5.default.gray(`Dropped ${config.migrationsSchema}.__bungres_push`));
|
|
3957
4305
|
}
|
|
3958
|
-
exSpinner.stop("
|
|
4306
|
+
exSpinner.stop("Items dropped.");
|
|
3959
4307
|
outro(import_picocolors5.default.cyan("\u2728 Drop complete."));
|
|
3960
4308
|
} catch (err) {
|
|
4309
|
+
ms.stop("Scan failed.");
|
|
3961
4310
|
log.error(import_picocolors5.default.red(`Drop failed: ${err.message}`));
|
|
3962
4311
|
outro("Failed.");
|
|
3963
4312
|
} finally {
|