@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 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.sql` which is built-in)
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 | 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) |
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 content = await Bun.file(join(migrationsDir, file)).text();
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.replace(/\$(\d+)/g, (_2, n2) => `$${parseInt(n2) + offset}`);
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.replace(/\$(\d+)/g, (_2, n2) => `$${parseInt(n2) + offset}`));
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 ?? key;
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 ?? key;
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
- const pgArray = "{" + val.map((item) => {
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
- const pgArray = "{" + value.map((item) => {
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
- const pgArray = "{" + value.map((item) => {
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 (existsSync(configPath)) {
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 (existsSync(join4(cwd, "src"))) {
3880
- if (!existsSync(join4(cwd, defaultDbDir, "schema.ts")) && !existsSync(join4(cwd, defaultDbDir, "client.ts"))) {
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
- const content = await Bun.file(join7(migrationsDir, lastMigration)).text();
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.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.0.0",
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": "latest",
5610
- typescript: "latest"
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,CA6Mf"}
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":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,wBAAsB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA0GtE"}
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":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AASnD,wBAAsB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoGtE"}
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":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA8FvE"}
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":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,wBAAsB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyErE"}
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;AAOnD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyEpE"}
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.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`);
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.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`));
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 ?? key;
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 ?? key;
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
- const pgArray = "{" + val.map((item) => {
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
- const pgArray = "{" + value.map((item) => {
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
- const pgArray = "{" + value.map((item) => {
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 content = await Bun.file(join4(migrationsDir, file)).text();
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.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.0.0",
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": "latest",
60
- "typescript": "latest"
59
+ "bun-types": "^1.3.14",
60
+ "typescript": "^7.0.2"
61
61
  }
62
62
  }