@bungres/kit 1.1.1 → 1.1.2
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 +17 -16
- package/dist/cli.js +199 -56
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/migrate.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/status.d.ts.map +1 -1
- package/dist/commands/tusky.d.ts.map +1 -1
- package/dist/index.js +134 -43
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ CLI toolkit for [`@bungres/orm`](https://www.npmjs.com/package/@bungres/orm) —
|
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
- **Bun ≥ 1.3** (uses `Bun.
|
|
7
|
+
- **Bun ≥ 1.3** (uses `Bun.SQL` which is built-in)
|
|
8
8
|
- **Postgres ≥ 16**
|
|
9
9
|
- Built to work alongside `@bungres/orm`.
|
|
10
10
|
|
|
@@ -38,21 +38,21 @@ Run the CLI using Bun:
|
|
|
38
38
|
bun run bungres --help
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
| Command
|
|
42
|
-
|
|
43
|
-
| `bungres init`
|
|
44
|
-
| `bungres generate` | Write a single timestamped `.sql` migration file with UP/DOWN sections
|
|
45
|
-
| `bungres migrate`
|
|
46
|
-
| `bungres rollback` | Automatically revert the last applied migration using its DOWN section
|
|
47
|
-
| `bungres push`
|
|
48
|
-
| `bungres pull`
|
|
49
|
-
| `bungres status`
|
|
50
|
-
| `bungres fresh`
|
|
51
|
-
| `bungres refresh`
|
|
52
|
-
| `bungres seed`
|
|
53
|
-
| `bungres studio`
|
|
54
|
-
| `bungres tusky`
|
|
55
|
-
| `bungres drop`
|
|
41
|
+
| Command | Description |
|
|
42
|
+
| ------------------ | ---------------------------------------------------------------------------------- |
|
|
43
|
+
| `bungres init` | Initialize bungres project with config file and db folder structure |
|
|
44
|
+
| `bungres generate` | Write a single timestamped `.sql` migration file with UP/DOWN sections |
|
|
45
|
+
| `bungres migrate` | Run pending `.sql` UP sections, track applied in `__bungres_migrations` |
|
|
46
|
+
| `bungres rollback` | Automatically revert the last applied migration using its DOWN section |
|
|
47
|
+
| `bungres push` | Apply schema directly to DB — no files (great for dev/prototyping) |
|
|
48
|
+
| `bungres pull` | Introspect the DB and generate TypeScript schema |
|
|
49
|
+
| `bungres status` | Show applied vs pending migrations |
|
|
50
|
+
| `bungres fresh` | Drop all tables and re-run all migrations from scratch |
|
|
51
|
+
| `bungres refresh` | Truncate all tables to quickly reset data without dropping schema |
|
|
52
|
+
| `bungres seed` | Execute the seed script, or run the Auto-Seeder using `@faker-js/faker` |
|
|
53
|
+
| `bungres studio` | Start a local web interface to browse database data |
|
|
54
|
+
| `bungres tusky` | Boot up a Node REPL connected to the database with schema loaded |
|
|
55
|
+
| `bungres drop` | Drop all tables, enums, and views defined in the schema (prompts for confirmation) |
|
|
56
56
|
|
|
57
57
|
### Usage Examples
|
|
58
58
|
|
|
@@ -82,4 +82,5 @@ bun run bungres drop --force # skip confirmation
|
|
|
82
82
|
- **Auto-Seeder**: Run `bungres seed` without a custom script to automatically generate mock data for your database using `@faker-js/faker`, resolving foreign key dependencies automatically!
|
|
83
83
|
|
|
84
84
|
## License
|
|
85
|
+
|
|
85
86
|
MIT
|
package/dist/cli.js
CHANGED
|
@@ -1668,10 +1668,26 @@ async function runDrop(config2, opts = {}) {
|
|
|
1668
1668
|
|
|
1669
1669
|
// src/commands/migrate.ts
|
|
1670
1670
|
import { join, resolve } from "path";
|
|
1671
|
+
import { existsSync, statSync } from "fs";
|
|
1671
1672
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
1672
1673
|
async function runMigrate(config2) {
|
|
1673
1674
|
intro(import_picocolors2.default.bgCyan(import_picocolors2.default.black(" @bungres/kit migrate ")));
|
|
1674
1675
|
const migrationsDir = resolve(config2.out);
|
|
1676
|
+
let s = spinner();
|
|
1677
|
+
s.start("Checking pending migrations...");
|
|
1678
|
+
if (!existsSync(migrationsDir)) {
|
|
1679
|
+
s.stop("No files found.");
|
|
1680
|
+
log.warn(import_picocolors2.default.yellow(`Migration directory does not exist: ${migrationsDir}`));
|
|
1681
|
+
log.info(`Run ${import_picocolors2.default.green("bungres generate")} first.`);
|
|
1682
|
+
outro("Done.");
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
if (!statSync(migrationsDir).isDirectory()) {
|
|
1686
|
+
s.stop("Failed.");
|
|
1687
|
+
log.error(import_picocolors2.default.red(`Migration path exists but is not a directory: ${migrationsDir}`));
|
|
1688
|
+
outro("Failed.");
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1675
1691
|
const sql = new Bun.SQL(config2.dbUrl);
|
|
1676
1692
|
const table = config2.migrationsTable;
|
|
1677
1693
|
const schema = config2.migrationsSchema;
|
|
@@ -1683,8 +1699,6 @@ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
|
1683
1699
|
name TEXT NOT NULL UNIQUE,
|
|
1684
1700
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
1685
1701
|
);`.trim();
|
|
1686
|
-
let s = spinner();
|
|
1687
|
-
s.start("Checking pending migrations...");
|
|
1688
1702
|
try {
|
|
1689
1703
|
await sql.unsafe(createSchema);
|
|
1690
1704
|
await sql.unsafe(createMigrationsTable);
|
|
@@ -1714,7 +1728,22 @@ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
|
1714
1728
|
for (const file of pending) {
|
|
1715
1729
|
s = spinner();
|
|
1716
1730
|
s.start(`Applying ${file}...`);
|
|
1717
|
-
const
|
|
1731
|
+
const filePath = join(migrationsDir, file);
|
|
1732
|
+
if (!existsSync(filePath)) {
|
|
1733
|
+
s.stop("Failed.");
|
|
1734
|
+
log.error(import_picocolors2.default.red(`Migration file not found: ${filePath}`));
|
|
1735
|
+
outro("Failed.");
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
let content = "";
|
|
1739
|
+
try {
|
|
1740
|
+
content = await Bun.file(filePath).text();
|
|
1741
|
+
} catch (err) {
|
|
1742
|
+
s.stop("Failed.");
|
|
1743
|
+
log.error(import_picocolors2.default.red(`Failed to read migration file ${file}: ${err.message}`));
|
|
1744
|
+
outro("Failed.");
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1718
1747
|
let upContent = content;
|
|
1719
1748
|
const upMatch = content.match(/-- ==== UP ====([\s\S]*?)(?:-- ==== DOWN ====|$)/i);
|
|
1720
1749
|
if (upMatch) {
|
|
@@ -1757,7 +1786,7 @@ Fresh complete.`);
|
|
|
1757
1786
|
|
|
1758
1787
|
// src/commands/generate.ts
|
|
1759
1788
|
import { join as join3, resolve as resolve3 } from "path";
|
|
1760
|
-
import { readdirSync } from "fs";
|
|
1789
|
+
import { readdirSync, existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
1761
1790
|
|
|
1762
1791
|
// ../bungres-orm/dist/index.js
|
|
1763
1792
|
var TableConfigSymbol = Symbol.for("BungresTableConfig");
|
|
@@ -1813,7 +1842,7 @@ function createTableFactory(casing) {
|
|
|
1813
1842
|
const tableObj = {
|
|
1814
1843
|
[TableConfigSymbol]: {
|
|
1815
1844
|
name,
|
|
1816
|
-
schema,
|
|
1845
|
+
...schema ? { schema } : {},
|
|
1817
1846
|
columns: columnConfigs,
|
|
1818
1847
|
indexes,
|
|
1819
1848
|
checks,
|
|
@@ -1893,6 +1922,41 @@ var textArray = col("text[]");
|
|
|
1893
1922
|
var integerArray = col("integer[]");
|
|
1894
1923
|
var varcharArray = col("varchar[]");
|
|
1895
1924
|
var uuidArray = col("uuid[]");
|
|
1925
|
+
function shiftParams(sql, paramsLength, offset) {
|
|
1926
|
+
if (paramsLength === 0)
|
|
1927
|
+
return sql;
|
|
1928
|
+
let result2 = "";
|
|
1929
|
+
let inString = false;
|
|
1930
|
+
let inIdent = false;
|
|
1931
|
+
let i2 = 0;
|
|
1932
|
+
while (i2 < sql.length) {
|
|
1933
|
+
if (sql[i2] === "'" && !inIdent) {
|
|
1934
|
+
inString = !inString;
|
|
1935
|
+
result2 += sql[i2];
|
|
1936
|
+
i2++;
|
|
1937
|
+
} else if (sql[i2] === '"' && !inString) {
|
|
1938
|
+
inIdent = !inIdent;
|
|
1939
|
+
result2 += sql[i2];
|
|
1940
|
+
i2++;
|
|
1941
|
+
} else if (!inString && !inIdent && sql[i2] === "$") {
|
|
1942
|
+
const match = sql.slice(i2).match(/^\$(\d+)/);
|
|
1943
|
+
if (match) {
|
|
1944
|
+
const num = parseInt(match[1], 10);
|
|
1945
|
+
if (num >= 1 && num <= paramsLength) {
|
|
1946
|
+
result2 += `$${num + offset}`;
|
|
1947
|
+
i2 += match[0].length;
|
|
1948
|
+
continue;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
result2 += sql[i2];
|
|
1952
|
+
i2++;
|
|
1953
|
+
} else {
|
|
1954
|
+
result2 += sql[i2];
|
|
1955
|
+
i2++;
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
return result2;
|
|
1959
|
+
}
|
|
1896
1960
|
function sql(strings, ...values) {
|
|
1897
1961
|
let query = "";
|
|
1898
1962
|
const params = [];
|
|
@@ -1902,7 +1966,7 @@ function sql(strings, ...values) {
|
|
|
1902
1966
|
const val = values[i2];
|
|
1903
1967
|
if (isSQLChunk(val)) {
|
|
1904
1968
|
const offset = params.length;
|
|
1905
|
-
query += val.sql
|
|
1969
|
+
query += shiftParams(val.sql, val.params.length, offset);
|
|
1906
1970
|
params.push(...val.params);
|
|
1907
1971
|
} else {
|
|
1908
1972
|
params.push(val);
|
|
@@ -1920,7 +1984,7 @@ function sqlJoin(chunks, separator = ", ") {
|
|
|
1920
1984
|
const parts = [];
|
|
1921
1985
|
for (const chunk of chunks) {
|
|
1922
1986
|
const offset = params.length;
|
|
1923
|
-
parts.push(chunk.sql
|
|
1987
|
+
parts.push(shiftParams(chunk.sql, chunk.params.length, offset));
|
|
1924
1988
|
params.push(...chunk.params);
|
|
1925
1989
|
}
|
|
1926
1990
|
return { sql: parts.join(separator), params };
|
|
@@ -1928,6 +1992,19 @@ function sqlJoin(chunks, separator = ", ") {
|
|
|
1928
1992
|
function rawSql(query) {
|
|
1929
1993
|
return { sql: query, params: [] };
|
|
1930
1994
|
}
|
|
1995
|
+
function toPgArray(val) {
|
|
1996
|
+
const formatted = val.map((item) => {
|
|
1997
|
+
if (item === null || item === undefined)
|
|
1998
|
+
return "NULL";
|
|
1999
|
+
if (typeof item === "number" || typeof item === "boolean")
|
|
2000
|
+
return String(item);
|
|
2001
|
+
if (Array.isArray(item))
|
|
2002
|
+
return toPgArray(item);
|
|
2003
|
+
const str = String(item).replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
2004
|
+
return `"${str}"`;
|
|
2005
|
+
});
|
|
2006
|
+
return `{${formatted.join(",")}}`;
|
|
2007
|
+
}
|
|
1931
2008
|
function colName(c2) {
|
|
1932
2009
|
if (typeof c2 === "string")
|
|
1933
2010
|
return `"${c2}"`;
|
|
@@ -2018,29 +2095,29 @@ var not = (condition) => ({
|
|
|
2018
2095
|
});
|
|
2019
2096
|
var asc = (column) => sql`${rawSql(colName(column))} ASC`;
|
|
2020
2097
|
var desc = (column) => sql`${rawSql(colName(column))} DESC`;
|
|
2021
|
-
function parseWhereObject(tableConfig, whereObj) {
|
|
2098
|
+
function parseWhereObject(tableConfig, whereObj, alias2) {
|
|
2022
2099
|
const conditions = [];
|
|
2023
2100
|
for (const [key, val] of Object.entries(whereObj)) {
|
|
2024
2101
|
if (val === undefined)
|
|
2025
2102
|
continue;
|
|
2026
2103
|
if (key === "OR") {
|
|
2027
|
-
const orConditions = val.map((o2) => parseWhereObject(tableConfig, o2));
|
|
2104
|
+
const orConditions = val.map((o2) => parseWhereObject(tableConfig, o2, alias2));
|
|
2028
2105
|
if (orConditions.length > 0)
|
|
2029
2106
|
conditions.push(or(...orConditions));
|
|
2030
2107
|
continue;
|
|
2031
2108
|
}
|
|
2032
2109
|
if (key === "AND") {
|
|
2033
|
-
const andConditions = val.map((o2) => parseWhereObject(tableConfig, o2));
|
|
2110
|
+
const andConditions = val.map((o2) => parseWhereObject(tableConfig, o2, alias2));
|
|
2034
2111
|
if (andConditions.length > 0)
|
|
2035
2112
|
conditions.push(and(...andConditions));
|
|
2036
2113
|
continue;
|
|
2037
2114
|
}
|
|
2038
2115
|
if (key === "NOT") {
|
|
2039
|
-
conditions.push(not(parseWhereObject(tableConfig, val)));
|
|
2116
|
+
conditions.push(not(parseWhereObject(tableConfig, val, alias2)));
|
|
2040
2117
|
continue;
|
|
2041
2118
|
}
|
|
2042
2119
|
const colConfig = tableConfig.columns[key];
|
|
2043
|
-
const columnArg = colConfig
|
|
2120
|
+
const columnArg = colConfig ? alias2 ? { ...colConfig, tableName: alias2 } : colConfig : alias2 ? { name: key, tableName: alias2 } : key;
|
|
2044
2121
|
if (val !== null && typeof val === "object" && !Array.isArray(val) && !(val instanceof Date) && !(val instanceof Uint8Array)) {
|
|
2045
2122
|
const opVal = val;
|
|
2046
2123
|
if (opVal.eq !== undefined)
|
|
@@ -2082,13 +2159,13 @@ function parseWhereObject(tableConfig, whereObj) {
|
|
|
2082
2159
|
}
|
|
2083
2160
|
return and(...conditions);
|
|
2084
2161
|
}
|
|
2085
|
-
function parseOrderByObject(tableConfig, orderByObj) {
|
|
2162
|
+
function parseOrderByObject(tableConfig, orderByObj, alias2) {
|
|
2086
2163
|
const parts = [];
|
|
2087
2164
|
for (const [key, dir] of Object.entries(orderByObj)) {
|
|
2088
2165
|
if (dir === undefined)
|
|
2089
2166
|
continue;
|
|
2090
2167
|
const colConfig = tableConfig.columns[key];
|
|
2091
|
-
const columnArg = colConfig
|
|
2168
|
+
const columnArg = colConfig ? alias2 ? { ...colConfig, tableName: alias2 } : colConfig : alias2 ? { name: key, tableName: alias2 } : key;
|
|
2092
2169
|
if (dir === "asc")
|
|
2093
2170
|
parts.push(asc(columnArg));
|
|
2094
2171
|
else if (dir === "desc")
|
|
@@ -2256,14 +2333,7 @@ class InsertBuilder {
|
|
|
2256
2333
|
if (colType === "json" || colType === "jsonb") {
|
|
2257
2334
|
params.push(val);
|
|
2258
2335
|
} else if (Array.isArray(val)) {
|
|
2259
|
-
|
|
2260
|
-
if (item === null || item === undefined)
|
|
2261
|
-
return "NULL";
|
|
2262
|
-
if (typeof item === "string")
|
|
2263
|
-
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
2264
|
-
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"") + '"' : String(item);
|
|
2265
|
-
}).join(",") + "}";
|
|
2266
|
-
params.push(pgArray);
|
|
2336
|
+
params.push(toPgArray(val));
|
|
2267
2337
|
} else {
|
|
2268
2338
|
params.push(JSON.stringify(val));
|
|
2269
2339
|
}
|
|
@@ -2314,14 +2384,7 @@ class InsertBuilder {
|
|
|
2314
2384
|
if (colType === "json" || colType === "jsonb") {
|
|
2315
2385
|
params.push(value);
|
|
2316
2386
|
} else if (Array.isArray(value)) {
|
|
2317
|
-
|
|
2318
|
-
if (item === null || item === undefined)
|
|
2319
|
-
return "NULL";
|
|
2320
|
-
if (typeof item === "string")
|
|
2321
|
-
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
2322
|
-
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"") + '"' : String(item);
|
|
2323
|
-
}).join(",") + "}";
|
|
2324
|
-
params.push(pgArray);
|
|
2387
|
+
params.push(toPgArray(value));
|
|
2325
2388
|
} else {
|
|
2326
2389
|
params.push(JSON.stringify(value));
|
|
2327
2390
|
}
|
|
@@ -2745,14 +2808,7 @@ class UpdateBuilder {
|
|
|
2745
2808
|
if (colType === "json" || colType === "jsonb") {
|
|
2746
2809
|
params.push(value);
|
|
2747
2810
|
} else if (Array.isArray(value)) {
|
|
2748
|
-
|
|
2749
|
-
if (item === null || item === undefined)
|
|
2750
|
-
return "NULL";
|
|
2751
|
-
if (typeof item === "string")
|
|
2752
|
-
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
2753
|
-
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"") + '"' : String(item);
|
|
2754
|
-
}).join(",") + "}";
|
|
2755
|
-
params.push(pgArray);
|
|
2811
|
+
params.push(toPgArray(value));
|
|
2756
2812
|
} else {
|
|
2757
2813
|
params.push(JSON.stringify(value));
|
|
2758
2814
|
}
|
|
@@ -2995,7 +3051,7 @@ class RelationalQueryBuilder {
|
|
|
2995
3051
|
const offset = params.length;
|
|
2996
3052
|
let whereChunk = args.where;
|
|
2997
3053
|
if (args.where && !args.where.sql) {
|
|
2998
|
-
whereChunk = parseWhereObject(tableConfig, args.where);
|
|
3054
|
+
whereChunk = parseWhereObject(tableConfig, args.where, alias2);
|
|
2999
3055
|
}
|
|
3000
3056
|
if (whereChunk && whereChunk.sql) {
|
|
3001
3057
|
fromSql += (joinCondition ? " AND " : " WHERE ") + whereChunk.sql.replace(/\$(\d+)/g, (_2, n2) => `$${parseInt(n2) + offset}`);
|
|
@@ -3010,7 +3066,7 @@ class RelationalQueryBuilder {
|
|
|
3010
3066
|
fromSql += ` ORDER BY ` + args.orderBy.sql.replace(/\$(\d+)/g, (_2, n2) => `$${parseInt(n2) + offset}`);
|
|
3011
3067
|
params.push(...args.orderBy.params);
|
|
3012
3068
|
} else {
|
|
3013
|
-
const chunks = parseOrderByObject(tableConfig, args.orderBy);
|
|
3069
|
+
const chunks = parseOrderByObject(tableConfig, args.orderBy, alias2);
|
|
3014
3070
|
if (chunks.length > 0) {
|
|
3015
3071
|
fromSql += ` ORDER BY ` + chunks.map((c2) => {
|
|
3016
3072
|
const offset = params.length;
|
|
@@ -3325,6 +3381,21 @@ function formatDefaultValue(value, dataType) {
|
|
|
3325
3381
|
return value ? "TRUE" : "FALSE";
|
|
3326
3382
|
if (typeof value === "number")
|
|
3327
3383
|
return String(value);
|
|
3384
|
+
if (value instanceof Date)
|
|
3385
|
+
return `'${value.toISOString()}'`;
|
|
3386
|
+
if (Array.isArray(value)) {
|
|
3387
|
+
if (dataType === "json" || dataType === "jsonb") {
|
|
3388
|
+
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
|
3389
|
+
}
|
|
3390
|
+
const pgArray = "{" + value.map((item) => {
|
|
3391
|
+
if (item === null || item === undefined)
|
|
3392
|
+
return "NULL";
|
|
3393
|
+
if (typeof item === "string")
|
|
3394
|
+
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
3395
|
+
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"").replace(/'/g, "''") + '"' : String(item);
|
|
3396
|
+
}).join(",") + "}";
|
|
3397
|
+
return `'${pgArray.replace(/'/g, "''")}'`;
|
|
3398
|
+
}
|
|
3328
3399
|
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
|
3329
3400
|
}
|
|
3330
3401
|
function buildIndex(table, idx) {
|
|
@@ -3688,6 +3759,12 @@ async function runGenerate(config2, name) {
|
|
|
3688
3759
|
}
|
|
3689
3760
|
s.stop(`Loaded ${schemas2.length} schemas.`);
|
|
3690
3761
|
const migrationsDir = resolve3(config2.out);
|
|
3762
|
+
if (existsSync2(migrationsDir) && !statSync2(migrationsDir).isDirectory()) {
|
|
3763
|
+
s.stop("Failed.");
|
|
3764
|
+
log.error(import_picocolors3.default.red(`Migration path exists but is not a directory: ${migrationsDir}`));
|
|
3765
|
+
outro("Failed.");
|
|
3766
|
+
return;
|
|
3767
|
+
}
|
|
3691
3768
|
const metaDir = join3(migrationsDir, "meta");
|
|
3692
3769
|
await Bun.$`mkdir -p ${migrationsDir}`.quiet();
|
|
3693
3770
|
await Bun.$`mkdir -p ${metaDir}`.quiet();
|
|
@@ -3863,21 +3940,21 @@ function topoSort(schemas2) {
|
|
|
3863
3940
|
|
|
3864
3941
|
// src/commands/init.ts
|
|
3865
3942
|
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
3866
|
-
import { existsSync } from "fs";
|
|
3943
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3867
3944
|
import { mkdir } from "fs/promises";
|
|
3868
3945
|
import { join as join4 } from "path";
|
|
3869
3946
|
async function runInit(cwd = process.cwd()) {
|
|
3870
3947
|
intro(import_picocolors4.default.bgCyan(import_picocolors4.default.black(" @bungres/kit init ")));
|
|
3871
3948
|
const configPath = join4(cwd, "bungres.config.ts");
|
|
3872
|
-
if (
|
|
3949
|
+
if (existsSync3(configPath)) {
|
|
3873
3950
|
log.warn(import_picocolors4.default.yellow("Config file already exists at bungres.config.ts"));
|
|
3874
3951
|
outro("Failed.");
|
|
3875
3952
|
return;
|
|
3876
3953
|
}
|
|
3877
3954
|
let shouldPrompt = true;
|
|
3878
3955
|
const defaultDbDir = "src/db";
|
|
3879
|
-
if (
|
|
3880
|
-
if (!
|
|
3956
|
+
if (existsSync3(join4(cwd, "src"))) {
|
|
3957
|
+
if (!existsSync3(join4(cwd, defaultDbDir, "schema.ts")) && !existsSync3(join4(cwd, defaultDbDir, "client.ts"))) {
|
|
3881
3958
|
shouldPrompt = false;
|
|
3882
3959
|
}
|
|
3883
3960
|
}
|
|
@@ -4339,16 +4416,29 @@ async function runPush(config2, opts = {}) {
|
|
|
4339
4416
|
|
|
4340
4417
|
// src/commands/refresh.ts
|
|
4341
4418
|
import { join as join6, resolve as resolve5 } from "path";
|
|
4419
|
+
import { existsSync as existsSync4, statSync as statSync3 } from "fs";
|
|
4342
4420
|
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
4343
4421
|
async function runRefresh(config2) {
|
|
4344
4422
|
intro(import_picocolors6.default.bgCyan(import_picocolors6.default.black(" @bungres/kit refresh ")));
|
|
4345
4423
|
const migrationsDir = resolve5(config2.out);
|
|
4424
|
+
let s = spinner();
|
|
4425
|
+
s.start("Checking applied migrations...");
|
|
4426
|
+
if (!existsSync4(migrationsDir)) {
|
|
4427
|
+
s.stop("No migration directory found.");
|
|
4428
|
+
log.warn(import_picocolors6.default.yellow(`Migration directory does not exist: ${migrationsDir}`));
|
|
4429
|
+
outro("Done.");
|
|
4430
|
+
return;
|
|
4431
|
+
}
|
|
4432
|
+
if (!statSync3(migrationsDir).isDirectory()) {
|
|
4433
|
+
s.stop("Failed.");
|
|
4434
|
+
log.error(import_picocolors6.default.red(`Migration path exists but is not a directory: ${migrationsDir}`));
|
|
4435
|
+
outro("Failed.");
|
|
4436
|
+
return;
|
|
4437
|
+
}
|
|
4346
4438
|
const sql2 = new Bun.SQL(config2.dbUrl);
|
|
4347
4439
|
const table = config2.migrationsTable;
|
|
4348
4440
|
const schema = config2.migrationsSchema;
|
|
4349
4441
|
const qualifiedTable = `"${schema}"."${table}"`;
|
|
4350
|
-
let s = spinner();
|
|
4351
|
-
s.start("Checking applied migrations...");
|
|
4352
4442
|
try {
|
|
4353
4443
|
const migTableCheck = await sql2.unsafe(`SELECT EXISTS (
|
|
4354
4444
|
SELECT 1 FROM information_schema.tables
|
|
@@ -4365,6 +4455,17 @@ async function runRefresh(config2) {
|
|
|
4365
4455
|
s.stop("No migrations to rollback.");
|
|
4366
4456
|
log.info(import_picocolors6.default.yellow("Database is empty."));
|
|
4367
4457
|
} else {
|
|
4458
|
+
const missingFiles = applied.filter((row) => !existsSync4(join6(migrationsDir, row.name)));
|
|
4459
|
+
if (missingFiles.length > 0) {
|
|
4460
|
+
s.stop("Migration files missing.");
|
|
4461
|
+
log.error(import_picocolors6.default.red(`The database tracks ${applied.length} migration(s), but ${missingFiles.length} file(s) are missing from ${migrationsDir}:`));
|
|
4462
|
+
for (const f2 of missingFiles) {
|
|
4463
|
+
log.error(import_picocolors6.default.red(` - ${f2.name}`));
|
|
4464
|
+
}
|
|
4465
|
+
log.info("Cannot refresh without local migration files.");
|
|
4466
|
+
outro("Failed.");
|
|
4467
|
+
return;
|
|
4468
|
+
}
|
|
4368
4469
|
s.stop(`Found ${applied.length} migrations to rollback.`);
|
|
4369
4470
|
const shouldRollback = await confirm({
|
|
4370
4471
|
message: `Are you sure you want to rollback all ${applied.length} migrations and re-apply them?`,
|
|
@@ -4423,16 +4524,29 @@ Re-running all migrations...`));
|
|
|
4423
4524
|
|
|
4424
4525
|
// src/commands/rollback.ts
|
|
4425
4526
|
import { join as join7, resolve as resolve6 } from "path";
|
|
4527
|
+
import { existsSync as existsSync5, statSync as statSync4 } from "fs";
|
|
4426
4528
|
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
4427
4529
|
async function runRollback(config2) {
|
|
4428
4530
|
intro(import_picocolors7.default.bgCyan(import_picocolors7.default.black(" @bungres/kit rollback ")));
|
|
4429
4531
|
const migrationsDir = resolve6(config2.out);
|
|
4532
|
+
let activeSpinner = spinner();
|
|
4533
|
+
activeSpinner.start("Checking applied migrations...");
|
|
4534
|
+
if (!existsSync5(migrationsDir)) {
|
|
4535
|
+
activeSpinner.stop("No migration directory found.");
|
|
4536
|
+
log.warn(import_picocolors7.default.yellow(`Migration directory does not exist: ${migrationsDir}`));
|
|
4537
|
+
outro("Done.");
|
|
4538
|
+
return;
|
|
4539
|
+
}
|
|
4540
|
+
if (!statSync4(migrationsDir).isDirectory()) {
|
|
4541
|
+
activeSpinner.stop("Failed.");
|
|
4542
|
+
log.error(import_picocolors7.default.red(`Migration path exists but is not a directory: ${migrationsDir}`));
|
|
4543
|
+
outro("Failed.");
|
|
4544
|
+
return;
|
|
4545
|
+
}
|
|
4430
4546
|
const sql2 = new Bun.SQL(config2.dbUrl);
|
|
4431
4547
|
const table = config2.migrationsTable;
|
|
4432
4548
|
const schema = config2.migrationsSchema;
|
|
4433
4549
|
const qualifiedTable = `"${schema}"."${table}"`;
|
|
4434
|
-
let activeSpinner = spinner();
|
|
4435
|
-
activeSpinner.start("Checking applied migrations...");
|
|
4436
4550
|
try {
|
|
4437
4551
|
const applied = await sql2.unsafe(`SELECT name FROM ${qualifiedTable} ORDER BY name DESC LIMIT 1`);
|
|
4438
4552
|
if (applied.length === 0) {
|
|
@@ -4442,6 +4556,13 @@ async function runRollback(config2) {
|
|
|
4442
4556
|
return;
|
|
4443
4557
|
}
|
|
4444
4558
|
const lastMigration = applied[0].name;
|
|
4559
|
+
const filePath = join7(migrationsDir, lastMigration);
|
|
4560
|
+
if (!existsSync5(filePath)) {
|
|
4561
|
+
activeSpinner.stop("Migration file missing.");
|
|
4562
|
+
log.error(import_picocolors7.default.red(`The database tracks '${lastMigration}' as applied, but the file does not exist in ${migrationsDir}.`));
|
|
4563
|
+
outro("Failed.");
|
|
4564
|
+
return;
|
|
4565
|
+
}
|
|
4445
4566
|
activeSpinner.stop(`Found migration to rollback: ${import_picocolors7.default.cyan(lastMigration)}`);
|
|
4446
4567
|
const shouldRollback = await confirm({
|
|
4447
4568
|
message: `Are you sure you want to rollback ${import_picocolors7.default.cyan(lastMigration)}?`,
|
|
@@ -4453,7 +4574,15 @@ async function runRollback(config2) {
|
|
|
4453
4574
|
}
|
|
4454
4575
|
activeSpinner = spinner();
|
|
4455
4576
|
activeSpinner.start(`Rolling back ${lastMigration}...`);
|
|
4456
|
-
|
|
4577
|
+
let content = "";
|
|
4578
|
+
try {
|
|
4579
|
+
content = await Bun.file(filePath).text();
|
|
4580
|
+
} catch (err) {
|
|
4581
|
+
activeSpinner.stop("Failed.");
|
|
4582
|
+
log.error(import_picocolors7.default.red(`Failed to read migration file ${lastMigration}: ${err.message}`));
|
|
4583
|
+
outro("Failed.");
|
|
4584
|
+
return;
|
|
4585
|
+
}
|
|
4457
4586
|
let downContent = "";
|
|
4458
4587
|
const downMatch = content.match(/-- ==== DOWN ====([\s\S]*)/i);
|
|
4459
4588
|
if (downMatch) {
|
|
@@ -4607,17 +4736,31 @@ async function runSeed(config2) {
|
|
|
4607
4736
|
}
|
|
4608
4737
|
|
|
4609
4738
|
// src/commands/status.ts
|
|
4610
|
-
import { resolve as resolve8 } from "path";
|
|
4611
4739
|
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
4740
|
+
import { existsSync as existsSync6, statSync as statSync5 } from "fs";
|
|
4741
|
+
import { resolve as resolve8 } from "path";
|
|
4612
4742
|
async function runStatus(config2) {
|
|
4613
4743
|
intro(import_picocolors9.default.bgCyan(import_picocolors9.default.black(" @bungres/kit status ")));
|
|
4614
4744
|
const migrationsDir = resolve8(config2.out);
|
|
4745
|
+
const s = spinner();
|
|
4746
|
+
s.start("Checking migration status...");
|
|
4747
|
+
if (!existsSync6(migrationsDir)) {
|
|
4748
|
+
s.stop("No migration directory found.");
|
|
4749
|
+
log.warn(import_picocolors9.default.yellow(`Migration directory does not exist: ${migrationsDir}`));
|
|
4750
|
+
log.info(`Run ${import_picocolors9.default.green("bungres generate")} first.`);
|
|
4751
|
+
outro("Done.");
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
if (!statSync5(migrationsDir).isDirectory()) {
|
|
4755
|
+
s.stop("Failed.");
|
|
4756
|
+
log.error(import_picocolors9.default.red(`Migration path exists but is not a directory: ${migrationsDir}`));
|
|
4757
|
+
outro("Failed.");
|
|
4758
|
+
return;
|
|
4759
|
+
}
|
|
4615
4760
|
const sql2 = new Bun.SQL(config2.dbUrl);
|
|
4616
4761
|
const table = config2.migrationsTable;
|
|
4617
4762
|
const schema = config2.migrationsSchema;
|
|
4618
4763
|
const qualifiedTable = `"${schema}"."${table}"`;
|
|
4619
|
-
const s = spinner();
|
|
4620
|
-
s.start("Checking migration status...");
|
|
4621
4764
|
try {
|
|
4622
4765
|
const tableCheck = await sql2.unsafe(`SELECT EXISTS (
|
|
4623
4766
|
SELECT 1 FROM information_schema.tables
|
|
@@ -5550,7 +5693,7 @@ async function ensureDatabase2(dbUrl) {
|
|
|
5550
5693
|
// package.json
|
|
5551
5694
|
var package_default = {
|
|
5552
5695
|
name: "@bungres/kit",
|
|
5553
|
-
version: "1.1.
|
|
5696
|
+
version: "1.1.2",
|
|
5554
5697
|
description: "CLI toolkit for @bungres/orm \u2014 migrate, push, generate, pull",
|
|
5555
5698
|
license: "MIT",
|
|
5556
5699
|
engines: {
|
|
@@ -5601,13 +5744,13 @@ var package_default = {
|
|
|
5601
5744
|
test: "bun test"
|
|
5602
5745
|
},
|
|
5603
5746
|
dependencies: {
|
|
5604
|
-
"@bungres/orm": "^1.
|
|
5747
|
+
"@bungres/orm": "^1.2.1",
|
|
5605
5748
|
"@clack/prompts": "^1.7.0",
|
|
5606
5749
|
picocolors: "^1.1.1"
|
|
5607
5750
|
},
|
|
5608
5751
|
devDependencies: {
|
|
5609
|
-
"bun-types": "
|
|
5610
|
-
typescript: "
|
|
5752
|
+
"bun-types": "^1.3.14",
|
|
5753
|
+
typescript: "^7.0.2"
|
|
5611
5754
|
}
|
|
5612
5755
|
};
|
|
5613
5756
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAanD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAanD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAqNf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,wBAAsB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA0ItE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/commands/refresh.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/commands/refresh.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AASnD,wBAAsB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA+HtE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../../src/commands/rollback.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rollback.d.ts","sourceRoot":"","sources":["../../src/commands/rollback.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA8HvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD,wBAAsB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyFrE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tusky.d.ts","sourceRoot":"","sources":["../../src/commands/tusky.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"tusky.d.ts","sourceRoot":"","sources":["../../src/commands/tusky.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAWnD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyEpE"}
|
package/dist/index.js
CHANGED
|
@@ -267,7 +267,7 @@ function createTableFactory(casing) {
|
|
|
267
267
|
const tableObj = {
|
|
268
268
|
[TableConfigSymbol]: {
|
|
269
269
|
name,
|
|
270
|
-
schema,
|
|
270
|
+
...schema ? { schema } : {},
|
|
271
271
|
columns: columnConfigs,
|
|
272
272
|
indexes,
|
|
273
273
|
checks,
|
|
@@ -347,6 +347,41 @@ var textArray = col("text[]");
|
|
|
347
347
|
var integerArray = col("integer[]");
|
|
348
348
|
var varcharArray = col("varchar[]");
|
|
349
349
|
var uuidArray = col("uuid[]");
|
|
350
|
+
function shiftParams(sql, paramsLength, offset) {
|
|
351
|
+
if (paramsLength === 0)
|
|
352
|
+
return sql;
|
|
353
|
+
let result = "";
|
|
354
|
+
let inString = false;
|
|
355
|
+
let inIdent = false;
|
|
356
|
+
let i = 0;
|
|
357
|
+
while (i < sql.length) {
|
|
358
|
+
if (sql[i] === "'" && !inIdent) {
|
|
359
|
+
inString = !inString;
|
|
360
|
+
result += sql[i];
|
|
361
|
+
i++;
|
|
362
|
+
} else if (sql[i] === '"' && !inString) {
|
|
363
|
+
inIdent = !inIdent;
|
|
364
|
+
result += sql[i];
|
|
365
|
+
i++;
|
|
366
|
+
} else if (!inString && !inIdent && sql[i] === "$") {
|
|
367
|
+
const match = sql.slice(i).match(/^\$(\d+)/);
|
|
368
|
+
if (match) {
|
|
369
|
+
const num = parseInt(match[1], 10);
|
|
370
|
+
if (num >= 1 && num <= paramsLength) {
|
|
371
|
+
result += `$${num + offset}`;
|
|
372
|
+
i += match[0].length;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
result += sql[i];
|
|
377
|
+
i++;
|
|
378
|
+
} else {
|
|
379
|
+
result += sql[i];
|
|
380
|
+
i++;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
350
385
|
function sql(strings, ...values) {
|
|
351
386
|
let query = "";
|
|
352
387
|
const params = [];
|
|
@@ -356,7 +391,7 @@ function sql(strings, ...values) {
|
|
|
356
391
|
const val = values[i];
|
|
357
392
|
if (isSQLChunk(val)) {
|
|
358
393
|
const offset = params.length;
|
|
359
|
-
query += val.sql
|
|
394
|
+
query += shiftParams(val.sql, val.params.length, offset);
|
|
360
395
|
params.push(...val.params);
|
|
361
396
|
} else {
|
|
362
397
|
params.push(val);
|
|
@@ -374,7 +409,7 @@ function sqlJoin(chunks, separator = ", ") {
|
|
|
374
409
|
const parts = [];
|
|
375
410
|
for (const chunk of chunks) {
|
|
376
411
|
const offset = params.length;
|
|
377
|
-
parts.push(chunk.sql
|
|
412
|
+
parts.push(shiftParams(chunk.sql, chunk.params.length, offset));
|
|
378
413
|
params.push(...chunk.params);
|
|
379
414
|
}
|
|
380
415
|
return { sql: parts.join(separator), params };
|
|
@@ -382,6 +417,19 @@ function sqlJoin(chunks, separator = ", ") {
|
|
|
382
417
|
function rawSql(query) {
|
|
383
418
|
return { sql: query, params: [] };
|
|
384
419
|
}
|
|
420
|
+
function toPgArray(val) {
|
|
421
|
+
const formatted = val.map((item) => {
|
|
422
|
+
if (item === null || item === undefined)
|
|
423
|
+
return "NULL";
|
|
424
|
+
if (typeof item === "number" || typeof item === "boolean")
|
|
425
|
+
return String(item);
|
|
426
|
+
if (Array.isArray(item))
|
|
427
|
+
return toPgArray(item);
|
|
428
|
+
const str = String(item).replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
429
|
+
return `"${str}"`;
|
|
430
|
+
});
|
|
431
|
+
return `{${formatted.join(",")}}`;
|
|
432
|
+
}
|
|
385
433
|
function colName(c) {
|
|
386
434
|
if (typeof c === "string")
|
|
387
435
|
return `"${c}"`;
|
|
@@ -472,29 +520,29 @@ var not = (condition) => ({
|
|
|
472
520
|
});
|
|
473
521
|
var asc = (column) => sql`${rawSql(colName(column))} ASC`;
|
|
474
522
|
var desc = (column) => sql`${rawSql(colName(column))} DESC`;
|
|
475
|
-
function parseWhereObject(tableConfig, whereObj) {
|
|
523
|
+
function parseWhereObject(tableConfig, whereObj, alias2) {
|
|
476
524
|
const conditions = [];
|
|
477
525
|
for (const [key, val] of Object.entries(whereObj)) {
|
|
478
526
|
if (val === undefined)
|
|
479
527
|
continue;
|
|
480
528
|
if (key === "OR") {
|
|
481
|
-
const orConditions = val.map((o) => parseWhereObject(tableConfig, o));
|
|
529
|
+
const orConditions = val.map((o) => parseWhereObject(tableConfig, o, alias2));
|
|
482
530
|
if (orConditions.length > 0)
|
|
483
531
|
conditions.push(or(...orConditions));
|
|
484
532
|
continue;
|
|
485
533
|
}
|
|
486
534
|
if (key === "AND") {
|
|
487
|
-
const andConditions = val.map((o) => parseWhereObject(tableConfig, o));
|
|
535
|
+
const andConditions = val.map((o) => parseWhereObject(tableConfig, o, alias2));
|
|
488
536
|
if (andConditions.length > 0)
|
|
489
537
|
conditions.push(and(...andConditions));
|
|
490
538
|
continue;
|
|
491
539
|
}
|
|
492
540
|
if (key === "NOT") {
|
|
493
|
-
conditions.push(not(parseWhereObject(tableConfig, val)));
|
|
541
|
+
conditions.push(not(parseWhereObject(tableConfig, val, alias2)));
|
|
494
542
|
continue;
|
|
495
543
|
}
|
|
496
544
|
const colConfig = tableConfig.columns[key];
|
|
497
|
-
const columnArg = colConfig
|
|
545
|
+
const columnArg = colConfig ? alias2 ? { ...colConfig, tableName: alias2 } : colConfig : alias2 ? { name: key, tableName: alias2 } : key;
|
|
498
546
|
if (val !== null && typeof val === "object" && !Array.isArray(val) && !(val instanceof Date) && !(val instanceof Uint8Array)) {
|
|
499
547
|
const opVal = val;
|
|
500
548
|
if (opVal.eq !== undefined)
|
|
@@ -536,13 +584,13 @@ function parseWhereObject(tableConfig, whereObj) {
|
|
|
536
584
|
}
|
|
537
585
|
return and(...conditions);
|
|
538
586
|
}
|
|
539
|
-
function parseOrderByObject(tableConfig, orderByObj) {
|
|
587
|
+
function parseOrderByObject(tableConfig, orderByObj, alias2) {
|
|
540
588
|
const parts = [];
|
|
541
589
|
for (const [key, dir] of Object.entries(orderByObj)) {
|
|
542
590
|
if (dir === undefined)
|
|
543
591
|
continue;
|
|
544
592
|
const colConfig = tableConfig.columns[key];
|
|
545
|
-
const columnArg = colConfig
|
|
593
|
+
const columnArg = colConfig ? alias2 ? { ...colConfig, tableName: alias2 } : colConfig : alias2 ? { name: key, tableName: alias2 } : key;
|
|
546
594
|
if (dir === "asc")
|
|
547
595
|
parts.push(asc(columnArg));
|
|
548
596
|
else if (dir === "desc")
|
|
@@ -710,14 +758,7 @@ class InsertBuilder {
|
|
|
710
758
|
if (colType === "json" || colType === "jsonb") {
|
|
711
759
|
params.push(val);
|
|
712
760
|
} else if (Array.isArray(val)) {
|
|
713
|
-
|
|
714
|
-
if (item === null || item === undefined)
|
|
715
|
-
return "NULL";
|
|
716
|
-
if (typeof item === "string")
|
|
717
|
-
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
718
|
-
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"") + '"' : String(item);
|
|
719
|
-
}).join(",") + "}";
|
|
720
|
-
params.push(pgArray);
|
|
761
|
+
params.push(toPgArray(val));
|
|
721
762
|
} else {
|
|
722
763
|
params.push(JSON.stringify(val));
|
|
723
764
|
}
|
|
@@ -768,14 +809,7 @@ class InsertBuilder {
|
|
|
768
809
|
if (colType === "json" || colType === "jsonb") {
|
|
769
810
|
params.push(value);
|
|
770
811
|
} else if (Array.isArray(value)) {
|
|
771
|
-
|
|
772
|
-
if (item === null || item === undefined)
|
|
773
|
-
return "NULL";
|
|
774
|
-
if (typeof item === "string")
|
|
775
|
-
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
776
|
-
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"") + '"' : String(item);
|
|
777
|
-
}).join(",") + "}";
|
|
778
|
-
params.push(pgArray);
|
|
812
|
+
params.push(toPgArray(value));
|
|
779
813
|
} else {
|
|
780
814
|
params.push(JSON.stringify(value));
|
|
781
815
|
}
|
|
@@ -1199,14 +1233,7 @@ class UpdateBuilder {
|
|
|
1199
1233
|
if (colType === "json" || colType === "jsonb") {
|
|
1200
1234
|
params.push(value);
|
|
1201
1235
|
} else if (Array.isArray(value)) {
|
|
1202
|
-
|
|
1203
|
-
if (item === null || item === undefined)
|
|
1204
|
-
return "NULL";
|
|
1205
|
-
if (typeof item === "string")
|
|
1206
|
-
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
1207
|
-
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"") + '"' : String(item);
|
|
1208
|
-
}).join(",") + "}";
|
|
1209
|
-
params.push(pgArray);
|
|
1236
|
+
params.push(toPgArray(value));
|
|
1210
1237
|
} else {
|
|
1211
1238
|
params.push(JSON.stringify(value));
|
|
1212
1239
|
}
|
|
@@ -1449,7 +1476,7 @@ class RelationalQueryBuilder {
|
|
|
1449
1476
|
const offset = params.length;
|
|
1450
1477
|
let whereChunk = args.where;
|
|
1451
1478
|
if (args.where && !args.where.sql) {
|
|
1452
|
-
whereChunk = parseWhereObject(tableConfig, args.where);
|
|
1479
|
+
whereChunk = parseWhereObject(tableConfig, args.where, alias2);
|
|
1453
1480
|
}
|
|
1454
1481
|
if (whereChunk && whereChunk.sql) {
|
|
1455
1482
|
fromSql += (joinCondition ? " AND " : " WHERE ") + whereChunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`);
|
|
@@ -1464,7 +1491,7 @@ class RelationalQueryBuilder {
|
|
|
1464
1491
|
fromSql += ` ORDER BY ` + args.orderBy.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`);
|
|
1465
1492
|
params.push(...args.orderBy.params);
|
|
1466
1493
|
} else {
|
|
1467
|
-
const chunks = parseOrderByObject(tableConfig, args.orderBy);
|
|
1494
|
+
const chunks = parseOrderByObject(tableConfig, args.orderBy, alias2);
|
|
1468
1495
|
if (chunks.length > 0) {
|
|
1469
1496
|
fromSql += ` ORDER BY ` + chunks.map((c) => {
|
|
1470
1497
|
const offset = params.length;
|
|
@@ -1779,6 +1806,21 @@ function formatDefaultValue(value, dataType) {
|
|
|
1779
1806
|
return value ? "TRUE" : "FALSE";
|
|
1780
1807
|
if (typeof value === "number")
|
|
1781
1808
|
return String(value);
|
|
1809
|
+
if (value instanceof Date)
|
|
1810
|
+
return `'${value.toISOString()}'`;
|
|
1811
|
+
if (Array.isArray(value)) {
|
|
1812
|
+
if (dataType === "json" || dataType === "jsonb") {
|
|
1813
|
+
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
|
1814
|
+
}
|
|
1815
|
+
const pgArray = "{" + value.map((item) => {
|
|
1816
|
+
if (item === null || item === undefined)
|
|
1817
|
+
return "NULL";
|
|
1818
|
+
if (typeof item === "string")
|
|
1819
|
+
return '"' + item.replace(/"/g, "\\\"") + '"';
|
|
1820
|
+
return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"").replace(/'/g, "''") + '"' : String(item);
|
|
1821
|
+
}).join(",") + "}";
|
|
1822
|
+
return `'${pgArray.replace(/'/g, "''")}'`;
|
|
1823
|
+
}
|
|
1782
1824
|
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
|
1783
1825
|
}
|
|
1784
1826
|
function buildIndex(table, idx) {
|
|
@@ -3655,7 +3697,7 @@ async function runPush(config, opts = {}) {
|
|
|
3655
3697
|
}
|
|
3656
3698
|
// src/commands/generate.ts
|
|
3657
3699
|
import { join as join3, resolve as resolve3 } from "path";
|
|
3658
|
-
import { readdirSync } from "fs";
|
|
3700
|
+
import { readdirSync, existsSync, statSync } from "fs";
|
|
3659
3701
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
3660
3702
|
async function runGenerate(config, name) {
|
|
3661
3703
|
intro(import_picocolors2.default.bgCyan(import_picocolors2.default.black(" @bungres/kit generate ")));
|
|
@@ -3670,6 +3712,12 @@ async function runGenerate(config, name) {
|
|
|
3670
3712
|
}
|
|
3671
3713
|
s.stop(`Loaded ${schemas.length} schemas.`);
|
|
3672
3714
|
const migrationsDir = resolve3(config.out);
|
|
3715
|
+
if (existsSync(migrationsDir) && !statSync(migrationsDir).isDirectory()) {
|
|
3716
|
+
s.stop("Failed.");
|
|
3717
|
+
log.error(import_picocolors2.default.red(`Migration path exists but is not a directory: ${migrationsDir}`));
|
|
3718
|
+
outro("Failed.");
|
|
3719
|
+
return;
|
|
3720
|
+
}
|
|
3673
3721
|
const metaDir = join3(migrationsDir, "meta");
|
|
3674
3722
|
await Bun.$`mkdir -p ${migrationsDir}`.quiet();
|
|
3675
3723
|
await Bun.$`mkdir -p ${metaDir}`.quiet();
|
|
@@ -3844,10 +3892,26 @@ function topoSort(schemas) {
|
|
|
3844
3892
|
}
|
|
3845
3893
|
// src/commands/migrate.ts
|
|
3846
3894
|
import { join as join4, resolve as resolve4 } from "path";
|
|
3895
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
3847
3896
|
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
3848
3897
|
async function runMigrate(config) {
|
|
3849
3898
|
intro(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" @bungres/kit migrate ")));
|
|
3850
3899
|
const migrationsDir = resolve4(config.out);
|
|
3900
|
+
let s = spinner();
|
|
3901
|
+
s.start("Checking pending migrations...");
|
|
3902
|
+
if (!existsSync2(migrationsDir)) {
|
|
3903
|
+
s.stop("No files found.");
|
|
3904
|
+
log.warn(import_picocolors3.default.yellow(`Migration directory does not exist: ${migrationsDir}`));
|
|
3905
|
+
log.info(`Run ${import_picocolors3.default.green("bungres generate")} first.`);
|
|
3906
|
+
outro("Done.");
|
|
3907
|
+
return;
|
|
3908
|
+
}
|
|
3909
|
+
if (!statSync2(migrationsDir).isDirectory()) {
|
|
3910
|
+
s.stop("Failed.");
|
|
3911
|
+
log.error(import_picocolors3.default.red(`Migration path exists but is not a directory: ${migrationsDir}`));
|
|
3912
|
+
outro("Failed.");
|
|
3913
|
+
return;
|
|
3914
|
+
}
|
|
3851
3915
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
3852
3916
|
const table = config.migrationsTable;
|
|
3853
3917
|
const schema = config.migrationsSchema;
|
|
@@ -3859,8 +3923,6 @@ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
|
3859
3923
|
name TEXT NOT NULL UNIQUE,
|
|
3860
3924
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
3861
3925
|
);`.trim();
|
|
3862
|
-
let s = spinner();
|
|
3863
|
-
s.start("Checking pending migrations...");
|
|
3864
3926
|
try {
|
|
3865
3927
|
await sql2.unsafe(createSchema);
|
|
3866
3928
|
await sql2.unsafe(createMigrationsTable);
|
|
@@ -3890,7 +3952,22 @@ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
|
|
|
3890
3952
|
for (const file of pending) {
|
|
3891
3953
|
s = spinner();
|
|
3892
3954
|
s.start(`Applying ${file}...`);
|
|
3893
|
-
const
|
|
3955
|
+
const filePath = join4(migrationsDir, file);
|
|
3956
|
+
if (!existsSync2(filePath)) {
|
|
3957
|
+
s.stop("Failed.");
|
|
3958
|
+
log.error(import_picocolors3.default.red(`Migration file not found: ${filePath}`));
|
|
3959
|
+
outro("Failed.");
|
|
3960
|
+
return;
|
|
3961
|
+
}
|
|
3962
|
+
let content = "";
|
|
3963
|
+
try {
|
|
3964
|
+
content = await Bun.file(filePath).text();
|
|
3965
|
+
} catch (err) {
|
|
3966
|
+
s.stop("Failed.");
|
|
3967
|
+
log.error(import_picocolors3.default.red(`Failed to read migration file ${file}: ${err.message}`));
|
|
3968
|
+
outro("Failed.");
|
|
3969
|
+
return;
|
|
3970
|
+
}
|
|
3894
3971
|
let upContent = content;
|
|
3895
3972
|
const upMatch = content.match(/-- ==== UP ====([\s\S]*?)(?:-- ==== DOWN ====|$)/i);
|
|
3896
3973
|
if (upMatch) {
|
|
@@ -4176,17 +4253,31 @@ function toCamelCase(str) {
|
|
|
4176
4253
|
return str.replace(/_([a-z])/g, (_2, c2) => c2.toUpperCase());
|
|
4177
4254
|
}
|
|
4178
4255
|
// src/commands/status.ts
|
|
4179
|
-
import { resolve as resolve6 } from "path";
|
|
4180
4256
|
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
4257
|
+
import { existsSync as existsSync3, statSync as statSync3 } from "fs";
|
|
4258
|
+
import { resolve as resolve6 } from "path";
|
|
4181
4259
|
async function runStatus(config) {
|
|
4182
4260
|
intro(import_picocolors4.default.bgCyan(import_picocolors4.default.black(" @bungres/kit status ")));
|
|
4183
4261
|
const migrationsDir = resolve6(config.out);
|
|
4262
|
+
const s = spinner();
|
|
4263
|
+
s.start("Checking migration status...");
|
|
4264
|
+
if (!existsSync3(migrationsDir)) {
|
|
4265
|
+
s.stop("No migration directory found.");
|
|
4266
|
+
log.warn(import_picocolors4.default.yellow(`Migration directory does not exist: ${migrationsDir}`));
|
|
4267
|
+
log.info(`Run ${import_picocolors4.default.green("bungres generate")} first.`);
|
|
4268
|
+
outro("Done.");
|
|
4269
|
+
return;
|
|
4270
|
+
}
|
|
4271
|
+
if (!statSync3(migrationsDir).isDirectory()) {
|
|
4272
|
+
s.stop("Failed.");
|
|
4273
|
+
log.error(import_picocolors4.default.red(`Migration path exists but is not a directory: ${migrationsDir}`));
|
|
4274
|
+
outro("Failed.");
|
|
4275
|
+
return;
|
|
4276
|
+
}
|
|
4184
4277
|
const sql2 = new Bun.SQL(config.dbUrl);
|
|
4185
4278
|
const table = config.migrationsTable;
|
|
4186
4279
|
const schema = config.migrationsSchema;
|
|
4187
4280
|
const qualifiedTable = `"${schema}"."${table}"`;
|
|
4188
|
-
const s = spinner();
|
|
4189
|
-
s.start("Checking migration status...");
|
|
4190
4281
|
try {
|
|
4191
4282
|
const tableCheck = await sql2.unsafe(`SELECT EXISTS (
|
|
4192
4283
|
SELECT 1 FROM information_schema.tables
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bungres/kit",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "CLI toolkit for @bungres/orm — migrate, push, generate, pull",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"test": "bun test"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@bungres/orm": "^1.
|
|
54
|
+
"@bungres/orm": "^1.2.1",
|
|
55
55
|
"@clack/prompts": "^1.7.0",
|
|
56
56
|
"picocolors": "^1.1.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"bun-types": "
|
|
60
|
-
"typescript": "
|
|
59
|
+
"bun-types": "^1.3.14",
|
|
60
|
+
"typescript": "^7.0.2"
|
|
61
61
|
}
|
|
62
62
|
}
|