@bungres/kit 0.6.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,10 +1,170 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
+ var __create = Object.create;
4
+ var __getProtoOf = Object.getPrototypeOf;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
13
+ var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
21
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
22
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
+ for (let key of __getOwnPropNames(mod))
24
+ if (!__hasOwnProp.call(to, key))
25
+ __defProp(to, key, {
26
+ get: __accessProp.bind(mod, key),
27
+ enumerable: true
28
+ });
29
+ if (canCache)
30
+ cache.set(mod, to);
31
+ return to;
32
+ };
33
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
+ var __require = import.meta.require;
35
+
36
+ // ../../node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
37
+ var require_src = __commonJS((exports, module) => {
38
+ var ESC2 = "\x1B";
39
+ var CSI2 = `${ESC2}[`;
40
+ var beep = "\x07";
41
+ var cursor = {
42
+ to(x, y) {
43
+ if (!y)
44
+ return `${CSI2}${x + 1}G`;
45
+ return `${CSI2}${y + 1};${x + 1}H`;
46
+ },
47
+ move(x, y) {
48
+ let ret = "";
49
+ if (x < 0)
50
+ ret += `${CSI2}${-x}D`;
51
+ else if (x > 0)
52
+ ret += `${CSI2}${x}C`;
53
+ if (y < 0)
54
+ ret += `${CSI2}${-y}A`;
55
+ else if (y > 0)
56
+ ret += `${CSI2}${y}B`;
57
+ return ret;
58
+ },
59
+ up: (count = 1) => `${CSI2}${count}A`,
60
+ down: (count = 1) => `${CSI2}${count}B`,
61
+ forward: (count = 1) => `${CSI2}${count}C`,
62
+ backward: (count = 1) => `${CSI2}${count}D`,
63
+ nextLine: (count = 1) => `${CSI2}E`.repeat(count),
64
+ prevLine: (count = 1) => `${CSI2}F`.repeat(count),
65
+ left: `${CSI2}G`,
66
+ hide: `${CSI2}?25l`,
67
+ show: `${CSI2}?25h`,
68
+ save: `${ESC2}7`,
69
+ restore: `${ESC2}8`
70
+ };
71
+ var scroll = {
72
+ up: (count = 1) => `${CSI2}S`.repeat(count),
73
+ down: (count = 1) => `${CSI2}T`.repeat(count)
74
+ };
75
+ var erase = {
76
+ screen: `${CSI2}2J`,
77
+ up: (count = 1) => `${CSI2}1J`.repeat(count),
78
+ down: (count = 1) => `${CSI2}J`.repeat(count),
79
+ line: `${CSI2}2K`,
80
+ lineEnd: `${CSI2}K`,
81
+ lineStart: `${CSI2}1K`,
82
+ lines(count) {
83
+ let clear = "";
84
+ for (let i = 0;i < count; i++)
85
+ clear += this.line + (i < count - 1 ? cursor.up() : "");
86
+ if (count)
87
+ clear += cursor.left;
88
+ return clear;
89
+ }
90
+ };
91
+ module.exports = { cursor, scroll, erase, beep };
92
+ });
93
+
94
+ // ../../node_modules/.bun/picocolors@1.1.1/node_modules/picocolors/picocolors.js
95
+ var require_picocolors = __commonJS((exports, module) => {
96
+ var p2 = process || {};
97
+ var argv = p2.argv || [];
98
+ var env = p2.env || {};
99
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p2.platform === "win32" || (p2.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
100
+ var formatter = (open, close, replace = open) => (input2) => {
101
+ let string = "" + input2, index = string.indexOf(close, open.length);
102
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
103
+ };
104
+ var replaceClose = (string, close, replace, index) => {
105
+ let result2 = "", cursor3 = 0;
106
+ do {
107
+ result2 += string.substring(cursor3, index) + replace;
108
+ cursor3 = index + close.length;
109
+ index = string.indexOf(close, cursor3);
110
+ } while (~index);
111
+ return result2 + string.substring(cursor3);
112
+ };
113
+ var createColors = (enabled = isColorSupported) => {
114
+ let f2 = enabled ? formatter : () => String;
115
+ return {
116
+ isColorSupported: enabled,
117
+ reset: f2("\x1B[0m", "\x1B[0m"),
118
+ bold: f2("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
119
+ dim: f2("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
120
+ italic: f2("\x1B[3m", "\x1B[23m"),
121
+ underline: f2("\x1B[4m", "\x1B[24m"),
122
+ inverse: f2("\x1B[7m", "\x1B[27m"),
123
+ hidden: f2("\x1B[8m", "\x1B[28m"),
124
+ strikethrough: f2("\x1B[9m", "\x1B[29m"),
125
+ black: f2("\x1B[30m", "\x1B[39m"),
126
+ red: f2("\x1B[31m", "\x1B[39m"),
127
+ green: f2("\x1B[32m", "\x1B[39m"),
128
+ yellow: f2("\x1B[33m", "\x1B[39m"),
129
+ blue: f2("\x1B[34m", "\x1B[39m"),
130
+ magenta: f2("\x1B[35m", "\x1B[39m"),
131
+ cyan: f2("\x1B[36m", "\x1B[39m"),
132
+ white: f2("\x1B[37m", "\x1B[39m"),
133
+ gray: f2("\x1B[90m", "\x1B[39m"),
134
+ bgBlack: f2("\x1B[40m", "\x1B[49m"),
135
+ bgRed: f2("\x1B[41m", "\x1B[49m"),
136
+ bgGreen: f2("\x1B[42m", "\x1B[49m"),
137
+ bgYellow: f2("\x1B[43m", "\x1B[49m"),
138
+ bgBlue: f2("\x1B[44m", "\x1B[49m"),
139
+ bgMagenta: f2("\x1B[45m", "\x1B[49m"),
140
+ bgCyan: f2("\x1B[46m", "\x1B[49m"),
141
+ bgWhite: f2("\x1B[47m", "\x1B[49m"),
142
+ blackBright: f2("\x1B[90m", "\x1B[39m"),
143
+ redBright: f2("\x1B[91m", "\x1B[39m"),
144
+ greenBright: f2("\x1B[92m", "\x1B[39m"),
145
+ yellowBright: f2("\x1B[93m", "\x1B[39m"),
146
+ blueBright: f2("\x1B[94m", "\x1B[39m"),
147
+ magentaBright: f2("\x1B[95m", "\x1B[39m"),
148
+ cyanBright: f2("\x1B[96m", "\x1B[39m"),
149
+ whiteBright: f2("\x1B[97m", "\x1B[39m"),
150
+ bgBlackBright: f2("\x1B[100m", "\x1B[49m"),
151
+ bgRedBright: f2("\x1B[101m", "\x1B[49m"),
152
+ bgGreenBright: f2("\x1B[102m", "\x1B[49m"),
153
+ bgYellowBright: f2("\x1B[103m", "\x1B[49m"),
154
+ bgBlueBright: f2("\x1B[104m", "\x1B[49m"),
155
+ bgMagentaBright: f2("\x1B[105m", "\x1B[49m"),
156
+ bgCyanBright: f2("\x1B[106m", "\x1B[49m"),
157
+ bgWhiteBright: f2("\x1B[107m", "\x1B[49m")
158
+ };
159
+ };
160
+ module.exports = createColors();
161
+ module.exports.createColors = createColors;
162
+ });
3
163
 
4
164
  // src/schema-loader.ts
5
165
  import { resolve, join } from "path";
6
166
 
7
- // ../bungres-orm/src/schema/table.ts
167
+ // ../bungres-orm/dist/index.js
8
168
  var TableConfigSymbol = Symbol.for("BungresTableConfig");
9
169
  function getTableConfig(table) {
10
170
  return table[TableConfigSymbol];
@@ -74,7 +234,6 @@ var table = createTableFactory("snake");
74
234
  var snakeCase = { table: createTableFactory("snake") };
75
235
  var camelCase = { table: createTableFactory("camel") };
76
236
  var noCasing = { table: createTableFactory("none") };
77
- // ../bungres-orm/src/schema/columns.ts
78
237
  function buildColumn(dataType, nameOrOpts, opts) {
79
238
  let name = "";
80
239
  let options = opts;
@@ -103,6 +262,13 @@ function buildColumn(dataType, nameOrOpts, opts) {
103
262
  return Object.assign(config2, {
104
263
  as(alias) {
105
264
  return Object.assign({}, this, { alias });
265
+ },
266
+ array() {
267
+ return Object.assign({}, this, { dataType: `${this.dataType}[]` });
268
+ },
269
+ generatedAlwaysAs(expr) {
270
+ const sqlStr = typeof expr === "string" ? expr : expr.sql;
271
+ return Object.assign({}, this, { generatedAs: sqlStr });
106
272
  }
107
273
  });
108
274
  }
@@ -132,7 +298,6 @@ var textArray = col("text[]");
132
298
  var integerArray = col("integer[]");
133
299
  var varcharArray = col("varchar[]");
134
300
  var uuidArray = col("uuid[]");
135
- // ../bungres-orm/src/core/sql.ts
136
301
  function sql(strings, ...values) {
137
302
  let query = "";
138
303
  const params = [];
@@ -175,7 +340,6 @@ function colName(c) {
175
340
  return c.sql;
176
341
  return c.tableName ? `${c.tableName}."${c.name}"` : `"${c.name}"`;
177
342
  }
178
- // ../bungres-orm/src/core/conditions.ts
179
343
  function isColumnConfig(val) {
180
344
  return val !== null && typeof val === "object" && "name" in val && "dataType" in val;
181
345
  }
@@ -329,14 +493,13 @@ function parseOrderByObject(tableConfig, orderByObj) {
329
493
  }
330
494
  return parts;
331
495
  }
332
-
333
- // ../bungres-orm/src/builders/delete.ts
334
496
  class DeleteBuilder {
335
497
  _table;
336
498
  _executor;
337
499
  _where = [];
338
500
  _returning;
339
501
  _comment;
502
+ _with = [];
340
503
  constructor(table2, executor) {
341
504
  this._table = table2;
342
505
  this._executor = executor;
@@ -355,6 +518,10 @@ class DeleteBuilder {
355
518
  }
356
519
  return this;
357
520
  }
521
+ with(...ctes) {
522
+ this._with.push(...ctes);
523
+ return this;
524
+ }
358
525
  returning(...columns) {
359
526
  this._returning = columns.length > 0 ? columns : ["*"];
360
527
  return this;
@@ -364,8 +531,20 @@ class DeleteBuilder {
364
531
  return this;
365
532
  }
366
533
  toSQL() {
367
- let query = `DELETE FROM ${getTableConfig(this._table).qualifiedName}`;
534
+ const tConfig = getTableConfig(this._table);
368
535
  const params = [];
536
+ let prefix = "";
537
+ if (this._with.length > 0) {
538
+ const cteStrs = [];
539
+ for (const cte of this._with) {
540
+ const chunk = cte.query.toSQL();
541
+ const offset = params.length;
542
+ params.push(...chunk.params);
543
+ cteStrs.push(`"${cte.alias}" AS (${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)})`);
544
+ }
545
+ prefix = `WITH ${cteStrs.join(", ")} `;
546
+ }
547
+ let query = `DELETE FROM ${tConfig.qualifiedName}`;
369
548
  if (this._where.length > 0) {
370
549
  const combined = sqlJoin(this._where, " AND ");
371
550
  query += " WHERE " + combined.sql;
@@ -377,17 +556,19 @@ class DeleteBuilder {
377
556
  if (this._comment) {
378
557
  query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
379
558
  }
380
- return { sql: query, params };
559
+ return { sql: prefix + query, params };
381
560
  }
382
561
  }
383
- // ../bungres-orm/src/builders/insert.ts
562
+
384
563
  class InsertBuilder {
385
564
  _table;
386
565
  _executor;
387
566
  _values = [];
388
567
  _onConflict;
568
+ _onConflictUpdateConfig;
389
569
  _returning;
390
570
  _comment;
571
+ _with = [];
391
572
  constructor(table2, executor) {
392
573
  this._table = table2;
393
574
  this._executor = executor;
@@ -414,6 +595,14 @@ class InsertBuilder {
414
595
  this._onConflict = clause;
415
596
  return this;
416
597
  }
598
+ onConflictDoUpdate(config2) {
599
+ this._onConflictUpdateConfig = config2;
600
+ return this;
601
+ }
602
+ with(...ctes) {
603
+ this._with.push(...ctes);
604
+ return this;
605
+ }
417
606
  returning(...columns) {
418
607
  this._returning = columns.length > 0 ? columns : ["*"];
419
608
  return this;
@@ -439,6 +628,17 @@ class InsertBuilder {
439
628
  }
440
629
  const columnsStr = keys.map((k) => `"${tConfig.columns[k]?.name ?? k}"`).join(", ");
441
630
  const params = [];
631
+ let prefix = "";
632
+ if (this._with.length > 0) {
633
+ const cteStrs = [];
634
+ for (const cte of this._with) {
635
+ const chunk = cte.query.toSQL();
636
+ const offset = params.length;
637
+ params.push(...chunk.params);
638
+ cteStrs.push(`"${cte.alias}" AS (${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)})`);
639
+ }
640
+ prefix = `WITH ${cteStrs.join(", ")} `;
641
+ }
442
642
  const valuesStrs = this._values.map((v) => {
443
643
  const vals = keys.map((k) => {
444
644
  const val = v[k];
@@ -482,6 +682,52 @@ class InsertBuilder {
482
682
  query += " " + this._onConflict.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`);
483
683
  params.push(...this._onConflict.params);
484
684
  }
685
+ } else if (this._onConflictUpdateConfig) {
686
+ const config2 = this._onConflictUpdateConfig;
687
+ const targets = Array.isArray(config2.target) ? config2.target : [config2.target];
688
+ const targetStrs = [];
689
+ for (const t of targets) {
690
+ if (typeof t === "string") {
691
+ targetStrs.push(`"${tConfig.columns[t]?.name ?? t}"`);
692
+ } else {
693
+ const offset = params.length;
694
+ targetStrs.push(t.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`));
695
+ params.push(...t.params);
696
+ }
697
+ }
698
+ const setEntries = Object.entries(config2.set);
699
+ if (setEntries.length === 0)
700
+ throw new Error("InsertBuilder: onConflictDoUpdate requires 'set' fields");
701
+ const setClauses = setEntries.map(([key, value]) => {
702
+ const dbCol = tConfig.columns[key]?.name ?? key;
703
+ if (value && typeof value === "object" && "sql" in value && "params" in value) {
704
+ const chunk = value;
705
+ const offset = params.length;
706
+ params.push(...chunk.params);
707
+ return `"${dbCol}" = ${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)}`;
708
+ }
709
+ if (value && typeof value === "object" && !(value instanceof Date)) {
710
+ const colType = tConfig.columns[key]?.dataType;
711
+ if (colType === "json" || colType === "jsonb") {
712
+ params.push(value);
713
+ } else if (Array.isArray(value)) {
714
+ const pgArray = "{" + value.map((item) => {
715
+ if (item === null || item === undefined)
716
+ return "NULL";
717
+ if (typeof item === "string")
718
+ return '"' + item.replace(/"/g, "\\\"") + '"';
719
+ return typeof item === "object" ? '"' + JSON.stringify(item).replace(/"/g, "\\\"") + '"' : String(item);
720
+ }).join(",") + "}";
721
+ params.push(pgArray);
722
+ } else {
723
+ params.push(JSON.stringify(value));
724
+ }
725
+ return `"${dbCol}" = $${params.length}`;
726
+ }
727
+ params.push(value);
728
+ return `"${dbCol}" = $${params.length}`;
729
+ });
730
+ query += ` ON CONFLICT (${targetStrs.join(", ")}) DO UPDATE SET ${setClauses.join(", ")}`;
485
731
  }
486
732
  if (this._returning) {
487
733
  query += " RETURNING " + (this._returning[0] === "*" ? Object.keys(tConfig.columns).map((c) => `"${tConfig.columns[c].name}" AS "${c}"`).join(", ") : this._returning.map((c) => `"${tConfig.columns[c]?.name ?? c}" AS "${c}"`).join(", "));
@@ -489,10 +735,10 @@ class InsertBuilder {
489
735
  if (this._comment) {
490
736
  query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
491
737
  }
492
- return { sql: query, params };
738
+ return { sql: prefix + query, params };
493
739
  }
494
740
  }
495
- // ../bungres-orm/src/builders/select.ts
741
+
496
742
  class SelectBuilder {
497
743
  _table;
498
744
  _executor;
@@ -505,6 +751,8 @@ class SelectBuilder {
505
751
  _select;
506
752
  _selection;
507
753
  _joins = [];
754
+ _with = [];
755
+ _setOperations = [];
508
756
  _comment;
509
757
  constructor(table2, executor, selection) {
510
758
  this._table = table2;
@@ -515,12 +763,35 @@ class SelectBuilder {
515
763
  return this._executor.execute(this).then(onfulfilled, onrejected);
516
764
  }
517
765
  async single() {
766
+ if (this._limit === undefined) {
767
+ this.limit(1);
768
+ }
518
769
  return this._executor.executeSingle(this);
519
770
  }
520
771
  select(...columns) {
521
772
  this._select = columns;
522
773
  return this;
523
774
  }
775
+ with(...ctes) {
776
+ this._with.push(...ctes);
777
+ return this;
778
+ }
779
+ union(other) {
780
+ this._setOperations.push({ type: "UNION", builder: other });
781
+ return this;
782
+ }
783
+ unionAll(other) {
784
+ this._setOperations.push({ type: "UNION ALL", builder: other });
785
+ return this;
786
+ }
787
+ intersect(other) {
788
+ this._setOperations.push({ type: "INTERSECT", builder: other });
789
+ return this;
790
+ }
791
+ except(other) {
792
+ this._setOperations.push({ type: "EXCEPT", builder: other });
793
+ return this;
794
+ }
524
795
  comment(tag) {
525
796
  this._comment = tag;
526
797
  return this;
@@ -623,7 +894,23 @@ class SelectBuilder {
623
894
  }).join(", ");
624
895
  } else {
625
896
  const qName = getTableConfig(this._table).qualifiedName;
626
- cols = Object.keys(getTableConfig(this._table).columns).map((c) => `${qName}."${getTableConfig(this._table).columns[c].name}" AS "${c}"`).join(", ");
897
+ const colsKeys = Object.keys(getTableConfig(this._table).columns);
898
+ if (colsKeys.length === 0) {
899
+ cols = "*";
900
+ } else {
901
+ cols = colsKeys.map((c) => `${qName}."${getTableConfig(this._table).columns[c].name}" AS "${c}"`).join(", ");
902
+ }
903
+ }
904
+ let prefix = "";
905
+ if (this._with.length > 0) {
906
+ const cteStrs = [];
907
+ for (const cte of this._with) {
908
+ const chunk = cte.query.toSQL();
909
+ const offset = params.length;
910
+ params.push(...chunk.params);
911
+ cteStrs.push(`"${cte.alias}" AS (${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)})`);
912
+ }
913
+ prefix = `WITH ${cteStrs.join(", ")} `;
627
914
  }
628
915
  let query = `SELECT ${cols} FROM ${getTableConfig(this._table).qualifiedName}`;
629
916
  if (this._joins.length > 0) {
@@ -683,10 +970,18 @@ class SelectBuilder {
683
970
  params.push(this._offset);
684
971
  query += ` OFFSET $${params.length}`;
685
972
  }
973
+ if (this._setOperations.length > 0) {
974
+ for (const op of this._setOperations) {
975
+ const chunk = op.builder.toSQL();
976
+ const offset = params.length;
977
+ params.push(...chunk.params);
978
+ query += ` ${op.type} ${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)}`;
979
+ }
980
+ }
686
981
  if (this._comment) {
687
982
  query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
688
983
  }
689
- return { sql: query, params };
984
+ return { sql: prefix + query, params };
690
985
  }
691
986
  }
692
987
 
@@ -701,7 +996,7 @@ class SelectBuilderIntermediate {
701
996
  return new SelectBuilder(table2, this._executor, this._selection);
702
997
  }
703
998
  }
704
- // ../bungres-orm/src/builders/update.ts
999
+
705
1000
  class UpdateBuilder {
706
1001
  _table;
707
1002
  _executor;
@@ -709,6 +1004,7 @@ class UpdateBuilder {
709
1004
  _where = [];
710
1005
  _returning;
711
1006
  _comment;
1007
+ _with = [];
712
1008
  constructor(table2, executor) {
713
1009
  this._table = table2;
714
1010
  this._executor = executor;
@@ -731,6 +1027,10 @@ class UpdateBuilder {
731
1027
  }
732
1028
  return this;
733
1029
  }
1030
+ with(...ctes) {
1031
+ this._with.push(...ctes);
1032
+ return this;
1033
+ }
734
1034
  returning(...columns) {
735
1035
  this._returning = columns.length > 0 ? columns : ["*"];
736
1036
  return this;
@@ -745,6 +1045,17 @@ class UpdateBuilder {
745
1045
  throw new Error("UpdateBuilder: no fields to set");
746
1046
  }
747
1047
  const params = [];
1048
+ let prefix = "";
1049
+ if (this._with.length > 0) {
1050
+ const cteStrs = [];
1051
+ for (const cte of this._with) {
1052
+ const chunk = cte.query.toSQL();
1053
+ const offset = params.length;
1054
+ params.push(...chunk.params);
1055
+ cteStrs.push(`"${cte.alias}" AS (${chunk.sql.replace(/\$(\d+)/g, (_, n) => `$${parseInt(n) + offset}`)})`);
1056
+ }
1057
+ prefix = `WITH ${cteStrs.join(", ")} `;
1058
+ }
748
1059
  const setClauses = entries.map(([key, value]) => {
749
1060
  const dbCol = getTableConfig(this._table).columns[key]?.name ?? key;
750
1061
  if (value && typeof value === "object" && "sql" in value && "params" in value) {
@@ -787,10 +1098,9 @@ class UpdateBuilder {
787
1098
  if (this._comment) {
788
1099
  query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
789
1100
  }
790
- return { sql: query, params };
1101
+ return { sql: prefix + query, params };
791
1102
  }
792
1103
  }
793
- // ../bungres-orm/src/builders/relational.ts
794
1104
  function getPkColumn(tableConfig) {
795
1105
  if (tableConfig.primaryKeys?.length > 0)
796
1106
  return tableConfig.primaryKeys[0];
@@ -1048,8 +1358,6 @@ class RelationalQueryBuilder {
1048
1358
  return { sql: sql2, params };
1049
1359
  }
1050
1360
  }
1051
-
1052
- // ../bungres-orm/src/core/db.ts
1053
1361
  function parseDBName(url) {
1054
1362
  try {
1055
1363
  return new URL(url).pathname.slice(1);
@@ -1158,6 +1466,7 @@ class BungresDB {
1158
1466
 
1159
1467
  class BungresTransaction {
1160
1468
  _sql;
1469
+ _savepointCounter = 0;
1161
1470
  constructor(sql2) {
1162
1471
  this._sql = sql2;
1163
1472
  }
@@ -1192,6 +1501,19 @@ class BungresTransaction {
1192
1501
  const result2 = await this._sql.unsafe(query, params);
1193
1502
  return Array.from(result2);
1194
1503
  }
1504
+ async transaction(fn) {
1505
+ this._savepointCounter++;
1506
+ const spName = `sp_${this._savepointCounter}`;
1507
+ await this._sql.unsafe(`SAVEPOINT ${spName}`);
1508
+ try {
1509
+ const result2 = await fn(this);
1510
+ await this._sql.unsafe(`RELEASE SAVEPOINT ${spName}`);
1511
+ return result2;
1512
+ } catch (e) {
1513
+ await this._sql.unsafe(`ROLLBACK TO SAVEPOINT ${spName}`);
1514
+ throw e;
1515
+ }
1516
+ }
1195
1517
  }
1196
1518
  function bungres(config2) {
1197
1519
  const db2 = new BungresDB(config2);
@@ -1215,7 +1537,6 @@ function bungres(config2) {
1215
1537
  }
1216
1538
  return db2;
1217
1539
  }
1218
- // ../bungres-orm/src/ddl.ts
1219
1540
  function generateCreateTable(config2, ifNotExists = true) {
1220
1541
  const tableName = config2.schema ? `"${config2.schema}"."${config2.name}"` : `"${config2.name}"`;
1221
1542
  const exists = ifNotExists ? " IF NOT EXISTS" : "";
@@ -1225,8 +1546,8 @@ function generateCreateTable(config2, ifNotExists = true) {
1225
1546
  columnDefs.push(`PRIMARY KEY (${pkCols})`);
1226
1547
  }
1227
1548
  if (config2.checks) {
1228
- for (const check of config2.checks) {
1229
- columnDefs.push(`CHECK (${check})`);
1549
+ for (const check2 of config2.checks) {
1550
+ columnDefs.push(`CHECK (${check2})`);
1230
1551
  }
1231
1552
  }
1232
1553
  let sql2 = `CREATE TABLE${exists} ${tableName} (
@@ -1264,7 +1585,9 @@ function buildColumnDDL(key, col2, tableName) {
1264
1585
  if (col2.unique && !col2.primaryKey) {
1265
1586
  parts.push("UNIQUE");
1266
1587
  }
1267
- if (col2.defaultFn) {
1588
+ if (col2.generatedAs) {
1589
+ parts.push(`GENERATED ALWAYS AS (${col2.generatedAs}) STORED`);
1590
+ } else if (col2.defaultFn) {
1268
1591
  parts.push(`DEFAULT ${col2.defaultFn}`);
1269
1592
  } else if (col2.defaultValue !== undefined) {
1270
1593
  parts.push(`DEFAULT ${formatDefaultValue(col2.defaultValue, col2.dataType)}`);
@@ -1285,6 +1608,13 @@ function buildColumnDDL(key, col2, tableName) {
1285
1608
  return parts.join(" ");
1286
1609
  }
1287
1610
  function buildType(col2) {
1611
+ if (col2.enumConfig) {
1612
+ return `"${col2.enumConfig.enumName}"`;
1613
+ }
1614
+ if (col2.dataType.endsWith("[]")) {
1615
+ const baseType = buildType({ ...col2, dataType: col2.dataType.slice(0, -2) });
1616
+ return `${baseType}[]`;
1617
+ }
1288
1618
  switch (col2.dataType) {
1289
1619
  case "varchar":
1290
1620
  return col2.length ? `VARCHAR(${col2.length})` : "VARCHAR";
@@ -1319,12 +1649,12 @@ function formatDefaultValue(value, dataType) {
1319
1649
  }
1320
1650
  function buildIndex(table2, idx) {
1321
1651
  const tableName = table2.schema ? `"${table2.schema}"."${table2.name}"` : `"${table2.name}"`;
1322
- const unique = idx.unique ? "UNIQUE " : "";
1652
+ const unique2 = idx.unique ? "UNIQUE " : "";
1323
1653
  const using = idx.using ? ` USING ${idx.using.toUpperCase()}` : "";
1324
1654
  const cols = idx.columns.map((c) => `"${c}"`).join(", ");
1325
1655
  const where = idx.where ? ` WHERE ${idx.where}` : "";
1326
1656
  const idxName = idx.name ?? `idx_${table2.name}_${idx.columns.join("_")}`;
1327
- return `CREATE ${unique}INDEX IF NOT EXISTS "${idxName}" ON ${tableName}${using} (${cols})${where};`;
1657
+ return `CREATE ${unique2}INDEX IF NOT EXISTS "${idxName}" ON ${tableName}${using} (${cols})${where};`;
1328
1658
  }
1329
1659
  function generateAddColumn(tableName, schema, key, col2) {
1330
1660
  const tbl = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
@@ -1334,6 +1664,7 @@ function generateDropColumn(tableName, schema, columnName) {
1334
1664
  const tbl = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
1335
1665
  return `ALTER TABLE ${tbl} DROP COLUMN IF EXISTS "${columnName}";`;
1336
1666
  }
1667
+
1337
1668
  // src/schema-loader.ts
1338
1669
  async function loadSchemas(patterns, cwd = process.cwd()) {
1339
1670
  const globs = Array.isArray(patterns) ? patterns : [patterns];
@@ -1361,144 +1692,1528 @@ function isTable(value) {
1361
1692
  return typeof value === "object" && value !== null && TableConfigSymbol in value;
1362
1693
  }
1363
1694
 
1364
- // src/utils/colors.ts
1365
- function colorize(text, color) {
1366
- const code2 = Bun.color(color, "ansi");
1367
- return code2 ? `${code2}${text}\x1B[0m` : text;
1368
- }
1695
+ // ../../node_modules/.bun/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
1696
+ import { styleText } from "util";
1697
+ import { stdout, stdin } from "process";
1698
+ import * as l from "readline";
1699
+ import l__default from "readline";
1369
1700
 
1370
- // src/commands/drop.ts
1371
- async function runDrop(config2, opts = {}) {
1372
- const schemas2 = await loadSchemas(config2.schema);
1373
- if (schemas2.length === 0) {
1374
- console.warn("No table definitions found in schema files.");
1375
- return;
1701
+ // ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/utils.js
1702
+ var getCodePointsLength = (() => {
1703
+ const SURROGATE_PAIR_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
1704
+ return (input2) => {
1705
+ let surrogatePairsNr = 0;
1706
+ SURROGATE_PAIR_RE.lastIndex = 0;
1707
+ while (SURROGATE_PAIR_RE.test(input2)) {
1708
+ surrogatePairsNr += 1;
1709
+ }
1710
+ return input2.length - surrogatePairsNr;
1711
+ };
1712
+ })();
1713
+ var isFullWidth = (x) => {
1714
+ return x === 12288 || x >= 65281 && x <= 65376 || x >= 65504 && x <= 65510;
1715
+ };
1716
+ var isWideNotCJKTNotEmoji = (x) => {
1717
+ return x === 8987 || x === 9001 || x >= 12272 && x <= 12287 || x >= 12289 && x <= 12350 || x >= 12441 && x <= 12543 || x >= 12549 && x <= 12591 || x >= 12593 && x <= 12686 || x >= 12688 && x <= 12771 || x >= 12783 && x <= 12830 || x >= 12832 && x <= 12871 || x >= 12880 && x <= 19903 || x >= 65040 && x <= 65049 || x >= 65072 && x <= 65106 || x >= 65108 && x <= 65126 || x >= 65128 && x <= 65131 || x >= 127488 && x <= 127490 || x >= 127504 && x <= 127547 || x >= 127552 && x <= 127560 || x >= 131072 && x <= 196605 || x >= 196608 && x <= 262141;
1718
+ };
1719
+
1720
+ // ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/index.js
1721
+ var ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
1722
+ var CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
1723
+ var CJKT_WIDE_RE = /(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu;
1724
+ var TAB_RE = /\t{1,1000}/y;
1725
+ var EMOJI_RE = /[\u{1F1E6}-\u{1F1FF}]{2}|\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}|(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation})(?:\u200D(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F\u20E3?))*/yu;
1726
+ var LATIN_RE = /(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y;
1727
+ var MODIFIER_RE = /\p{M}+/gu;
1728
+ var NO_TRUNCATION = { limit: Infinity, ellipsis: "" };
1729
+ var getStringTruncatedWidth = (input2, truncationOptions = {}, widthOptions = {}) => {
1730
+ const LIMIT = truncationOptions.limit ?? Infinity;
1731
+ const ELLIPSIS = truncationOptions.ellipsis ?? "";
1732
+ const ELLIPSIS_WIDTH = truncationOptions?.ellipsisWidth ?? (ELLIPSIS ? getStringTruncatedWidth(ELLIPSIS, NO_TRUNCATION, widthOptions).width : 0);
1733
+ const ANSI_WIDTH = 0;
1734
+ const CONTROL_WIDTH = widthOptions.controlWidth ?? 0;
1735
+ const TAB_WIDTH = widthOptions.tabWidth ?? 8;
1736
+ const EMOJI_WIDTH = widthOptions.emojiWidth ?? 2;
1737
+ const FULL_WIDTH_WIDTH = 2;
1738
+ const REGULAR_WIDTH = widthOptions.regularWidth ?? 1;
1739
+ const WIDE_WIDTH = widthOptions.wideWidth ?? FULL_WIDTH_WIDTH;
1740
+ const PARSE_BLOCKS = [
1741
+ [LATIN_RE, REGULAR_WIDTH],
1742
+ [ANSI_RE, ANSI_WIDTH],
1743
+ [CONTROL_RE, CONTROL_WIDTH],
1744
+ [TAB_RE, TAB_WIDTH],
1745
+ [EMOJI_RE, EMOJI_WIDTH],
1746
+ [CJKT_WIDE_RE, WIDE_WIDTH]
1747
+ ];
1748
+ let indexPrev = 0;
1749
+ let index = 0;
1750
+ let length = input2.length;
1751
+ let lengthExtra = 0;
1752
+ let truncationEnabled = false;
1753
+ let truncationIndex = length;
1754
+ let truncationLimit = Math.max(0, LIMIT - ELLIPSIS_WIDTH);
1755
+ let unmatchedStart = 0;
1756
+ let unmatchedEnd = 0;
1757
+ let width = 0;
1758
+ let widthExtra = 0;
1759
+ outer:
1760
+ while (true) {
1761
+ if (unmatchedEnd > unmatchedStart || index >= length && index > indexPrev) {
1762
+ const unmatched = input2.slice(unmatchedStart, unmatchedEnd) || input2.slice(indexPrev, index);
1763
+ lengthExtra = 0;
1764
+ for (const char of unmatched.replaceAll(MODIFIER_RE, "")) {
1765
+ const codePoint = char.codePointAt(0) || 0;
1766
+ if (isFullWidth(codePoint)) {
1767
+ widthExtra = FULL_WIDTH_WIDTH;
1768
+ } else if (isWideNotCJKTNotEmoji(codePoint)) {
1769
+ widthExtra = WIDE_WIDTH;
1770
+ } else {
1771
+ widthExtra = REGULAR_WIDTH;
1772
+ }
1773
+ if (width + widthExtra > truncationLimit) {
1774
+ truncationIndex = Math.min(truncationIndex, Math.max(unmatchedStart, indexPrev) + lengthExtra);
1775
+ }
1776
+ if (width + widthExtra > LIMIT) {
1777
+ truncationEnabled = true;
1778
+ break outer;
1779
+ }
1780
+ lengthExtra += char.length;
1781
+ width += widthExtra;
1782
+ }
1783
+ unmatchedStart = unmatchedEnd = 0;
1784
+ }
1785
+ if (index >= length) {
1786
+ break outer;
1787
+ }
1788
+ for (let i = 0, l = PARSE_BLOCKS.length;i < l; i++) {
1789
+ const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i];
1790
+ BLOCK_RE.lastIndex = index;
1791
+ if (BLOCK_RE.test(input2)) {
1792
+ lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input2.slice(index, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index;
1793
+ widthExtra = lengthExtra * BLOCK_WIDTH;
1794
+ if (width + widthExtra > truncationLimit) {
1795
+ truncationIndex = Math.min(truncationIndex, index + Math.floor((truncationLimit - width) / BLOCK_WIDTH));
1796
+ }
1797
+ if (width + widthExtra > LIMIT) {
1798
+ truncationEnabled = true;
1799
+ break outer;
1800
+ }
1801
+ width += widthExtra;
1802
+ unmatchedStart = indexPrev;
1803
+ unmatchedEnd = index;
1804
+ index = indexPrev = BLOCK_RE.lastIndex;
1805
+ continue outer;
1806
+ }
1807
+ }
1808
+ index += 1;
1809
+ }
1810
+ return {
1811
+ width: truncationEnabled ? truncationLimit : width,
1812
+ index: truncationEnabled ? truncationIndex : length,
1813
+ truncated: truncationEnabled,
1814
+ ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH
1815
+ };
1816
+ };
1817
+ var dist_default = getStringTruncatedWidth;
1818
+
1819
+ // ../../node_modules/.bun/fast-string-width@3.0.2/node_modules/fast-string-width/dist/index.js
1820
+ var NO_TRUNCATION2 = {
1821
+ limit: Infinity,
1822
+ ellipsis: "",
1823
+ ellipsisWidth: 0
1824
+ };
1825
+ var fastStringWidth = (input2, options = {}) => {
1826
+ return dist_default(input2, NO_TRUNCATION2, options).width;
1827
+ };
1828
+ var dist_default2 = fastStringWidth;
1829
+
1830
+ // ../../node_modules/.bun/fast-wrap-ansi@0.2.2/node_modules/fast-wrap-ansi/lib/main.js
1831
+ var ESC = "\x1B";
1832
+ var CSI = "\x9B";
1833
+ var END_CODE = 39;
1834
+ var ANSI_ESCAPE_BELL = "\x07";
1835
+ var ANSI_CSI = "[";
1836
+ var ANSI_OSC = "]";
1837
+ var ANSI_SGR_TERMINATOR = "m";
1838
+ var ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
1839
+ var GROUP_REGEX = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`, "y");
1840
+ var getClosingCode = (openingCode) => {
1841
+ if (openingCode >= 30 && openingCode <= 37)
1842
+ return 39;
1843
+ if (openingCode >= 90 && openingCode <= 97)
1844
+ return 39;
1845
+ if (openingCode >= 40 && openingCode <= 47)
1846
+ return 49;
1847
+ if (openingCode >= 100 && openingCode <= 107)
1848
+ return 49;
1849
+ if (openingCode === 1 || openingCode === 2)
1850
+ return 22;
1851
+ if (openingCode === 3)
1852
+ return 23;
1853
+ if (openingCode === 4)
1854
+ return 24;
1855
+ if (openingCode === 7)
1856
+ return 27;
1857
+ if (openingCode === 8)
1858
+ return 28;
1859
+ if (openingCode === 9)
1860
+ return 29;
1861
+ if (openingCode === 0)
1862
+ return 0;
1863
+ return;
1864
+ };
1865
+ var wrapAnsiCode = (code2) => `${ESC}${ANSI_CSI}${code2}${ANSI_SGR_TERMINATOR}`;
1866
+ var wrapAnsiHyperlink = (url) => `${ESC}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
1867
+ var wrapWord = (rows, word, columns) => {
1868
+ const characters = word[Symbol.iterator]();
1869
+ let isInsideEscape = false;
1870
+ let isInsideLinkEscape = false;
1871
+ let lastRow = rows.at(-1);
1872
+ let visible = lastRow === undefined ? 0 : dist_default2(lastRow);
1873
+ let currentCharacter = characters.next();
1874
+ let nextCharacter = characters.next();
1875
+ let rawCharacterIndex = 0;
1876
+ while (!currentCharacter.done) {
1877
+ const character = currentCharacter.value;
1878
+ const characterLength = dist_default2(character);
1879
+ if (visible + characterLength <= columns) {
1880
+ rows[rows.length - 1] += character;
1881
+ } else {
1882
+ rows.push(character);
1883
+ visible = 0;
1884
+ }
1885
+ if (character === ESC || character === CSI) {
1886
+ isInsideEscape = true;
1887
+ isInsideLinkEscape = word.startsWith(ANSI_ESCAPE_LINK, rawCharacterIndex + 1);
1888
+ }
1889
+ if (isInsideEscape) {
1890
+ if (isInsideLinkEscape) {
1891
+ if (character === ANSI_ESCAPE_BELL) {
1892
+ isInsideEscape = false;
1893
+ isInsideLinkEscape = false;
1894
+ }
1895
+ } else if (character === ANSI_SGR_TERMINATOR) {
1896
+ isInsideEscape = false;
1897
+ }
1898
+ } else {
1899
+ visible += characterLength;
1900
+ if (visible === columns && !nextCharacter.done) {
1901
+ rows.push("");
1902
+ visible = 0;
1903
+ }
1904
+ }
1905
+ currentCharacter = nextCharacter;
1906
+ nextCharacter = characters.next();
1907
+ rawCharacterIndex += character.length;
1376
1908
  }
1377
- const sql2 = new Bun.SQL(config2.dbUrl);
1378
- try {
1379
- const userSchema = config2.dbSchema;
1380
- const existingUserTablesResult = await sql2.unsafe(`SELECT table_name FROM information_schema.tables WHERE table_schema = $1`, [userSchema]);
1381
- const existingTableNames = new Set(existingUserTablesResult.map((r) => r.table_name));
1382
- const migTableCheck = await sql2.unsafe(`SELECT EXISTS (
1383
- SELECT 1 FROM information_schema.tables
1384
- WHERE table_schema = $1 AND table_name = $2
1385
- ) AS exists`, [config2.migrationsSchema, config2.migrationsTable]);
1386
- const migrationTableExists = migTableCheck[0]?.exists ?? false;
1387
- const pushTableCheck = await sql2.unsafe(`SELECT EXISTS (
1388
- SELECT 1 FROM information_schema.tables
1389
- WHERE table_schema = $1 AND table_name = $2
1390
- ) AS exists`, [config2.migrationsSchema, "__bungres_push"]);
1391
- const pushTableExists = pushTableCheck[0]?.exists ?? false;
1392
- const tablesToDrop = schemas2.filter((s) => existingTableNames.has(s.config.name));
1393
- if (tablesToDrop.length === 0 && !migrationTableExists && !pushTableExists) {
1394
- console.log("No tables to drop (they either don't exist or were already dropped).");
1395
- return;
1909
+ lastRow = rows.at(-1);
1910
+ if (!visible && lastRow !== undefined && lastRow.length && rows.length > 1) {
1911
+ rows[rows.length - 2] += rows.pop();
1912
+ }
1913
+ };
1914
+ var stringVisibleTrimSpacesRight = (string) => {
1915
+ const words = string.split(" ");
1916
+ let last = words.length;
1917
+ while (last) {
1918
+ if (dist_default2(words[last - 1])) {
1919
+ break;
1396
1920
  }
1397
- const tableNamesToPrint = tablesToDrop.map((s) => (s.config.schema ? s.config.schema + "." : "") + s.config.name);
1398
- if (migrationTableExists) {
1399
- tableNamesToPrint.push(`${config2.migrationsSchema}.${config2.migrationsTable}`);
1921
+ last--;
1922
+ }
1923
+ if (last === words.length) {
1924
+ return string;
1925
+ }
1926
+ return words.slice(0, last).join(" ") + words.slice(last).join("");
1927
+ };
1928
+ var exec = (string, columns, options = {}) => {
1929
+ if (options.trim !== false && string.trim() === "") {
1930
+ return "";
1931
+ }
1932
+ let returnValue = "";
1933
+ let escapeCode;
1934
+ let escapeUrl;
1935
+ const words = string.split(" ");
1936
+ let rows = [""];
1937
+ let rowLength = 0;
1938
+ for (let index = 0;index < words.length; index++) {
1939
+ const word = words[index];
1940
+ if (options.trim !== false) {
1941
+ const row = rows.at(-1) ?? "";
1942
+ const trimmed = row.trimStart();
1943
+ if (row.length !== trimmed.length) {
1944
+ rows[rows.length - 1] = trimmed;
1945
+ rowLength = dist_default2(trimmed);
1946
+ }
1400
1947
  }
1401
- if (pushTableExists) {
1402
- tableNamesToPrint.push(`${config2.migrationsSchema}.__bungres_push`);
1948
+ if (index !== 0) {
1949
+ if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
1950
+ rows.push("");
1951
+ rowLength = 0;
1952
+ }
1953
+ if (rowLength || options.trim === false) {
1954
+ rows[rows.length - 1] += " ";
1955
+ rowLength++;
1956
+ }
1403
1957
  }
1404
- if (!opts.force) {
1405
- console.warn(colorize(`
1406
- \u26A0\uFE0F WARNING: This will drop the following tables and ALL their data:
1407
- `, "red"));
1408
- for (const name of tableNamesToPrint)
1409
- console.log(colorize(` - ${name}`, "yellow"));
1410
- process.stdout.write(colorize(`
1411
- Are you sure? Type YES to continue: `, "cyan"));
1412
- for await (const line2 of console) {
1413
- if (line2.trim().toLowerCase() !== "yes") {
1414
- console.log("Aborted.");
1415
- return;
1416
- }
1417
- break;
1958
+ const wordLength = dist_default2(word);
1959
+ if (options.hard && wordLength > columns) {
1960
+ const remainingColumns = columns - rowLength;
1961
+ const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) / columns);
1962
+ const breaksStartingNextLine = Math.floor((wordLength - 1) / columns);
1963
+ if (breaksStartingNextLine < breaksStartingThisLine) {
1964
+ rows.push("");
1418
1965
  }
1966
+ wrapWord(rows, word, columns);
1967
+ rowLength = dist_default2(rows.at(-1) ?? "");
1968
+ continue;
1419
1969
  }
1420
- for (const entry of tablesToDrop) {
1421
- const ddl = `DROP TABLE IF EXISTS "${entry.config.name}" CASCADE;`;
1422
- await sql2.unsafe(ddl);
1423
- console.log(colorize(` \u2713 dropped ${entry.config.name}`, "green"));
1970
+ if (rowLength + wordLength > columns && rowLength && wordLength) {
1971
+ if (options.wordWrap === false && rowLength < columns) {
1972
+ wrapWord(rows, word, columns);
1973
+ rowLength = dist_default2(rows.at(-1) ?? "");
1974
+ continue;
1975
+ }
1976
+ rows.push("");
1977
+ rowLength = 0;
1424
1978
  }
1425
- if (migrationTableExists) {
1426
- const qualifiedMigTable = `"${config2.migrationsSchema}"."${config2.migrationsTable}"`;
1427
- await sql2.unsafe(`DROP TABLE IF EXISTS ${qualifiedMigTable} CASCADE`);
1428
- console.log(colorize(` \u2713 dropped ${config2.migrationsSchema}.${config2.migrationsTable}`, "green"));
1979
+ if (rowLength + wordLength > columns && options.wordWrap === false) {
1980
+ wrapWord(rows, word, columns);
1981
+ rowLength = dist_default2(rows.at(-1) ?? "");
1982
+ continue;
1429
1983
  }
1430
- if (pushTableExists) {
1431
- await sql2.unsafe(`DROP TABLE IF EXISTS "${config2.migrationsSchema}"."__bungres_push" CASCADE`);
1432
- console.log(colorize(` \u2713 dropped ${config2.migrationsSchema}.__bungres_push`, "green"));
1984
+ rows[rows.length - 1] += word;
1985
+ rowLength += wordLength;
1986
+ }
1987
+ if (options.trim !== false) {
1988
+ rows = rows.map((row) => stringVisibleTrimSpacesRight(row));
1989
+ }
1990
+ const preString = rows.join(`
1991
+ `);
1992
+ let inSurrogate = false;
1993
+ for (let i = 0;i < preString.length; i++) {
1994
+ const character = preString[i];
1995
+ returnValue += character;
1996
+ if (!inSurrogate) {
1997
+ inSurrogate = character >= "\uD800" && character <= "\uDBFF";
1998
+ if (inSurrogate) {
1999
+ continue;
2000
+ }
2001
+ } else {
2002
+ inSurrogate = false;
2003
+ }
2004
+ if (character === ESC || character === CSI) {
2005
+ GROUP_REGEX.lastIndex = i + 1;
2006
+ const groupsResult = GROUP_REGEX.exec(preString);
2007
+ const groups = groupsResult?.groups;
2008
+ if (groups?.code !== undefined) {
2009
+ const code2 = Number.parseFloat(groups.code);
2010
+ escapeCode = code2 === END_CODE ? undefined : code2;
2011
+ } else if (groups?.uri !== undefined) {
2012
+ escapeUrl = groups.uri.length === 0 ? undefined : groups.uri;
2013
+ }
2014
+ }
2015
+ if (preString[i + 1] === `
2016
+ `) {
2017
+ if (escapeUrl) {
2018
+ returnValue += wrapAnsiHyperlink("");
2019
+ }
2020
+ const closingCode = escapeCode ? getClosingCode(escapeCode) : undefined;
2021
+ if (escapeCode && closingCode) {
2022
+ returnValue += wrapAnsiCode(closingCode);
2023
+ }
2024
+ } else if (character === `
2025
+ `) {
2026
+ if (escapeCode && getClosingCode(escapeCode)) {
2027
+ returnValue += wrapAnsiCode(escapeCode);
2028
+ }
2029
+ if (escapeUrl) {
2030
+ returnValue += wrapAnsiHyperlink(escapeUrl);
2031
+ }
1433
2032
  }
1434
- console.log(colorize(`
1435
- Drop complete.`, "green"));
1436
- } finally {
1437
- await sql2.end();
1438
2033
  }
2034
+ return returnValue;
2035
+ };
2036
+ var CRLF_OR_LF = /\r?\n/;
2037
+ function wrapAnsi(string, columns, options) {
2038
+ return String(string).normalize().split(CRLF_OR_LF).map((line2) => exec(line2, columns, options)).join(`
2039
+ `);
1439
2040
  }
1440
2041
 
1441
- // src/commands/migrate.ts
1442
- import { join as join2, resolve as resolve2 } from "path";
1443
- async function runMigrate(config2) {
1444
- const migrationsDir = resolve2(config2.out);
1445
- const sql2 = new Bun.SQL(config2.dbUrl);
1446
- const table2 = config2.migrationsTable;
1447
- const schema = config2.migrationsSchema;
1448
- const qualifiedTable = `"${schema}"."${table2}"`;
1449
- const createSchema = `CREATE SCHEMA IF NOT EXISTS "${schema}";`;
1450
- const createMigrationsTable = `
1451
- CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
1452
- id SERIAL PRIMARY KEY,
1453
- name TEXT NOT NULL UNIQUE,
1454
- applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
1455
- );`.trim();
1456
- try {
1457
- await sql2.unsafe(createSchema);
1458
- await sql2.unsafe(createMigrationsTable);
1459
- const glob = new Bun.Glob("*.sql");
1460
- const files = [];
1461
- for await (const file of glob.scan({ cwd: migrationsDir, absolute: false })) {
1462
- files.push(file);
2042
+ // ../../node_modules/.bun/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
2043
+ var import_sisteransi = __toESM(require_src(), 1);
2044
+ import { ReadStream } from "tty";
2045
+ function findCursor(s, o, l2) {
2046
+ if (!l2.some((r) => !r.disabled))
2047
+ return s;
2048
+ const t = s + o, n = Math.max(l2.length - 1, 0), e = t < 0 ? n : t > n ? 0 : t;
2049
+ return l2[e]?.disabled ? findCursor(e, o < 0 ? -1 : 1, l2) : e;
2050
+ }
2051
+ function findTextCursor(s, o, l2, i) {
2052
+ const t = i.split(`
2053
+ `);
2054
+ let n = 0, e = s;
2055
+ for (const r of t) {
2056
+ if (e <= r.length)
2057
+ break;
2058
+ e -= r.length + 1, n++;
2059
+ }
2060
+ for (n = Math.max(0, Math.min(t.length - 1, n + l2)), e = Math.min(e, t[n].length) + o;e < 0 && n > 0; )
2061
+ n--, e += t[n].length + 1;
2062
+ for (;e > t[n].length && n < t.length - 1; )
2063
+ e -= t[n].length + 1, n++;
2064
+ e = Math.max(0, Math.min(t[n].length, e));
2065
+ let h = 0;
2066
+ for (let r = 0;r < n; r++)
2067
+ h += t[r].length + 1;
2068
+ return h + e;
2069
+ }
2070
+ var a$1 = ["up", "down", "left", "right", "space", "enter", "cancel"];
2071
+ var t = [
2072
+ "January",
2073
+ "February",
2074
+ "March",
2075
+ "April",
2076
+ "May",
2077
+ "June",
2078
+ "July",
2079
+ "August",
2080
+ "September",
2081
+ "October",
2082
+ "November",
2083
+ "December"
2084
+ ];
2085
+ var settings = {
2086
+ actions: new Set(a$1),
2087
+ aliases: /* @__PURE__ */ new Map([
2088
+ ["k", "up"],
2089
+ ["j", "down"],
2090
+ ["h", "left"],
2091
+ ["l", "right"],
2092
+ ["\x03", "cancel"],
2093
+ ["escape", "cancel"]
2094
+ ]),
2095
+ messages: {
2096
+ cancel: "Canceled",
2097
+ error: "Something went wrong"
2098
+ },
2099
+ withGuide: true,
2100
+ date: {
2101
+ monthNames: [...t],
2102
+ messages: {
2103
+ required: "Please enter a valid date",
2104
+ invalidMonth: "There are only 12 months in a year",
2105
+ invalidDay: (n, e) => `There are only ${n} days in ${e}`,
2106
+ afterMin: (n) => `Date must be on or after ${n.toISOString().slice(0, 10)}`,
2107
+ beforeMax: (n) => `Date must be on or before ${n.toISOString().slice(0, 10)}`
1463
2108
  }
1464
- files.sort();
1465
- if (files.length === 0) {
1466
- console.log(colorize("No migration files found in " + migrationsDir, "yellow"));
1467
- console.log(colorize("Run `bungres generate` first.", "yellow"));
2109
+ }
2110
+ };
2111
+ function isActionKey(n, e) {
2112
+ if (typeof n == "string")
2113
+ return settings.aliases.get(n) === e;
2114
+ for (const s of n)
2115
+ if (s !== undefined && isActionKey(s, e))
2116
+ return true;
2117
+ return false;
2118
+ }
2119
+ function diffLines(i, s) {
2120
+ if (i === s)
2121
+ return;
2122
+ const e = i.split(`
2123
+ `), t2 = s.split(`
2124
+ `), r = Math.max(e.length, t2.length), f = [];
2125
+ for (let n = 0;n < r; n++)
2126
+ e[n] !== t2[n] && f.push(n);
2127
+ return {
2128
+ lines: f,
2129
+ numLinesBefore: e.length,
2130
+ numLinesAfter: t2.length,
2131
+ numLines: r
2132
+ };
2133
+ }
2134
+ var R = globalThis.process.platform.startsWith("win");
2135
+ var CANCEL_SYMBOL = Symbol("clack:cancel");
2136
+ function isCancel(e) {
2137
+ return e === CANCEL_SYMBOL;
2138
+ }
2139
+ function setRawMode(e, r) {
2140
+ const o = e;
2141
+ o.isTTY && o.setRawMode(r);
2142
+ }
2143
+ function block({
2144
+ input: e = stdin,
2145
+ output: r = stdout,
2146
+ overwrite: o = true,
2147
+ hideCursor: t2 = true
2148
+ } = {}) {
2149
+ const s = l.createInterface({
2150
+ input: e,
2151
+ output: r,
2152
+ prompt: "",
2153
+ tabSize: 1
2154
+ });
2155
+ l.emitKeypressEvents(e, s), e instanceof ReadStream && e.isTTY && e.setRawMode(true);
2156
+ const n = (f, { name: a, sequence: p }) => {
2157
+ const c = String(f);
2158
+ if (isActionKey([c, a, p], "cancel")) {
2159
+ t2 && r.write(import_sisteransi.cursor.show), process.exit(0);
1468
2160
  return;
1469
2161
  }
1470
- const applied = await sql2.unsafe(`SELECT name FROM ${qualifiedTable}`);
1471
- const appliedSet = new Set(applied.map((r) => r.name));
1472
- const pending = files.filter((f) => !appliedSet.has(f));
1473
- if (pending.length === 0) {
1474
- console.log(colorize("Everything is up to date.", "green"));
2162
+ if (!o)
1475
2163
  return;
1476
- }
1477
- console.log(colorize(`
1478
- Running ${pending.length} pending migration(s)...
1479
- `, "cyan"));
1480
- for (const file of pending) {
1481
- const content = await Bun.file(join2(migrationsDir, file)).text();
1482
- if (config2.verbose) {
1483
- console.log(`-- ${file} --
1484
- ${content}
2164
+ const i = a === "return" ? 0 : -1, m = a === "return" ? -1 : 0;
2165
+ l.moveCursor(r, i, m, () => {
2166
+ l.clearLine(r, 1, () => {
2167
+ e.once("keypress", n);
2168
+ });
2169
+ });
2170
+ };
2171
+ return t2 && r.write(import_sisteransi.cursor.hide), e.once("keypress", n), () => {
2172
+ e.off("keypress", n), t2 && r.write(import_sisteransi.cursor.show), e instanceof ReadStream && e.isTTY && !R && e.setRawMode(false), s.terminal = false, s.close();
2173
+ };
2174
+ }
2175
+ var getColumns = (e) => ("columns" in e) && typeof e.columns == "number" ? e.columns : 80;
2176
+ var getRows = (e) => ("rows" in e) && typeof e.rows == "number" ? e.rows : 20;
2177
+ function wrapTextWithPrefix(e, r, o, t2 = o, s = o, n) {
2178
+ const f = getColumns(e ?? stdout);
2179
+ return wrapAnsi(r, f - o.length, {
2180
+ hard: true,
2181
+ trim: false
2182
+ }).split(`
2183
+ `).map((c, i, m) => {
2184
+ const d = n ? n(c, i) : c;
2185
+ return i === 0 ? `${t2}${d}` : i === m.length - 1 ? `${s}${d}` : `${o}${d}`;
2186
+ }).join(`
1485
2187
  `);
2188
+ }
2189
+ function runValidation(e, n) {
2190
+ if ("~standard" in e) {
2191
+ const a = e["~standard"].validate(n);
2192
+ if (a instanceof Promise)
2193
+ throw new TypeError("Schema validation must be synchronous. Update `validate()` and remove any asynchronous logic.");
2194
+ return a.issues?.at(0)?.message;
2195
+ }
2196
+ return e(n);
2197
+ }
2198
+
2199
+ class V {
2200
+ input;
2201
+ output;
2202
+ _abortSignal;
2203
+ rl;
2204
+ opts;
2205
+ _render;
2206
+ _track = false;
2207
+ _prevFrame = "";
2208
+ _subscribers = /* @__PURE__ */ new Map;
2209
+ _cursor = 0;
2210
+ state = "initial";
2211
+ error = "";
2212
+ value;
2213
+ userInput = "";
2214
+ constructor(t2, e = true) {
2215
+ const { input: i = stdin, output: n = stdout, render: s, signal: r, ...o } = t2;
2216
+ this.opts = o, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = s.bind(this), this._track = e, this._abortSignal = r, this.input = i, this.output = n;
2217
+ }
2218
+ unsubscribe() {
2219
+ this._subscribers.clear();
2220
+ }
2221
+ setSubscriber(t2, e) {
2222
+ const i = this._subscribers.get(t2) ?? [];
2223
+ i.push(e), this._subscribers.set(t2, i);
2224
+ }
2225
+ on(t2, e) {
2226
+ this.setSubscriber(t2, { cb: e });
2227
+ }
2228
+ once(t2, e) {
2229
+ this.setSubscriber(t2, { cb: e, once: true });
2230
+ }
2231
+ emit(t2, ...e) {
2232
+ const i = this._subscribers.get(t2) ?? [], n = [];
2233
+ for (const s of i)
2234
+ s.cb(...e), s.once && n.push(() => i.splice(i.indexOf(s), 1));
2235
+ for (const s of n)
2236
+ s();
2237
+ }
2238
+ prompt() {
2239
+ return new Promise((t2) => {
2240
+ if (this._abortSignal) {
2241
+ if (this._abortSignal.aborted)
2242
+ return this.state = "cancel", this.close(), t2(CANCEL_SYMBOL);
2243
+ this._abortSignal.addEventListener("abort", () => {
2244
+ this.state = "cancel", this.close();
2245
+ }, { once: true });
1486
2246
  }
1487
- await sql2.transaction(async (txSql) => {
1488
- const statements = config2.breakpoints ? content.split(/-->statement-breakpoint/g).flatMap((chunk) => chunk.split(";").map((s) => s.trim()).filter(Boolean)) : content.split(";").map((s) => s.trim()).filter(Boolean);
1489
- for (const stmt of statements) {
1490
- await txSql.unsafe(stmt + ";");
1491
- }
1492
- await txSql.unsafe(`INSERT INTO ${qualifiedTable} (name) VALUES ($1)`, [file]);
2247
+ this.rl = l__default.createInterface({
2248
+ input: this.input,
2249
+ tabSize: 2,
2250
+ prompt: "",
2251
+ escapeCodeTimeout: 50,
2252
+ terminal: true
2253
+ }), this.rl.prompt(), this.opts.initialUserInput !== undefined && this._setUserInput(this.opts.initialUserInput, true), this.input.on("keypress", this.onKeypress), setRawMode(this.input, true), this.output.on("resize", this.render), this.render(), this.once("submit", () => {
2254
+ this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t2(this.value);
2255
+ }), this.once("cancel", () => {
2256
+ this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t2(CANCEL_SYMBOL);
1493
2257
  });
1494
- console.log(colorize(` \u2713 ${file}`, "green"));
1495
- }
1496
- console.log(colorize(`
1497
- Done.`, "green"));
1498
- } finally {
1499
- await sql2.end();
2258
+ });
1500
2259
  }
1501
- }
2260
+ _isActionKey(t2, e) {
2261
+ return t2 === "\t";
2262
+ }
2263
+ _shouldSubmit(t2, e) {
2264
+ return true;
2265
+ }
2266
+ _setValue(t2) {
2267
+ this.value = t2, this.emit("value", this.value);
2268
+ }
2269
+ _setUserInput(t2, e) {
2270
+ this.userInput = t2 ?? "", this.emit("userInput", this.userInput), e && this._track && this.rl && (this.rl.write(this.userInput), this._cursor = this.rl.cursor);
2271
+ }
2272
+ _clearUserInput() {
2273
+ this.rl?.write(null, { ctrl: true, name: "u" }), this._setUserInput("");
2274
+ }
2275
+ onKeypress(t2, e) {
2276
+ if (this._track && e.name !== "return" && (e.name && this._isActionKey(t2, e) && this.rl?.write(null, { ctrl: true, name: "h" }), this._cursor = this.rl?.cursor ?? 0, this._setUserInput(this.rl?.line)), this.state === "error" && (this.state = "active"), e?.name && (!this._track && settings.aliases.has(e.name) && this.emit("cursor", settings.aliases.get(e.name)), settings.actions.has(e.name) && this.emit("cursor", e.name)), t2 && (t2.toLowerCase() === "y" || t2.toLowerCase() === "n") && this.emit("confirm", t2.toLowerCase() === "y"), this.emit("key", t2, e), e?.name === "return" && this._shouldSubmit(t2, e)) {
2277
+ if (this.opts.validate) {
2278
+ const i = runValidation(this.opts.validate, this.value);
2279
+ i && (this.error = i instanceof Error ? i.message : i, this.state = "error", this.rl?.write(this.userInput));
2280
+ }
2281
+ this.state !== "error" && (this.state = "submit");
2282
+ }
2283
+ isActionKey([t2, e?.name, e?.sequence], "cancel") && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close();
2284
+ }
2285
+ close() {
2286
+ this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
2287
+ `), setRawMode(this.input, false), this.rl?.close(), this.rl = undefined, this.emit(`${this.state}`, this.value), this.unsubscribe();
2288
+ }
2289
+ restoreCursor() {
2290
+ const t2 = wrapAnsi(this._prevFrame, process.stdout.columns, { hard: true, trim: false }).split(`
2291
+ `).length - 1;
2292
+ this.output.write(import_sisteransi.cursor.move(-999, t2 * -1));
2293
+ }
2294
+ render() {
2295
+ const t2 = wrapAnsi(this._render(this) ?? "", process.stdout.columns, {
2296
+ hard: true,
2297
+ trim: false
2298
+ });
2299
+ if (t2 !== this._prevFrame) {
2300
+ if (this.state === "initial")
2301
+ this.output.write(import_sisteransi.cursor.hide);
2302
+ else {
2303
+ const e = diffLines(this._prevFrame, t2), i = getRows(this.output);
2304
+ if (this.restoreCursor(), e) {
2305
+ const n = Math.max(0, e.numLinesAfter - i), s = Math.max(0, e.numLinesBefore - i);
2306
+ let r = e.lines.find((o) => o >= n);
2307
+ if (r === undefined) {
2308
+ this._prevFrame = t2;
2309
+ return;
2310
+ }
2311
+ if (e.lines.length === 1) {
2312
+ this.output.write(import_sisteransi.cursor.move(0, r - s)), this.output.write(import_sisteransi.erase.lines(1));
2313
+ const o = t2.split(`
2314
+ `);
2315
+ this.output.write(o[r]), this._prevFrame = t2, this.output.write(import_sisteransi.cursor.move(0, o.length - r - 1));
2316
+ return;
2317
+ } else if (e.lines.length > 1) {
2318
+ if (n < s)
2319
+ r = n;
2320
+ else {
2321
+ const h = r - s;
2322
+ h > 0 && this.output.write(import_sisteransi.cursor.move(0, h));
2323
+ }
2324
+ this.output.write(import_sisteransi.erase.down());
2325
+ const f = t2.split(`
2326
+ `).slice(r);
2327
+ this.output.write(f.join(`
2328
+ `)), this._prevFrame = t2;
2329
+ return;
2330
+ }
2331
+ }
2332
+ this.output.write(import_sisteransi.erase.down());
2333
+ }
2334
+ this.output.write(t2), this.state === "initial" && (this.state = "active"), this._prevFrame = t2;
2335
+ }
2336
+ }
2337
+ }
2338
+ function p$1(l2, e) {
2339
+ if (l2 === undefined || e.length === 0)
2340
+ return 0;
2341
+ const i = e.findIndex((s) => s.value === l2);
2342
+ return i !== -1 ? i : 0;
2343
+ }
2344
+ function g(l2, e) {
2345
+ return (e.label ?? String(e.value)).toLowerCase().includes(l2.toLowerCase());
2346
+ }
2347
+ function m(l2, e) {
2348
+ if (e)
2349
+ return l2 ? e : e[0];
2350
+ }
2351
+ var T$1 = class T extends V {
2352
+ filteredOptions;
2353
+ multiple;
2354
+ isNavigating = false;
2355
+ selectedValues = [];
2356
+ focusedValue;
2357
+ #e = 0;
2358
+ #s = "";
2359
+ #t;
2360
+ #i;
2361
+ #n;
2362
+ get cursor() {
2363
+ return this.#e;
2364
+ }
2365
+ get userInputWithCursor() {
2366
+ if (!this.userInput)
2367
+ return styleText(["inverse", "hidden"], "_");
2368
+ if (this._cursor >= this.userInput.length)
2369
+ return `${this.userInput}\u2588`;
2370
+ const e = this.userInput.slice(0, this.cursor), t2 = this.userInput.slice(this.cursor, this.cursor + 1), i = this.userInput.slice(this.cursor + 1);
2371
+ return `${e}${styleText("inverse", t2)}${i}`;
2372
+ }
2373
+ get options() {
2374
+ return typeof this.#i == "function" ? this.#i() : this.#i;
2375
+ }
2376
+ constructor(e) {
2377
+ super(e), this.#i = e.options, this.#n = e.placeholder;
2378
+ const t2 = this.options;
2379
+ this.filteredOptions = [...t2], this.multiple = e.multiple === true, this.#t = typeof e.options == "function" ? e.filter : e.filter ?? g;
2380
+ let i;
2381
+ if (e.initialValue && Array.isArray(e.initialValue) ? this.multiple ? i = e.initialValue : i = e.initialValue.slice(0, 1) : !this.multiple && this.options.length > 0 && (i = [this.options[0]?.value]), i)
2382
+ for (const s of i) {
2383
+ const n = t2.findIndex((o) => o.value === s);
2384
+ n !== -1 && (this.toggleSelected(s), this.#e = n);
2385
+ }
2386
+ this.focusedValue = this.options[this.#e]?.value, this.on("key", (s, n) => this.#l(s, n)), this.on("userInput", (s) => this.#u(s));
2387
+ }
2388
+ _isActionKey(e, t2) {
2389
+ return e === "\t" || this.multiple && this.isNavigating && t2.name === "space" && e !== undefined && e !== "";
2390
+ }
2391
+ #l(e, t2) {
2392
+ const i = t2.name === "up", s = t2.name === "down", n = t2.name === "return", o = this.userInput === "" || this.userInput === "\t", u = this.#n, a = this.options, f = u !== undefined && u !== "" && a.some((r) => !r.disabled && (this.#t ? this.#t(u, r) : true));
2393
+ if (t2.name === "tab" && o && f) {
2394
+ this.userInput === "\t" && this._clearUserInput(), this._setUserInput(u, true), this.isNavigating = false;
2395
+ return;
2396
+ }
2397
+ i || s ? (this.#e = findCursor(this.#e, i ? -1 : 1, this.filteredOptions), this.focusedValue = this.filteredOptions[this.#e]?.value, this.multiple || (this.selectedValues = [this.focusedValue]), this.isNavigating = true) : n ? this.value = m(this.multiple, this.selectedValues) : this.multiple ? this.focusedValue !== undefined && (t2.name === "tab" || this.isNavigating && t2.name === "space") ? this.toggleSelected(this.focusedValue) : this.isNavigating = false : (this.focusedValue && (this.selectedValues = [this.focusedValue]), this.isNavigating = false);
2398
+ }
2399
+ deselectAll() {
2400
+ this.selectedValues = [];
2401
+ }
2402
+ toggleSelected(e) {
2403
+ this.filteredOptions.length !== 0 && (this.multiple ? this.selectedValues.includes(e) ? this.selectedValues = this.selectedValues.filter((t2) => t2 !== e) : this.selectedValues = [...this.selectedValues, e] : this.selectedValues = [e]);
2404
+ }
2405
+ #u(e) {
2406
+ if (e !== this.#s) {
2407
+ this.#s = e;
2408
+ const t2 = this.options;
2409
+ e && this.#t ? this.filteredOptions = t2.filter((n) => this.#t?.(e, n)) : this.filteredOptions = [...t2];
2410
+ const i = p$1(this.focusedValue, this.filteredOptions);
2411
+ this.#e = findCursor(i, 0, this.filteredOptions);
2412
+ const s = this.filteredOptions[this.#e];
2413
+ s && !s.disabled ? this.focusedValue = s.value : this.focusedValue = undefined, this.multiple || (this.focusedValue !== undefined ? this.toggleSelected(this.focusedValue) : this.deselectAll());
2414
+ }
2415
+ }
2416
+ };
2417
+
2418
+ class r extends V {
2419
+ get cursor() {
2420
+ return this.value ? 0 : 1;
2421
+ }
2422
+ get _value() {
2423
+ return this.cursor === 0;
2424
+ }
2425
+ constructor(t2) {
2426
+ super(t2, false), this.value = !!t2.initialValue, this.on("userInput", () => {
2427
+ this.value = this._value;
2428
+ }), this.on("confirm", (i) => {
2429
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = i, this.state = "submit", this.close();
2430
+ }), this.on("cursor", () => {
2431
+ this.value = !this.value;
2432
+ });
2433
+ }
2434
+ }
2435
+ var _ = {
2436
+ Y: { type: "year", len: 4 },
2437
+ M: { type: "month", len: 2 },
2438
+ D: { type: "day", len: 2 }
2439
+ };
2440
+ function M(r2) {
2441
+ return [...r2].map((t2) => _[t2]);
2442
+ }
2443
+ function P(r2) {
2444
+ const i = new Intl.DateTimeFormat(r2, {
2445
+ year: "numeric",
2446
+ month: "2-digit",
2447
+ day: "2-digit"
2448
+ }).formatToParts(new Date(2000, 0, 15)), s = [];
2449
+ let n = "/";
2450
+ for (const e of i)
2451
+ e.type === "literal" ? n = e.value.trim() || e.value : (e.type === "year" || e.type === "month" || e.type === "day") && s.push({ type: e.type, len: e.type === "year" ? 4 : 2 });
2452
+ return { segments: s, separator: n };
2453
+ }
2454
+ function p(r2) {
2455
+ return Number.parseInt((r2 || "0").replace(/_/g, "0"), 10) || 0;
2456
+ }
2457
+ function f(r2) {
2458
+ return {
2459
+ year: p(r2.year),
2460
+ month: p(r2.month),
2461
+ day: p(r2.day)
2462
+ };
2463
+ }
2464
+ function c(r2, t2) {
2465
+ return new Date(r2 || 2001, t2 || 1, 0).getDate();
2466
+ }
2467
+ function b(r2) {
2468
+ const { year: t2, month: i, day: s } = f(r2);
2469
+ if (!t2 || t2 < 0 || t2 > 9999 || !i || i < 1 || i > 12 || !s || s < 1)
2470
+ return;
2471
+ const n = new Date(Date.UTC(t2, i - 1, s));
2472
+ if (!(n.getUTCFullYear() !== t2 || n.getUTCMonth() !== i - 1 || n.getUTCDate() !== s))
2473
+ return { year: t2, month: i, day: s };
2474
+ }
2475
+ function C(r2) {
2476
+ const t2 = b(r2);
2477
+ return t2 ? new Date(Date.UTC(t2.year, t2.month - 1, t2.day)) : undefined;
2478
+ }
2479
+ function T2(r2, t2, i, s) {
2480
+ const n = i ? {
2481
+ year: i.getUTCFullYear(),
2482
+ month: i.getUTCMonth() + 1,
2483
+ day: i.getUTCDate()
2484
+ } : null, e = s ? {
2485
+ year: s.getUTCFullYear(),
2486
+ month: s.getUTCMonth() + 1,
2487
+ day: s.getUTCDate()
2488
+ } : null;
2489
+ return r2 === "year" ? { min: n?.year ?? 1, max: e?.year ?? 9999 } : r2 === "month" ? {
2490
+ min: n && t2.year === n.year ? n.month : 1,
2491
+ max: e && t2.year === e.year ? e.month : 12
2492
+ } : {
2493
+ min: n && t2.year === n.year && t2.month === n.month ? n.day : 1,
2494
+ max: e && t2.year === e.year && t2.month === e.month ? e.day : c(t2.year, t2.month)
2495
+ };
2496
+ }
2497
+
2498
+ class U extends V {
2499
+ #i;
2500
+ #o;
2501
+ #t;
2502
+ #h;
2503
+ #u;
2504
+ #e = { segmentIndex: 0, positionInSegment: 0 };
2505
+ #n = true;
2506
+ #s = null;
2507
+ inlineError = "";
2508
+ get segmentCursor() {
2509
+ return { ...this.#e };
2510
+ }
2511
+ get segmentValues() {
2512
+ return { ...this.#t };
2513
+ }
2514
+ get segments() {
2515
+ return this.#i;
2516
+ }
2517
+ get separator() {
2518
+ return this.#o;
2519
+ }
2520
+ get formattedValue() {
2521
+ return this.#l(this.#t);
2522
+ }
2523
+ #l(t2) {
2524
+ return this.#i.map((i) => t2[i.type]).join(this.#o);
2525
+ }
2526
+ #r() {
2527
+ this._setUserInput(this.#l(this.#t)), this._setValue(C(this.#t) ?? undefined);
2528
+ }
2529
+ constructor(t2) {
2530
+ const i = t2.format ? { segments: M(t2.format), separator: t2.separator ?? "/" } : P(t2.locale), s = t2.separator ?? i.separator, n = t2.format ? M(t2.format) : i.segments, e = t2.initialValue ?? t2.defaultValue, m2 = e ? {
2531
+ year: String(e.getUTCFullYear()).padStart(4, "0"),
2532
+ month: String(e.getUTCMonth() + 1).padStart(2, "0"),
2533
+ day: String(e.getUTCDate()).padStart(2, "0")
2534
+ } : { year: "____", month: "__", day: "__" }, o = n.map((a) => m2[a.type]).join(s);
2535
+ super({ ...t2, initialUserInput: o }, false), this.#i = n, this.#o = s, this.#t = m2, this.#h = t2.minDate, this.#u = t2.maxDate, this.#r(), this.on("cursor", (a) => this.#f(a)), this.on("key", (a, u) => this.#y(a, u)), this.on("finalize", () => this.#p(t2));
2536
+ }
2537
+ #a() {
2538
+ const t2 = Math.max(0, Math.min(this.#e.segmentIndex, this.#i.length - 1)), i = this.#i[t2];
2539
+ if (i)
2540
+ return this.#e.positionInSegment = Math.max(0, Math.min(this.#e.positionInSegment, i.len - 1)), { segment: i, index: t2 };
2541
+ }
2542
+ #m(t2) {
2543
+ this.inlineError = "", this.#s = null;
2544
+ const i = this.#a();
2545
+ i && (this.#e.segmentIndex = Math.max(0, Math.min(this.#i.length - 1, i.index + t2)), this.#e.positionInSegment = 0, this.#n = true);
2546
+ }
2547
+ #d(t2) {
2548
+ const i = this.#a();
2549
+ if (!i)
2550
+ return;
2551
+ const { segment: s } = i, n = this.#t[s.type], e = !n || n.replace(/_/g, "") === "", m2 = Number.parseInt((n || "0").replace(/_/g, "0"), 10) || 0, o = T2(s.type, f(this.#t), this.#h, this.#u);
2552
+ let a;
2553
+ e ? a = t2 === 1 ? o.min : o.max : a = Math.max(Math.min(o.max, m2 + t2), o.min), this.#t = {
2554
+ ...this.#t,
2555
+ [s.type]: a.toString().padStart(s.len, "0")
2556
+ }, this.#n = true, this.#s = null, this.#r();
2557
+ }
2558
+ #f(t2) {
2559
+ if (t2)
2560
+ switch (t2) {
2561
+ case "right":
2562
+ return this.#m(1);
2563
+ case "left":
2564
+ return this.#m(-1);
2565
+ case "up":
2566
+ return this.#d(1);
2567
+ case "down":
2568
+ return this.#d(-1);
2569
+ }
2570
+ }
2571
+ #y(t2, i) {
2572
+ if (i?.name === "backspace" || i?.sequence === "\x7F" || i?.sequence === "\b" || t2 === "\x7F" || t2 === "\b") {
2573
+ this.inlineError = "";
2574
+ const n = this.#a();
2575
+ if (!n)
2576
+ return;
2577
+ if (!this.#t[n.segment.type].replace(/_/g, "")) {
2578
+ this.#m(-1);
2579
+ return;
2580
+ }
2581
+ this.#t[n.segment.type] = "_".repeat(n.segment.len), this.#n = true, this.#e.positionInSegment = 0, this.#r();
2582
+ return;
2583
+ }
2584
+ if (i?.name === "tab") {
2585
+ this.inlineError = "";
2586
+ const n = this.#a();
2587
+ if (!n)
2588
+ return;
2589
+ const e = i.shift ? -1 : 1, m2 = n.index + e;
2590
+ m2 >= 0 && m2 < this.#i.length && (this.#e.segmentIndex = m2, this.#e.positionInSegment = 0, this.#n = true);
2591
+ return;
2592
+ }
2593
+ if (t2 && /^[0-9]$/.test(t2)) {
2594
+ const n = this.#a();
2595
+ if (!n)
2596
+ return;
2597
+ const { segment: e } = n, m2 = !this.#t[e.type].replace(/_/g, "");
2598
+ if (this.#n && this.#s !== null && !m2) {
2599
+ const h = this.#s + t2, d = { ...this.#t, [e.type]: h }, g2 = this.#g(d, e);
2600
+ if (g2) {
2601
+ this.inlineError = g2, this.#s = null, this.#n = false;
2602
+ return;
2603
+ }
2604
+ this.inlineError = "", this.#t[e.type] = h, this.#s = null, this.#n = false, this.#r(), n.index < this.#i.length - 1 && (this.#e.segmentIndex = n.index + 1, this.#e.positionInSegment = 0, this.#n = true);
2605
+ return;
2606
+ }
2607
+ this.#n && !m2 && (this.#t[e.type] = "_".repeat(e.len), this.#e.positionInSegment = 0), this.#n = false, this.#s = null;
2608
+ const o = this.#t[e.type], a = o.indexOf("_"), u = a >= 0 ? a : Math.min(this.#e.positionInSegment, e.len - 1);
2609
+ if (u < 0 || u >= e.len)
2610
+ return;
2611
+ let l2 = o.slice(0, u) + t2 + o.slice(u + 1), D = false;
2612
+ if (u === 0 && o === "__" && (e.type === "month" || e.type === "day")) {
2613
+ const h = Number.parseInt(t2, 10);
2614
+ l2 = `0${t2}`, D = h <= (e.type === "month" ? 1 : 2);
2615
+ }
2616
+ if (e.type === "year" && (l2 = (o.replace(/_/g, "") + t2).padStart(e.len, "_")), !l2.includes("_")) {
2617
+ const h = { ...this.#t, [e.type]: l2 }, d = this.#g(h, e);
2618
+ if (d) {
2619
+ this.inlineError = d;
2620
+ return;
2621
+ }
2622
+ }
2623
+ this.inlineError = "", this.#t[e.type] = l2;
2624
+ const y = l2.includes("_") ? undefined : b(this.#t);
2625
+ if (y) {
2626
+ const { year: h, month: d } = y, g2 = c(h, d);
2627
+ this.#t = {
2628
+ year: String(Math.max(0, Math.min(9999, h))).padStart(4, "0"),
2629
+ month: String(Math.max(1, Math.min(12, d))).padStart(2, "0"),
2630
+ day: String(Math.max(1, Math.min(g2, y.day))).padStart(2, "0")
2631
+ };
2632
+ }
2633
+ this.#r();
2634
+ const S = l2.indexOf("_");
2635
+ D ? (this.#n = true, this.#s = t2) : S >= 0 ? this.#e.positionInSegment = S : a >= 0 && n.index < this.#i.length - 1 ? (this.#e.segmentIndex = n.index + 1, this.#e.positionInSegment = 0, this.#n = true) : this.#e.positionInSegment = Math.min(u + 1, e.len - 1);
2636
+ }
2637
+ }
2638
+ #g(t2, i) {
2639
+ const { month: s, day: n } = f(t2);
2640
+ if (i.type === "month" && (s < 0 || s > 12))
2641
+ return settings.date.messages.invalidMonth;
2642
+ if (i.type === "day" && (n < 0 || n > 31))
2643
+ return settings.date.messages.invalidDay(31, "any month");
2644
+ }
2645
+ #p(t2) {
2646
+ const { year: i, month: s, day: n } = f(this.#t);
2647
+ if (i && s && n) {
2648
+ const e = c(i, s);
2649
+ this.#t = {
2650
+ ...this.#t,
2651
+ day: String(Math.min(n, e)).padStart(2, "0")
2652
+ };
2653
+ }
2654
+ this.value = C(this.#t) ?? t2.defaultValue ?? undefined;
2655
+ }
2656
+ }
2657
+ var u$2 = class u extends V {
2658
+ options;
2659
+ cursor = 0;
2660
+ #t;
2661
+ getGroupItems(t2) {
2662
+ return this.options.filter((r2) => r2.group === t2);
2663
+ }
2664
+ isGroupSelected(t2) {
2665
+ const r2 = this.getGroupItems(t2), e = this.value;
2666
+ return e === undefined ? false : r2.every((s) => e.includes(s.value));
2667
+ }
2668
+ toggleValue() {
2669
+ const t2 = this.options[this.cursor];
2670
+ if (t2 !== undefined)
2671
+ if (this.value === undefined && (this.value = []), t2.group === true) {
2672
+ const r2 = t2.value, e = this.getGroupItems(r2);
2673
+ this.isGroupSelected(r2) ? this.value = this.value.filter((s) => e.findIndex((i) => i.value === s) === -1) : this.value = [...this.value, ...e.map((s) => s.value)], this.value = Array.from(new Set(this.value));
2674
+ } else {
2675
+ const r2 = this.value.includes(t2.value);
2676
+ this.value = r2 ? this.value.filter((e) => e !== t2.value) : [...this.value, t2.value];
2677
+ }
2678
+ }
2679
+ constructor(t2) {
2680
+ super(t2, false);
2681
+ const { options: r2 } = t2;
2682
+ this.#t = t2.selectableGroups !== false, this.options = Object.entries(r2).flatMap(([e, s]) => [
2683
+ { value: e, group: true, label: e },
2684
+ ...s.map((i) => ({ ...i, group: e }))
2685
+ ]), this.value = [...t2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: e }) => e === t2.cursorAt), this.#t ? 0 : 1), this.on("cursor", (e) => {
2686
+ switch (e) {
2687
+ case "left":
2688
+ case "up": {
2689
+ this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
2690
+ const s = this.options[this.cursor]?.group === true;
2691
+ !this.#t && s && (this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1);
2692
+ break;
2693
+ }
2694
+ case "down":
2695
+ case "right": {
2696
+ this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
2697
+ const s = this.options[this.cursor]?.group === true;
2698
+ !this.#t && s && (this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1);
2699
+ break;
2700
+ }
2701
+ case "space":
2702
+ this.toggleValue();
2703
+ break;
2704
+ }
2705
+ });
2706
+ }
2707
+ };
2708
+ var o = /* @__PURE__ */ new Set(["up", "down", "left", "right"]);
2709
+
2710
+ class h extends V {
2711
+ #t = false;
2712
+ #s;
2713
+ focused = "editor";
2714
+ get userInputWithCursor() {
2715
+ if (this.state === "submit")
2716
+ return this.userInput;
2717
+ const t2 = this.userInput;
2718
+ if (this.cursor >= t2.length)
2719
+ return `${t2}\u2588`;
2720
+ const s = t2.slice(0, this.cursor), r2 = t2.slice(this.cursor, this.cursor + 1), i = t2.slice(this.cursor + 1);
2721
+ return r2 === `
2722
+ ` ? `${s}\u2588
2723
+ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
2724
+ }
2725
+ get cursor() {
2726
+ return this._cursor;
2727
+ }
2728
+ #r(t2) {
2729
+ if (this.userInput.length === 0) {
2730
+ this._setUserInput(t2);
2731
+ return;
2732
+ }
2733
+ this._setUserInput(this.userInput.slice(0, this.cursor) + t2 + this.userInput.slice(this.cursor));
2734
+ }
2735
+ #i(t2) {
2736
+ const s = this.value ?? "";
2737
+ switch (t2) {
2738
+ case "up":
2739
+ this._cursor = findTextCursor(this._cursor, 0, -1, s);
2740
+ return;
2741
+ case "down":
2742
+ this._cursor = findTextCursor(this._cursor, 0, 1, s);
2743
+ return;
2744
+ case "left":
2745
+ this._cursor = findTextCursor(this._cursor, -1, 0, s);
2746
+ return;
2747
+ case "right":
2748
+ this._cursor = findTextCursor(this._cursor, 1, 0, s);
2749
+ return;
2750
+ }
2751
+ }
2752
+ _shouldSubmit(t2, s) {
2753
+ if (this.#s)
2754
+ return this.focused === "submit" ? true : (this.#r(`
2755
+ `), this._cursor++, false);
2756
+ const r2 = this.#t;
2757
+ return this.#t = true, r2 && this.cursor === this.userInput.length ? (this.userInput[this.cursor - 1] === `
2758
+ ` && (this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--), true) : (this.#r(`
2759
+ `), this._cursor++, false);
2760
+ }
2761
+ constructor(t2) {
2762
+ const s = t2.initialUserInput ?? t2.initialValue;
2763
+ super({
2764
+ ...t2,
2765
+ initialUserInput: s
2766
+ }, false), s !== undefined && (this._cursor = s.length), this.#s = t2.showSubmit ?? false, this.on("key", (r2, i) => {
2767
+ if (i?.name && o.has(i.name)) {
2768
+ this.#t = false, this.#i(i.name);
2769
+ return;
2770
+ }
2771
+ if (r2 === "\t" && this.#s) {
2772
+ this.focused = this.focused === "editor" ? "submit" : "editor";
2773
+ return;
2774
+ }
2775
+ if (i?.name !== "return") {
2776
+ if (this.#t = false, i?.name === "backspace" && this.cursor > 0) {
2777
+ this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--;
2778
+ return;
2779
+ }
2780
+ if (i?.name === "delete" && this.cursor < this.userInput.length) {
2781
+ this._setUserInput(this.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1));
2782
+ return;
2783
+ }
2784
+ r2 && (this.#s && this.focused === "submit" && (this.focused = "editor"), this.#r(r2 ?? ""), this._cursor++);
2785
+ }
2786
+ }), this.on("userInput", (r2) => {
2787
+ this._setValue(r2);
2788
+ }), this.on("finalize", () => {
2789
+ this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
2790
+ });
2791
+ }
2792
+ }
2793
+
2794
+ // ../../node_modules/.bun/@clack+prompts@1.7.0/node_modules/@clack/prompts/dist/index.mjs
2795
+ import { styleText as styleText2, stripVTControlCharacters } from "util";
2796
+ import process$1 from "process";
2797
+ var import_sisteransi2 = __toESM(require_src(), 1);
2798
+ function isUnicodeSupported() {
2799
+ if (process$1.platform !== "win32") {
2800
+ return process$1.env.TERM !== "linux";
2801
+ }
2802
+ return Boolean(process$1.env.CI) || Boolean(process$1.env.WT_SESSION) || Boolean(process$1.env.TERMINUS_SUBLIME) || process$1.env.ConEmuTask === "{cmd::Cmder}" || process$1.env.TERM_PROGRAM === "Terminus-Sublime" || process$1.env.TERM_PROGRAM === "vscode" || process$1.env.TERM === "xterm-256color" || process$1.env.TERM === "alacritty" || process$1.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
2803
+ }
2804
+ var unicode = isUnicodeSupported();
2805
+ var isCI = () => process.env.CI === "true";
2806
+ var unicodeOr = (o2, e) => unicode ? o2 : e;
2807
+ var S_STEP_ACTIVE = unicodeOr("\u25C6", "*");
2808
+ var S_STEP_CANCEL = unicodeOr("\u25A0", "x");
2809
+ var S_STEP_ERROR = unicodeOr("\u25B2", "x");
2810
+ var S_STEP_SUBMIT = unicodeOr("\u25C7", "o");
2811
+ var S_BAR_START = unicodeOr("\u250C", "T");
2812
+ var S_BAR = unicodeOr("\u2502", "|");
2813
+ var S_BAR_END = unicodeOr("\u2514", "\u2014");
2814
+ var S_BAR_START_RIGHT = unicodeOr("\u2510", "T");
2815
+ var S_BAR_END_RIGHT = unicodeOr("\u2518", "\u2014");
2816
+ var S_RADIO_ACTIVE = unicodeOr("\u25CF", ">");
2817
+ var S_RADIO_INACTIVE = unicodeOr("\u25CB", " ");
2818
+ var S_CHECKBOX_ACTIVE = unicodeOr("\u25FB", "[\u2022]");
2819
+ var S_CHECKBOX_SELECTED = unicodeOr("\u25FC", "[+]");
2820
+ var S_CHECKBOX_INACTIVE = unicodeOr("\u25FB", "[ ]");
2821
+ var S_PASSWORD_MASK = unicodeOr("\u25AA", "\u2022");
2822
+ var S_BAR_H = unicodeOr("\u2500", "-");
2823
+ var S_CORNER_TOP_RIGHT = unicodeOr("\u256E", "+");
2824
+ var S_CONNECT_LEFT = unicodeOr("\u251C", "+");
2825
+ var S_CORNER_BOTTOM_RIGHT = unicodeOr("\u256F", "+");
2826
+ var S_CORNER_BOTTOM_LEFT = unicodeOr("\u2570", "+");
2827
+ var S_CORNER_TOP_LEFT = unicodeOr("\u256D", "+");
2828
+ var S_INFO = unicodeOr("\u25CF", "\u2022");
2829
+ var S_SUCCESS = unicodeOr("\u25C6", "*");
2830
+ var S_WARN = unicodeOr("\u25B2", "!");
2831
+ var S_ERROR = unicodeOr("\u25A0", "x");
2832
+ var symbol = (o2) => {
2833
+ switch (o2) {
2834
+ case "initial":
2835
+ case "active":
2836
+ return styleText2("cyan", S_STEP_ACTIVE);
2837
+ case "cancel":
2838
+ return styleText2("red", S_STEP_CANCEL);
2839
+ case "error":
2840
+ return styleText2("yellow", S_STEP_ERROR);
2841
+ case "submit":
2842
+ return styleText2("green", S_STEP_SUBMIT);
2843
+ }
2844
+ };
2845
+ var confirm = (i) => {
2846
+ const a2 = i.active ?? "Yes", s = i.inactive ?? "No";
2847
+ return new r({
2848
+ active: a2,
2849
+ inactive: s,
2850
+ signal: i.signal,
2851
+ input: i.input,
2852
+ output: i.output,
2853
+ initialValue: i.initialValue ?? true,
2854
+ render() {
2855
+ const e = i.withGuide ?? settings.withGuide, u3 = `${symbol(this.state)} `, l2 = e ? `${styleText2("gray", S_BAR)} ` : "", f2 = wrapTextWithPrefix(i.output, i.message, l2, u3), o2 = `${e ? `${styleText2("gray", S_BAR)}
2856
+ ` : ""}${f2}
2857
+ `, c2 = this.value ? a2 : s;
2858
+ switch (this.state) {
2859
+ case "submit": {
2860
+ const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
2861
+ return `${o2}${r2}${styleText2("dim", c2)}`;
2862
+ }
2863
+ case "cancel": {
2864
+ const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
2865
+ return `${o2}${r2}${styleText2(["strikethrough", "dim"], c2)}${e ? `
2866
+ ${styleText2("gray", S_BAR)}` : ""}`;
2867
+ }
2868
+ default: {
2869
+ const r2 = e ? `${styleText2("cyan", S_BAR)} ` : "", g2 = e ? styleText2("cyan", S_BAR_END) : "";
2870
+ return `${o2}${r2}${this.value ? `${styleText2("green", S_RADIO_ACTIVE)} ${a2}` : `${styleText2("dim", S_RADIO_INACTIVE)} ${styleText2("dim", a2)}`}${i.vertical ? e ? `
2871
+ ${styleText2("cyan", S_BAR)} ` : `
2872
+ ` : ` ${styleText2("dim", "/")} `}${this.value ? `${styleText2("dim", S_RADIO_INACTIVE)} ${styleText2("dim", s)}` : `${styleText2("green", S_RADIO_ACTIVE)} ${s}`}
2873
+ ${g2}
2874
+ `;
2875
+ }
2876
+ }
2877
+ }
2878
+ }).prompt();
2879
+ };
2880
+ var MULTISELECT_INSTRUCTIONS = [
2881
+ `${styleText2("dim", "\u2191/\u2193")} to navigate`,
2882
+ `${styleText2("dim", "Space:")} select`,
2883
+ `${styleText2("dim", "Enter:")} confirm`
2884
+ ];
2885
+ var log = {
2886
+ message: (s = [], {
2887
+ symbol: e = styleText2("gray", S_BAR),
2888
+ secondarySymbol: r2 = styleText2("gray", S_BAR),
2889
+ output: m2 = process.stdout,
2890
+ spacing: l2 = 1,
2891
+ withGuide: c2
2892
+ } = {}) => {
2893
+ const t2 = [], o2 = c2 ?? settings.withGuide, f2 = o2 ? r2 : "", O = o2 ? `${e} ` : "", u3 = o2 ? `${r2} ` : "";
2894
+ for (let i = 0;i < l2; i++)
2895
+ t2.push(f2);
2896
+ const g2 = Array.isArray(s) ? s : s.split(`
2897
+ `);
2898
+ if (g2.length > 0) {
2899
+ const [i, ...y] = g2;
2900
+ i.length > 0 ? t2.push(`${O}${i}`) : t2.push(o2 ? e : "");
2901
+ for (const p2 of y)
2902
+ p2.length > 0 ? t2.push(`${u3}${p2}`) : t2.push(o2 ? r2 : "");
2903
+ }
2904
+ m2.write(`${t2.join(`
2905
+ `)}
2906
+ `);
2907
+ },
2908
+ info: (s, e) => {
2909
+ log.message(s, { ...e, symbol: styleText2("blue", S_INFO) });
2910
+ },
2911
+ success: (s, e) => {
2912
+ log.message(s, { ...e, symbol: styleText2("green", S_SUCCESS) });
2913
+ },
2914
+ step: (s, e) => {
2915
+ log.message(s, { ...e, symbol: styleText2("green", S_STEP_SUBMIT) });
2916
+ },
2917
+ warn: (s, e) => {
2918
+ log.message(s, { ...e, symbol: styleText2("yellow", S_WARN) });
2919
+ },
2920
+ warning: (s, e) => {
2921
+ log.warn(s, e);
2922
+ },
2923
+ error: (s, e) => {
2924
+ log.message(s, { ...e, symbol: styleText2("red", S_ERROR) });
2925
+ }
2926
+ };
2927
+ var intro = (o2 = "", t2) => {
2928
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_START)} ` : "";
2929
+ i.write(`${e}${o2}
2930
+ `);
2931
+ };
2932
+ var outro = (o2 = "", t2) => {
2933
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR)}
2934
+ ${styleText2("gray", S_BAR_END)} ` : "";
2935
+ i.write(`${e}${o2}
2936
+
2937
+ `);
2938
+ };
2939
+ var W$1 = (o2) => o2;
2940
+ var C2 = (o2, e, s) => {
2941
+ const a2 = {
2942
+ hard: true,
2943
+ trim: false
2944
+ }, i = wrapAnsi(o2, e, a2).split(`
2945
+ `), c2 = i.reduce((n2, t2) => Math.max(dist_default2(t2), n2), 0), u3 = i.map(s).reduce((n2, t2) => Math.max(dist_default2(t2), n2), 0), g2 = e - (u3 - c2);
2946
+ return wrapAnsi(o2, g2, a2);
2947
+ };
2948
+ var note = (o2 = "", e = "", s) => {
2949
+ const a2 = s?.output ?? process$1.stdout, i = s?.withGuide ?? settings.withGuide, c2 = s?.format ?? W$1, g2 = ["", ...C2(o2, getColumns(a2) - 6, c2).split(`
2950
+ `).map(c2), ""], n2 = dist_default2(e), t2 = Math.max(g2.reduce((m2, F) => {
2951
+ const O = dist_default2(F);
2952
+ return O > m2 ? O : m2;
2953
+ }, 0), n2) + 2, h2 = g2.map((m2) => `${styleText2("gray", S_BAR)} ${m2}${" ".repeat(t2 - dist_default2(m2))}${styleText2("gray", S_BAR)}`).join(`
2954
+ `), T3 = i ? `${styleText2("gray", S_BAR)}
2955
+ ` : "", l$1 = i ? S_CONNECT_LEFT : S_CORNER_BOTTOM_LEFT;
2956
+ a2.write(`${T3}${styleText2("green", S_STEP_SUBMIT)} ${styleText2("reset", e)} ${styleText2("gray", S_BAR_H.repeat(Math.max(t2 - n2 - 1, 1)) + S_CORNER_TOP_RIGHT)}
2957
+ ${h2}
2958
+ ${styleText2("gray", l$1 + S_BAR_H.repeat(t2 + 2) + S_CORNER_BOTTOM_RIGHT)}
2959
+ `);
2960
+ };
2961
+ var W = (l2) => styleText2("magenta", l2);
2962
+ var spinner = ({
2963
+ indicator: l2 = "dots",
2964
+ onCancel: h2,
2965
+ output: n2 = process.stdout,
2966
+ cancelMessage: G,
2967
+ errorMessage: O,
2968
+ frames: E = unicode ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"],
2969
+ delay: F = unicode ? 80 : 120,
2970
+ signal: m2,
2971
+ ...I
2972
+ } = {}) => {
2973
+ const u3 = isCI();
2974
+ let M2, T3, d = false, S = false, s = "", p2, w = performance.now();
2975
+ const x = getColumns(n2), k = I?.styleFrame ?? W, g2 = (e) => {
2976
+ const r2 = e > 1 ? O ?? settings.messages.error : G ?? settings.messages.cancel;
2977
+ S = e === 1, d && (a2(r2, e), S && typeof h2 == "function" && h2());
2978
+ }, f2 = () => g2(2), i = () => g2(1), A = () => {
2979
+ process.on("uncaughtExceptionMonitor", f2), process.on("unhandledRejection", f2), process.on("SIGINT", i), process.on("SIGTERM", i), process.on("exit", g2), m2 && m2.addEventListener("abort", i);
2980
+ }, H = () => {
2981
+ process.removeListener("uncaughtExceptionMonitor", f2), process.removeListener("unhandledRejection", f2), process.removeListener("SIGINT", i), process.removeListener("SIGTERM", i), process.removeListener("exit", g2), m2 && m2.removeEventListener("abort", i);
2982
+ }, y = () => {
2983
+ if (p2 === undefined)
2984
+ return;
2985
+ u3 && n2.write(`
2986
+ `);
2987
+ const r2 = wrapAnsi(p2, x, {
2988
+ hard: true,
2989
+ trim: false
2990
+ }).split(`
2991
+ `);
2992
+ r2.length > 1 && n2.write(import_sisteransi2.cursor.up(r2.length - 1)), n2.write(import_sisteransi2.cursor.to(0)), n2.write(import_sisteransi2.erase.down());
2993
+ }, C3 = (e) => e.replace(/\.+$/, ""), _2 = (e) => {
2994
+ const r2 = (performance.now() - e) / 1000, t2 = Math.floor(r2 / 60), o2 = Math.floor(r2 % 60);
2995
+ return t2 > 0 ? `[${t2}m ${o2}s]` : `[${o2}s]`;
2996
+ }, N = I.withGuide ?? settings.withGuide, P2 = (e = "") => {
2997
+ d = true, M2 = block({ output: n2 }), s = C3(e), w = performance.now(), N && n2.write(`${styleText2("gray", S_BAR)}
2998
+ `);
2999
+ let r2 = 0, t2 = 0;
3000
+ A(), T3 = setInterval(() => {
3001
+ if (u3 && s === p2)
3002
+ return;
3003
+ y(), p2 = s;
3004
+ const o2 = k(E[r2]);
3005
+ let v;
3006
+ if (u3)
3007
+ v = `${o2} ${s}...`;
3008
+ else if (l2 === "timer")
3009
+ v = `${o2} ${s} ${_2(w)}`;
3010
+ else {
3011
+ const B = ".".repeat(Math.floor(t2)).slice(0, 3);
3012
+ v = `${o2} ${s}${B}`;
3013
+ }
3014
+ const j = wrapAnsi(v, x, {
3015
+ hard: true,
3016
+ trim: false
3017
+ });
3018
+ n2.write(j), r2 = r2 + 1 < E.length ? r2 + 1 : 0, t2 = t2 < 4 ? t2 + 0.125 : 0;
3019
+ }, F);
3020
+ }, a2 = (e = "", r2 = 0, t2 = false) => {
3021
+ if (!d)
3022
+ return;
3023
+ d = false, clearInterval(T3), y();
3024
+ const o2 = r2 === 0 ? styleText2("green", S_STEP_SUBMIT) : r2 === 1 ? styleText2("red", S_STEP_CANCEL) : styleText2("red", S_STEP_ERROR);
3025
+ s = e ?? s, t2 || (l2 === "timer" ? n2.write(`${o2} ${s} ${_2(w)}
3026
+ `) : n2.write(`${o2} ${s}
3027
+ `)), H(), M2();
3028
+ };
3029
+ return {
3030
+ start: P2,
3031
+ stop: (e = "") => a2(e, 0),
3032
+ message: (e = "") => {
3033
+ s = C3(e ?? s);
3034
+ },
3035
+ cancel: (e = "") => a2(e, 1),
3036
+ error: (e = "") => a2(e, 2),
3037
+ clear: () => a2("", 0, true),
3038
+ get isCancelled() {
3039
+ return S;
3040
+ }
3041
+ };
3042
+ };
3043
+ var u3 = {
3044
+ light: unicodeOr("\u2500", "-"),
3045
+ heavy: unicodeOr("\u2501", "="),
3046
+ block: unicodeOr("\u2588", "#")
3047
+ };
3048
+ var SELECT_INSTRUCTIONS = [
3049
+ `${styleText2("dim", "\u2191/\u2193")} to navigate`,
3050
+ `${styleText2("dim", "Enter:")} confirm`
3051
+ ];
3052
+ var i = `${styleText2("gray", S_BAR)} `;
3053
+
3054
+ // src/commands/drop.ts
3055
+ var import_picocolors = __toESM(require_picocolors(), 1);
3056
+ async function runDrop(config2, opts = {}) {
3057
+ intro(import_picocolors.default.bgCyan(import_picocolors.default.black(" @bungres/kit drop ")));
3058
+ const s = spinner();
3059
+ s.start("Loading schemas...");
3060
+ const schemas2 = await loadSchemas(config2.schema);
3061
+ if (schemas2.length === 0) {
3062
+ s.stop("No schemas found.");
3063
+ log.warn(import_picocolors.default.yellow("No table definitions found in schema files."));
3064
+ outro("Failed.");
3065
+ return;
3066
+ }
3067
+ s.stop(`Loaded ${schemas2.length} schemas.`);
3068
+ const ms = spinner();
3069
+ ms.start("Checking database tables...");
3070
+ const sql2 = new Bun.SQL(config2.dbUrl);
3071
+ try {
3072
+ const userSchema = config2.dbSchema;
3073
+ const existingUserTablesResult = await sql2.unsafe(`SELECT table_name FROM information_schema.tables WHERE table_schema = $1`, [userSchema]);
3074
+ const existingTableNames = new Set(existingUserTablesResult.map((r2) => r2.table_name));
3075
+ const migTableCheck = await sql2.unsafe(`SELECT EXISTS (
3076
+ SELECT 1 FROM information_schema.tables
3077
+ WHERE table_schema = $1 AND table_name = $2
3078
+ ) AS exists`, [config2.migrationsSchema, config2.migrationsTable]);
3079
+ const migrationTableExists = migTableCheck[0]?.exists ?? false;
3080
+ const pushTableCheck = await sql2.unsafe(`SELECT EXISTS (
3081
+ SELECT 1 FROM information_schema.tables
3082
+ WHERE table_schema = $1 AND table_name = $2
3083
+ ) AS exists`, [config2.migrationsSchema, "__bungres_push"]);
3084
+ const pushTableExists = pushTableCheck[0]?.exists ?? false;
3085
+ const tablesToDrop = schemas2.filter((sch) => existingTableNames.has(sch.config.name));
3086
+ if (tablesToDrop.length === 0 && !migrationTableExists && !pushTableExists) {
3087
+ ms.stop("Nothing to do.");
3088
+ log.success(import_picocolors.default.green("No tables to drop (they either don't exist or were already dropped)."));
3089
+ outro("Done.");
3090
+ return;
3091
+ }
3092
+ ms.stop("Database check complete.");
3093
+ const tableNamesToPrint = tablesToDrop.map((sch) => (sch.config.schema ? sch.config.schema + "." : "") + sch.config.name);
3094
+ if (migrationTableExists) {
3095
+ tableNamesToPrint.push(`${config2.migrationsSchema}.${config2.migrationsTable}`);
3096
+ }
3097
+ if (pushTableExists) {
3098
+ tableNamesToPrint.push(`${config2.migrationsSchema}.__bungres_push`);
3099
+ }
3100
+ if (!opts.force) {
3101
+ log.warn(import_picocolors.default.bgRed(import_picocolors.default.white(" \u26A0\uFE0F WARNING ")));
3102
+ log.message(import_picocolors.default.bold(import_picocolors.default.red("This will drop the following tables and ALL their data:")));
3103
+ for (const name of tableNamesToPrint) {
3104
+ log.info(import_picocolors.default.yellow(` - ${name}`));
3105
+ }
3106
+ const confirm2 = await confirm({
3107
+ message: "Are you sure you want to proceed?",
3108
+ initialValue: false
3109
+ });
3110
+ if (isCancel(confirm2) || !confirm2) {
3111
+ outro(import_picocolors.default.gray("Drop aborted."));
3112
+ return;
3113
+ }
3114
+ }
3115
+ const exSpinner = spinner();
3116
+ exSpinner.start("Dropping tables...");
3117
+ for (const entry of tablesToDrop) {
3118
+ const ddl = `DROP TABLE IF EXISTS "${entry.config.name}" CASCADE;`;
3119
+ await sql2.unsafe(ddl);
3120
+ log.step(import_picocolors.default.gray(`Dropped ${entry.config.name}`));
3121
+ }
3122
+ if (migrationTableExists) {
3123
+ const qualifiedMigTable = `"${config2.migrationsSchema}"."${config2.migrationsTable}"`;
3124
+ await sql2.unsafe(`DROP TABLE IF EXISTS ${qualifiedMigTable} CASCADE`);
3125
+ log.step(import_picocolors.default.gray(`Dropped ${config2.migrationsSchema}.${config2.migrationsTable}`));
3126
+ }
3127
+ if (pushTableExists) {
3128
+ await sql2.unsafe(`DROP TABLE IF EXISTS "${config2.migrationsSchema}"."__bungres_push" CASCADE`);
3129
+ log.step(import_picocolors.default.gray(`Dropped ${config2.migrationsSchema}.__bungres_push`));
3130
+ }
3131
+ exSpinner.stop("Tables dropped.");
3132
+ outro(import_picocolors.default.cyan("\u2728 Drop complete."));
3133
+ } catch (err) {
3134
+ log.error(import_picocolors.default.red(`Drop failed: ${err.message}`));
3135
+ outro("Failed.");
3136
+ } finally {
3137
+ await sql2.end();
3138
+ }
3139
+ }
3140
+
3141
+ // src/commands/migrate.ts
3142
+ import { join as join2, resolve as resolve2 } from "path";
3143
+ var import_picocolors2 = __toESM(require_picocolors(), 1);
3144
+ async function runMigrate(config2) {
3145
+ intro(import_picocolors2.default.bgCyan(import_picocolors2.default.black(" @bungres/kit migrate ")));
3146
+ const migrationsDir = resolve2(config2.out);
3147
+ const sql2 = new Bun.SQL(config2.dbUrl);
3148
+ const table2 = config2.migrationsTable;
3149
+ const schema = config2.migrationsSchema;
3150
+ const qualifiedTable = `"${schema}"."${table2}"`;
3151
+ const createSchema = `CREATE SCHEMA IF NOT EXISTS "${schema}";`;
3152
+ const createMigrationsTable = `
3153
+ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
3154
+ id SERIAL PRIMARY KEY,
3155
+ name TEXT NOT NULL UNIQUE,
3156
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
3157
+ );`.trim();
3158
+ const s = spinner();
3159
+ s.start("Checking pending migrations...");
3160
+ try {
3161
+ await sql2.unsafe(createSchema);
3162
+ await sql2.unsafe(createMigrationsTable);
3163
+ const glob = new Bun.Glob("*.sql");
3164
+ const files = [];
3165
+ for await (const file of glob.scan({ cwd: migrationsDir, absolute: false })) {
3166
+ files.push(file);
3167
+ }
3168
+ files.sort();
3169
+ if (files.length === 0) {
3170
+ s.stop("No files found.");
3171
+ log.warn(import_picocolors2.default.yellow(`No migration files found in ${migrationsDir}`));
3172
+ log.info(`Run ${import_picocolors2.default.green("bungres generate")} first.`);
3173
+ outro("Done.");
3174
+ return;
3175
+ }
3176
+ const applied = await sql2.unsafe(`SELECT name FROM ${qualifiedTable}`);
3177
+ const appliedSet = new Set(applied.map((r2) => r2.name));
3178
+ const pending = files.filter((f2) => !appliedSet.has(f2));
3179
+ if (pending.length === 0) {
3180
+ s.stop("Up to date.");
3181
+ log.success(import_picocolors2.default.green("Everything is up to date."));
3182
+ outro("Done.");
3183
+ return;
3184
+ }
3185
+ s.stop(`Found ${pending.length} pending migration(s).`);
3186
+ for (const file of pending) {
3187
+ const ms = spinner();
3188
+ ms.start(`Applying ${file}...`);
3189
+ const content = await Bun.file(join2(migrationsDir, file)).text();
3190
+ let upContent = content;
3191
+ const upMatch = content.match(/-- ==== UP ====([\s\S]*?)(?:-- ==== DOWN ====|$)/i);
3192
+ if (upMatch) {
3193
+ upContent = upMatch[1].trim();
3194
+ }
3195
+ if (config2.verbose) {
3196
+ log.info(import_picocolors2.default.gray(`-- ${file} --
3197
+ ${upContent}
3198
+ `));
3199
+ }
3200
+ await sql2.transaction(async (txSql) => {
3201
+ const statements = config2.breakpoints ? upContent.split(/-->statement-breakpoint/g).flatMap((chunk) => chunk.split(";").map((s2) => s2.trim()).filter(Boolean)) : upContent.split(";").map((s2) => s2.trim()).filter(Boolean);
3202
+ for (const stmt of statements) {
3203
+ await txSql.unsafe(stmt + ";");
3204
+ }
3205
+ await txSql.unsafe(`INSERT INTO ${qualifiedTable} (name) VALUES ($1)`, [file]);
3206
+ });
3207
+ ms.stop(import_picocolors2.default.green(`\u2713 Applied ${file}`));
3208
+ }
3209
+ outro(import_picocolors2.default.cyan("\u2728 All migrations applied successfully."));
3210
+ } catch (err) {
3211
+ log.error(import_picocolors2.default.red(`Migration failed: ${err.message}`));
3212
+ outro("Failed.");
3213
+ } finally {
3214
+ await sql2.end();
3215
+ }
3216
+ }
1502
3217
 
1503
3218
  // src/commands/fresh.ts
1504
3219
  async function runFresh(config2) {
@@ -1513,6 +3228,7 @@ Fresh complete.`);
1513
3228
 
1514
3229
  // src/commands/generate.ts
1515
3230
  import { join as join3, resolve as resolve3 } from "path";
3231
+ import { readdirSync } from "fs";
1516
3232
 
1517
3233
  // src/differ.ts
1518
3234
  function diffSchemas(prev, next) {
@@ -1594,20 +3310,20 @@ function diffSchemas(prev, next) {
1594
3310
  }
1595
3311
  }
1596
3312
  }
1597
- const prevIdxNames = new Set((prevConfig.indexes ?? []).map((i) => i.name ?? `idx_${tableName}_${i.columns.join("_")}`));
3313
+ const prevIdxNames = new Set((prevConfig.indexes ?? []).map((i2) => i2.name ?? `idx_${tableName}_${i2.columns.join("_")}`));
1598
3314
  for (const idx of nextConfig.indexes ?? []) {
1599
3315
  const idxName = idx.name ?? `idx_${tableName}_${idx.columns.join("_")}`;
1600
3316
  if (!prevIdxNames.has(idxName)) {
1601
3317
  const tbl = nextConfig.schema ? `"${nextConfig.schema}"."${tableName}"` : `"${tableName}"`;
1602
- const unique2 = idx.unique ? "UNIQUE " : "";
3318
+ const unique = idx.unique ? "UNIQUE " : "";
1603
3319
  const using = idx.using ? ` USING ${idx.using.toUpperCase()}` : "";
1604
- const cols = idx.columns.map((c) => `"${c}"`).join(", ");
3320
+ const cols = idx.columns.map((c2) => `"${c2}"`).join(", ");
1605
3321
  const where = idx.where ? ` WHERE ${idx.where}` : "";
1606
- statements.push(`CREATE ${unique2}INDEX IF NOT EXISTS "${idxName}" ON ${tbl}${using} (${cols})${where};`);
3322
+ statements.push(`CREATE ${unique}INDEX IF NOT EXISTS "${idxName}" ON ${tbl}${using} (${cols})${where};`);
1607
3323
  summary.push(`CREATE INDEX ${idxName} ON ${tableName}`);
1608
3324
  }
1609
3325
  }
1610
- const nextIdxNames = new Set((nextConfig.indexes ?? []).map((i) => i.name ?? `idx_${tableName}_${i.columns.join("_")}`));
3326
+ const nextIdxNames = new Set((nextConfig.indexes ?? []).map((i2) => i2.name ?? `idx_${tableName}_${i2.columns.join("_")}`));
1611
3327
  for (const idx of prevConfig.indexes ?? []) {
1612
3328
  const idxName = idx.name ?? `idx_${tableName}_${idx.columns.join("_")}`;
1613
3329
  if (!nextIdxNames.has(idxName)) {
@@ -1619,11 +3335,11 @@ function diffSchemas(prev, next) {
1619
3335
  return { statements, summary, warnings };
1620
3336
  }
1621
3337
  function topoSortConfigs(tables) {
1622
- const byName = new Map(tables.map((t) => [t.name, t]));
3338
+ const byName = new Map(tables.map((t2) => [t2.name, t2]));
1623
3339
  const visited = new Set;
1624
3340
  const result2 = [];
1625
3341
  function deps(config2) {
1626
- return Object.values(config2.columns).map((col2) => col2.references?.table).filter((t) => t !== undefined && byName.has(t) && t !== config2.name);
3342
+ return Object.values(config2.columns).map((col2) => col2.references?.table).filter((t2) => t2 !== undefined && byName.has(t2) && t2 !== config2.name);
1627
3343
  }
1628
3344
  function visit(name) {
1629
3345
  if (visited.has(name))
@@ -1688,21 +3404,40 @@ function formatDefault(value, dataType) {
1688
3404
  }
1689
3405
 
1690
3406
  // src/commands/generate.ts
1691
- var SNAPSHOT_FILE = ".snapshot.json";
3407
+ var import_picocolors3 = __toESM(require_picocolors(), 1);
1692
3408
  async function runGenerate(config2, name) {
1693
- console.log("@bungres/kit generate: loading schemas...");
3409
+ intro(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" @bungres/kit generate ")));
3410
+ const s = spinner();
3411
+ s.start("Loading schemas...");
1694
3412
  const schemas2 = await loadSchemas(config2.schema);
1695
3413
  if (schemas2.length === 0) {
1696
- console.warn(colorize("No table definitions found. Check your schema glob pattern.", "yellow"));
3414
+ s.stop("No table definitions found.");
3415
+ log.warn(import_picocolors3.default.yellow("Check your schema glob pattern."));
3416
+ outro("Failed.");
1697
3417
  return;
1698
3418
  }
3419
+ s.stop(`Loaded ${schemas2.length} schemas.`);
1699
3420
  const migrationsDir = resolve3(config2.out);
3421
+ const metaDir = join3(migrationsDir, "meta");
1700
3422
  await Bun.$`mkdir -p ${migrationsDir}`.quiet();
1701
- const currentSnapshot = Object.fromEntries(schemas2.map((s) => [s.config.name, s.config]));
1702
- const snapshotPath = join3(migrationsDir, SNAPSHOT_FILE);
1703
- const snapshotFile = Bun.file(snapshotPath);
1704
- const isFirstMigration = !await snapshotFile.exists();
1705
- const prevSnapshot = isFirstMigration ? {} : JSON.parse(await snapshotFile.text());
3423
+ await Bun.$`mkdir -p ${metaDir}`.quiet();
3424
+ const currentSnapshot = Object.fromEntries(schemas2.map((s2) => [s2.config.name, s2.config]));
3425
+ let prevSnapshot = {};
3426
+ let isFirstMigration = true;
3427
+ try {
3428
+ const files = readdirSync(metaDir).filter((f2) => f2.endsWith("_snapshot.json")).sort();
3429
+ if (files.length > 0) {
3430
+ const latest = files[files.length - 1];
3431
+ prevSnapshot = JSON.parse(await Bun.file(join3(metaDir, latest)).text());
3432
+ isFirstMigration = false;
3433
+ } else {
3434
+ const legacyFile2 = Bun.file(join3(migrationsDir, ".snapshot.json"));
3435
+ if (await legacyFile2.exists()) {
3436
+ prevSnapshot = JSON.parse(await legacyFile2.text());
3437
+ isFirstMigration = false;
3438
+ }
3439
+ }
3440
+ } catch (e) {}
1706
3441
  const now = new Date;
1707
3442
  const yyyy = now.getUTCFullYear();
1708
3443
  const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
@@ -1713,60 +3448,86 @@ async function runGenerate(config2, name) {
1713
3448
  const prefix = `${yyyy}_${mm}_${dd}_${HH}${MM}${SS}`;
1714
3449
  const filename = name ? `${prefix}_${name}.sql` : `${prefix}.sql`;
1715
3450
  const outPath = join3(migrationsDir, filename);
1716
- let statements;
3451
+ let upStatements;
3452
+ let downStatements;
1717
3453
  let summary;
1718
3454
  let warnings = [];
1719
3455
  if (isFirstMigration) {
1720
3456
  const sorted = topoSort(schemas2);
1721
- statements = [
1722
- ...sorted.flatMap((entry) => [
1723
- `-- ${entry.exportName}`,
1724
- generateCreateTable(entry.config, true),
1725
- ``
1726
- ])
1727
- ];
1728
- summary = sorted.map((s) => `CREATE TABLE ${s.config.name}`);
3457
+ upStatements = sorted.flatMap((entry) => [
3458
+ `-- ${entry.exportName}`,
3459
+ generateCreateTable(entry.config, true),
3460
+ ``
3461
+ ]);
3462
+ downStatements = [...sorted].reverse().flatMap((entry) => {
3463
+ const tbl = entry.config.schema ? `"${entry.config.schema}"."${entry.config.name}"` : `"${entry.config.name}"`;
3464
+ return [`DROP TABLE IF EXISTS ${tbl};`];
3465
+ });
3466
+ summary = sorted.map((s2) => `CREATE TABLE ${s2.config.name}`);
1729
3467
  } else {
1730
- const diff = diffSchemas(prevSnapshot, currentSnapshot);
1731
- if (diff.statements.length === 0) {
1732
- console.log(colorize(`
1733
- No schema changes detected. Nothing to generate.`, "yellow"));
3468
+ const upDiff = diffSchemas(prevSnapshot, currentSnapshot);
3469
+ if (upDiff.statements.length === 0) {
3470
+ log.warn(import_picocolors3.default.yellow("No schema changes detected. Nothing to generate."));
3471
+ outro("Done.");
1734
3472
  return;
1735
3473
  }
1736
- statements = diff.statements;
1737
- summary = diff.summary;
1738
- warnings = diff.warnings;
3474
+ upStatements = upDiff.statements;
3475
+ summary = upDiff.summary;
3476
+ warnings = upDiff.warnings;
3477
+ const downDiff = diffSchemas(currentSnapshot, prevSnapshot);
3478
+ downStatements = downDiff.statements;
3479
+ }
3480
+ log.message(import_picocolors3.default.bold("Schema changes detected:"));
3481
+ for (const s2 of summary) {
3482
+ if (s2.startsWith("CREATE") || s2.startsWith("ALTER TABLE") && s2.includes("ADD")) {
3483
+ log.success(import_picocolors3.default.green(` + ${s2}`));
3484
+ } else if (s2.startsWith("DROP") || s2.startsWith("ALTER TABLE") && s2.includes("DROP")) {
3485
+ log.error(import_picocolors3.default.red(` - ${s2}`));
3486
+ } else {
3487
+ log.info(import_picocolors3.default.blue(` ~ ${s2}`));
3488
+ }
3489
+ }
3490
+ if (warnings.length > 0) {
3491
+ log.warn(import_picocolors3.default.bgRed(import_picocolors3.default.white(" \u26A0\uFE0F DATA LOSS DETECTED ")));
3492
+ for (const w of warnings)
3493
+ log.warn(import_picocolors3.default.red(` ! ${w}`));
3494
+ }
3495
+ const shouldGenerate = await confirm({
3496
+ message: "Generate this migration?",
3497
+ initialValue: true
3498
+ });
3499
+ if (isCancel(shouldGenerate) || !shouldGenerate) {
3500
+ outro(import_picocolors3.default.gray("Generation cancelled."));
3501
+ return;
1739
3502
  }
3503
+ s.start("Writing files...");
1740
3504
  const lines = [
1741
3505
  `-- Migration: ${filename}`,
1742
3506
  `-- Generated by @bungres/kit at ${new Date().toISOString()}`,
1743
3507
  `-- Changes: ${summary.join(", ")}`,
1744
3508
  ``,
1745
- ...statements
3509
+ `-- ==== UP ====`,
3510
+ ...upStatements,
3511
+ ``,
3512
+ `-- ==== DOWN ====`,
3513
+ ...downStatements,
3514
+ ``
1746
3515
  ];
1747
3516
  await Bun.write(outPath, lines.join(`
1748
3517
  `));
1749
- await Bun.write(snapshotPath, JSON.stringify(currentSnapshot, null, 2));
1750
- console.log(colorize(`
1751
- Generated: ${outPath}`, "green"));
1752
- console.log(` Changes:`);
1753
- for (const s of summary)
1754
- console.log(colorize(` + ${s}`, "cyan"));
1755
- if (warnings.length > 0) {
1756
- console.warn(colorize(`
1757
- \u26A0\uFE0F WARNING: Data Loss Detected!`, "red"));
1758
- for (const w of warnings)
1759
- console.warn(colorize(` ! ${w}`, "red"));
1760
- console.warn(colorize(` Please review the generated migration carefully before applying it.
1761
- `, "yellow"));
1762
- }
1763
- console.log(colorize(`
1764
- Run \`bungres migrate\` to apply it.`, "cyan"));
3518
+ const metaPath = join3(metaDir, `${prefix}_snapshot.json`);
3519
+ await Bun.write(metaPath, JSON.stringify(currentSnapshot, null, 2));
3520
+ const legacyFile = Bun.file(join3(migrationsDir, ".snapshot.json"));
3521
+ if (await legacyFile.exists()) {
3522
+ await Bun.$`rm ${legacyFile.name}`.quiet();
3523
+ }
3524
+ s.stop(`Generated ${import_picocolors3.default.cyan(filename)}`);
3525
+ outro(`Run ${import_picocolors3.default.green("bungres migrate")} to apply it.`);
1765
3526
  }
1766
3527
  function topoSort(schemas2) {
1767
3528
  const byName = new Map(schemas2.map((s) => [s.config.name, s]));
1768
3529
  function deps(config2) {
1769
- return Object.values(config2.columns).map((col2) => col2.references?.table).filter((t) => t !== undefined && byName.has(t) && t !== config2.name);
3530
+ return Object.values(config2.columns).map((col2) => col2.references?.table).filter((t2) => t2 !== undefined && byName.has(t2) && t2 !== config2.name);
1770
3531
  }
1771
3532
  const visited = new Set;
1772
3533
  const result2 = [];
@@ -1790,18 +3551,25 @@ function topoSort(schemas2) {
1790
3551
  import { existsSync } from "fs";
1791
3552
  import { mkdir } from "fs/promises";
1792
3553
  import { join as join4 } from "path";
3554
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
1793
3555
  async function runInit(cwd = process.cwd()) {
3556
+ intro(import_picocolors4.default.bgCyan(import_picocolors4.default.black(" @bungres/kit init ")));
1794
3557
  const configPath = join4(cwd, "bungres.config.ts");
1795
3558
  const dbDir = join4(cwd, "src", "db");
1796
3559
  if (existsSync(configPath)) {
1797
- console.log("Config file already exists at bungres.config.ts");
3560
+ log.warn(import_picocolors4.default.yellow("Config file already exists at bungres.config.ts"));
3561
+ outro("Failed.");
1798
3562
  return;
1799
3563
  }
3564
+ const s = spinner();
3565
+ s.start("Creating project structure...");
1800
3566
  try {
1801
3567
  await mkdir(dbDir, { recursive: true });
1802
- console.log("Created src/db directory");
3568
+ log.step(`Created ${import_picocolors4.default.cyan("src/db")} directory`);
1803
3569
  } catch (e) {
1804
- console.error("Failed to create src/db directory:", e);
3570
+ s.stop("Failed");
3571
+ log.error(`Failed to create src/db directory: ${e}`);
3572
+ outro("Failed.");
1805
3573
  return;
1806
3574
  }
1807
3575
  const configContent = `import { defineConfig } from "@bungres/kit";
@@ -1816,9 +3584,11 @@ export default defineConfig({
1816
3584
  `;
1817
3585
  try {
1818
3586
  await Bun.write(configPath, configContent);
1819
- console.log("Created bungres.config.ts");
3587
+ log.step(`Created ${import_picocolors4.default.cyan("bungres.config.ts")}`);
1820
3588
  } catch (e) {
1821
- console.error("Failed to create config file:", e);
3589
+ s.stop("Failed");
3590
+ log.error(`Failed to create config file: ${e}`);
3591
+ outro("Failed.");
1822
3592
  return;
1823
3593
  }
1824
3594
  const schemaContent = `import {
@@ -1842,9 +3612,9 @@ export const users = table("users", {
1842
3612
  `;
1843
3613
  try {
1844
3614
  await Bun.write(join4(dbDir, "schema.ts"), schemaContent);
1845
- console.log("Created src/db/schema.ts with example table");
3615
+ log.step(`Created ${import_picocolors4.default.cyan("src/db/schema.ts")} with example table`);
1846
3616
  } catch (e) {
1847
- console.error("Failed to create schema file:", e);
3617
+ log.error(`Failed to create schema file: ${e}`);
1848
3618
  }
1849
3619
  const clientContent = `import { bungres } from "@bungres/orm";
1850
3620
  import * as schema from "./schema";
@@ -1855,24 +3625,23 @@ export const db = bungres({ url, schema });
1855
3625
  `;
1856
3626
  try {
1857
3627
  await Bun.write(join4(dbDir, "client.ts"), clientContent);
1858
- console.log("Created src/db/client.ts");
3628
+ log.step(`Created ${import_picocolors4.default.cyan("src/db/client.ts")}`);
1859
3629
  } catch (e) {
1860
- console.error("Failed to create client file:", e);
1861
- }
1862
- console.log(`
1863
- \u2728 Bungres project initialized!`);
1864
- console.log(`
1865
- Next steps:`);
1866
- console.log(" 1. Set DATABASE_URL in your .env file");
1867
- console.log(" 2. Edit src/db/schema.ts to define your tables");
1868
- console.log(" 3. Run 'bungres generate' to create migrations");
1869
- console.log(" 4. Run 'bungres migrate' to apply migrations");
3630
+ log.error(`Failed to create client file: ${e}`);
3631
+ }
3632
+ s.stop("Project initialized.");
3633
+ const nextSteps = `1. Set DATABASE_URL in your .env file
3634
+ 2. Edit src/db/schema.ts to define your tables
3635
+ 3. Run ${import_picocolors4.default.green("bungres generate")} to create migrations
3636
+ 4. Run ${import_picocolors4.default.green("bungres migrate")} to apply migrations`;
3637
+ note(nextSteps, "Next steps");
3638
+ outro(import_picocolors4.default.cyan("\u2728 Bungres project initialized!"));
1870
3639
  }
1871
3640
 
1872
3641
  // src/commands/pull.ts
1873
3642
  import { resolve as resolve4, join as join5 } from "path";
1874
3643
  async function introspectDb(sql2, dbSchema) {
1875
- const columns2 = await sql2.unsafe(`SELECT
3644
+ const columns = await sql2.unsafe(`SELECT
1876
3645
  c.table_name,
1877
3646
  c.column_name,
1878
3647
  c.data_type,
@@ -1906,7 +3675,7 @@ async function introspectDb(sql2, dbSchema) {
1906
3675
  const indexes = await sql2.unsafe(`SELECT tablename, indexname, indexdef
1907
3676
  FROM pg_indexes
1908
3677
  WHERE schemaname = $1`, [dbSchema]);
1909
- return groupByTable(columns2, constraints, indexes);
3678
+ return groupByTable(columns, constraints, indexes);
1910
3679
  }
1911
3680
  async function runPull(config2) {
1912
3681
  console.log("@bungres/kit pull: introspecting database...");
@@ -1929,20 +3698,20 @@ async function runPull(config2) {
1929
3698
  await sql2.end();
1930
3699
  }
1931
3700
  }
1932
- function groupByTable(columns2, constraints, indexes) {
3701
+ function groupByTable(columns, constraints, indexes) {
1933
3702
  const map = new Map;
1934
- for (const col2 of columns2) {
3703
+ for (const col2 of columns) {
1935
3704
  if (!map.has(col2.table_name)) {
1936
3705
  map.set(col2.table_name, {
1937
3706
  tableName: col2.table_name,
1938
3707
  columns: [],
1939
- indexes: indexes.filter((i) => i.tablename === col2.table_name)
3708
+ indexes: indexes.filter((i2) => i2.tablename === col2.table_name)
1940
3709
  });
1941
3710
  }
1942
- const tableConstraints = constraints.filter((c) => c.table_name === col2.table_name && c.column_name === col2.column_name);
1943
- const pkConstraint = tableConstraints.find((c) => c.constraint_type === "PRIMARY KEY");
1944
- const uniqueConstraint = tableConstraints.find((c) => c.constraint_type === "UNIQUE");
1945
- const fkConstraint = tableConstraints.find((c) => c.constraint_type === "FOREIGN KEY");
3711
+ const tableConstraints = constraints.filter((c2) => c2.table_name === col2.table_name && c2.column_name === col2.column_name);
3712
+ const pkConstraint = tableConstraints.find((c2) => c2.constraint_type === "PRIMARY KEY");
3713
+ const uniqueConstraint = tableConstraints.find((c2) => c2.constraint_type === "UNIQUE");
3714
+ const fkConstraint = tableConstraints.find((c2) => c2.constraint_type === "FOREIGN KEY");
1946
3715
  map.get(col2.table_name).columns.push({
1947
3716
  name: col2.column_name,
1948
3717
  dataType: col2.data_type,
@@ -1991,19 +3760,19 @@ function generateSchemaTS(tableMap, dbSchema) {
1991
3760
  if (table2.indexes.length > 0) {
1992
3761
  const idxLines = [];
1993
3762
  for (const idx of table2.indexes) {
1994
- const m = idx.indexdef.match(/CREATE (UNIQUE )?INDEX (.+) ON (.+) USING (\w+) \((.+)\)(?: WHERE (.+))?/i);
1995
- if (m) {
1996
- const isUnique = !!m[1];
1997
- const name = m[2].trim().replace(/^"|"$/g, "");
3763
+ const m2 = idx.indexdef.match(/CREATE (UNIQUE )?INDEX (.+) ON (.+) USING (\w+) \((.+)\)(?: WHERE (.+))?/i);
3764
+ if (m2) {
3765
+ const isUnique = !!m2[1];
3766
+ const name = m2[2].trim().replace(/^"|"$/g, "");
1998
3767
  if (name.endsWith("_pkey") || name.endsWith("_key"))
1999
3768
  continue;
2000
- const using = m[4].toLowerCase();
2001
- const cols = m[5].split(",").map((c) => `"${c.trim().replace(/^"|"$/g, "")}"`);
3769
+ const using = m2[4].toLowerCase();
3770
+ const cols = m2[5].split(",").map((c2) => `"${c2.trim().replace(/^"|"$/g, "")}"`);
2002
3771
  let idxStr = `{ name: "${name}", columns: [${cols.join(", ")}], using: "${using}"`;
2003
3772
  if (isUnique)
2004
3773
  idxStr += `, unique: true`;
2005
- if (m[6])
2006
- idxStr += `, where: \`${m[6].trim()}\``;
3774
+ if (m2[6])
3775
+ idxStr += `, where: \`${m2[6].trim()}\``;
2007
3776
  idxStr += ` }`;
2008
3777
  idxLines.push(idxStr);
2009
3778
  }
@@ -2122,19 +3891,26 @@ function pgTypeToBungresBuilderName(col2) {
2122
3891
  return "text";
2123
3892
  }
2124
3893
  function toCamelCase(str) {
2125
- return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
3894
+ return str.replace(/_([a-z])/g, (_2, c2) => c2.toUpperCase());
2126
3895
  }
2127
3896
 
2128
3897
  // src/commands/push.ts
2129
- import * as fs from "fs";
3898
+ var import_picocolors5 = __toESM(require_picocolors(), 1);
2130
3899
  async function runPush(config2, opts = {}) {
2131
- console.log("@bungres/kit push: loading schemas...");
3900
+ intro(import_picocolors5.default.bgCyan(import_picocolors5.default.black(" @bungres/kit push ")));
3901
+ const s = spinner();
3902
+ s.start("Loading schemas...");
2132
3903
  const schemas2 = await loadSchemas(config2.schema);
2133
3904
  if (schemas2.length === 0) {
2134
- console.warn("No table definitions found. Check your schema glob pattern.");
3905
+ s.stop("No schemas found.");
3906
+ log.warn(import_picocolors5.default.yellow("No table definitions found. Check your schema glob pattern."));
3907
+ outro("Failed.");
2135
3908
  return;
2136
3909
  }
3910
+ s.stop(`Loaded ${schemas2.length} schemas.`);
2137
3911
  const sql2 = new Bun.SQL(config2.dbUrl);
3912
+ const ms = spinner();
3913
+ ms.start("Computing diff from database...");
2138
3914
  try {
2139
3915
  await sql2.unsafe(`CREATE SCHEMA IF NOT EXISTS "${config2.migrationsSchema}";`);
2140
3916
  const qualifiedPush = `"${config2.migrationsSchema}"."__bungres_push"`;
@@ -2149,56 +3925,62 @@ async function runPush(config2, opts = {}) {
2149
3925
  if (rows.length > 0) {
2150
3926
  prevSnapshot = typeof rows[0].snapshot === "string" ? JSON.parse(rows[0].snapshot) : rows[0].snapshot;
2151
3927
  }
2152
- const currentSnapshot = Object.fromEntries(schemas2.map((s) => [s.config.name, s.config]));
3928
+ const currentSnapshot = Object.fromEntries(schemas2.map((schemaEntry) => [schemaEntry.config.name, schemaEntry.config]));
2153
3929
  const diff = diffSchemas(prevSnapshot, currentSnapshot);
2154
3930
  if (diff.statements.length === 0) {
2155
- console.log(`
2156
- No schema changes detected. Database is up to date.`);
3931
+ ms.stop("Up to date.");
3932
+ log.success(import_picocolors5.default.green("No schema changes detected. Database is up to date."));
3933
+ outro("Done.");
2157
3934
  return;
2158
3935
  }
2159
- console.log(`
2160
- Changes to apply:`);
2161
- for (const s of diff.summary)
2162
- console.log(` + ${s}`);
3936
+ ms.stop(`Computed ${diff.statements.length} statements.`);
3937
+ log.message(import_picocolors5.default.bold("Changes to apply:"));
3938
+ for (const change of diff.summary) {
3939
+ if (change.startsWith("CREATE") || change.startsWith("ALTER TABLE") && change.includes("ADD")) {
3940
+ log.success(import_picocolors5.default.green(` + ${change}`));
3941
+ } else if (change.startsWith("DROP") || change.startsWith("ALTER TABLE") && change.includes("DROP")) {
3942
+ log.error(import_picocolors5.default.red(` - ${change}`));
3943
+ } else {
3944
+ log.info(import_picocolors5.default.blue(` ~ ${change}`));
3945
+ }
3946
+ }
2163
3947
  if (diff.warnings && diff.warnings.length > 0) {
2164
- console.warn(`
2165
- \u26A0\uFE0F WARNING: Data Loss Detected!`);
3948
+ log.warn(import_picocolors5.default.bgRed(import_picocolors5.default.white(" \u26A0\uFE0F DATA LOSS DETECTED ")));
2166
3949
  for (const w of diff.warnings)
2167
- console.warn(` ! ${w}`);
2168
- console.warn(`
2169
- These changes will be immediately executed against the database!`);
3950
+ log.warn(import_picocolors5.default.red(` ! ${w}`));
3951
+ log.warn(import_picocolors5.default.yellow("These changes will be immediately executed against the database!"));
2170
3952
  }
2171
3953
  if (!opts.force) {
2172
- process.stdout.write(`
2173
- Are you sure you want to push these changes? Type YES to continue: `);
2174
- const answer = await readLine();
2175
- if (answer.trim().toLowerCase() !== "yes") {
2176
- console.log("Aborted.");
3954
+ const confirm2 = await confirm({
3955
+ message: "Are you sure you want to push these changes?",
3956
+ initialValue: true
3957
+ });
3958
+ if (isCancel(confirm2) || !confirm2) {
3959
+ outro(import_picocolors5.default.gray("Push aborted."));
2177
3960
  return;
2178
3961
  }
2179
3962
  }
2180
- console.log(`
2181
- Pushing changes...`);
3963
+ const exSpinner = spinner();
3964
+ exSpinner.start("Pushing changes...");
2182
3965
  for (const stmt of diff.statements) {
2183
3966
  if (config2.verbose) {
2184
- console.log(`-- ${stmt}`);
3967
+ log.info(import_picocolors5.default.gray(`-- ${stmt}`));
2185
3968
  }
2186
3969
  await sql2.unsafe(stmt);
2187
3970
  }
2188
3971
  await sql2.unsafe(`INSERT INTO ${qualifiedPush} (snapshot) VALUES ($1);`, [JSON.stringify(currentSnapshot)]);
2189
- console.log(`
2190
- Push complete.`);
3972
+ exSpinner.stop(import_picocolors5.default.green("Changes applied successfully."));
3973
+ outro(import_picocolors5.default.cyan("\u2728 Push complete."));
3974
+ } catch (err) {
3975
+ log.error(import_picocolors5.default.red(`Push failed: ${err.message}`));
3976
+ outro("Failed.");
2191
3977
  } finally {
2192
3978
  await sql2.end();
2193
3979
  }
2194
3980
  }
2195
- async function readLine() {
2196
- const buf = Buffer.alloc(256);
2197
- const n = fs.readSync(0, buf, 0, 256, null);
2198
- return buf.subarray(0, n).toString().trim();
2199
- }
2200
3981
 
2201
3982
  // src/commands/refresh.ts
3983
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
2202
3984
  async function runRefresh(config2) {
2203
3985
  const schemas2 = await loadSchemas(config2.schema);
2204
3986
  if (schemas2.length === 0) {
@@ -2212,55 +3994,210 @@ async function runRefresh(config2) {
2212
3994
  for (const entry of schemas2) {
2213
3995
  const ddl = `TRUNCATE TABLE "${entry.config.name}" CASCADE;`;
2214
3996
  await sql2.unsafe(ddl);
2215
- console.log(colorize(` \u2713 truncated ${entry.config.name}`, "green"));
3997
+ console.log(import_picocolors6.default.green(` \u2713 truncated ${entry.config.name}`));
2216
3998
  }
2217
- console.log(colorize(`
2218
- Refresh complete. All tables are now empty.`, "green"));
3999
+ console.log(import_picocolors6.default.green(`
4000
+ Refresh complete. All tables are now empty.`));
4001
+ } finally {
4002
+ await sql2.end();
4003
+ }
4004
+ }
4005
+
4006
+ // src/commands/rollback.ts
4007
+ import { join as join6, resolve as resolve5 } from "path";
4008
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
4009
+ async function runRollback(config2) {
4010
+ intro(import_picocolors7.default.bgCyan(import_picocolors7.default.black(" @bungres/kit rollback ")));
4011
+ const migrationsDir = resolve5(config2.out);
4012
+ const sql2 = new Bun.SQL(config2.dbUrl);
4013
+ const table2 = config2.migrationsTable;
4014
+ const schema = config2.migrationsSchema;
4015
+ const qualifiedTable = `"${schema}"."${table2}"`;
4016
+ const s = spinner();
4017
+ s.start("Checking applied migrations...");
4018
+ try {
4019
+ const applied = await sql2.unsafe(`SELECT name FROM ${qualifiedTable} ORDER BY name DESC LIMIT 1`);
4020
+ if (applied.length === 0) {
4021
+ s.stop("No migrations to rollback.");
4022
+ log.warn(import_picocolors7.default.yellow("No applied migrations found in the database."));
4023
+ outro("Done.");
4024
+ return;
4025
+ }
4026
+ const lastMigration = applied[0].name;
4027
+ s.stop(`Found migration to rollback: ${import_picocolors7.default.cyan(lastMigration)}`);
4028
+ const shouldRollback = await confirm({
4029
+ message: `Are you sure you want to rollback ${import_picocolors7.default.cyan(lastMigration)}?`,
4030
+ initialValue: true
4031
+ });
4032
+ if (isCancel(shouldRollback) || !shouldRollback) {
4033
+ outro(import_picocolors7.default.gray("Rollback cancelled."));
4034
+ return;
4035
+ }
4036
+ const ms = spinner();
4037
+ ms.start(`Rolling back ${lastMigration}...`);
4038
+ const content = await Bun.file(join6(migrationsDir, lastMigration)).text();
4039
+ let downContent = "";
4040
+ const downMatch = content.match(/-- ==== DOWN ====([\s\S]*)/i);
4041
+ if (downMatch) {
4042
+ downContent = downMatch[1].trim();
4043
+ } else {
4044
+ ms.stop("Failed.");
4045
+ log.error(import_picocolors7.default.red(`No '-- ==== DOWN ====' section found in ${lastMigration}. Cannot rollback safely.`));
4046
+ outro("Failed.");
4047
+ return;
4048
+ }
4049
+ if (!downContent) {
4050
+ log.warn(import_picocolors7.default.yellow(`Down section is empty in ${lastMigration}. Nothing to execute.`));
4051
+ }
4052
+ if (config2.verbose) {
4053
+ log.info(import_picocolors7.default.gray(`-- ${lastMigration} (DOWN) --
4054
+ ${downContent}
4055
+ `));
4056
+ }
4057
+ await sql2.transaction(async (txSql) => {
4058
+ if (downContent) {
4059
+ const statements = config2.breakpoints ? downContent.split(/-->statement-breakpoint/g).flatMap((chunk) => chunk.split(";").map((s2) => s2.trim()).filter(Boolean)) : downContent.split(";").map((s2) => s2.trim()).filter(Boolean);
4060
+ for (const stmt of statements) {
4061
+ await txSql.unsafe(stmt + ";");
4062
+ }
4063
+ }
4064
+ await txSql.unsafe(`DELETE FROM ${qualifiedTable} WHERE name = $1`, [lastMigration]);
4065
+ });
4066
+ ms.stop(import_picocolors7.default.green(`\u2713 Rolled back ${lastMigration}`));
4067
+ outro(import_picocolors7.default.cyan("\u2728 Rollback successful."));
4068
+ } catch (err) {
4069
+ log.error(import_picocolors7.default.red(`Rollback failed: ${err.message}`));
4070
+ outro("Failed.");
2219
4071
  } finally {
2220
4072
  await sql2.end();
2221
4073
  }
2222
4074
  }
2223
4075
 
2224
4076
  // src/commands/seed.ts
2225
- import { resolve as resolve5 } from "path";
4077
+ import { resolve as resolve6 } from "path";
4078
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
2226
4079
  async function runSeed(config2) {
2227
- if (!config2.seed) {
2228
- console.log(colorize(`No seed file configured in bungres.config.ts`, "yellow"));
4080
+ intro(import_picocolors8.default.bgCyan(import_picocolors8.default.black(" @bungres/kit seed ")));
4081
+ if (config2.seed) {
4082
+ const seedPath = resolve6(process.cwd(), config2.seed);
4083
+ const file = Bun.file(seedPath);
4084
+ if (await file.exists()) {
4085
+ const s2 = spinner();
4086
+ s2.start(`Running seeder: ${config2.seed}...`);
4087
+ const proc = Bun.spawn(["bun", "run", seedPath], {
4088
+ cwd: process.cwd(),
4089
+ stdout: "inherit",
4090
+ stderr: "inherit",
4091
+ env: { ...Bun.env, DATABASE_URL: config2.dbUrl }
4092
+ });
4093
+ const exitCode = await proc.exited;
4094
+ if (exitCode === 0) {
4095
+ s2.stop("Seed complete.");
4096
+ outro(import_picocolors8.default.cyan("\u2728 Seeding successful."));
4097
+ } else {
4098
+ s2.stop("Seed failed.");
4099
+ log.error(import_picocolors8.default.red(`Seed failed with exit code ${exitCode}.`));
4100
+ outro("Failed.");
4101
+ process.exit(exitCode);
4102
+ }
4103
+ return;
4104
+ }
4105
+ }
4106
+ log.info(import_picocolors8.default.blue("No custom seed script found. Initiating Auto-Seeder..."));
4107
+ let faker;
4108
+ try {
4109
+ const fakerModule = await import("@faker-js/faker");
4110
+ faker = fakerModule.faker;
4111
+ } catch (err) {
4112
+ log.error(import_picocolors8.default.red("Auto-seeder requires @faker-js/faker."));
4113
+ log.message(`Please install it: ${import_picocolors8.default.green("bun add -d @faker-js/faker")}`);
4114
+ outro("Failed.");
2229
4115
  return;
2230
4116
  }
2231
- const seedPath = resolve5(process.cwd(), config2.seed);
2232
- const file = Bun.file(seedPath);
2233
- if (!await file.exists()) {
2234
- console.error(colorize(`Seed file not found at ${seedPath}`, "red"));
2235
- process.exit(1);
4117
+ const s = spinner();
4118
+ s.start("Loading schemas for auto-seeding...");
4119
+ const schemas2 = await loadSchemas(config2.schema);
4120
+ if (schemas2.length === 0) {
4121
+ s.stop("No schemas.");
4122
+ outro("Nothing to seed.");
4123
+ return;
2236
4124
  }
2237
- console.log(colorize(`
2238
- Running seeder: ${config2.seed}...`, "cyan"));
2239
- const proc = Bun.spawn(["bun", "run", seedPath], {
2240
- cwd: process.cwd(),
2241
- stdout: "inherit",
2242
- stderr: "inherit",
2243
- env: { ...Bun.env, DATABASE_URL: config2.dbUrl }
2244
- });
2245
- const exitCode = await proc.exited;
2246
- if (exitCode === 0) {
2247
- console.log(`
2248
- Seed complete.`);
2249
- } else {
2250
- console.error(`
2251
- Seed failed with exit code ${exitCode}.`);
2252
- process.exit(exitCode);
4125
+ const tableConfigs = schemas2.map((s2) => s2.config);
4126
+ const sorted = topoSortConfigs(tableConfigs);
4127
+ s.stop(`Loaded ${sorted.length} tables in topological order.`);
4128
+ const sql2 = new Bun.SQL(config2.dbUrl);
4129
+ const insertedData = {};
4130
+ try {
4131
+ for (const table2 of sorted) {
4132
+ const ms = spinner();
4133
+ ms.start(`Seeding ${table2.name}...`);
4134
+ const rowsToInsert = 10;
4135
+ const rows = [];
4136
+ for (let i2 = 0;i2 < rowsToInsert; i2++) {
4137
+ const row = {};
4138
+ for (const [colName2, col2] of Object.entries(table2.columns)) {
4139
+ if (col2.references) {
4140
+ const parentTable = col2.references.table;
4141
+ const parentRows = insertedData[parentTable];
4142
+ if (parentRows && parentRows.length > 0) {
4143
+ const parentRow = parentRows[Math.floor(Math.random() * parentRows.length)];
4144
+ row[col2.name] = parentRow[col2.references.column];
4145
+ continue;
4146
+ }
4147
+ }
4148
+ if (col2.dataType === "uuid")
4149
+ row[col2.name] = faker.string.uuid();
4150
+ else if (col2.dataType === "varchar" || col2.dataType === "text")
4151
+ row[col2.name] = faker.lorem.word();
4152
+ else if (col2.dataType === "integer")
4153
+ row[col2.name] = faker.number.int({ min: 1, max: 1000 });
4154
+ else if (col2.dataType === "boolean")
4155
+ row[col2.name] = faker.datatype.boolean();
4156
+ else if (col2.dataType === "timestamptz" || col2.dataType === "timestamp")
4157
+ row[col2.name] = faker.date.recent().toISOString();
4158
+ else if (col2.dataType === "jsonb" || col2.dataType === "json")
4159
+ row[col2.name] = JSON.stringify({ key: faker.lorem.word() });
4160
+ else
4161
+ row[col2.name] = null;
4162
+ }
4163
+ rows.push(row);
4164
+ }
4165
+ const insertedRows = await sql2.transaction(async (tx) => {
4166
+ const results = [];
4167
+ for (const row of rows) {
4168
+ const keys = Object.keys(row).map((k) => `"${k}"`);
4169
+ const values = Object.values(row);
4170
+ const placeholders = values.map((_2, i2) => `$${i2 + 1}`);
4171
+ const q = `INSERT INTO "${table2.schema || "public"}"."${table2.name}" (${keys.join(", ")}) VALUES (${placeholders.join(", ")}) RETURNING *`;
4172
+ const res = await tx.unsafe(q, values);
4173
+ results.push(res[0]);
4174
+ }
4175
+ return results;
4176
+ });
4177
+ insertedData[table2.name] = insertedRows;
4178
+ ms.stop(import_picocolors8.default.green(`\u2713 Seeded ${rowsToInsert} rows into ${table2.name}`));
4179
+ }
4180
+ outro(import_picocolors8.default.cyan("\u2728 Auto-seeding complete."));
4181
+ } catch (err) {
4182
+ log.error(import_picocolors8.default.red(`Auto-seeding failed: ${err.message}`));
4183
+ outro("Failed.");
4184
+ } finally {
4185
+ await sql2.end();
2253
4186
  }
2254
4187
  }
2255
4188
 
2256
4189
  // src/commands/status.ts
2257
- import { resolve as resolve6 } from "path";
4190
+ import { resolve as resolve7 } from "path";
4191
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
2258
4192
  async function runStatus(config2) {
2259
- const migrationsDir = resolve6(config2.out);
4193
+ intro(import_picocolors9.default.bgCyan(import_picocolors9.default.black(" @bungres/kit status ")));
4194
+ const migrationsDir = resolve7(config2.out);
2260
4195
  const sql2 = new Bun.SQL(config2.dbUrl);
2261
4196
  const table2 = config2.migrationsTable;
2262
4197
  const schema = config2.migrationsSchema;
2263
4198
  const qualifiedTable = `"${schema}"."${table2}"`;
4199
+ const s = spinner();
4200
+ s.start("Checking migration status...");
2264
4201
  try {
2265
4202
  const tableCheck = await sql2.unsafe(`SELECT EXISTS (
2266
4203
  SELECT 1 FROM information_schema.tables
@@ -2274,33 +4211,247 @@ async function runStatus(config2) {
2274
4211
  }
2275
4212
  files.sort();
2276
4213
  if (files.length === 0) {
2277
- console.log(colorize("No migration files found. Run `bungres generate` first.", "yellow"));
4214
+ s.stop("No files found.");
4215
+ log.warn(import_picocolors9.default.yellow("No migration files found."));
4216
+ log.info(`Run ${import_picocolors9.default.green("bungres generate")} first.`);
4217
+ outro("Done.");
2278
4218
  return;
2279
4219
  }
2280
4220
  let appliedSet = new Set;
2281
4221
  if (trackingExists) {
2282
4222
  const applied = await sql2.unsafe(`SELECT name FROM ${qualifiedTable} ORDER BY applied_at`);
2283
- appliedSet = new Set(applied.map((r) => r.name));
4223
+ appliedSet = new Set(applied.map((r2) => r2.name));
2284
4224
  }
2285
- console.log(colorize(`
2286
- Migration status:
2287
- `, "cyan"));
4225
+ s.stop("Status check complete.");
4226
+ log.message(import_picocolors9.default.bold("Migration status:"));
2288
4227
  let pendingCount = 0;
2289
4228
  for (const file of files) {
2290
4229
  const isApplied = appliedSet.has(file);
2291
- const status = isApplied ? colorize("\u2713 applied ", "green") : colorize("\u2717 pending ", "yellow");
2292
- if (!isApplied)
4230
+ if (isApplied) {
4231
+ log.success(`${import_picocolors9.default.green("\u2713 applied ")} ${file}`);
4232
+ } else {
2293
4233
  pendingCount++;
2294
- console.log(` ${status} ${file}`);
4234
+ log.warn(`${import_picocolors9.default.yellow("\u2717 pending ")} ${file}`);
4235
+ }
2295
4236
  }
2296
- console.log(`
2297
- ${colorize(appliedSet.size.toString(), "green")} applied, ${colorize(pendingCount.toString(), "yellow")} pending.
2298
- `);
4237
+ log.info(`${import_picocolors9.default.green(appliedSet.size.toString())} applied, ${import_picocolors9.default.yellow(pendingCount.toString())} pending.`);
4238
+ outro("Done.");
4239
+ } catch (err) {
4240
+ log.error(import_picocolors9.default.red(`Status check failed: ${err.message}`));
4241
+ outro("Failed.");
2299
4242
  } finally {
2300
4243
  await sql2.end();
2301
4244
  }
2302
4245
  }
2303
4246
 
4247
+ // src/commands/studio.ts
4248
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
4249
+
4250
+ // src/commands/template.ts
4251
+ var indexHtml = `<!DOCTYPE html>
4252
+ <html lang="en">
4253
+ <head>
4254
+ <meta charset="UTF-8">
4255
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4256
+ <title>Bungres Studio</title>
4257
+
4258
+ <link rel="preconnect" href="https://fonts.googleapis.com">
4259
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
4260
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
4261
+ <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
4262
+
4263
+ <script src="https://unpkg.com/htmx.org@2.0.0" defer></script>
4264
+ <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
4265
+ <script src="https://cdn.tailwindcss.com"></script>
4266
+
4267
+ <script>
4268
+ tailwind.config = {
4269
+ darkMode: "class",
4270
+ theme: {
4271
+ extend: {
4272
+ fontFamily: {
4273
+ sans: ['Outfit', 'sans-serif'],
4274
+ mono: ['"Fira Code"', 'monospace'],
4275
+ },
4276
+ colors: {
4277
+ background: 'var(--background)',
4278
+ foreground: 'var(--foreground)',
4279
+ card: 'var(--card)',
4280
+ 'card-foreground': 'var(--card-foreground)',
4281
+ popover: 'var(--popover)',
4282
+ 'popover-foreground': 'var(--popover-foreground)',
4283
+ primary: 'var(--primary)',
4284
+ 'primary-foreground': 'var(--primary-foreground)',
4285
+ secondary: 'var(--secondary)',
4286
+ 'secondary-foreground': 'var(--secondary-foreground)',
4287
+ muted: 'var(--muted)',
4288
+ 'muted-foreground': 'var(--muted-foreground)',
4289
+ accent: 'var(--accent)',
4290
+ 'accent-foreground': 'var(--accent-foreground)',
4291
+ destructive: 'var(--destructive)',
4292
+ border: 'var(--border)',
4293
+ input: 'var(--input)',
4294
+ ring: 'var(--ring)',
4295
+ sidebar: {
4296
+ DEFAULT: 'var(--sidebar)',
4297
+ foreground: 'var(--sidebar-foreground)',
4298
+ primary: 'var(--sidebar-primary)',
4299
+ 'primary-foreground': 'var(--sidebar-primary-foreground)',
4300
+ accent: 'var(--sidebar-accent)',
4301
+ 'accent-foreground': 'var(--sidebar-accent-foreground)',
4302
+ border: 'var(--sidebar-border)',
4303
+ ring: 'var(--sidebar-ring)',
4304
+ }
4305
+ }
4306
+ }
4307
+ }
4308
+ }
4309
+ </script>
4310
+
4311
+ <style>
4312
+ :root {
4313
+ --background: oklch(1 0 0);
4314
+ --foreground: oklch(0.148 0.004 228.8);
4315
+ --card: oklch(1 0 0);
4316
+ --card-foreground: oklch(0.148 0.004 228.8);
4317
+ --popover: oklch(1 0 0);
4318
+ --popover-foreground: oklch(0.148 0.004 228.8);
4319
+ --primary: oklch(0.508 0.118 165.612);
4320
+ --primary-foreground: oklch(0.979 0.021 166.113);
4321
+ --secondary: oklch(0.967 0.001 286.375);
4322
+ --secondary-foreground: oklch(0.21 0.006 285.885);
4323
+ --muted: oklch(0.963 0.002 197.1);
4324
+ --muted-foreground: oklch(0.56 0.021 213.5);
4325
+ --accent: oklch(0.963 0.002 197.1);
4326
+ --accent-foreground: oklch(0.218 0.008 223.9);
4327
+ --destructive: oklch(0.577 0.245 27.325);
4328
+ --border: oklch(0.925 0.005 214.3);
4329
+ --input: oklch(0.925 0.005 214.3);
4330
+ --ring: oklch(0.723 0.014 214.4);
4331
+ --chart-1: oklch(0.845 0.143 164.978);
4332
+ --chart-2: oklch(0.696 0.17 162.48);
4333
+ --chart-3: oklch(0.596 0.145 163.225);
4334
+ --chart-4: oklch(0.508 0.118 165.612);
4335
+ --chart-5: oklch(0.432 0.095 166.913);
4336
+ --radius: 0.625rem;
4337
+ --sidebar: oklch(0.987 0.002 197.1);
4338
+ --sidebar-foreground: oklch(0.148 0.004 228.8);
4339
+ --sidebar-primary: oklch(0.596 0.145 163.225);
4340
+ --sidebar-primary-foreground: oklch(0.979 0.021 166.113);
4341
+ --sidebar-accent: oklch(0.963 0.002 197.1);
4342
+ --sidebar-accent-foreground: oklch(0.218 0.008 223.9);
4343
+ --sidebar-border: oklch(0.925 0.005 214.3);
4344
+ --sidebar-ring: oklch(0.723 0.014 214.4);
4345
+ }
4346
+
4347
+ .dark {
4348
+ --background: oklch(0.148 0.004 228.8);
4349
+ --foreground: oklch(0.987 0.002 197.1);
4350
+ --card: oklch(0.218 0.008 223.9);
4351
+ --card-foreground: oklch(0.987 0.002 197.1);
4352
+ --popover: oklch(0.218 0.008 223.9);
4353
+ --popover-foreground: oklch(0.987 0.002 197.1);
4354
+ --primary: oklch(0.432 0.095 166.913);
4355
+ --primary-foreground: oklch(0.979 0.021 166.113);
4356
+ --secondary: oklch(0.274 0.006 286.033);
4357
+ --secondary-foreground: oklch(0.985 0 0);
4358
+ --muted: oklch(0.275 0.011 216.9);
4359
+ --muted-foreground: oklch(0.723 0.014 214.4);
4360
+ --accent: oklch(0.275 0.011 216.9);
4361
+ --accent-foreground: oklch(0.987 0.002 197.1);
4362
+ --destructive: oklch(0.704 0.191 22.216);
4363
+ --border: oklch(1 0 0 / 10%);
4364
+ --input: oklch(1 0 0 / 15%);
4365
+ --ring: oklch(0.56 0.021 213.5);
4366
+ --chart-1: oklch(0.845 0.143 164.978);
4367
+ --chart-2: oklch(0.696 0.17 162.48);
4368
+ --chart-3: oklch(0.596 0.145 163.225);
4369
+ --chart-4: oklch(0.508 0.118 165.612);
4370
+ --chart-5: oklch(0.432 0.095 166.913);
4371
+ --sidebar: oklch(0.218 0.008 223.9);
4372
+ --sidebar-foreground: oklch(0.987 0.002 197.1);
4373
+ --sidebar-primary: oklch(0.696 0.17 162.48);
4374
+ --sidebar-primary-foreground: oklch(0.262 0.051 172.552);
4375
+ --sidebar-accent: oklch(0.275 0.011 216.9);
4376
+ --sidebar-accent-foreground: oklch(0.987 0.002 197.1);
4377
+ --sidebar-border: oklch(1 0 0 / 10%);
4378
+ --sidebar-ring: oklch(0.56 0.021 213.5);
4379
+ }
4380
+
4381
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
4382
+ ::-webkit-scrollbar-track { background: transparent; }
4383
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
4384
+ ::-webkit-scrollbar-thumb:hover { background: var(--muted-foreground); }
4385
+ ::-webkit-scrollbar-corner { background: transparent; }
4386
+ [x-cloak] { display: none !important; }
4387
+ .htmx-indicator { opacity: 0; transition: opacity 200ms ease-in; }
4388
+ .htmx-request .htmx-indicator { opacity: 1; }
4389
+ .htmx-request.htmx-indicator { opacity: 1; }
4390
+ </style>
4391
+ </head>
4392
+ <body class="dark bg-background text-foreground font-sans h-screen flex overflow-hidden selection:bg-primary/30">
4393
+ <div x-data="{ currentTable: null, pageSize: 25 }" class="flex h-screen w-full">
4394
+ <aside class="w-64 bg-sidebar border-r border-sidebar-border flex flex-col z-20 shrink-0 text-sidebar-foreground">
4395
+ <div class="p-4 border-b border-sidebar-border flex items-center gap-3 bg-sidebar">
4396
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-primary">
4397
+ <ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
4398
+ <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
4399
+ <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
4400
+ </svg>
4401
+ <h1 class="text-sm font-semibold tracking-tight m-0">Bungres Studio</h1>
4402
+ </div>
4403
+ <div class="flex-1 overflow-y-auto p-2" hx-get="/htmx/sidebar" hx-trigger="load">
4404
+ <div class="p-4 text-xs text-muted-foreground animate-pulse text-center">Loading schemas...</div>
4405
+ </div>
4406
+ </aside>
4407
+ <main class="flex-1 flex flex-col relative w-[calc(100vw-16rem)]">
4408
+ <header class="h-14 px-4 border-b border-border flex items-center justify-between bg-card z-10 shrink-0 text-card-foreground">
4409
+ <h2 class="text-sm font-semibold tracking-tight flex items-center gap-2">
4410
+ <template x-if="currentTable">
4411
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-primary">
4412
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
4413
+ <line x1="3" y1="9" x2="21" y2="9"></line>
4414
+ <line x1="9" y1="21" x2="9" y2="9"></line>
4415
+ </svg>
4416
+ </template>
4417
+ <template x-if="!currentTable">
4418
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-muted-foreground">
4419
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
4420
+ <line x1="3" y1="9" x2="21" y2="9"></line>
4421
+ <line x1="9" y1="21" x2="9" y2="9"></line>
4422
+ </svg>
4423
+ </template>
4424
+ <span x-text="currentTable ? currentTable : 'Select a table'"></span>
4425
+ </h2>
4426
+ <button x-bind:disabled="!currentTable" class="bg-card border border-border text-card-foreground px-3 py-1.5 rounded-md text-xs font-medium cursor-pointer transition-all hover:bg-accent hover:text-accent-foreground active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1.5 group" x-on:click="$dispatch('refresh-table')">
4427
+ <svg class="w-4 h-4 group-hover:rotate-180 transition-transform duration-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
4428
+ <polyline points="23 4 23 10 17 10"></polyline>
4429
+ <polyline points="1 20 1 14 7 14"></polyline>
4430
+ <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
4431
+ </svg>
4432
+ Refresh
4433
+ </button>
4434
+ </header>
4435
+ <div class="flex-1 overflow-auto relative z-[1] text-left bg-background">
4436
+ <div id="data-container" class="h-full w-full flex flex-col" x-on:refresh-table.window="if(currentTable) htmx.ajax('GET', '/htmx/tables/' + currentTable + '?limit=' + pageSize, {target: '#data-container'})">
4437
+ <div class="flex flex-col items-center justify-center h-full w-full text-muted-foreground animate-[fadeIn_0.5s_ease-out]">
4438
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="w-16 h-16 mb-4 text-muted">
4439
+ <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
4440
+ <polyline points="14 2 14 8 20 8"></polyline>
4441
+ </svg>
4442
+ <h3 class="text-foreground m-0 mb-2 text-lg">No Table Selected</h3>
4443
+ <p class="text-sm">Choose a table from the sidebar to view its data</p>
4444
+ </div>
4445
+ </div>
4446
+ </div>
4447
+ </main>
4448
+ </div>
4449
+ <script>
4450
+ document.addEventListener('alpine:init', () => {})
4451
+ </script>
4452
+ </body>
4453
+ </html>`;
4454
+
2304
4455
  // src/commands/studio.ts
2305
4456
  async function runStudio(config2) {
2306
4457
  const schemas2 = await loadSchemas(config2.schema);
@@ -2326,7 +4477,7 @@ async function runStudio(config2) {
2326
4477
  fk.columns.forEach((col2) => fkColumns.add(col2));
2327
4478
  });
2328
4479
  }
2329
- const columns2 = Object.entries(s.config.columns).map(([key, col2]) => {
4480
+ const columns = Object.entries(s.config.columns).map(([key, col2]) => {
2330
4481
  const fk = col2.references ? {
2331
4482
  table: col2.references.table,
2332
4483
  column: col2.references.column
@@ -2341,7 +4492,7 @@ async function runStudio(config2) {
2341
4492
  return {
2342
4493
  name: s.config.name,
2343
4494
  exportName: s.exportName,
2344
- columns: columns2,
4495
+ columns,
2345
4496
  foreignKeys: s.config.foreignKeys || []
2346
4497
  };
2347
4498
  });
@@ -2398,804 +4549,181 @@ async function runStudio(config2) {
2398
4549
  return new Response("Invalid request", { status: 400 });
2399
4550
  }
2400
4551
  }
2401
- if (req.method === "GET" && url.pathname === "/") {
2402
- return new Response(htmlTemplate, {
2403
- headers: { "Content-Type": "text/html" }
2404
- });
2405
- }
2406
- return new Response("Not found", { status: 404 });
2407
- }
2408
- });
2409
- console.log(colorize("=========================================", "cyan"));
2410
- console.log(colorize(`\uD83D\uDC18 Bungres Studio is running!`, "cyan"));
2411
- console.log(colorize(` Local: http://localhost:${server.port}`, "green"));
2412
- console.log(colorize("=========================================", "cyan"));
2413
- }
2414
- var htmlTemplate = `
2415
- <!DOCTYPE html>
2416
- <html lang="en">
2417
- <head>
2418
- <meta charset="UTF-8">
2419
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
2420
- <title>Bungres Studio</title>
2421
- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
2422
- <style>
2423
- :root {
2424
- --bg-color: #09090b;
2425
- --panel-bg: #18181b;
2426
- --border-color: #27272a;
2427
- --text-main: #fafafa;
2428
- --text-muted: #a1a1aa;
2429
- --accent-color: #8b5cf6;
2430
- --accent-hover: #7c3aed;
2431
- --header-bg: #09090b;
2432
- --row-hover: #27272a;
2433
- --input-bg: #18181b;
2434
- --muted-bg: #27272a;
2435
-
2436
- --type-number: #f472b6;
2437
- --type-string: #60a5fa;
2438
- --type-boolean: #34d399;
2439
- --type-date: #c084fc;
2440
- --type-json: #fbbf24;
2441
- --type-null: #71717a;
2442
- }
2443
-
2444
- * { box-sizing: border-box; }
2445
-
2446
- body {
2447
- margin: 0;
2448
- padding: 0;
2449
- font-family: 'Outfit', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
2450
- background-color: var(--bg-color);
2451
- color: var(--text-main);
2452
- display: flex;
2453
- height: 100vh;
2454
- overflow: hidden;
2455
- }
2456
-
2457
- /* Sidebar */
2458
- .sidebar {
2459
- width: 240px;
2460
- background-color: var(--panel-bg);
2461
- border-right: 1px solid var(--border-color);
2462
- display: flex;
2463
- flex-direction: column;
2464
- z-index: 20;
2465
- }
2466
-
2467
- .sidebar-header {
2468
- padding: 16px;
2469
- border-bottom: 1px solid var(--border-color);
2470
- display: flex;
2471
- align-items: center;
2472
- gap: 8px;
2473
- background-color: var(--panel-bg);
2474
- }
2475
-
2476
- .sidebar-header h1 {
2477
- margin: 0;
2478
- font-size: 14px;
2479
- font-weight: 600;
2480
- color: var(--text-main);
2481
- letter-spacing: -0.25px;
2482
- }
2483
-
2484
- .sidebar-header svg {
2485
- color: var(--accent-color);
2486
- }
2487
-
2488
- .table-list {
2489
- flex: 1;
2490
- overflow-y: auto;
2491
- padding: 8px;
2492
- list-style: none;
2493
- margin: 0;
2494
- }
2495
-
2496
- .table-list::-webkit-scrollbar {
2497
- width: 6px;
2498
- }
2499
- .table-list::-webkit-scrollbar-thumb {
2500
- background: #3f3f46;
2501
- border-radius: 4px;
2502
- }
2503
-
2504
- .table-item {
2505
- padding: 8px 12px;
2506
- cursor: pointer;
2507
- font-size: 13px;
2508
- font-weight: 500;
2509
- color: var(--text-muted);
2510
- border-radius: 6px;
2511
- transition: all 0.15s ease;
2512
- display: flex;
2513
- align-items: center;
2514
- gap: 8px;
2515
- margin-bottom: 2px;
2516
- border: 1px solid transparent;
2517
- }
2518
-
2519
- .table-item:hover {
2520
- background-color: var(--muted-bg);
2521
- color: var(--text-main);
2522
- }
2523
-
2524
- .table-item.active {
2525
- background-color: var(--accent-color);
2526
- color: #fff;
2527
- border-color: var(--accent-color);
2528
- }
2529
-
2530
- .table-item svg {
2531
- flex-shrink: 0;
2532
- }
2533
- .table-item.active svg {
2534
- color: #fff;
2535
- }
2536
-
2537
- /* Main Content */
2538
- .main {
2539
- flex: 1;
2540
- display: flex;
2541
- flex-direction: column;
2542
- position: relative;
2543
- }
2544
-
2545
- .main-header {
2546
- height: 56px;
2547
- padding: 0 16px;
2548
- border-bottom: 1px solid var(--border-color);
2549
- display: flex;
2550
- align-items: center;
2551
- justify-content: space-between;
2552
- background-color: var(--header-bg);
2553
- z-index: 10;
2554
- }
2555
-
2556
- .main-header h2 {
2557
- margin: 0;
2558
- font-size: 14px;
2559
- font-weight: 600;
2560
- color: var(--text-main);
2561
- letter-spacing: -0.25px;
2562
- display: flex;
2563
- align-items: center;
2564
- gap: 8px;
2565
- }
2566
-
2567
- .btn {
2568
- background-color: var(--input-bg);
2569
- border: 1px solid var(--border-color);
2570
- color: var(--text-main);
2571
- padding: 6px 12px;
2572
- border-radius: 6px;
2573
- font-size: 13px;
2574
- font-weight: 500;
2575
- font-family: 'Outfit', sans-serif;
2576
- cursor: pointer;
2577
- transition: all 0.15s ease;
2578
- display: flex;
2579
- align-items: center;
2580
- gap: 6px;
2581
- }
2582
-
2583
- .btn:hover:not(:disabled) {
2584
- background-color: var(--muted-bg);
2585
- }
2586
-
2587
- .btn:active:not(:disabled) {
2588
- transform: translateY(1px);
2589
- }
2590
-
2591
- .btn:disabled {
2592
- opacity: 0.5;
2593
- cursor: not-allowed;
2594
- }
2595
-
2596
- .content-area {
2597
- flex: 1;
2598
- overflow: auto;
2599
- padding: 0;
2600
- position: relative;
2601
- z-index: 1;
2602
- text-align: left;
2603
- }
2604
-
2605
- /* Data Table */
2606
- .data-grid-container {
2607
- background-color: var(--bg-color);
2608
- border: 1px solid var(--border-color);
2609
- border-radius: 0;
2610
- overflow: hidden;
2611
- height: 100%;
2612
- width: 100%;
2613
- display: flex;
2614
- flex-direction: column;
2615
- }
2616
-
2617
- .data-grid-container > .table-wrapper {
2618
- flex: 1;
2619
- overflow: auto;
2620
- min-height: 0;
2621
- }
2622
-
2623
- .data-grid-container::-webkit-scrollbar {
2624
- width: 8px;
2625
- height: 8px;
2626
- }
2627
- .data-grid-container::-webkit-scrollbar-corner {
2628
- background: transparent;
2629
- }
2630
- .data-grid-container::-webkit-scrollbar-thumb {
2631
- background: #3f3f46;
2632
- border-radius: 4px;
2633
- }
2634
-
2635
-
2636
- table {
2637
- width: auto;
2638
- border-collapse: separate;
2639
- border-spacing: 0;
2640
- text-align: left;
2641
- font-size: 14px;
2642
- table-layout: auto;
2643
- }
2644
-
2645
- th {
2646
- background-color: var(--panel-bg);
2647
- color: var(--text-main);
2648
- font-weight: 500;
2649
- font-size: 12px;
2650
- padding: 10px 16px;
2651
- border-bottom: 1px solid var(--border-color);
2652
- border-right: 1px solid var(--border-color);
2653
- white-space: nowrap;
2654
- position: sticky;
2655
- top: 0;
2656
- z-index: 10;
2657
- }
2658
-
2659
- .col-header {
2660
- display: flex;
2661
- flex-direction: column;
2662
- gap: 2px;
2663
- }
2664
-
2665
- .col-name {
2666
- font-weight: 600;
2667
- font-size: 13px;
2668
- display: flex;
2669
- align-items: center;
2670
- gap: 6px;
2671
- }
2672
-
2673
- .pk-badge {
2674
- font-size: 10px;
2675
- background: rgba(251, 191, 36, 0.15);
2676
- color: #fbbf24;
2677
- padding: 2px 5px;
2678
- border-radius: 4px;
2679
- font-weight: 500;
2680
- letter-spacing: 0.25px;
2681
- border: 1px solid rgba(251, 191, 36, 0.3);
2682
- }
2683
-
2684
- .fk-badge {
2685
- font-size: 10px;
2686
- background: rgba(96, 165, 250, 0.15);
2687
- color: #60a5fa;
2688
- padding: 2px 5px;
2689
- border-radius: 4px;
2690
- font-weight: 500;
2691
- letter-spacing: 0.25px;
2692
- border: 1px solid rgba(96, 165, 250, 0.3);
2693
- }
2694
-
2695
- .col-type {
2696
- font-size: 11px;
2697
- color: var(--text-muted);
2698
- font-family: 'JetBrains Mono', 'Fira Code', monospace;
2699
- text-transform: uppercase;
2700
- }
2701
-
2702
- td {
2703
- padding: 10px 16px;
2704
- border-bottom: 1px solid var(--border-color);
2705
- border-right: 1px solid var(--border-color);
2706
- color: var(--text-main);
2707
- max-width: 300px;
2708
- overflow: hidden;
2709
- text-overflow: ellipsis;
2710
- white-space: nowrap;
2711
- transition: background-color 0.15s ease;
2712
- font-size: 13px;
2713
- }
2714
-
2715
-
2716
-
2717
- tr:hover td {
2718
- background-color: var(--row-hover);
2719
- }
2720
-
2721
- .type-number { color: var(--type-number); }
2722
- .type-string { color: var(--type-string); }
2723
- .type-boolean { color: var(--type-boolean); }
2724
- .type-date { color: var(--type-date); }
2725
- .type-json { color: var(--type-json); font-family: monospace; }
2726
- .type-null { color: var(--type-null); font-style: italic; }
2727
-
2728
- .cell-value {
2729
- font-family: 'JetBrains Mono', 'Fira Code', monospace;
2730
- font-size: 13px;
2731
- }
2732
-
2733
- .empty-state {
2734
- display: flex;
2735
- flex-direction: column;
2736
- align-items: center;
2737
- justify-content: center;
2738
- height: 100%;
2739
- width: 100%;
2740
- color: var(--text-muted);
2741
- animation: fadeIn 0.5s ease-out;
2742
- }
2743
-
2744
- .empty-state svg {
2745
- margin-bottom: 16px;
2746
- color: #3f3f46;
2747
- width: 64px;
2748
- height: 64px;
2749
- }
2750
-
2751
- .empty-state h3 {
2752
- color: var(--text-main);
2753
- margin: 0 0 8px 0;
2754
- font-size: 18px;
2755
- }
2756
-
2757
- @keyframes fadeIn {
2758
- from { opacity: 0; transform: translateY(10px); }
2759
- to { opacity: 1; transform: translateY(0); }
2760
- }
2761
-
2762
- @keyframes spin {
2763
- from { transform: rotate(0deg); }
2764
- to { transform: rotate(360deg); }
2765
- }
2766
- .spinning {
2767
- animation: spin 1s linear infinite;
2768
- }
2769
-
2770
- /* Pagination */
2771
- .pagination {
2772
- display: flex;
2773
- align-items: center;
2774
- justify-content: space-between;
2775
- padding: 12px 16px;
2776
- background-color: var(--panel-bg);
2777
- border-top: 1px solid var(--border-color);
2778
- font-size: 13px;
2779
- color: var(--text-muted);
2780
- width: fit-content;
2781
- min-width: 100%;
2782
- flex-shrink: 0;
2783
- }
2784
-
2785
- .pagination-info {
2786
- display: flex;
2787
- align-items: center;
2788
- gap: 12px;
2789
- }
2790
-
2791
- .pagination-controls {
2792
- display: flex;
2793
- align-items: center;
2794
- gap: 6px;
2795
- }
2796
-
2797
- .pagination-btn {
2798
- background-color: var(--input-bg);
2799
- border: 1px solid var(--border-color);
2800
- color: var(--text-main);
2801
- padding: 6px 12px;
2802
- border-radius: 6px;
2803
- cursor: pointer;
2804
- font-size: 13px;
2805
- transition: all 0.15s ease;
2806
- }
2807
-
2808
- .pagination-btn:hover:not(:disabled) {
2809
- background-color: var(--muted-bg);
2810
- }
2811
-
2812
- .pagination-btn:disabled {
2813
- opacity: 0.5;
2814
- cursor: not-allowed;
2815
- }
2816
-
2817
- .pagination-btn.active {
2818
- background-color: var(--accent-color);
2819
- border-color: var(--accent-color);
2820
- }
2821
-
2822
- .page-input {
2823
- background-color: var(--input-bg);
2824
- border: 1px solid var(--border-color);
2825
- color: var(--text-main);
2826
- padding: 6px 8px;
2827
- border-radius: 6px;
2828
- width: 50px;
2829
- text-align: center;
2830
- font-size: 13px;
2831
- }
2832
-
2833
- .page-size-select {
2834
- background-color: var(--input-bg);
2835
- border: 1px solid var(--border-color);
2836
- color: var(--text-main);
2837
- padding: 6px 8px;
2838
- border-radius: 6px;
2839
- font-size: 13px;
2840
- cursor: pointer;
2841
- }
2842
-
2843
- .page-size-select:hover {
2844
- background-color: var(--muted-bg);
2845
- }
2846
- </style>
2847
- </head>
2848
- <body>
2849
-
2850
- <div class="sidebar">
2851
- <div class="sidebar-header">
2852
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2853
- <ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
2854
- <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
2855
- <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
2856
- </svg>
2857
- <h1>Bungres Studio</h1>
2858
- </div>
2859
- <ul class="table-list" id="table-list">
2860
- <!-- Populated by JS -->
2861
- </ul>
2862
- </div>
2863
-
2864
- <div class="main">
2865
- <div class="main-header">
2866
- <h2 id="current-table-name">
2867
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: var(--text-muted);">
2868
- <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
2869
- <line x1="3" y1="9" x2="21" y2="9"></line>
2870
- <line x1="9" y1="21" x2="9" y2="9"></line>
2871
- </svg>
2872
- Select a table
2873
- </h2>
2874
- <button id="refresh-btn" class="btn" disabled>
2875
- <svg id="refresh-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2876
- <polyline points="23 4 23 10 17 10"></polyline>
2877
- <polyline points="1 20 1 14 7 14"></polyline>
2878
- <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
2879
- </svg>
2880
- Refresh
2881
- </button>
2882
- </div>
2883
- <div class="content-area">
2884
- <div id="data-container" class="empty-state">
2885
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
2886
- <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
2887
- <polyline points="14 2 14 8 20 8"></polyline>
2888
- </svg>
2889
- <h3>No Table Selected</h3>
2890
- <p>Choose a table from the sidebar to view its data</p>
2891
- </div>
2892
- </div>
2893
- </div>
2894
-
2895
- <script>
2896
- let currentTable = null;
2897
- let tablesData = [];
2898
- let tableSchemas = {};
2899
- let currentPage = 1;
2900
- let totalPages = 1;
2901
- let totalRecords = 0;
2902
- let pageSize = 25;
2903
-
2904
- // Format values for the data grid
2905
- function formatValue(val) {
2906
- if (val === null || val === undefined) return 'null';
2907
-
2908
- if (typeof val === 'object') {
2909
- if (val instanceof Date) {
2910
- return val.toISOString();
2911
- }
2912
- // Handle JSON objects
2913
- const safeJson = JSON.stringify(val)
2914
- .replace(/&/g, "&amp;")
2915
- .replace(/</g, "&lt;")
2916
- .replace(/>/g, "&gt;");
2917
- return safeJson;
2918
- }
2919
-
2920
- if (typeof val === 'string') {
2921
- // Escape HTML to prevent XSS
2922
- const safeStr = String(val)
2923
- .replace(/&/g, "&amp;")
2924
- .replace(/</g, "&lt;")
2925
- .replace(/>/g, "&gt;");
2926
- return safeStr;
2927
- }
2928
-
2929
- return String(val);
2930
- }
2931
-
2932
- async function loadTables() {
2933
- try {
2934
- const res = await fetch('/api/tables');
2935
- tablesData = await res.json();
2936
-
2937
- const list = document.getElementById('table-list');
2938
- list.innerHTML = '';
2939
- tableSchemas = {};
2940
-
2941
- tablesData.forEach(t => {
2942
- tableSchemas[t.name] = t;
2943
- const li = document.createElement('li');
2944
- li.className = 'table-item';
2945
- li.innerHTML = \`
2946
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2947
- <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
2948
- <line x1="3" y1="9" x2="21" y2="9"></line>
2949
- <line x1="9" y1="21" x2="9" y2="9"></line>
2950
- </svg>
2951
- \${t.name}
2952
- \`;
2953
- li.onclick = () => selectTable(t.name);
2954
- list.appendChild(li);
4552
+ if (req.method === "GET" && url.pathname === "/htmx/sidebar") {
4553
+ let html = '<ul class="flex flex-col gap-0.5 p-2 m-0 list-none">';
4554
+ schemas2.forEach((s) => {
4555
+ const tableName = s.config.name;
4556
+ html += `
4557
+ <li>
4558
+ <button
4559
+ hx-get="/htmx/tables/${tableName}?page=1&limit=25"
4560
+ hx-target="#data-container"
4561
+ @click="currentTable = '${tableName}'; pageSize = 25"
4562
+ :class="currentTable === '${tableName}' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground'"
4563
+ class="w-full text-left px-3 py-2 rounded-md text-sm font-medium flex items-center gap-2 transition-colors focus:outline-none"
4564
+ >
4565
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4566
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
4567
+ <line x1="3" y1="9" x2="21" y2="9"></line>
4568
+ <line x1="9" y1="21" x2="9" y2="9"></line>
4569
+ </svg>
4570
+ ${tableName}
4571
+ </button>
4572
+ </li>
4573
+ `;
2955
4574
  });
2956
- } catch (e) {
2957
- console.error("Failed to load tables", e);
2958
- }
2959
- }
2960
-
2961
- async function selectTable(name, page = 1) {
2962
- currentTable = name;
2963
- currentPage = page;
2964
-
2965
- // Update UI active state
2966
- document.querySelectorAll('.table-item').forEach(el => {
2967
- if (el.textContent.trim() === name) el.classList.add('active');
2968
- else el.classList.remove('active');
2969
- });
2970
-
2971
- document.getElementById('current-table-name').innerHTML = \`
2972
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: var(--accent-color);">
2973
- <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
2974
- <line x1="3" y1="9" x2="21" y2="9"></line>
2975
- <line x1="9" y1="21" x2="9" y2="9"></line>
2976
- </svg>
2977
- \${name}
2978
- \`;
2979
- document.getElementById('refresh-btn').disabled = false;
2980
-
2981
- const container = document.getElementById('data-container');
2982
- const existingTable = container.querySelector('.data-grid-container');
2983
-
2984
- if (existingTable) {
2985
- // Show loading overlay instead of replacing content
2986
- existingTable.style.opacity = '0.5';
2987
- existingTable.style.pointerEvents = 'none';
2988
- } else {
2989
- container.innerHTML = \`
2990
- <div class="empty-state">
2991
- <svg class="spinning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2992
- <line x1="12" y1="2" x2="12" y2="6"></line>
2993
- <line x1="12" y1="18" x2="12" y2="22"></line>
2994
- <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
2995
- <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
2996
- <line x1="2" y1="12" x2="6" y2="12"></line>
2997
- <line x1="18" y1="12" x2="22" y2="12"></line>
2998
- <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
2999
- <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
3000
- </svg>
3001
- <p>Loading data...</p>
3002
- </div>
3003
- \`;
4575
+ html += "</ul>";
4576
+ return new Response(html, { headers: { "Content-Type": "text/html" } });
3004
4577
  }
3005
-
3006
- try {
3007
- const res = await fetch(\`/api/tables/\${name}/data?page=\${page}&limit=\${pageSize}\`);
3008
- if (!res.ok) throw new Error(await res.text());
3009
- const response = await res.json();
3010
-
3011
- const rows = response.data || response;
3012
- totalRecords = response.total || 0;
3013
- totalPages = response.totalPages || 1;
3014
-
3015
- const tableSchema = tableSchemas[name];
3016
-
3017
- if (rows.length === 0) {
3018
- container.innerHTML = \`
3019
- <div class="empty-state">
3020
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
3021
- <circle cx="12" cy="12" r="10"></circle>
3022
- <line x1="12" y1="8" x2="12" y2="12"></line>
3023
- <line x1="12" y1="16" x2="12.01" y2="16"></line>
3024
- </svg>
3025
- <h3>Table is Empty</h3>
3026
- <p>No records found in "\${name}"</p>
3027
- </div>
3028
- \`;
3029
- return;
3030
- }
3031
-
3032
- // Use all columns from the actual data to ensure foreign keys and all data are shown
3033
- let columns = [];
3034
- const allKeys = new Set();
3035
- rows.forEach(row => {
3036
- Object.keys(row).forEach(key => allKeys.add(key));
3037
- });
3038
-
3039
- // Try to get type info from schema if available
3040
- const schemaColMap = {};
3041
- if (tableSchema && tableSchema.columns && tableSchema.columns.length > 0) {
3042
- tableSchema.columns.forEach(col => {
3043
- schemaColMap[col.name] = col;
3044
- });
4578
+ if (req.method === "GET" && url.pathname.startsWith("/htmx/tables/")) {
4579
+ const tableName = url.pathname.split("/")[3];
4580
+ const schema = schemas2.find((s) => s.config.name === tableName);
4581
+ if (!schema) {
4582
+ return new Response(`<div class="p-4 text-red-500">Table not found</div>`, { status: 404, headers: { "Content-Type": "text/html" } });
3045
4583
  }
3046
-
3047
- columns = Array.from(allKeys).map(key => {
3048
- const schemaCol = schemaColMap[key];
3049
- let type = 'text';
3050
-
3051
- // First try to get type from schema
3052
- if (schemaCol && schemaCol.type) {
3053
- type = schemaCol.type;
3054
- } else if (rows[0] && rows[0][key] !== undefined) {
3055
- // Fallback to type detection from actual value
3056
- const val = rows[0][key];
3057
- if (typeof val === 'number') type = 'integer';
3058
- else if (typeof val === 'boolean') type = 'boolean';
3059
- else if (val instanceof Date) type = 'timestamptz';
3060
- else if (typeof val === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(val)) type = 'uuid';
3061
- else type = 'text';
3062
- }
3063
-
3064
- return {
3065
- name: key,
3066
- type,
3067
- primaryKey: schemaCol ? schemaCol.primaryKey : false,
3068
- foreignKey: schemaCol ? schemaCol.foreignKey : null
4584
+ try {
4585
+ const page = parseInt(url.searchParams.get("page") || "1", 10);
4586
+ const limit = parseInt(url.searchParams.get("limit") || "25", 10);
4587
+ const offset = (page - 1) * limit;
4588
+ const countResult = await db2.select({ count: schema.table }).from(schema.table);
4589
+ const total = Array.isArray(countResult) ? countResult.length : 0;
4590
+ const totalPages = Math.ceil(total / limit) || 1;
4591
+ const data = await db2.select().from(schema.table).limit(limit).offset(offset);
4592
+ const formatValue = (val) => {
4593
+ if (val === null || val === undefined)
4594
+ return '<span class="italic text-muted-foreground">null</span>';
4595
+ if (typeof val === "number")
4596
+ return `<span class="text-foreground">${val}</span>`;
4597
+ if (typeof val === "boolean")
4598
+ return `<span class="text-foreground">${val}</span>`;
4599
+ if (val instanceof Date)
4600
+ return `<span class="text-foreground">${val.toISOString()}</span>`;
4601
+ if (typeof val === "object")
4602
+ return `<span class="text-foreground font-mono text-xs">${JSON.stringify(val).replace(/&/g, "&amp;").replace(/</g, "&lt;")}</span>`;
4603
+ return `<span class="text-foreground">${String(val).replace(/&/g, "&amp;").replace(/</g, "&lt;")}</span>`;
3069
4604
  };
3070
- });
3071
-
3072
- let html = '<div class="data-grid-container"><div class="table-wrapper"><table><thead><tr>';
3073
- columns.forEach(col => {
3074
- html += \`
3075
- <th>
3076
- <div class="col-header">
3077
- <div class="col-name">
3078
- \${col.name}
3079
- \${col.primaryKey ? '<span class="pk-badge">PK</span>' : ''}
3080
- \${col.foreignKey ? '<span class="fk-badge">FK</span>' : ''}
4605
+ if (data.length === 0) {
4606
+ return new Response(`
4607
+ <div class="flex flex-col items-center justify-center h-full w-full text-muted-foreground animate-[fadeIn_0.5s_ease-out]">
4608
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="w-16 h-16 mb-4 text-muted">
4609
+ <circle cx="12" cy="12" r="10"></circle>
4610
+ <line x1="12" y1="8" x2="12" y2="12"></line>
4611
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
4612
+ </svg>
4613
+ <h3 class="text-foreground m-0 mb-2 text-lg">Table is Empty</h3>
4614
+ <p class="text-sm">No records found in "${tableName}"</p>
4615
+ </div>
4616
+ `, { headers: { "Content-Type": "text/html" } });
4617
+ }
4618
+ const columns = Object.keys(data[0] || {});
4619
+ let html = '<div class="flex flex-col h-full w-full bg-background">';
4620
+ html += '<div class="flex-1 overflow-auto">';
4621
+ html += '<table class="text-left border-collapse text-sm whitespace-nowrap">';
4622
+ html += "<thead><tr>";
4623
+ columns.forEach((col2) => {
4624
+ const colConfig = schema.config.columns ? schema.config.columns[col2] : null;
4625
+ let typeLabel = colConfig ? colConfig.dataType : "unknown";
4626
+ let indexLabel = "";
4627
+ if (colConfig?.primaryKey) {
4628
+ indexLabel += '<span class="ml-1 text-[10px] bg-emerald-500/20 text-emerald-500 px-1 rounded uppercase" title="Primary Key">PK</span>';
4629
+ }
4630
+ if (colConfig?.unique) {
4631
+ indexLabel += '<span class="ml-1 text-[10px] bg-amber-500/20 text-amber-500 px-1 rounded uppercase" title="Unique">UQ</span>';
4632
+ }
4633
+ if (colConfig?.references) {
4634
+ indexLabel += '<span class="ml-1 text-[10px] bg-yellow-500/20 text-yellow-500 px-1 rounded uppercase" title="Foreign Key">FK</span>';
4635
+ }
4636
+ if (schema.config.indexes) {
4637
+ const isIndexed = schema.config.indexes.some((idx) => idx.columns.includes(col2));
4638
+ if (isIndexed) {
4639
+ indexLabel += '<span class="ml-1 text-[10px] bg-blue-500/20 text-blue-500 px-1 rounded uppercase" title="Indexed">IDX</span>';
4640
+ }
4641
+ }
4642
+ html += `<th class="bg-card border-b border-r border-border px-4 py-2.5 font-medium text-muted-foreground sticky top-0 z-10">
4643
+ <div class="flex flex-col gap-0.5">
4644
+ <span class="text-foreground">${col2}</span>
4645
+ <div class="flex items-center flex-wrap gap-1 mt-0.5">
4646
+ <span class="text-[10px] font-mono text-muted-foreground/70 mr-1">${typeLabel}</span>
4647
+ ${indexLabel}
3081
4648
  </div>
3082
- <div class="col-type">\${col.type}</div>
3083
4649
  </div>
3084
- </th>
3085
- \`;
3086
- });
3087
- html += '</tr></thead><tbody>';
3088
-
3089
- rows.forEach(row => {
3090
- html += '<tr>';
3091
- columns.forEach(col => {
3092
- html += \`<td>\${formatValue(row[col.name])}</td>\`;
4650
+ </th>`;
3093
4651
  });
3094
- html += '</tr>';
3095
- });
3096
-
3097
- html += '</tbody></table>';
3098
-
3099
- // Add pagination inside the table wrapper to match table width
3100
- html += renderPagination();
3101
-
3102
- html += '</div></div>';
3103
- container.innerHTML = html;
3104
-
3105
- // Fade in the new content
3106
- const newTable = container.querySelector('.data-grid-container');
3107
- if (newTable) {
3108
- newTable.style.opacity = '0';
3109
- requestAnimationFrame(() => {
3110
- newTable.style.transition = 'opacity 0.15s ease';
3111
- newTable.style.opacity = '1';
4652
+ html += "</tr></thead><tbody>";
4653
+ data.forEach((row) => {
4654
+ html += '<tr class="hover:bg-muted/50 transition-colors">';
4655
+ columns.forEach((col2) => {
4656
+ html += `<td class="border-b border-r border-border px-4 py-2 max-w-[300px] overflow-hidden text-ellipsis">${formatValue(row[col2])}</td>`;
4657
+ });
4658
+ html += "</tr>";
3112
4659
  });
4660
+ html += "</tbody></table></div>";
4661
+ const startRecord = (page - 1) * limit + 1;
4662
+ const endRecord = Math.min(page * limit, total);
4663
+ html += `
4664
+ <div class="flex items-center justify-between px-4 py-3 bg-card border-t border-border text-xs text-muted-foreground shrink-0 sticky bottom-0 z-10 w-full">
4665
+ <div class="flex items-center gap-4">
4666
+ <span>${total} records</span>
4667
+ <span>${startRecord}-${endRecord}</span>
4668
+ </div>
4669
+ <div class="flex items-center gap-2">
4670
+ <select
4671
+ name="limit"
4672
+ class="bg-background border border-border text-foreground px-2 py-1.5 rounded-md focus:outline-none focus:ring-1 focus:ring-ring cursor-pointer"
4673
+ @change="pageSize = $event.target.value; htmx.ajax('GET', '/htmx/tables/${tableName}?page=1&limit=' + pageSize, {target: '#data-container'})"
4674
+ >
4675
+ <option value="10" ${limit === 10 ? "selected" : ""}>10</option>
4676
+ <option value="25" ${limit === 25 ? "selected" : ""}>25</option>
4677
+ <option value="50" ${limit === 50 ? "selected" : ""}>50</option>
4678
+ <option value="100" ${limit === 100 ? "selected" : ""}>100</option>
4679
+ </select>
4680
+
4681
+ <button
4682
+ ${page <= 1 ? "disabled" : ""}
4683
+ hx-get="/htmx/tables/${tableName}?page=${page - 1}&limit=${limit}"
4684
+ hx-target="#data-container"
4685
+ class="bg-background border border-border text-foreground px-3 py-1.5 rounded-md hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
4686
+ >&larr;</button>
4687
+
4688
+ <span class="min-w-[40px] text-center">${page} / ${totalPages}</span>
4689
+
4690
+ <button
4691
+ ${page >= totalPages ? "disabled" : ""}
4692
+ hx-get="/htmx/tables/${tableName}?page=${page + 1}&limit=${limit}"
4693
+ hx-target="#data-container"
4694
+ class="bg-background border border-border text-foreground px-3 py-1.5 rounded-md hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
4695
+ >&rarr;</button>
4696
+ </div>
4697
+ </div>
4698
+ `;
4699
+ html += "</div>";
4700
+ return new Response(html, { headers: { "Content-Type": "text/html" } });
4701
+ } catch (e) {
4702
+ return new Response(`
4703
+ <div class="p-6">
4704
+ <div class="bg-red-500/10 border border-red-500/20 text-red-400 p-4 rounded-lg">
4705
+ <h3 class="font-semibold mb-1">Error Loading Data</h3>
4706
+ <p class="text-sm opacity-80">${e.message}</p>
4707
+ </div>
4708
+ </div>
4709
+ `, { headers: { "Content-Type": "text/html" } });
3113
4710
  }
3114
-
3115
- } catch (e) {
3116
- container.innerHTML = \`
3117
- <div class="empty-state">
3118
- <svg viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
3119
- <circle cx="12" cy="12" r="10"></circle>
3120
- <line x1="12" y1="8" x2="12" y2="12"></line>
3121
- <line x1="12" y1="16" x2="12.01" y2="16"></line>
3122
- </svg>
3123
- <h3 style="color: #ef4444;">Error Loading Data</h3>
3124
- <p>\${e.message}</p>
3125
- </div>
3126
- \`;
3127
4711
  }
3128
- }
3129
-
3130
- function renderPagination() {
3131
- const startRecord = (currentPage - 1) * pageSize + 1;
3132
- const endRecord = Math.min(currentPage * pageSize, totalRecords);
3133
-
3134
- let html = '<div class="pagination">';
3135
- html += '<div class="pagination-info">';
3136
- html += \`<span>\${totalRecords} records</span>\`;
3137
- html += \`<span>\${startRecord}-\${endRecord}</span>\`;
3138
- html += '</div>';
3139
-
3140
- html += '<div class="pagination-controls">';
3141
-
3142
- // Page size selector
3143
- html += '<select class="page-size-select" onchange="changePageSize(this.value)">';
3144
- [10, 25, 50, 100].forEach(size => {
3145
- html += \`<option value="\${size}" \${pageSize === size ? 'selected' : ''}>\${size}</option>\`;
3146
- });
3147
- html += '</select>';
3148
-
3149
- // Previous button
3150
- html += \`<button class="pagination-btn" onclick="changePage(\${currentPage - 1})" \${currentPage === 1 ? 'disabled' : ''}>\u2190</button>\`;
3151
-
3152
- // Page indicator
3153
- html += \`<span style="min-width: 60px; text-align: center;">\${currentPage} / \${totalPages}</span>\`;
3154
-
3155
- // Next button
3156
- html += \`<button class="pagination-btn" onclick="changePage(\${currentPage + 1})" \${currentPage === totalPages ? 'disabled' : ''}>\u2192</button>\`;
3157
-
3158
- html += '</div>';
3159
- html += '</div>';
3160
-
3161
- return html;
3162
- }
3163
-
3164
- function changePageSize(newSize) {
3165
- pageSize = parseInt(newSize, 10);
3166
- currentPage = 1;
3167
- selectTable(currentTable, currentPage);
3168
- }
3169
-
3170
- function changePage(page) {
3171
- if (page < 1 || page > totalPages) return;
3172
- selectTable(currentTable, page);
3173
- }
3174
-
3175
- document.getElementById('refresh-btn').addEventListener('click', async () => {
3176
- if (!currentTable) return;
3177
-
3178
- const icon = document.getElementById('refresh-icon');
3179
- icon.classList.add('spinning');
3180
-
3181
- try {
3182
- await selectTable(currentTable);
3183
- } catch (e) {
3184
- console.error("Failed to refresh data", e);
3185
- } finally {
3186
- setTimeout(() => { icon.classList.remove('spinning'); }, 500);
4712
+ if (req.method === "GET" && url.pathname === "/") {
4713
+ return new Response(indexHtml, {
4714
+ headers: { "Content-Type": "text/html" }
4715
+ });
3187
4716
  }
3188
- });
3189
-
3190
- // Init
3191
- loadTables();
3192
- </script>
3193
- </body>
3194
- </html>
3195
- `;
4717
+ return new Response("Not found", { status: 404 });
4718
+ }
4719
+ });
4720
+ intro(import_picocolors10.default.bgCyan(import_picocolors10.default.black(" \uD83D\uDC18 Bungres Studio ")));
4721
+ log.success(`Studio is running at ${import_picocolors10.default.green(`http://localhost:${server.port}`)}`);
4722
+ outro(import_picocolors10.default.gray("Press Ctrl+C to stop"));
4723
+ }
3196
4724
 
3197
4725
  // src/commands/tusky.ts
3198
- import * as readline from "readline";
4726
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
3199
4727
  async function runTusky(config) {
3200
4728
  const schemas = await loadSchemas(config.schema);
3201
4729
  const schemaObj = {};
@@ -3203,40 +4731,23 @@ async function runTusky(config) {
3203
4731
  schemaObj[s.exportName] = s.table;
3204
4732
  }
3205
4733
  const db = bungres({ url: config.dbUrl, schema: schemaObj });
3206
- console.log("=========================================");
3207
- console.log("\uD83D\uDC18 Welcome to Bungres REPL (Tusky)");
3208
- console.log("=========================================");
3209
- console.log(`
3210
- Database connection established.`);
3211
- console.log(`
3212
- Pre-loaded Context:`);
3213
- console.log(" - db (Bungres Database Client)");
3214
- for (const s of schemas) {
3215
- console.log(` - ${s.exportName} (Table)`);
3216
- }
3217
- console.log(`
3218
- Example query: await db.select().from(users)`);
3219
- console.log(`Type .exit to quit.
3220
- `);
3221
- const rl = readline.createInterface({
3222
- input: process.stdin,
3223
- output: process.stdout,
3224
- prompt: "bungres> "
3225
- });
4734
+ intro(import_picocolors11.default.bgCyan(import_picocolors11.default.black(" \uD83D\uDC18 Bungres REPL (Tusky) ")));
4735
+ log.success(import_picocolors11.default.green("Database connection established."));
4736
+ note(`Example: ${import_picocolors11.default.cyan("db.select().from(users);")}
4737
+ Exit: ${import_picocolors11.default.cyan("exit")}`, "Commands");
3226
4738
  globalThis.db = db;
3227
4739
  for (const s of schemas) {
3228
4740
  globalThis[s.exportName] = s.table;
3229
4741
  }
3230
- rl.prompt();
3231
- rl.on("line", async (line) => {
4742
+ process.stdout.write("bungres> ");
4743
+ for await (const line of console) {
3232
4744
  const input = line.trim();
3233
- if (input === ".exit") {
3234
- rl.close();
3235
- return;
4745
+ if (["exit", ".exit", "quit", ".quit", "exit()", "quit()"].includes(input.toLowerCase())) {
4746
+ break;
3236
4747
  }
3237
4748
  if (!input) {
3238
- rl.prompt();
3239
- return;
4749
+ process.stdout.write("bungres> ");
4750
+ continue;
3240
4751
  }
3241
4752
  try {
3242
4753
  let code = input;
@@ -3245,22 +4756,32 @@ Example query: await db.select().from(users)`);
3245
4756
  console.log(`(Running as raw SQL: await db.raw(\`${input}\`))`);
3246
4757
  code = `(async () => { return await db.raw(\`${input}\`); })()`;
3247
4758
  } else if (input.includes("await ")) {
3248
- code = `(async () => { return ${input}; })()`;
4759
+ code = `(async () => { return await ${input}; })()`;
3249
4760
  }
4761
+ const start = performance.now();
3250
4762
  const result = await eval(code);
3251
- console.log(result);
4763
+ const end = performance.now();
4764
+ const duration = (end - start).toFixed(2);
4765
+ if (Array.isArray(result) && result.length > 0 && typeof result[0] === "object") {
4766
+ console.table(result);
4767
+ } else {
4768
+ console.log(result);
4769
+ }
4770
+ console.log(`
4771
+ (Execution time: ${duration}ms)`);
3252
4772
  } catch (err) {
3253
4773
  console.error(err.message || err);
3254
4774
  }
3255
- rl.prompt();
3256
- });
3257
- rl.on("close", () => {
3258
- process.exit(0);
3259
- });
4775
+ process.stdout.write("bungres> ");
4776
+ }
4777
+ process.exit(0);
3260
4778
  }
3261
4779
 
4780
+ // src/cli.ts
4781
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
4782
+
3262
4783
  // src/config.ts
3263
- import { join as join6, resolve as resolve7 } from "path";
4784
+ import { join as join7, resolve as resolve8 } from "path";
3264
4785
  var CONFIG_FILES = [
3265
4786
  "bungres.config.ts",
3266
4787
  "bungres.config.js",
@@ -3270,9 +4791,9 @@ var CONFIG_FILES = [
3270
4791
  async function loadConfig(cwd = process.cwd()) {
3271
4792
  let userConfig = {};
3272
4793
  for (const file of CONFIG_FILES) {
3273
- const configPath = join6(cwd, file);
4794
+ const configPath = join7(cwd, file);
3274
4795
  if (await Bun.file(configPath).exists()) {
3275
- const mod = await import(resolve7(configPath));
4796
+ const mod = await import(resolve8(configPath));
3276
4797
  userConfig = mod.default ?? mod;
3277
4798
  break;
3278
4799
  }
@@ -3334,7 +4855,7 @@ async function ensureDatabase2(dbUrl) {
3334
4855
  // package.json
3335
4856
  var package_default = {
3336
4857
  name: "@bungres/kit",
3337
- version: "0.6.1",
4858
+ version: "1.0.0",
3338
4859
  description: "CLI toolkit for @bungres/orm \u2014 migrate, push, generate, pull",
3339
4860
  license: "MIT",
3340
4861
  engines: {
@@ -3384,7 +4905,9 @@ var package_default = {
3384
4905
  test: "bun test"
3385
4906
  },
3386
4907
  dependencies: {
3387
- "@bungres/orm": "workspace:*"
4908
+ "@bungres/orm": "^1.0.0",
4909
+ "@clack/prompts": "^1.7.0",
4910
+ picocolors: "^1.1.1"
3388
4911
  },
3389
4912
  devDependencies: {
3390
4913
  "bun-types": "latest",
@@ -3395,15 +4918,16 @@ var package_default = {
3395
4918
  // src/cli.ts
3396
4919
  var VERSION = package_default.version;
3397
4920
  var HELP = `
3398
- ${colorize("\uD83D\uDC18 Bungres ORM CLI", "cyan")} v${VERSION}
4921
+ ${import_picocolors12.default.cyan("\uD83D\uDC18 Bungres ORM CLI")} v${VERSION}
3399
4922
 
3400
- ${colorize("Usage:", "yellow")}
4923
+ ${import_picocolors12.default.yellow("Usage:")}
3401
4924
  bungres <command> [options]
3402
4925
 
3403
- ${colorize("Commands:", "yellow")}
4926
+ ${import_picocolors12.default.yellow("Commands:")}
3404
4927
  init Initialize bungres project with config file and db folder structure
3405
4928
  generate Generate SQL migration files from your schema definitions
3406
4929
  migrate Run pending migration files against the database
4930
+ rollback Revert the last applied migration
3407
4931
  push Apply schema directly to the DB (no migration files, dev mode)
3408
4932
  pull Introspect the database and generate TypeScript schema
3409
4933
  status Show applied vs. pending migrations
@@ -3414,17 +4938,18 @@ ${colorize("Commands:", "yellow")}
3414
4938
  tusky Boot up a Node REPL connected to the database with schema loaded
3415
4939
  drop Drop all tables defined in the schema (dev only)
3416
4940
 
3417
- ${colorize("Options:", "yellow")}
4941
+ ${import_picocolors12.default.yellow("Options:")}
3418
4942
  --config Path to config file (default: bungres.config.ts)
3419
4943
  --verbose Enable verbose SQL logging
3420
4944
  --force Skip confirmation prompts (use with drop)
3421
4945
  --version Show version
3422
4946
  --help Show this help
3423
4947
 
3424
- ${colorize("Examples:", "yellow")}
4948
+ ${import_picocolors12.default.yellow("Examples:")}
3425
4949
  bungres init
3426
4950
  bungres generate
3427
4951
  bungres migrate
4952
+ bungres rollback
3428
4953
  bungres push
3429
4954
  bungres pull
3430
4955
  bungres status
@@ -3454,7 +4979,7 @@ async function main() {
3454
4979
  const config2 = await loadConfig(process.cwd());
3455
4980
  if (flags.verbose)
3456
4981
  config2.verbose = true;
3457
- if (command && ["migrate", "push", "drop", "status", "fresh", "refresh"].includes(command)) {
4982
+ if (command && ["migrate", "rollback", "push", "drop", "status", "fresh", "refresh"].includes(command)) {
3458
4983
  await ensureDatabase2(config2.dbUrl);
3459
4984
  }
3460
4985
  switch (command) {
@@ -3464,6 +4989,9 @@ async function main() {
3464
4989
  case "migrate":
3465
4990
  await runMigrate(config2);
3466
4991
  break;
4992
+ case "rollback":
4993
+ await runRollback(config2);
4994
+ break;
3467
4995
  case "push":
3468
4996
  await runPush(config2, { force: !!flags.force });
3469
4997
  break;
@@ -3500,14 +5028,14 @@ async function main() {
3500
5028
  }
3501
5029
  function parseFlags(args) {
3502
5030
  const flags = {};
3503
- for (let i = 0;i < args.length; i++) {
3504
- const arg = args[i];
5031
+ for (let i2 = 0;i2 < args.length; i2++) {
5032
+ const arg = args[i2];
3505
5033
  if (arg?.startsWith("--")) {
3506
5034
  const key = arg.slice(2);
3507
- const next = args[i + 1];
5035
+ const next = args[i2 + 1];
3508
5036
  if (next && !next.startsWith("--")) {
3509
5037
  flags[key] = next;
3510
- i++;
5038
+ i2++;
3511
5039
  } else {
3512
5040
  flags[key] = true;
3513
5041
  }