@bungres/kit 0.6.1 → 0.7.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,5 +1,165 @@
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: (count2 = 1) => `${CSI2}${count2}A`,
60
+ down: (count2 = 1) => `${CSI2}${count2}B`,
61
+ forward: (count2 = 1) => `${CSI2}${count2}C`,
62
+ backward: (count2 = 1) => `${CSI2}${count2}D`,
63
+ nextLine: (count2 = 1) => `${CSI2}E`.repeat(count2),
64
+ prevLine: (count2 = 1) => `${CSI2}F`.repeat(count2),
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: (count2 = 1) => `${CSI2}S`.repeat(count2),
73
+ down: (count2 = 1) => `${CSI2}T`.repeat(count2)
74
+ };
75
+ var erase = {
76
+ screen: `${CSI2}2J`,
77
+ up: (count2 = 1) => `${CSI2}1J`.repeat(count2),
78
+ down: (count2 = 1) => `${CSI2}J`.repeat(count2),
79
+ line: `${CSI2}2K`,
80
+ lineEnd: `${CSI2}K`,
81
+ lineStart: `${CSI2}1K`,
82
+ lines(count2) {
83
+ let clear = "";
84
+ for (let i = 0;i < count2; i++)
85
+ clear += this.line + (i < count2 - 1 ? cursor.up() : "");
86
+ if (count2)
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, index2 = string.indexOf(close, open.length);
102
+ return ~index2 ? open + replaceClose(string, close, replace, index2) + close : open + string + close;
103
+ };
104
+ var replaceClose = (string, close, replace, index2) => {
105
+ let result2 = "", cursor3 = 0;
106
+ do {
107
+ result2 += string.substring(cursor3, index2) + replace;
108
+ cursor3 = index2 + close.length;
109
+ index2 = string.indexOf(close, cursor3);
110
+ } while (~index2);
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";
@@ -1361,203 +1521,1588 @@ function isTable(value) {
1361
1521
  return typeof value === "object" && value !== null && TableConfigSymbol in value;
1362
1522
  }
1363
1523
 
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
- }
1524
+ // ../../node_modules/.bun/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
1525
+ import { styleText } from "util";
1526
+ import { stdout, stdin } from "process";
1527
+ import * as l from "readline";
1528
+ import l__default from "readline";
1369
1529
 
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;
1530
+ // ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/utils.js
1531
+ var getCodePointsLength = (() => {
1532
+ const SURROGATE_PAIR_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
1533
+ return (input2) => {
1534
+ let surrogatePairsNr = 0;
1535
+ SURROGATE_PAIR_RE.lastIndex = 0;
1536
+ while (SURROGATE_PAIR_RE.test(input2)) {
1537
+ surrogatePairsNr += 1;
1538
+ }
1539
+ return input2.length - surrogatePairsNr;
1540
+ };
1541
+ })();
1542
+ var isFullWidth = (x) => {
1543
+ return x === 12288 || x >= 65281 && x <= 65376 || x >= 65504 && x <= 65510;
1544
+ };
1545
+ var isWideNotCJKTNotEmoji = (x) => {
1546
+ 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;
1547
+ };
1548
+
1549
+ // ../../node_modules/.bun/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/index.js
1550
+ var ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
1551
+ var CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
1552
+ 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;
1553
+ var TAB_RE = /\t{1,1000}/y;
1554
+ 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;
1555
+ var LATIN_RE = /(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y;
1556
+ var MODIFIER_RE = /\p{M}+/gu;
1557
+ var NO_TRUNCATION = { limit: Infinity, ellipsis: "" };
1558
+ var getStringTruncatedWidth = (input2, truncationOptions = {}, widthOptions = {}) => {
1559
+ const LIMIT = truncationOptions.limit ?? Infinity;
1560
+ const ELLIPSIS = truncationOptions.ellipsis ?? "";
1561
+ const ELLIPSIS_WIDTH = truncationOptions?.ellipsisWidth ?? (ELLIPSIS ? getStringTruncatedWidth(ELLIPSIS, NO_TRUNCATION, widthOptions).width : 0);
1562
+ const ANSI_WIDTH = 0;
1563
+ const CONTROL_WIDTH = widthOptions.controlWidth ?? 0;
1564
+ const TAB_WIDTH = widthOptions.tabWidth ?? 8;
1565
+ const EMOJI_WIDTH = widthOptions.emojiWidth ?? 2;
1566
+ const FULL_WIDTH_WIDTH = 2;
1567
+ const REGULAR_WIDTH = widthOptions.regularWidth ?? 1;
1568
+ const WIDE_WIDTH = widthOptions.wideWidth ?? FULL_WIDTH_WIDTH;
1569
+ const PARSE_BLOCKS = [
1570
+ [LATIN_RE, REGULAR_WIDTH],
1571
+ [ANSI_RE, ANSI_WIDTH],
1572
+ [CONTROL_RE, CONTROL_WIDTH],
1573
+ [TAB_RE, TAB_WIDTH],
1574
+ [EMOJI_RE, EMOJI_WIDTH],
1575
+ [CJKT_WIDE_RE, WIDE_WIDTH]
1576
+ ];
1577
+ let indexPrev = 0;
1578
+ let index2 = 0;
1579
+ let length = input2.length;
1580
+ let lengthExtra = 0;
1581
+ let truncationEnabled = false;
1582
+ let truncationIndex = length;
1583
+ let truncationLimit = Math.max(0, LIMIT - ELLIPSIS_WIDTH);
1584
+ let unmatchedStart = 0;
1585
+ let unmatchedEnd = 0;
1586
+ let width = 0;
1587
+ let widthExtra = 0;
1588
+ outer:
1589
+ while (true) {
1590
+ if (unmatchedEnd > unmatchedStart || index2 >= length && index2 > indexPrev) {
1591
+ const unmatched = input2.slice(unmatchedStart, unmatchedEnd) || input2.slice(indexPrev, index2);
1592
+ lengthExtra = 0;
1593
+ for (const char of unmatched.replaceAll(MODIFIER_RE, "")) {
1594
+ const codePoint = char.codePointAt(0) || 0;
1595
+ if (isFullWidth(codePoint)) {
1596
+ widthExtra = FULL_WIDTH_WIDTH;
1597
+ } else if (isWideNotCJKTNotEmoji(codePoint)) {
1598
+ widthExtra = WIDE_WIDTH;
1599
+ } else {
1600
+ widthExtra = REGULAR_WIDTH;
1601
+ }
1602
+ if (width + widthExtra > truncationLimit) {
1603
+ truncationIndex = Math.min(truncationIndex, Math.max(unmatchedStart, indexPrev) + lengthExtra);
1604
+ }
1605
+ if (width + widthExtra > LIMIT) {
1606
+ truncationEnabled = true;
1607
+ break outer;
1608
+ }
1609
+ lengthExtra += char.length;
1610
+ width += widthExtra;
1611
+ }
1612
+ unmatchedStart = unmatchedEnd = 0;
1613
+ }
1614
+ if (index2 >= length) {
1615
+ break outer;
1616
+ }
1617
+ for (let i = 0, l = PARSE_BLOCKS.length;i < l; i++) {
1618
+ const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i];
1619
+ BLOCK_RE.lastIndex = index2;
1620
+ if (BLOCK_RE.test(input2)) {
1621
+ lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input2.slice(index2, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index2;
1622
+ widthExtra = lengthExtra * BLOCK_WIDTH;
1623
+ if (width + widthExtra > truncationLimit) {
1624
+ truncationIndex = Math.min(truncationIndex, index2 + Math.floor((truncationLimit - width) / BLOCK_WIDTH));
1625
+ }
1626
+ if (width + widthExtra > LIMIT) {
1627
+ truncationEnabled = true;
1628
+ break outer;
1629
+ }
1630
+ width += widthExtra;
1631
+ unmatchedStart = indexPrev;
1632
+ unmatchedEnd = index2;
1633
+ index2 = indexPrev = BLOCK_RE.lastIndex;
1634
+ continue outer;
1635
+ }
1636
+ }
1637
+ index2 += 1;
1638
+ }
1639
+ return {
1640
+ width: truncationEnabled ? truncationLimit : width,
1641
+ index: truncationEnabled ? truncationIndex : length,
1642
+ truncated: truncationEnabled,
1643
+ ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH
1644
+ };
1645
+ };
1646
+ var dist_default = getStringTruncatedWidth;
1647
+
1648
+ // ../../node_modules/.bun/fast-string-width@3.0.2/node_modules/fast-string-width/dist/index.js
1649
+ var NO_TRUNCATION2 = {
1650
+ limit: Infinity,
1651
+ ellipsis: "",
1652
+ ellipsisWidth: 0
1653
+ };
1654
+ var fastStringWidth = (input2, options = {}) => {
1655
+ return dist_default(input2, NO_TRUNCATION2, options).width;
1656
+ };
1657
+ var dist_default2 = fastStringWidth;
1658
+
1659
+ // ../../node_modules/.bun/fast-wrap-ansi@0.2.2/node_modules/fast-wrap-ansi/lib/main.js
1660
+ var ESC = "\x1B";
1661
+ var CSI = "\x9B";
1662
+ var END_CODE = 39;
1663
+ var ANSI_ESCAPE_BELL = "\x07";
1664
+ var ANSI_CSI = "[";
1665
+ var ANSI_OSC = "]";
1666
+ var ANSI_SGR_TERMINATOR = "m";
1667
+ var ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
1668
+ var GROUP_REGEX = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`, "y");
1669
+ var getClosingCode = (openingCode) => {
1670
+ if (openingCode >= 30 && openingCode <= 37)
1671
+ return 39;
1672
+ if (openingCode >= 90 && openingCode <= 97)
1673
+ return 39;
1674
+ if (openingCode >= 40 && openingCode <= 47)
1675
+ return 49;
1676
+ if (openingCode >= 100 && openingCode <= 107)
1677
+ return 49;
1678
+ if (openingCode === 1 || openingCode === 2)
1679
+ return 22;
1680
+ if (openingCode === 3)
1681
+ return 23;
1682
+ if (openingCode === 4)
1683
+ return 24;
1684
+ if (openingCode === 7)
1685
+ return 27;
1686
+ if (openingCode === 8)
1687
+ return 28;
1688
+ if (openingCode === 9)
1689
+ return 29;
1690
+ if (openingCode === 0)
1691
+ return 0;
1692
+ return;
1693
+ };
1694
+ var wrapAnsiCode = (code2) => `${ESC}${ANSI_CSI}${code2}${ANSI_SGR_TERMINATOR}`;
1695
+ var wrapAnsiHyperlink = (url) => `${ESC}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
1696
+ var wrapWord = (rows, word, columns2) => {
1697
+ const characters = word[Symbol.iterator]();
1698
+ let isInsideEscape = false;
1699
+ let isInsideLinkEscape = false;
1700
+ let lastRow = rows.at(-1);
1701
+ let visible = lastRow === undefined ? 0 : dist_default2(lastRow);
1702
+ let currentCharacter = characters.next();
1703
+ let nextCharacter = characters.next();
1704
+ let rawCharacterIndex = 0;
1705
+ while (!currentCharacter.done) {
1706
+ const character = currentCharacter.value;
1707
+ const characterLength = dist_default2(character);
1708
+ if (visible + characterLength <= columns2) {
1709
+ rows[rows.length - 1] += character;
1710
+ } else {
1711
+ rows.push(character);
1712
+ visible = 0;
1713
+ }
1714
+ if (character === ESC || character === CSI) {
1715
+ isInsideEscape = true;
1716
+ isInsideLinkEscape = word.startsWith(ANSI_ESCAPE_LINK, rawCharacterIndex + 1);
1717
+ }
1718
+ if (isInsideEscape) {
1719
+ if (isInsideLinkEscape) {
1720
+ if (character === ANSI_ESCAPE_BELL) {
1721
+ isInsideEscape = false;
1722
+ isInsideLinkEscape = false;
1723
+ }
1724
+ } else if (character === ANSI_SGR_TERMINATOR) {
1725
+ isInsideEscape = false;
1726
+ }
1727
+ } else {
1728
+ visible += characterLength;
1729
+ if (visible === columns2 && !nextCharacter.done) {
1730
+ rows.push("");
1731
+ visible = 0;
1732
+ }
1733
+ }
1734
+ currentCharacter = nextCharacter;
1735
+ nextCharacter = characters.next();
1736
+ rawCharacterIndex += character.length;
1376
1737
  }
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;
1738
+ lastRow = rows.at(-1);
1739
+ if (!visible && lastRow !== undefined && lastRow.length && rows.length > 1) {
1740
+ rows[rows.length - 2] += rows.pop();
1741
+ }
1742
+ };
1743
+ var stringVisibleTrimSpacesRight = (string) => {
1744
+ const words = string.split(" ");
1745
+ let last = words.length;
1746
+ while (last) {
1747
+ if (dist_default2(words[last - 1])) {
1748
+ break;
1396
1749
  }
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}`);
1750
+ last--;
1751
+ }
1752
+ if (last === words.length) {
1753
+ return string;
1754
+ }
1755
+ return words.slice(0, last).join(" ") + words.slice(last).join("");
1756
+ };
1757
+ var exec = (string, columns2, options = {}) => {
1758
+ if (options.trim !== false && string.trim() === "") {
1759
+ return "";
1760
+ }
1761
+ let returnValue = "";
1762
+ let escapeCode;
1763
+ let escapeUrl;
1764
+ const words = string.split(" ");
1765
+ let rows = [""];
1766
+ let rowLength = 0;
1767
+ for (let index2 = 0;index2 < words.length; index2++) {
1768
+ const word = words[index2];
1769
+ if (options.trim !== false) {
1770
+ const row = rows.at(-1) ?? "";
1771
+ const trimmed = row.trimStart();
1772
+ if (row.length !== trimmed.length) {
1773
+ rows[rows.length - 1] = trimmed;
1774
+ rowLength = dist_default2(trimmed);
1775
+ }
1400
1776
  }
1401
- if (pushTableExists) {
1402
- tableNamesToPrint.push(`${config2.migrationsSchema}.__bungres_push`);
1777
+ if (index2 !== 0) {
1778
+ if (rowLength >= columns2 && (options.wordWrap === false || options.trim === false)) {
1779
+ rows.push("");
1780
+ rowLength = 0;
1781
+ }
1782
+ if (rowLength || options.trim === false) {
1783
+ rows[rows.length - 1] += " ";
1784
+ rowLength++;
1785
+ }
1403
1786
  }
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;
1787
+ const wordLength = dist_default2(word);
1788
+ if (options.hard && wordLength > columns2) {
1789
+ const remainingColumns = columns2 - rowLength;
1790
+ const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) / columns2);
1791
+ const breaksStartingNextLine = Math.floor((wordLength - 1) / columns2);
1792
+ if (breaksStartingNextLine < breaksStartingThisLine) {
1793
+ rows.push("");
1418
1794
  }
1795
+ wrapWord(rows, word, columns2);
1796
+ rowLength = dist_default2(rows.at(-1) ?? "");
1797
+ continue;
1419
1798
  }
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"));
1799
+ if (rowLength + wordLength > columns2 && rowLength && wordLength) {
1800
+ if (options.wordWrap === false && rowLength < columns2) {
1801
+ wrapWord(rows, word, columns2);
1802
+ rowLength = dist_default2(rows.at(-1) ?? "");
1803
+ continue;
1804
+ }
1805
+ rows.push("");
1806
+ rowLength = 0;
1424
1807
  }
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"));
1808
+ if (rowLength + wordLength > columns2 && options.wordWrap === false) {
1809
+ wrapWord(rows, word, columns2);
1810
+ rowLength = dist_default2(rows.at(-1) ?? "");
1811
+ continue;
1429
1812
  }
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"));
1813
+ rows[rows.length - 1] += word;
1814
+ rowLength += wordLength;
1815
+ }
1816
+ if (options.trim !== false) {
1817
+ rows = rows.map((row) => stringVisibleTrimSpacesRight(row));
1818
+ }
1819
+ const preString = rows.join(`
1820
+ `);
1821
+ let inSurrogate = false;
1822
+ for (let i = 0;i < preString.length; i++) {
1823
+ const character = preString[i];
1824
+ returnValue += character;
1825
+ if (!inSurrogate) {
1826
+ inSurrogate = character >= "\uD800" && character <= "\uDBFF";
1827
+ if (inSurrogate) {
1828
+ continue;
1829
+ }
1830
+ } else {
1831
+ inSurrogate = false;
1832
+ }
1833
+ if (character === ESC || character === CSI) {
1834
+ GROUP_REGEX.lastIndex = i + 1;
1835
+ const groupsResult = GROUP_REGEX.exec(preString);
1836
+ const groups = groupsResult?.groups;
1837
+ if (groups?.code !== undefined) {
1838
+ const code2 = Number.parseFloat(groups.code);
1839
+ escapeCode = code2 === END_CODE ? undefined : code2;
1840
+ } else if (groups?.uri !== undefined) {
1841
+ escapeUrl = groups.uri.length === 0 ? undefined : groups.uri;
1842
+ }
1843
+ }
1844
+ if (preString[i + 1] === `
1845
+ `) {
1846
+ if (escapeUrl) {
1847
+ returnValue += wrapAnsiHyperlink("");
1848
+ }
1849
+ const closingCode = escapeCode ? getClosingCode(escapeCode) : undefined;
1850
+ if (escapeCode && closingCode) {
1851
+ returnValue += wrapAnsiCode(closingCode);
1852
+ }
1853
+ } else if (character === `
1854
+ `) {
1855
+ if (escapeCode && getClosingCode(escapeCode)) {
1856
+ returnValue += wrapAnsiCode(escapeCode);
1857
+ }
1858
+ if (escapeUrl) {
1859
+ returnValue += wrapAnsiHyperlink(escapeUrl);
1860
+ }
1433
1861
  }
1434
- console.log(colorize(`
1435
- Drop complete.`, "green"));
1436
- } finally {
1437
- await sql2.end();
1438
1862
  }
1863
+ return returnValue;
1864
+ };
1865
+ var CRLF_OR_LF = /\r?\n/;
1866
+ function wrapAnsi(string, columns2, options) {
1867
+ return String(string).normalize().split(CRLF_OR_LF).map((line2) => exec(line2, columns2, options)).join(`
1868
+ `);
1439
1869
  }
1440
1870
 
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);
1871
+ // ../../node_modules/.bun/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
1872
+ var import_sisteransi = __toESM(require_src(), 1);
1873
+ import { ReadStream } from "tty";
1874
+ function findCursor(s, o, l2) {
1875
+ if (!l2.some((r) => !r.disabled))
1876
+ return s;
1877
+ const t = s + o, n = Math.max(l2.length - 1, 0), e = t < 0 ? n : t > n ? 0 : t;
1878
+ return l2[e]?.disabled ? findCursor(e, o < 0 ? -1 : 1, l2) : e;
1879
+ }
1880
+ function findTextCursor(s, o, l2, i) {
1881
+ const t = i.split(`
1882
+ `);
1883
+ let n = 0, e = s;
1884
+ for (const r of t) {
1885
+ if (e <= r.length)
1886
+ break;
1887
+ e -= r.length + 1, n++;
1888
+ }
1889
+ for (n = Math.max(0, Math.min(t.length - 1, n + l2)), e = Math.min(e, t[n].length) + o;e < 0 && n > 0; )
1890
+ n--, e += t[n].length + 1;
1891
+ for (;e > t[n].length && n < t.length - 1; )
1892
+ e -= t[n].length + 1, n++;
1893
+ e = Math.max(0, Math.min(t[n].length, e));
1894
+ let h = 0;
1895
+ for (let r = 0;r < n; r++)
1896
+ h += t[r].length + 1;
1897
+ return h + e;
1898
+ }
1899
+ var a$1 = ["up", "down", "left", "right", "space", "enter", "cancel"];
1900
+ var t = [
1901
+ "January",
1902
+ "February",
1903
+ "March",
1904
+ "April",
1905
+ "May",
1906
+ "June",
1907
+ "July",
1908
+ "August",
1909
+ "September",
1910
+ "October",
1911
+ "November",
1912
+ "December"
1913
+ ];
1914
+ var settings = {
1915
+ actions: new Set(a$1),
1916
+ aliases: /* @__PURE__ */ new Map([
1917
+ ["k", "up"],
1918
+ ["j", "down"],
1919
+ ["h", "left"],
1920
+ ["l", "right"],
1921
+ ["\x03", "cancel"],
1922
+ ["escape", "cancel"]
1923
+ ]),
1924
+ messages: {
1925
+ cancel: "Canceled",
1926
+ error: "Something went wrong"
1927
+ },
1928
+ withGuide: true,
1929
+ date: {
1930
+ monthNames: [...t],
1931
+ messages: {
1932
+ required: "Please enter a valid date",
1933
+ invalidMonth: "There are only 12 months in a year",
1934
+ invalidDay: (n, e) => `There are only ${n} days in ${e}`,
1935
+ afterMin: (n) => `Date must be on or after ${n.toISOString().slice(0, 10)}`,
1936
+ beforeMax: (n) => `Date must be on or before ${n.toISOString().slice(0, 10)}`
1463
1937
  }
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"));
1938
+ }
1939
+ };
1940
+ function isActionKey(n, e) {
1941
+ if (typeof n == "string")
1942
+ return settings.aliases.get(n) === e;
1943
+ for (const s of n)
1944
+ if (s !== undefined && isActionKey(s, e))
1945
+ return true;
1946
+ return false;
1947
+ }
1948
+ function diffLines(i, s) {
1949
+ if (i === s)
1950
+ return;
1951
+ const e = i.split(`
1952
+ `), t2 = s.split(`
1953
+ `), r = Math.max(e.length, t2.length), f = [];
1954
+ for (let n = 0;n < r; n++)
1955
+ e[n] !== t2[n] && f.push(n);
1956
+ return {
1957
+ lines: f,
1958
+ numLinesBefore: e.length,
1959
+ numLinesAfter: t2.length,
1960
+ numLines: r
1961
+ };
1962
+ }
1963
+ var R = globalThis.process.platform.startsWith("win");
1964
+ var CANCEL_SYMBOL = Symbol("clack:cancel");
1965
+ function isCancel(e) {
1966
+ return e === CANCEL_SYMBOL;
1967
+ }
1968
+ function setRawMode(e, r) {
1969
+ const o = e;
1970
+ o.isTTY && o.setRawMode(r);
1971
+ }
1972
+ function block({
1973
+ input: e = stdin,
1974
+ output: r = stdout,
1975
+ overwrite: o = true,
1976
+ hideCursor: t2 = true
1977
+ } = {}) {
1978
+ const s = l.createInterface({
1979
+ input: e,
1980
+ output: r,
1981
+ prompt: "",
1982
+ tabSize: 1
1983
+ });
1984
+ l.emitKeypressEvents(e, s), e instanceof ReadStream && e.isTTY && e.setRawMode(true);
1985
+ const n = (f, { name: a, sequence: p }) => {
1986
+ const c = String(f);
1987
+ if (isActionKey([c, a, p], "cancel")) {
1988
+ t2 && r.write(import_sisteransi.cursor.show), process.exit(0);
1468
1989
  return;
1469
1990
  }
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"));
1991
+ if (!o)
1475
1992
  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}
1993
+ const i = a === "return" ? 0 : -1, m = a === "return" ? -1 : 0;
1994
+ l.moveCursor(r, i, m, () => {
1995
+ l.clearLine(r, 1, () => {
1996
+ e.once("keypress", n);
1997
+ });
1998
+ });
1999
+ };
2000
+ return t2 && r.write(import_sisteransi.cursor.hide), e.once("keypress", n), () => {
2001
+ 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();
2002
+ };
2003
+ }
2004
+ var getColumns = (e) => ("columns" in e) && typeof e.columns == "number" ? e.columns : 80;
2005
+ var getRows = (e) => ("rows" in e) && typeof e.rows == "number" ? e.rows : 20;
2006
+ function wrapTextWithPrefix(e, r, o, t2 = o, s = o, n) {
2007
+ const f = getColumns(e ?? stdout);
2008
+ return wrapAnsi(r, f - o.length, {
2009
+ hard: true,
2010
+ trim: false
2011
+ }).split(`
2012
+ `).map((c, i, m) => {
2013
+ const d = n ? n(c, i) : c;
2014
+ return i === 0 ? `${t2}${d}` : i === m.length - 1 ? `${s}${d}` : `${o}${d}`;
2015
+ }).join(`
1485
2016
  `);
2017
+ }
2018
+ function runValidation(e, n) {
2019
+ if ("~standard" in e) {
2020
+ const a = e["~standard"].validate(n);
2021
+ if (a instanceof Promise)
2022
+ throw new TypeError("Schema validation must be synchronous. Update `validate()` and remove any asynchronous logic.");
2023
+ return a.issues?.at(0)?.message;
2024
+ }
2025
+ return e(n);
2026
+ }
2027
+
2028
+ class V {
2029
+ input;
2030
+ output;
2031
+ _abortSignal;
2032
+ rl;
2033
+ opts;
2034
+ _render;
2035
+ _track = false;
2036
+ _prevFrame = "";
2037
+ _subscribers = /* @__PURE__ */ new Map;
2038
+ _cursor = 0;
2039
+ state = "initial";
2040
+ error = "";
2041
+ value;
2042
+ userInput = "";
2043
+ constructor(t2, e = true) {
2044
+ const { input: i = stdin, output: n = stdout, render: s, signal: r, ...o } = t2;
2045
+ 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;
2046
+ }
2047
+ unsubscribe() {
2048
+ this._subscribers.clear();
2049
+ }
2050
+ setSubscriber(t2, e) {
2051
+ const i = this._subscribers.get(t2) ?? [];
2052
+ i.push(e), this._subscribers.set(t2, i);
2053
+ }
2054
+ on(t2, e) {
2055
+ this.setSubscriber(t2, { cb: e });
2056
+ }
2057
+ once(t2, e) {
2058
+ this.setSubscriber(t2, { cb: e, once: true });
2059
+ }
2060
+ emit(t2, ...e) {
2061
+ const i = this._subscribers.get(t2) ?? [], n = [];
2062
+ for (const s of i)
2063
+ s.cb(...e), s.once && n.push(() => i.splice(i.indexOf(s), 1));
2064
+ for (const s of n)
2065
+ s();
2066
+ }
2067
+ prompt() {
2068
+ return new Promise((t2) => {
2069
+ if (this._abortSignal) {
2070
+ if (this._abortSignal.aborted)
2071
+ return this.state = "cancel", this.close(), t2(CANCEL_SYMBOL);
2072
+ this._abortSignal.addEventListener("abort", () => {
2073
+ this.state = "cancel", this.close();
2074
+ }, { once: true });
1486
2075
  }
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]);
2076
+ this.rl = l__default.createInterface({
2077
+ input: this.input,
2078
+ tabSize: 2,
2079
+ prompt: "",
2080
+ escapeCodeTimeout: 50,
2081
+ terminal: true
2082
+ }), 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", () => {
2083
+ this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t2(this.value);
2084
+ }), this.once("cancel", () => {
2085
+ this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), setRawMode(this.input, false), t2(CANCEL_SYMBOL);
1493
2086
  });
1494
- console.log(colorize(` \u2713 ${file}`, "green"));
2087
+ });
2088
+ }
2089
+ _isActionKey(t2, e) {
2090
+ return t2 === "\t";
2091
+ }
2092
+ _shouldSubmit(t2, e) {
2093
+ return true;
2094
+ }
2095
+ _setValue(t2) {
2096
+ this.value = t2, this.emit("value", this.value);
2097
+ }
2098
+ _setUserInput(t2, e) {
2099
+ this.userInput = t2 ?? "", this.emit("userInput", this.userInput), e && this._track && this.rl && (this.rl.write(this.userInput), this._cursor = this.rl.cursor);
2100
+ }
2101
+ _clearUserInput() {
2102
+ this.rl?.write(null, { ctrl: true, name: "u" }), this._setUserInput("");
2103
+ }
2104
+ onKeypress(t2, e) {
2105
+ 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)) {
2106
+ if (this.opts.validate) {
2107
+ const i = runValidation(this.opts.validate, this.value);
2108
+ i && (this.error = i instanceof Error ? i.message : i, this.state = "error", this.rl?.write(this.userInput));
2109
+ }
2110
+ this.state !== "error" && (this.state = "submit");
2111
+ }
2112
+ 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();
2113
+ }
2114
+ close() {
2115
+ this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
2116
+ `), setRawMode(this.input, false), this.rl?.close(), this.rl = undefined, this.emit(`${this.state}`, this.value), this.unsubscribe();
2117
+ }
2118
+ restoreCursor() {
2119
+ const t2 = wrapAnsi(this._prevFrame, process.stdout.columns, { hard: true, trim: false }).split(`
2120
+ `).length - 1;
2121
+ this.output.write(import_sisteransi.cursor.move(-999, t2 * -1));
2122
+ }
2123
+ render() {
2124
+ const t2 = wrapAnsi(this._render(this) ?? "", process.stdout.columns, {
2125
+ hard: true,
2126
+ trim: false
2127
+ });
2128
+ if (t2 !== this._prevFrame) {
2129
+ if (this.state === "initial")
2130
+ this.output.write(import_sisteransi.cursor.hide);
2131
+ else {
2132
+ const e = diffLines(this._prevFrame, t2), i = getRows(this.output);
2133
+ if (this.restoreCursor(), e) {
2134
+ const n = Math.max(0, e.numLinesAfter - i), s = Math.max(0, e.numLinesBefore - i);
2135
+ let r = e.lines.find((o) => o >= n);
2136
+ if (r === undefined) {
2137
+ this._prevFrame = t2;
2138
+ return;
2139
+ }
2140
+ if (e.lines.length === 1) {
2141
+ this.output.write(import_sisteransi.cursor.move(0, r - s)), this.output.write(import_sisteransi.erase.lines(1));
2142
+ const o = t2.split(`
2143
+ `);
2144
+ this.output.write(o[r]), this._prevFrame = t2, this.output.write(import_sisteransi.cursor.move(0, o.length - r - 1));
2145
+ return;
2146
+ } else if (e.lines.length > 1) {
2147
+ if (n < s)
2148
+ r = n;
2149
+ else {
2150
+ const h = r - s;
2151
+ h > 0 && this.output.write(import_sisteransi.cursor.move(0, h));
2152
+ }
2153
+ this.output.write(import_sisteransi.erase.down());
2154
+ const f = t2.split(`
2155
+ `).slice(r);
2156
+ this.output.write(f.join(`
2157
+ `)), this._prevFrame = t2;
2158
+ return;
2159
+ }
2160
+ }
2161
+ this.output.write(import_sisteransi.erase.down());
2162
+ }
2163
+ this.output.write(t2), this.state === "initial" && (this.state = "active"), this._prevFrame = t2;
1495
2164
  }
1496
- console.log(colorize(`
1497
- Done.`, "green"));
1498
- } finally {
1499
- await sql2.end();
1500
2165
  }
1501
2166
  }
1502
-
1503
- // src/commands/fresh.ts
1504
- async function runFresh(config2) {
1505
- console.log("Dropping all tables...");
1506
- await runDrop(config2, { force: true });
1507
- console.log(`
1508
- Re-running migrations...`);
1509
- await runMigrate(config2);
1510
- console.log(`
1511
- Fresh complete.`);
2167
+ function p$1(l2, e) {
2168
+ if (l2 === undefined || e.length === 0)
2169
+ return 0;
2170
+ const i = e.findIndex((s) => s.value === l2);
2171
+ return i !== -1 ? i : 0;
1512
2172
  }
1513
-
1514
- // src/commands/generate.ts
1515
- import { join as join3, resolve as resolve3 } from "path";
1516
-
1517
- // src/differ.ts
1518
- function diffSchemas(prev, next) {
1519
- const statements = [];
1520
- const summary = [];
1521
- const warnings = [];
1522
- const prevTables = new Set(Object.keys(prev));
1523
- const nextTables = new Set(Object.keys(next));
1524
- const newTableConfigs = [];
1525
- for (const tableName of nextTables) {
1526
- if (!prevTables.has(tableName)) {
1527
- newTableConfigs.push(next[tableName]);
1528
- }
2173
+ function g(l2, e) {
2174
+ return (e.label ?? String(e.value)).toLowerCase().includes(l2.toLowerCase());
2175
+ }
2176
+ function m(l2, e) {
2177
+ if (e)
2178
+ return l2 ? e : e[0];
2179
+ }
2180
+ var T$1 = class T extends V {
2181
+ filteredOptions;
2182
+ multiple;
2183
+ isNavigating = false;
2184
+ selectedValues = [];
2185
+ focusedValue;
2186
+ #e = 0;
2187
+ #s = "";
2188
+ #t;
2189
+ #i;
2190
+ #n;
2191
+ get cursor() {
2192
+ return this.#e;
2193
+ }
2194
+ get userInputWithCursor() {
2195
+ if (!this.userInput)
2196
+ return styleText(["inverse", "hidden"], "_");
2197
+ if (this._cursor >= this.userInput.length)
2198
+ return `${this.userInput}\u2588`;
2199
+ const e = this.userInput.slice(0, this.cursor), t2 = this.userInput.slice(this.cursor, this.cursor + 1), i = this.userInput.slice(this.cursor + 1);
2200
+ return `${e}${styleText("inverse", t2)}${i}`;
2201
+ }
2202
+ get options() {
2203
+ return typeof this.#i == "function" ? this.#i() : this.#i;
2204
+ }
2205
+ constructor(e) {
2206
+ super(e), this.#i = e.options, this.#n = e.placeholder;
2207
+ const t2 = this.options;
2208
+ this.filteredOptions = [...t2], this.multiple = e.multiple === true, this.#t = typeof e.options == "function" ? e.filter : e.filter ?? g;
2209
+ let i;
2210
+ 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)
2211
+ for (const s of i) {
2212
+ const n = t2.findIndex((o) => o.value === s);
2213
+ n !== -1 && (this.toggleSelected(s), this.#e = n);
2214
+ }
2215
+ this.focusedValue = this.options[this.#e]?.value, this.on("key", (s, n) => this.#l(s, n)), this.on("userInput", (s) => this.#u(s));
1529
2216
  }
1530
- for (const config2 of topoSortConfigs(newTableConfigs)) {
1531
- statements.push(generateCreateTable(config2, true));
1532
- summary.push(`CREATE TABLE ${config2.name}`);
2217
+ _isActionKey(e, t2) {
2218
+ return e === "\t" || this.multiple && this.isNavigating && t2.name === "space" && e !== undefined && e !== "";
1533
2219
  }
1534
- for (const tableName of prevTables) {
1535
- if (!nextTables.has(tableName)) {
1536
- const config2 = prev[tableName];
1537
- const tbl = config2.schema ? `"${config2.schema}"."${tableName}"` : `"${tableName}"`;
1538
- statements.push(`DROP TABLE IF EXISTS ${tbl};`);
1539
- summary.push(`DROP TABLE ${tableName}`);
1540
- warnings.push(`Data loss warning: Table '${tableName}' will be permanently deleted.`);
2220
+ #l(e, t2) {
2221
+ 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));
2222
+ if (t2.name === "tab" && o && f) {
2223
+ this.userInput === "\t" && this._clearUserInput(), this._setUserInput(u, true), this.isNavigating = false;
2224
+ return;
1541
2225
  }
2226
+ 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);
1542
2227
  }
1543
- for (const tableName of nextTables) {
1544
- if (!prevTables.has(tableName))
1545
- continue;
1546
- const prevConfig = prev[tableName];
1547
- const nextConfig = next[tableName];
1548
- const prevCols = prevConfig.columns;
1549
- const nextCols = nextConfig.columns;
1550
- const prevColNames = new Set(Object.keys(prevCols));
1551
- const nextColNames = new Set(Object.keys(nextCols));
1552
- for (const key of nextColNames) {
1553
- if (!prevColNames.has(key)) {
1554
- const col2 = nextCols[key];
1555
- statements.push(generateAddColumn(tableName, nextConfig.schema, key, col2));
1556
- summary.push(`ALTER TABLE ${tableName} ADD COLUMN ${col2.name}`);
1557
- }
2228
+ deselectAll() {
2229
+ this.selectedValues = [];
2230
+ }
2231
+ toggleSelected(e) {
2232
+ 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]);
2233
+ }
2234
+ #u(e) {
2235
+ if (e !== this.#s) {
2236
+ this.#s = e;
2237
+ const t2 = this.options;
2238
+ e && this.#t ? this.filteredOptions = t2.filter((n) => this.#t?.(e, n)) : this.filteredOptions = [...t2];
2239
+ const i = p$1(this.focusedValue, this.filteredOptions);
2240
+ this.#e = findCursor(i, 0, this.filteredOptions);
2241
+ const s = this.filteredOptions[this.#e];
2242
+ s && !s.disabled ? this.focusedValue = s.value : this.focusedValue = undefined, this.multiple || (this.focusedValue !== undefined ? this.toggleSelected(this.focusedValue) : this.deselectAll());
1558
2243
  }
1559
- for (const key of prevColNames) {
1560
- if (!nextColNames.has(key)) {
2244
+ }
2245
+ };
2246
+
2247
+ class r extends V {
2248
+ get cursor() {
2249
+ return this.value ? 0 : 1;
2250
+ }
2251
+ get _value() {
2252
+ return this.cursor === 0;
2253
+ }
2254
+ constructor(t2) {
2255
+ super(t2, false), this.value = !!t2.initialValue, this.on("userInput", () => {
2256
+ this.value = this._value;
2257
+ }), this.on("confirm", (i) => {
2258
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = i, this.state = "submit", this.close();
2259
+ }), this.on("cursor", () => {
2260
+ this.value = !this.value;
2261
+ });
2262
+ }
2263
+ }
2264
+ var _ = {
2265
+ Y: { type: "year", len: 4 },
2266
+ M: { type: "month", len: 2 },
2267
+ D: { type: "day", len: 2 }
2268
+ };
2269
+ function M(r2) {
2270
+ return [...r2].map((t2) => _[t2]);
2271
+ }
2272
+ function P(r2) {
2273
+ const i = new Intl.DateTimeFormat(r2, {
2274
+ year: "numeric",
2275
+ month: "2-digit",
2276
+ day: "2-digit"
2277
+ }).formatToParts(new Date(2000, 0, 15)), s = [];
2278
+ let n = "/";
2279
+ for (const e of i)
2280
+ 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 });
2281
+ return { segments: s, separator: n };
2282
+ }
2283
+ function p(r2) {
2284
+ return Number.parseInt((r2 || "0").replace(/_/g, "0"), 10) || 0;
2285
+ }
2286
+ function f(r2) {
2287
+ return {
2288
+ year: p(r2.year),
2289
+ month: p(r2.month),
2290
+ day: p(r2.day)
2291
+ };
2292
+ }
2293
+ function c(r2, t2) {
2294
+ return new Date(r2 || 2001, t2 || 1, 0).getDate();
2295
+ }
2296
+ function b(r2) {
2297
+ const { year: t2, month: i, day: s } = f(r2);
2298
+ if (!t2 || t2 < 0 || t2 > 9999 || !i || i < 1 || i > 12 || !s || s < 1)
2299
+ return;
2300
+ const n = new Date(Date.UTC(t2, i - 1, s));
2301
+ if (!(n.getUTCFullYear() !== t2 || n.getUTCMonth() !== i - 1 || n.getUTCDate() !== s))
2302
+ return { year: t2, month: i, day: s };
2303
+ }
2304
+ function C(r2) {
2305
+ const t2 = b(r2);
2306
+ return t2 ? new Date(Date.UTC(t2.year, t2.month - 1, t2.day)) : undefined;
2307
+ }
2308
+ function T2(r2, t2, i, s) {
2309
+ const n = i ? {
2310
+ year: i.getUTCFullYear(),
2311
+ month: i.getUTCMonth() + 1,
2312
+ day: i.getUTCDate()
2313
+ } : null, e = s ? {
2314
+ year: s.getUTCFullYear(),
2315
+ month: s.getUTCMonth() + 1,
2316
+ day: s.getUTCDate()
2317
+ } : null;
2318
+ return r2 === "year" ? { min: n?.year ?? 1, max: e?.year ?? 9999 } : r2 === "month" ? {
2319
+ min: n && t2.year === n.year ? n.month : 1,
2320
+ max: e && t2.year === e.year ? e.month : 12
2321
+ } : {
2322
+ min: n && t2.year === n.year && t2.month === n.month ? n.day : 1,
2323
+ max: e && t2.year === e.year && t2.month === e.month ? e.day : c(t2.year, t2.month)
2324
+ };
2325
+ }
2326
+
2327
+ class U extends V {
2328
+ #i;
2329
+ #o;
2330
+ #t;
2331
+ #h;
2332
+ #u;
2333
+ #e = { segmentIndex: 0, positionInSegment: 0 };
2334
+ #n = true;
2335
+ #s = null;
2336
+ inlineError = "";
2337
+ get segmentCursor() {
2338
+ return { ...this.#e };
2339
+ }
2340
+ get segmentValues() {
2341
+ return { ...this.#t };
2342
+ }
2343
+ get segments() {
2344
+ return this.#i;
2345
+ }
2346
+ get separator() {
2347
+ return this.#o;
2348
+ }
2349
+ get formattedValue() {
2350
+ return this.#l(this.#t);
2351
+ }
2352
+ #l(t2) {
2353
+ return this.#i.map((i) => t2[i.type]).join(this.#o);
2354
+ }
2355
+ #r() {
2356
+ this._setUserInput(this.#l(this.#t)), this._setValue(C(this.#t) ?? undefined);
2357
+ }
2358
+ constructor(t2) {
2359
+ 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 ? {
2360
+ year: String(e.getUTCFullYear()).padStart(4, "0"),
2361
+ month: String(e.getUTCMonth() + 1).padStart(2, "0"),
2362
+ day: String(e.getUTCDate()).padStart(2, "0")
2363
+ } : { year: "____", month: "__", day: "__" }, o = n.map((a) => m2[a.type]).join(s);
2364
+ 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));
2365
+ }
2366
+ #a() {
2367
+ const t2 = Math.max(0, Math.min(this.#e.segmentIndex, this.#i.length - 1)), i = this.#i[t2];
2368
+ if (i)
2369
+ return this.#e.positionInSegment = Math.max(0, Math.min(this.#e.positionInSegment, i.len - 1)), { segment: i, index: t2 };
2370
+ }
2371
+ #m(t2) {
2372
+ this.inlineError = "", this.#s = null;
2373
+ const i = this.#a();
2374
+ i && (this.#e.segmentIndex = Math.max(0, Math.min(this.#i.length - 1, i.index + t2)), this.#e.positionInSegment = 0, this.#n = true);
2375
+ }
2376
+ #d(t2) {
2377
+ const i = this.#a();
2378
+ if (!i)
2379
+ return;
2380
+ 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);
2381
+ let a;
2382
+ e ? a = t2 === 1 ? o.min : o.max : a = Math.max(Math.min(o.max, m2 + t2), o.min), this.#t = {
2383
+ ...this.#t,
2384
+ [s.type]: a.toString().padStart(s.len, "0")
2385
+ }, this.#n = true, this.#s = null, this.#r();
2386
+ }
2387
+ #f(t2) {
2388
+ if (t2)
2389
+ switch (t2) {
2390
+ case "right":
2391
+ return this.#m(1);
2392
+ case "left":
2393
+ return this.#m(-1);
2394
+ case "up":
2395
+ return this.#d(1);
2396
+ case "down":
2397
+ return this.#d(-1);
2398
+ }
2399
+ }
2400
+ #y(t2, i) {
2401
+ if (i?.name === "backspace" || i?.sequence === "\x7F" || i?.sequence === "\b" || t2 === "\x7F" || t2 === "\b") {
2402
+ this.inlineError = "";
2403
+ const n = this.#a();
2404
+ if (!n)
2405
+ return;
2406
+ if (!this.#t[n.segment.type].replace(/_/g, "")) {
2407
+ this.#m(-1);
2408
+ return;
2409
+ }
2410
+ this.#t[n.segment.type] = "_".repeat(n.segment.len), this.#n = true, this.#e.positionInSegment = 0, this.#r();
2411
+ return;
2412
+ }
2413
+ if (i?.name === "tab") {
2414
+ this.inlineError = "";
2415
+ const n = this.#a();
2416
+ if (!n)
2417
+ return;
2418
+ const e = i.shift ? -1 : 1, m2 = n.index + e;
2419
+ m2 >= 0 && m2 < this.#i.length && (this.#e.segmentIndex = m2, this.#e.positionInSegment = 0, this.#n = true);
2420
+ return;
2421
+ }
2422
+ if (t2 && /^[0-9]$/.test(t2)) {
2423
+ const n = this.#a();
2424
+ if (!n)
2425
+ return;
2426
+ const { segment: e } = n, m2 = !this.#t[e.type].replace(/_/g, "");
2427
+ if (this.#n && this.#s !== null && !m2) {
2428
+ const h = this.#s + t2, d = { ...this.#t, [e.type]: h }, g2 = this.#g(d, e);
2429
+ if (g2) {
2430
+ this.inlineError = g2, this.#s = null, this.#n = false;
2431
+ return;
2432
+ }
2433
+ 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);
2434
+ return;
2435
+ }
2436
+ this.#n && !m2 && (this.#t[e.type] = "_".repeat(e.len), this.#e.positionInSegment = 0), this.#n = false, this.#s = null;
2437
+ const o = this.#t[e.type], a = o.indexOf("_"), u = a >= 0 ? a : Math.min(this.#e.positionInSegment, e.len - 1);
2438
+ if (u < 0 || u >= e.len)
2439
+ return;
2440
+ let l2 = o.slice(0, u) + t2 + o.slice(u + 1), D = false;
2441
+ if (u === 0 && o === "__" && (e.type === "month" || e.type === "day")) {
2442
+ const h = Number.parseInt(t2, 10);
2443
+ l2 = `0${t2}`, D = h <= (e.type === "month" ? 1 : 2);
2444
+ }
2445
+ if (e.type === "year" && (l2 = (o.replace(/_/g, "") + t2).padStart(e.len, "_")), !l2.includes("_")) {
2446
+ const h = { ...this.#t, [e.type]: l2 }, d = this.#g(h, e);
2447
+ if (d) {
2448
+ this.inlineError = d;
2449
+ return;
2450
+ }
2451
+ }
2452
+ this.inlineError = "", this.#t[e.type] = l2;
2453
+ const y = l2.includes("_") ? undefined : b(this.#t);
2454
+ if (y) {
2455
+ const { year: h, month: d } = y, g2 = c(h, d);
2456
+ this.#t = {
2457
+ year: String(Math.max(0, Math.min(9999, h))).padStart(4, "0"),
2458
+ month: String(Math.max(1, Math.min(12, d))).padStart(2, "0"),
2459
+ day: String(Math.max(1, Math.min(g2, y.day))).padStart(2, "0")
2460
+ };
2461
+ }
2462
+ this.#r();
2463
+ const S = l2.indexOf("_");
2464
+ 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);
2465
+ }
2466
+ }
2467
+ #g(t2, i) {
2468
+ const { month: s, day: n } = f(t2);
2469
+ if (i.type === "month" && (s < 0 || s > 12))
2470
+ return settings.date.messages.invalidMonth;
2471
+ if (i.type === "day" && (n < 0 || n > 31))
2472
+ return settings.date.messages.invalidDay(31, "any month");
2473
+ }
2474
+ #p(t2) {
2475
+ const { year: i, month: s, day: n } = f(this.#t);
2476
+ if (i && s && n) {
2477
+ const e = c(i, s);
2478
+ this.#t = {
2479
+ ...this.#t,
2480
+ day: String(Math.min(n, e)).padStart(2, "0")
2481
+ };
2482
+ }
2483
+ this.value = C(this.#t) ?? t2.defaultValue ?? undefined;
2484
+ }
2485
+ }
2486
+ var u$2 = class u extends V {
2487
+ options;
2488
+ cursor = 0;
2489
+ #t;
2490
+ getGroupItems(t2) {
2491
+ return this.options.filter((r2) => r2.group === t2);
2492
+ }
2493
+ isGroupSelected(t2) {
2494
+ const r2 = this.getGroupItems(t2), e = this.value;
2495
+ return e === undefined ? false : r2.every((s) => e.includes(s.value));
2496
+ }
2497
+ toggleValue() {
2498
+ const t2 = this.options[this.cursor];
2499
+ if (t2 !== undefined)
2500
+ if (this.value === undefined && (this.value = []), t2.group === true) {
2501
+ const r2 = t2.value, e = this.getGroupItems(r2);
2502
+ 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));
2503
+ } else {
2504
+ const r2 = this.value.includes(t2.value);
2505
+ this.value = r2 ? this.value.filter((e) => e !== t2.value) : [...this.value, t2.value];
2506
+ }
2507
+ }
2508
+ constructor(t2) {
2509
+ super(t2, false);
2510
+ const { options: r2 } = t2;
2511
+ this.#t = t2.selectableGroups !== false, this.options = Object.entries(r2).flatMap(([e, s]) => [
2512
+ { value: e, group: true, label: e },
2513
+ ...s.map((i) => ({ ...i, group: e }))
2514
+ ]), this.value = [...t2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: e }) => e === t2.cursorAt), this.#t ? 0 : 1), this.on("cursor", (e) => {
2515
+ switch (e) {
2516
+ case "left":
2517
+ case "up": {
2518
+ this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
2519
+ const s = this.options[this.cursor]?.group === true;
2520
+ !this.#t && s && (this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1);
2521
+ break;
2522
+ }
2523
+ case "down":
2524
+ case "right": {
2525
+ this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
2526
+ const s = this.options[this.cursor]?.group === true;
2527
+ !this.#t && s && (this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1);
2528
+ break;
2529
+ }
2530
+ case "space":
2531
+ this.toggleValue();
2532
+ break;
2533
+ }
2534
+ });
2535
+ }
2536
+ };
2537
+ var o = /* @__PURE__ */ new Set(["up", "down", "left", "right"]);
2538
+
2539
+ class h extends V {
2540
+ #t = false;
2541
+ #s;
2542
+ focused = "editor";
2543
+ get userInputWithCursor() {
2544
+ if (this.state === "submit")
2545
+ return this.userInput;
2546
+ const t2 = this.userInput;
2547
+ if (this.cursor >= t2.length)
2548
+ return `${t2}\u2588`;
2549
+ const s = t2.slice(0, this.cursor), r2 = t2.slice(this.cursor, this.cursor + 1), i = t2.slice(this.cursor + 1);
2550
+ return r2 === `
2551
+ ` ? `${s}\u2588
2552
+ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
2553
+ }
2554
+ get cursor() {
2555
+ return this._cursor;
2556
+ }
2557
+ #r(t2) {
2558
+ if (this.userInput.length === 0) {
2559
+ this._setUserInput(t2);
2560
+ return;
2561
+ }
2562
+ this._setUserInput(this.userInput.slice(0, this.cursor) + t2 + this.userInput.slice(this.cursor));
2563
+ }
2564
+ #i(t2) {
2565
+ const s = this.value ?? "";
2566
+ switch (t2) {
2567
+ case "up":
2568
+ this._cursor = findTextCursor(this._cursor, 0, -1, s);
2569
+ return;
2570
+ case "down":
2571
+ this._cursor = findTextCursor(this._cursor, 0, 1, s);
2572
+ return;
2573
+ case "left":
2574
+ this._cursor = findTextCursor(this._cursor, -1, 0, s);
2575
+ return;
2576
+ case "right":
2577
+ this._cursor = findTextCursor(this._cursor, 1, 0, s);
2578
+ return;
2579
+ }
2580
+ }
2581
+ _shouldSubmit(t2, s) {
2582
+ if (this.#s)
2583
+ return this.focused === "submit" ? true : (this.#r(`
2584
+ `), this._cursor++, false);
2585
+ const r2 = this.#t;
2586
+ return this.#t = true, r2 && this.cursor === this.userInput.length ? (this.userInput[this.cursor - 1] === `
2587
+ ` && (this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--), true) : (this.#r(`
2588
+ `), this._cursor++, false);
2589
+ }
2590
+ constructor(t2) {
2591
+ const s = t2.initialUserInput ?? t2.initialValue;
2592
+ super({
2593
+ ...t2,
2594
+ initialUserInput: s
2595
+ }, false), s !== undefined && (this._cursor = s.length), this.#s = t2.showSubmit ?? false, this.on("key", (r2, i) => {
2596
+ if (i?.name && o.has(i.name)) {
2597
+ this.#t = false, this.#i(i.name);
2598
+ return;
2599
+ }
2600
+ if (r2 === "\t" && this.#s) {
2601
+ this.focused = this.focused === "editor" ? "submit" : "editor";
2602
+ return;
2603
+ }
2604
+ if (i?.name !== "return") {
2605
+ if (this.#t = false, i?.name === "backspace" && this.cursor > 0) {
2606
+ this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--;
2607
+ return;
2608
+ }
2609
+ if (i?.name === "delete" && this.cursor < this.userInput.length) {
2610
+ this._setUserInput(this.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1));
2611
+ return;
2612
+ }
2613
+ r2 && (this.#s && this.focused === "submit" && (this.focused = "editor"), this.#r(r2 ?? ""), this._cursor++);
2614
+ }
2615
+ }), this.on("userInput", (r2) => {
2616
+ this._setValue(r2);
2617
+ }), this.on("finalize", () => {
2618
+ this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
2619
+ });
2620
+ }
2621
+ }
2622
+
2623
+ // ../../node_modules/.bun/@clack+prompts@1.7.0/node_modules/@clack/prompts/dist/index.mjs
2624
+ import { styleText as styleText2, stripVTControlCharacters } from "util";
2625
+ import process$1 from "process";
2626
+ var import_sisteransi2 = __toESM(require_src(), 1);
2627
+ function isUnicodeSupported() {
2628
+ if (process$1.platform !== "win32") {
2629
+ return process$1.env.TERM !== "linux";
2630
+ }
2631
+ 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";
2632
+ }
2633
+ var unicode = isUnicodeSupported();
2634
+ var isCI = () => process.env.CI === "true";
2635
+ var unicodeOr = (o2, e) => unicode ? o2 : e;
2636
+ var S_STEP_ACTIVE = unicodeOr("\u25C6", "*");
2637
+ var S_STEP_CANCEL = unicodeOr("\u25A0", "x");
2638
+ var S_STEP_ERROR = unicodeOr("\u25B2", "x");
2639
+ var S_STEP_SUBMIT = unicodeOr("\u25C7", "o");
2640
+ var S_BAR_START = unicodeOr("\u250C", "T");
2641
+ var S_BAR = unicodeOr("\u2502", "|");
2642
+ var S_BAR_END = unicodeOr("\u2514", "\u2014");
2643
+ var S_BAR_START_RIGHT = unicodeOr("\u2510", "T");
2644
+ var S_BAR_END_RIGHT = unicodeOr("\u2518", "\u2014");
2645
+ var S_RADIO_ACTIVE = unicodeOr("\u25CF", ">");
2646
+ var S_RADIO_INACTIVE = unicodeOr("\u25CB", " ");
2647
+ var S_CHECKBOX_ACTIVE = unicodeOr("\u25FB", "[\u2022]");
2648
+ var S_CHECKBOX_SELECTED = unicodeOr("\u25FC", "[+]");
2649
+ var S_CHECKBOX_INACTIVE = unicodeOr("\u25FB", "[ ]");
2650
+ var S_PASSWORD_MASK = unicodeOr("\u25AA", "\u2022");
2651
+ var S_BAR_H = unicodeOr("\u2500", "-");
2652
+ var S_CORNER_TOP_RIGHT = unicodeOr("\u256E", "+");
2653
+ var S_CONNECT_LEFT = unicodeOr("\u251C", "+");
2654
+ var S_CORNER_BOTTOM_RIGHT = unicodeOr("\u256F", "+");
2655
+ var S_CORNER_BOTTOM_LEFT = unicodeOr("\u2570", "+");
2656
+ var S_CORNER_TOP_LEFT = unicodeOr("\u256D", "+");
2657
+ var S_INFO = unicodeOr("\u25CF", "\u2022");
2658
+ var S_SUCCESS = unicodeOr("\u25C6", "*");
2659
+ var S_WARN = unicodeOr("\u25B2", "!");
2660
+ var S_ERROR = unicodeOr("\u25A0", "x");
2661
+ var symbol = (o2) => {
2662
+ switch (o2) {
2663
+ case "initial":
2664
+ case "active":
2665
+ return styleText2("cyan", S_STEP_ACTIVE);
2666
+ case "cancel":
2667
+ return styleText2("red", S_STEP_CANCEL);
2668
+ case "error":
2669
+ return styleText2("yellow", S_STEP_ERROR);
2670
+ case "submit":
2671
+ return styleText2("green", S_STEP_SUBMIT);
2672
+ }
2673
+ };
2674
+ var confirm = (i) => {
2675
+ const a2 = i.active ?? "Yes", s = i.inactive ?? "No";
2676
+ return new r({
2677
+ active: a2,
2678
+ inactive: s,
2679
+ signal: i.signal,
2680
+ input: i.input,
2681
+ output: i.output,
2682
+ initialValue: i.initialValue ?? true,
2683
+ render() {
2684
+ 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)}
2685
+ ` : ""}${f2}
2686
+ `, c2 = this.value ? a2 : s;
2687
+ switch (this.state) {
2688
+ case "submit": {
2689
+ const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
2690
+ return `${o2}${r2}${styleText2("dim", c2)}`;
2691
+ }
2692
+ case "cancel": {
2693
+ const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
2694
+ return `${o2}${r2}${styleText2(["strikethrough", "dim"], c2)}${e ? `
2695
+ ${styleText2("gray", S_BAR)}` : ""}`;
2696
+ }
2697
+ default: {
2698
+ const r2 = e ? `${styleText2("cyan", S_BAR)} ` : "", g2 = e ? styleText2("cyan", S_BAR_END) : "";
2699
+ return `${o2}${r2}${this.value ? `${styleText2("green", S_RADIO_ACTIVE)} ${a2}` : `${styleText2("dim", S_RADIO_INACTIVE)} ${styleText2("dim", a2)}`}${i.vertical ? e ? `
2700
+ ${styleText2("cyan", S_BAR)} ` : `
2701
+ ` : ` ${styleText2("dim", "/")} `}${this.value ? `${styleText2("dim", S_RADIO_INACTIVE)} ${styleText2("dim", s)}` : `${styleText2("green", S_RADIO_ACTIVE)} ${s}`}
2702
+ ${g2}
2703
+ `;
2704
+ }
2705
+ }
2706
+ }
2707
+ }).prompt();
2708
+ };
2709
+ var MULTISELECT_INSTRUCTIONS = [
2710
+ `${styleText2("dim", "\u2191/\u2193")} to navigate`,
2711
+ `${styleText2("dim", "Space:")} select`,
2712
+ `${styleText2("dim", "Enter:")} confirm`
2713
+ ];
2714
+ var log = {
2715
+ message: (s = [], {
2716
+ symbol: e = styleText2("gray", S_BAR),
2717
+ secondarySymbol: r2 = styleText2("gray", S_BAR),
2718
+ output: m2 = process.stdout,
2719
+ spacing: l2 = 1,
2720
+ withGuide: c2
2721
+ } = {}) => {
2722
+ const t2 = [], o2 = c2 ?? settings.withGuide, f2 = o2 ? r2 : "", O = o2 ? `${e} ` : "", u3 = o2 ? `${r2} ` : "";
2723
+ for (let i = 0;i < l2; i++)
2724
+ t2.push(f2);
2725
+ const g2 = Array.isArray(s) ? s : s.split(`
2726
+ `);
2727
+ if (g2.length > 0) {
2728
+ const [i, ...y] = g2;
2729
+ i.length > 0 ? t2.push(`${O}${i}`) : t2.push(o2 ? e : "");
2730
+ for (const p2 of y)
2731
+ p2.length > 0 ? t2.push(`${u3}${p2}`) : t2.push(o2 ? r2 : "");
2732
+ }
2733
+ m2.write(`${t2.join(`
2734
+ `)}
2735
+ `);
2736
+ },
2737
+ info: (s, e) => {
2738
+ log.message(s, { ...e, symbol: styleText2("blue", S_INFO) });
2739
+ },
2740
+ success: (s, e) => {
2741
+ log.message(s, { ...e, symbol: styleText2("green", S_SUCCESS) });
2742
+ },
2743
+ step: (s, e) => {
2744
+ log.message(s, { ...e, symbol: styleText2("green", S_STEP_SUBMIT) });
2745
+ },
2746
+ warn: (s, e) => {
2747
+ log.message(s, { ...e, symbol: styleText2("yellow", S_WARN) });
2748
+ },
2749
+ warning: (s, e) => {
2750
+ log.warn(s, e);
2751
+ },
2752
+ error: (s, e) => {
2753
+ log.message(s, { ...e, symbol: styleText2("red", S_ERROR) });
2754
+ }
2755
+ };
2756
+ var intro = (o2 = "", t2) => {
2757
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_START)} ` : "";
2758
+ i.write(`${e}${o2}
2759
+ `);
2760
+ };
2761
+ var outro = (o2 = "", t2) => {
2762
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR)}
2763
+ ${styleText2("gray", S_BAR_END)} ` : "";
2764
+ i.write(`${e}${o2}
2765
+
2766
+ `);
2767
+ };
2768
+ var W$1 = (o2) => o2;
2769
+ var C2 = (o2, e, s) => {
2770
+ const a2 = {
2771
+ hard: true,
2772
+ trim: false
2773
+ }, i = wrapAnsi(o2, e, a2).split(`
2774
+ `), 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);
2775
+ return wrapAnsi(o2, g2, a2);
2776
+ };
2777
+ var note = (o2 = "", e = "", s) => {
2778
+ 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(`
2779
+ `).map(c2), ""], n2 = dist_default2(e), t2 = Math.max(g2.reduce((m2, F) => {
2780
+ const O = dist_default2(F);
2781
+ return O > m2 ? O : m2;
2782
+ }, 0), n2) + 2, h2 = g2.map((m2) => `${styleText2("gray", S_BAR)} ${m2}${" ".repeat(t2 - dist_default2(m2))}${styleText2("gray", S_BAR)}`).join(`
2783
+ `), T3 = i ? `${styleText2("gray", S_BAR)}
2784
+ ` : "", l$1 = i ? S_CONNECT_LEFT : S_CORNER_BOTTOM_LEFT;
2785
+ 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)}
2786
+ ${h2}
2787
+ ${styleText2("gray", l$1 + S_BAR_H.repeat(t2 + 2) + S_CORNER_BOTTOM_RIGHT)}
2788
+ `);
2789
+ };
2790
+ var W = (l2) => styleText2("magenta", l2);
2791
+ var spinner = ({
2792
+ indicator: l2 = "dots",
2793
+ onCancel: h2,
2794
+ output: n2 = process.stdout,
2795
+ cancelMessage: G,
2796
+ errorMessage: O,
2797
+ frames: E = unicode ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"],
2798
+ delay: F = unicode ? 80 : 120,
2799
+ signal: m2,
2800
+ ...I
2801
+ } = {}) => {
2802
+ const u3 = isCI();
2803
+ let M2, T3, d = false, S = false, s = "", p2, w = performance.now();
2804
+ const x = getColumns(n2), k = I?.styleFrame ?? W, g2 = (e) => {
2805
+ const r2 = e > 1 ? O ?? settings.messages.error : G ?? settings.messages.cancel;
2806
+ S = e === 1, d && (a2(r2, e), S && typeof h2 == "function" && h2());
2807
+ }, f2 = () => g2(2), i = () => g2(1), A = () => {
2808
+ 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);
2809
+ }, H = () => {
2810
+ 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);
2811
+ }, y = () => {
2812
+ if (p2 === undefined)
2813
+ return;
2814
+ u3 && n2.write(`
2815
+ `);
2816
+ const r2 = wrapAnsi(p2, x, {
2817
+ hard: true,
2818
+ trim: false
2819
+ }).split(`
2820
+ `);
2821
+ 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());
2822
+ }, C3 = (e) => e.replace(/\.+$/, ""), _2 = (e) => {
2823
+ const r2 = (performance.now() - e) / 1000, t2 = Math.floor(r2 / 60), o2 = Math.floor(r2 % 60);
2824
+ return t2 > 0 ? `[${t2}m ${o2}s]` : `[${o2}s]`;
2825
+ }, N = I.withGuide ?? settings.withGuide, P2 = (e = "") => {
2826
+ d = true, M2 = block({ output: n2 }), s = C3(e), w = performance.now(), N && n2.write(`${styleText2("gray", S_BAR)}
2827
+ `);
2828
+ let r2 = 0, t2 = 0;
2829
+ A(), T3 = setInterval(() => {
2830
+ if (u3 && s === p2)
2831
+ return;
2832
+ y(), p2 = s;
2833
+ const o2 = k(E[r2]);
2834
+ let v;
2835
+ if (u3)
2836
+ v = `${o2} ${s}...`;
2837
+ else if (l2 === "timer")
2838
+ v = `${o2} ${s} ${_2(w)}`;
2839
+ else {
2840
+ const B = ".".repeat(Math.floor(t2)).slice(0, 3);
2841
+ v = `${o2} ${s}${B}`;
2842
+ }
2843
+ const j = wrapAnsi(v, x, {
2844
+ hard: true,
2845
+ trim: false
2846
+ });
2847
+ n2.write(j), r2 = r2 + 1 < E.length ? r2 + 1 : 0, t2 = t2 < 4 ? t2 + 0.125 : 0;
2848
+ }, F);
2849
+ }, a2 = (e = "", r2 = 0, t2 = false) => {
2850
+ if (!d)
2851
+ return;
2852
+ d = false, clearInterval(T3), y();
2853
+ const o2 = r2 === 0 ? styleText2("green", S_STEP_SUBMIT) : r2 === 1 ? styleText2("red", S_STEP_CANCEL) : styleText2("red", S_STEP_ERROR);
2854
+ s = e ?? s, t2 || (l2 === "timer" ? n2.write(`${o2} ${s} ${_2(w)}
2855
+ `) : n2.write(`${o2} ${s}
2856
+ `)), H(), M2();
2857
+ };
2858
+ return {
2859
+ start: P2,
2860
+ stop: (e = "") => a2(e, 0),
2861
+ message: (e = "") => {
2862
+ s = C3(e ?? s);
2863
+ },
2864
+ cancel: (e = "") => a2(e, 1),
2865
+ error: (e = "") => a2(e, 2),
2866
+ clear: () => a2("", 0, true),
2867
+ get isCancelled() {
2868
+ return S;
2869
+ }
2870
+ };
2871
+ };
2872
+ var u3 = {
2873
+ light: unicodeOr("\u2500", "-"),
2874
+ heavy: unicodeOr("\u2501", "="),
2875
+ block: unicodeOr("\u2588", "#")
2876
+ };
2877
+ var SELECT_INSTRUCTIONS = [
2878
+ `${styleText2("dim", "\u2191/\u2193")} to navigate`,
2879
+ `${styleText2("dim", "Enter:")} confirm`
2880
+ ];
2881
+ var i = `${styleText2("gray", S_BAR)} `;
2882
+
2883
+ // src/commands/drop.ts
2884
+ var import_picocolors = __toESM(require_picocolors(), 1);
2885
+ async function runDrop(config2, opts = {}) {
2886
+ intro(import_picocolors.default.bgCyan(import_picocolors.default.black(" @bungres/kit drop ")));
2887
+ const s = spinner();
2888
+ s.start("Loading schemas...");
2889
+ const schemas2 = await loadSchemas(config2.schema);
2890
+ if (schemas2.length === 0) {
2891
+ s.stop("No schemas found.");
2892
+ log.warn(import_picocolors.default.yellow("No table definitions found in schema files."));
2893
+ outro("Failed.");
2894
+ return;
2895
+ }
2896
+ s.stop(`Loaded ${schemas2.length} schemas.`);
2897
+ const ms = spinner();
2898
+ ms.start("Checking database tables...");
2899
+ const sql2 = new Bun.SQL(config2.dbUrl);
2900
+ try {
2901
+ const userSchema = config2.dbSchema;
2902
+ const existingUserTablesResult = await sql2.unsafe(`SELECT table_name FROM information_schema.tables WHERE table_schema = $1`, [userSchema]);
2903
+ const existingTableNames = new Set(existingUserTablesResult.map((r2) => r2.table_name));
2904
+ const migTableCheck = await sql2.unsafe(`SELECT EXISTS (
2905
+ SELECT 1 FROM information_schema.tables
2906
+ WHERE table_schema = $1 AND table_name = $2
2907
+ ) AS exists`, [config2.migrationsSchema, config2.migrationsTable]);
2908
+ const migrationTableExists = migTableCheck[0]?.exists ?? false;
2909
+ const pushTableCheck = await sql2.unsafe(`SELECT EXISTS (
2910
+ SELECT 1 FROM information_schema.tables
2911
+ WHERE table_schema = $1 AND table_name = $2
2912
+ ) AS exists`, [config2.migrationsSchema, "__bungres_push"]);
2913
+ const pushTableExists = pushTableCheck[0]?.exists ?? false;
2914
+ const tablesToDrop = schemas2.filter((sch) => existingTableNames.has(sch.config.name));
2915
+ if (tablesToDrop.length === 0 && !migrationTableExists && !pushTableExists) {
2916
+ ms.stop("Nothing to do.");
2917
+ log.success(import_picocolors.default.green("No tables to drop (they either don't exist or were already dropped)."));
2918
+ outro("Done.");
2919
+ return;
2920
+ }
2921
+ ms.stop("Database check complete.");
2922
+ const tableNamesToPrint = tablesToDrop.map((sch) => (sch.config.schema ? sch.config.schema + "." : "") + sch.config.name);
2923
+ if (migrationTableExists) {
2924
+ tableNamesToPrint.push(`${config2.migrationsSchema}.${config2.migrationsTable}`);
2925
+ }
2926
+ if (pushTableExists) {
2927
+ tableNamesToPrint.push(`${config2.migrationsSchema}.__bungres_push`);
2928
+ }
2929
+ if (!opts.force) {
2930
+ log.warn(import_picocolors.default.bgRed(import_picocolors.default.white(" \u26A0\uFE0F WARNING ")));
2931
+ log.message(import_picocolors.default.bold(import_picocolors.default.red("This will drop the following tables and ALL their data:")));
2932
+ for (const name of tableNamesToPrint) {
2933
+ log.info(import_picocolors.default.yellow(` - ${name}`));
2934
+ }
2935
+ const confirm2 = await confirm({
2936
+ message: "Are you sure you want to proceed?",
2937
+ initialValue: false
2938
+ });
2939
+ if (isCancel(confirm2) || !confirm2) {
2940
+ outro(import_picocolors.default.gray("Drop aborted."));
2941
+ return;
2942
+ }
2943
+ }
2944
+ const exSpinner = spinner();
2945
+ exSpinner.start("Dropping tables...");
2946
+ for (const entry of tablesToDrop) {
2947
+ const ddl = `DROP TABLE IF EXISTS "${entry.config.name}" CASCADE;`;
2948
+ await sql2.unsafe(ddl);
2949
+ log.step(import_picocolors.default.gray(`Dropped ${entry.config.name}`));
2950
+ }
2951
+ if (migrationTableExists) {
2952
+ const qualifiedMigTable = `"${config2.migrationsSchema}"."${config2.migrationsTable}"`;
2953
+ await sql2.unsafe(`DROP TABLE IF EXISTS ${qualifiedMigTable} CASCADE`);
2954
+ log.step(import_picocolors.default.gray(`Dropped ${config2.migrationsSchema}.${config2.migrationsTable}`));
2955
+ }
2956
+ if (pushTableExists) {
2957
+ await sql2.unsafe(`DROP TABLE IF EXISTS "${config2.migrationsSchema}"."__bungres_push" CASCADE`);
2958
+ log.step(import_picocolors.default.gray(`Dropped ${config2.migrationsSchema}.__bungres_push`));
2959
+ }
2960
+ exSpinner.stop("Tables dropped.");
2961
+ outro(import_picocolors.default.cyan("\u2728 Drop complete."));
2962
+ } catch (err) {
2963
+ log.error(import_picocolors.default.red(`Drop failed: ${err.message}`));
2964
+ outro("Failed.");
2965
+ } finally {
2966
+ await sql2.end();
2967
+ }
2968
+ }
2969
+
2970
+ // src/commands/migrate.ts
2971
+ import { join as join2, resolve as resolve2 } from "path";
2972
+ var import_picocolors2 = __toESM(require_picocolors(), 1);
2973
+ async function runMigrate(config2) {
2974
+ intro(import_picocolors2.default.bgCyan(import_picocolors2.default.black(" @bungres/kit migrate ")));
2975
+ const migrationsDir = resolve2(config2.out);
2976
+ const sql2 = new Bun.SQL(config2.dbUrl);
2977
+ const table2 = config2.migrationsTable;
2978
+ const schema = config2.migrationsSchema;
2979
+ const qualifiedTable = `"${schema}"."${table2}"`;
2980
+ const createSchema = `CREATE SCHEMA IF NOT EXISTS "${schema}";`;
2981
+ const createMigrationsTable = `
2982
+ CREATE TABLE IF NOT EXISTS ${qualifiedTable} (
2983
+ id SERIAL PRIMARY KEY,
2984
+ name TEXT NOT NULL UNIQUE,
2985
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
2986
+ );`.trim();
2987
+ const s = spinner();
2988
+ s.start("Checking pending migrations...");
2989
+ try {
2990
+ await sql2.unsafe(createSchema);
2991
+ await sql2.unsafe(createMigrationsTable);
2992
+ const glob = new Bun.Glob("*.sql");
2993
+ const files = [];
2994
+ for await (const file of glob.scan({ cwd: migrationsDir, absolute: false })) {
2995
+ files.push(file);
2996
+ }
2997
+ files.sort();
2998
+ if (files.length === 0) {
2999
+ s.stop("No files found.");
3000
+ log.warn(import_picocolors2.default.yellow(`No migration files found in ${migrationsDir}`));
3001
+ log.info(`Run ${import_picocolors2.default.green("bungres generate")} first.`);
3002
+ outro("Done.");
3003
+ return;
3004
+ }
3005
+ const applied = await sql2.unsafe(`SELECT name FROM ${qualifiedTable}`);
3006
+ const appliedSet = new Set(applied.map((r2) => r2.name));
3007
+ const pending = files.filter((f2) => !appliedSet.has(f2));
3008
+ if (pending.length === 0) {
3009
+ s.stop("Up to date.");
3010
+ log.success(import_picocolors2.default.green("Everything is up to date."));
3011
+ outro("Done.");
3012
+ return;
3013
+ }
3014
+ s.stop(`Found ${pending.length} pending migration(s).`);
3015
+ for (const file of pending) {
3016
+ const ms = spinner();
3017
+ ms.start(`Applying ${file}...`);
3018
+ const content = await Bun.file(join2(migrationsDir, file)).text();
3019
+ let upContent = content;
3020
+ const upMatch = content.match(/-- ==== UP ====([\s\S]*?)(?:-- ==== DOWN ====|$)/i);
3021
+ if (upMatch) {
3022
+ upContent = upMatch[1].trim();
3023
+ }
3024
+ if (config2.verbose) {
3025
+ log.info(import_picocolors2.default.gray(`-- ${file} --
3026
+ ${upContent}
3027
+ `));
3028
+ }
3029
+ await sql2.transaction(async (txSql) => {
3030
+ 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);
3031
+ for (const stmt of statements) {
3032
+ await txSql.unsafe(stmt + ";");
3033
+ }
3034
+ await txSql.unsafe(`INSERT INTO ${qualifiedTable} (name) VALUES ($1)`, [file]);
3035
+ });
3036
+ ms.stop(import_picocolors2.default.green(`\u2713 Applied ${file}`));
3037
+ }
3038
+ outro(import_picocolors2.default.cyan("\u2728 All migrations applied successfully."));
3039
+ } catch (err) {
3040
+ log.error(import_picocolors2.default.red(`Migration failed: ${err.message}`));
3041
+ outro("Failed.");
3042
+ } finally {
3043
+ await sql2.end();
3044
+ }
3045
+ }
3046
+
3047
+ // src/commands/fresh.ts
3048
+ async function runFresh(config2) {
3049
+ console.log("Dropping all tables...");
3050
+ await runDrop(config2, { force: true });
3051
+ console.log(`
3052
+ Re-running migrations...`);
3053
+ await runMigrate(config2);
3054
+ console.log(`
3055
+ Fresh complete.`);
3056
+ }
3057
+
3058
+ // src/commands/generate.ts
3059
+ import { join as join3, resolve as resolve3 } from "path";
3060
+ import { readdirSync } from "fs";
3061
+
3062
+ // src/differ.ts
3063
+ function diffSchemas(prev, next) {
3064
+ const statements = [];
3065
+ const summary = [];
3066
+ const warnings = [];
3067
+ const prevTables = new Set(Object.keys(prev));
3068
+ const nextTables = new Set(Object.keys(next));
3069
+ const newTableConfigs = [];
3070
+ for (const tableName of nextTables) {
3071
+ if (!prevTables.has(tableName)) {
3072
+ newTableConfigs.push(next[tableName]);
3073
+ }
3074
+ }
3075
+ for (const config2 of topoSortConfigs(newTableConfigs)) {
3076
+ statements.push(generateCreateTable(config2, true));
3077
+ summary.push(`CREATE TABLE ${config2.name}`);
3078
+ }
3079
+ for (const tableName of prevTables) {
3080
+ if (!nextTables.has(tableName)) {
3081
+ const config2 = prev[tableName];
3082
+ const tbl = config2.schema ? `"${config2.schema}"."${tableName}"` : `"${tableName}"`;
3083
+ statements.push(`DROP TABLE IF EXISTS ${tbl};`);
3084
+ summary.push(`DROP TABLE ${tableName}`);
3085
+ warnings.push(`Data loss warning: Table '${tableName}' will be permanently deleted.`);
3086
+ }
3087
+ }
3088
+ for (const tableName of nextTables) {
3089
+ if (!prevTables.has(tableName))
3090
+ continue;
3091
+ const prevConfig = prev[tableName];
3092
+ const nextConfig = next[tableName];
3093
+ const prevCols = prevConfig.columns;
3094
+ const nextCols = nextConfig.columns;
3095
+ const prevColNames = new Set(Object.keys(prevCols));
3096
+ const nextColNames = new Set(Object.keys(nextCols));
3097
+ for (const key of nextColNames) {
3098
+ if (!prevColNames.has(key)) {
3099
+ const col2 = nextCols[key];
3100
+ statements.push(generateAddColumn(tableName, nextConfig.schema, key, col2));
3101
+ summary.push(`ALTER TABLE ${tableName} ADD COLUMN ${col2.name}`);
3102
+ }
3103
+ }
3104
+ for (const key of prevColNames) {
3105
+ if (!nextColNames.has(key)) {
1561
3106
  const col2 = prevCols[key];
1562
3107
  statements.push(generateDropColumn(tableName, prevConfig.schema, col2.name));
1563
3108
  summary.push(`ALTER TABLE ${tableName} DROP COLUMN ${col2.name}`);
@@ -1594,20 +3139,20 @@ function diffSchemas(prev, next) {
1594
3139
  }
1595
3140
  }
1596
3141
  }
1597
- const prevIdxNames = new Set((prevConfig.indexes ?? []).map((i) => i.name ?? `idx_${tableName}_${i.columns.join("_")}`));
3142
+ const prevIdxNames = new Set((prevConfig.indexes ?? []).map((i2) => i2.name ?? `idx_${tableName}_${i2.columns.join("_")}`));
1598
3143
  for (const idx of nextConfig.indexes ?? []) {
1599
3144
  const idxName = idx.name ?? `idx_${tableName}_${idx.columns.join("_")}`;
1600
3145
  if (!prevIdxNames.has(idxName)) {
1601
3146
  const tbl = nextConfig.schema ? `"${nextConfig.schema}"."${tableName}"` : `"${tableName}"`;
1602
3147
  const unique2 = idx.unique ? "UNIQUE " : "";
1603
3148
  const using = idx.using ? ` USING ${idx.using.toUpperCase()}` : "";
1604
- const cols = idx.columns.map((c) => `"${c}"`).join(", ");
3149
+ const cols = idx.columns.map((c2) => `"${c2}"`).join(", ");
1605
3150
  const where = idx.where ? ` WHERE ${idx.where}` : "";
1606
3151
  statements.push(`CREATE ${unique2}INDEX IF NOT EXISTS "${idxName}" ON ${tbl}${using} (${cols})${where};`);
1607
3152
  summary.push(`CREATE INDEX ${idxName} ON ${tableName}`);
1608
3153
  }
1609
3154
  }
1610
- const nextIdxNames = new Set((nextConfig.indexes ?? []).map((i) => i.name ?? `idx_${tableName}_${i.columns.join("_")}`));
3155
+ const nextIdxNames = new Set((nextConfig.indexes ?? []).map((i2) => i2.name ?? `idx_${tableName}_${i2.columns.join("_")}`));
1611
3156
  for (const idx of prevConfig.indexes ?? []) {
1612
3157
  const idxName = idx.name ?? `idx_${tableName}_${idx.columns.join("_")}`;
1613
3158
  if (!nextIdxNames.has(idxName)) {
@@ -1619,11 +3164,11 @@ function diffSchemas(prev, next) {
1619
3164
  return { statements, summary, warnings };
1620
3165
  }
1621
3166
  function topoSortConfigs(tables) {
1622
- const byName = new Map(tables.map((t) => [t.name, t]));
3167
+ const byName = new Map(tables.map((t2) => [t2.name, t2]));
1623
3168
  const visited = new Set;
1624
3169
  const result2 = [];
1625
3170
  function deps(config2) {
1626
- return Object.values(config2.columns).map((col2) => col2.references?.table).filter((t) => t !== undefined && byName.has(t) && t !== config2.name);
3171
+ return Object.values(config2.columns).map((col2) => col2.references?.table).filter((t2) => t2 !== undefined && byName.has(t2) && t2 !== config2.name);
1627
3172
  }
1628
3173
  function visit(name) {
1629
3174
  if (visited.has(name))
@@ -1688,21 +3233,40 @@ function formatDefault(value, dataType) {
1688
3233
  }
1689
3234
 
1690
3235
  // src/commands/generate.ts
1691
- var SNAPSHOT_FILE = ".snapshot.json";
3236
+ var import_picocolors3 = __toESM(require_picocolors(), 1);
1692
3237
  async function runGenerate(config2, name) {
1693
- console.log("@bungres/kit generate: loading schemas...");
3238
+ intro(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" @bungres/kit generate ")));
3239
+ const s = spinner();
3240
+ s.start("Loading schemas...");
1694
3241
  const schemas2 = await loadSchemas(config2.schema);
1695
3242
  if (schemas2.length === 0) {
1696
- console.warn(colorize("No table definitions found. Check your schema glob pattern.", "yellow"));
3243
+ s.stop("No table definitions found.");
3244
+ log.warn(import_picocolors3.default.yellow("Check your schema glob pattern."));
3245
+ outro("Failed.");
1697
3246
  return;
1698
3247
  }
3248
+ s.stop(`Loaded ${schemas2.length} schemas.`);
1699
3249
  const migrationsDir = resolve3(config2.out);
3250
+ const metaDir = join3(migrationsDir, "meta");
1700
3251
  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());
3252
+ await Bun.$`mkdir -p ${metaDir}`.quiet();
3253
+ const currentSnapshot = Object.fromEntries(schemas2.map((s2) => [s2.config.name, s2.config]));
3254
+ let prevSnapshot = {};
3255
+ let isFirstMigration = true;
3256
+ try {
3257
+ const files = readdirSync(metaDir).filter((f2) => f2.endsWith("_snapshot.json")).sort();
3258
+ if (files.length > 0) {
3259
+ const latest = files[files.length - 1];
3260
+ prevSnapshot = JSON.parse(await Bun.file(join3(metaDir, latest)).text());
3261
+ isFirstMigration = false;
3262
+ } else {
3263
+ const legacyFile2 = Bun.file(join3(migrationsDir, ".snapshot.json"));
3264
+ if (await legacyFile2.exists()) {
3265
+ prevSnapshot = JSON.parse(await legacyFile2.text());
3266
+ isFirstMigration = false;
3267
+ }
3268
+ }
3269
+ } catch (e) {}
1706
3270
  const now = new Date;
1707
3271
  const yyyy = now.getUTCFullYear();
1708
3272
  const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
@@ -1713,60 +3277,86 @@ async function runGenerate(config2, name) {
1713
3277
  const prefix = `${yyyy}_${mm}_${dd}_${HH}${MM}${SS}`;
1714
3278
  const filename = name ? `${prefix}_${name}.sql` : `${prefix}.sql`;
1715
3279
  const outPath = join3(migrationsDir, filename);
1716
- let statements;
3280
+ let upStatements;
3281
+ let downStatements;
1717
3282
  let summary;
1718
3283
  let warnings = [];
1719
3284
  if (isFirstMigration) {
1720
3285
  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}`);
3286
+ upStatements = sorted.flatMap((entry) => [
3287
+ `-- ${entry.exportName}`,
3288
+ generateCreateTable(entry.config, true),
3289
+ ``
3290
+ ]);
3291
+ downStatements = [...sorted].reverse().flatMap((entry) => {
3292
+ const tbl = entry.config.schema ? `"${entry.config.schema}"."${entry.config.name}"` : `"${entry.config.name}"`;
3293
+ return [`DROP TABLE IF EXISTS ${tbl};`];
3294
+ });
3295
+ summary = sorted.map((s2) => `CREATE TABLE ${s2.config.name}`);
1729
3296
  } 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"));
3297
+ const upDiff = diffSchemas(prevSnapshot, currentSnapshot);
3298
+ if (upDiff.statements.length === 0) {
3299
+ log.warn(import_picocolors3.default.yellow("No schema changes detected. Nothing to generate."));
3300
+ outro("Done.");
1734
3301
  return;
1735
3302
  }
1736
- statements = diff.statements;
1737
- summary = diff.summary;
1738
- warnings = diff.warnings;
3303
+ upStatements = upDiff.statements;
3304
+ summary = upDiff.summary;
3305
+ warnings = upDiff.warnings;
3306
+ const downDiff = diffSchemas(currentSnapshot, prevSnapshot);
3307
+ downStatements = downDiff.statements;
3308
+ }
3309
+ log.message(import_picocolors3.default.bold("Schema changes detected:"));
3310
+ for (const s2 of summary) {
3311
+ if (s2.startsWith("CREATE") || s2.startsWith("ALTER TABLE") && s2.includes("ADD")) {
3312
+ log.success(import_picocolors3.default.green(` + ${s2}`));
3313
+ } else if (s2.startsWith("DROP") || s2.startsWith("ALTER TABLE") && s2.includes("DROP")) {
3314
+ log.error(import_picocolors3.default.red(` - ${s2}`));
3315
+ } else {
3316
+ log.info(import_picocolors3.default.blue(` ~ ${s2}`));
3317
+ }
1739
3318
  }
3319
+ if (warnings.length > 0) {
3320
+ log.warn(import_picocolors3.default.bgRed(import_picocolors3.default.white(" \u26A0\uFE0F DATA LOSS DETECTED ")));
3321
+ for (const w of warnings)
3322
+ log.warn(import_picocolors3.default.red(` ! ${w}`));
3323
+ }
3324
+ const shouldGenerate = await confirm({
3325
+ message: "Generate this migration?",
3326
+ initialValue: true
3327
+ });
3328
+ if (isCancel(shouldGenerate) || !shouldGenerate) {
3329
+ outro(import_picocolors3.default.gray("Generation cancelled."));
3330
+ return;
3331
+ }
3332
+ s.start("Writing files...");
1740
3333
  const lines = [
1741
3334
  `-- Migration: ${filename}`,
1742
3335
  `-- Generated by @bungres/kit at ${new Date().toISOString()}`,
1743
3336
  `-- Changes: ${summary.join(", ")}`,
1744
3337
  ``,
1745
- ...statements
3338
+ `-- ==== UP ====`,
3339
+ ...upStatements,
3340
+ ``,
3341
+ `-- ==== DOWN ====`,
3342
+ ...downStatements,
3343
+ ``
1746
3344
  ];
1747
3345
  await Bun.write(outPath, lines.join(`
1748
3346
  `));
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"));
3347
+ const metaPath = join3(metaDir, `${prefix}_snapshot.json`);
3348
+ await Bun.write(metaPath, JSON.stringify(currentSnapshot, null, 2));
3349
+ const legacyFile = Bun.file(join3(migrationsDir, ".snapshot.json"));
3350
+ if (await legacyFile.exists()) {
3351
+ await Bun.$`rm ${legacyFile.name}`.quiet();
3352
+ }
3353
+ s.stop(`Generated ${import_picocolors3.default.cyan(filename)}`);
3354
+ outro(`Run ${import_picocolors3.default.green("bungres migrate")} to apply it.`);
1765
3355
  }
1766
3356
  function topoSort(schemas2) {
1767
3357
  const byName = new Map(schemas2.map((s) => [s.config.name, s]));
1768
3358
  function deps(config2) {
1769
- return Object.values(config2.columns).map((col2) => col2.references?.table).filter((t) => t !== undefined && byName.has(t) && t !== config2.name);
3359
+ return Object.values(config2.columns).map((col2) => col2.references?.table).filter((t2) => t2 !== undefined && byName.has(t2) && t2 !== config2.name);
1770
3360
  }
1771
3361
  const visited = new Set;
1772
3362
  const result2 = [];
@@ -1790,18 +3380,25 @@ function topoSort(schemas2) {
1790
3380
  import { existsSync } from "fs";
1791
3381
  import { mkdir } from "fs/promises";
1792
3382
  import { join as join4 } from "path";
3383
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
1793
3384
  async function runInit(cwd = process.cwd()) {
3385
+ intro(import_picocolors4.default.bgCyan(import_picocolors4.default.black(" @bungres/kit init ")));
1794
3386
  const configPath = join4(cwd, "bungres.config.ts");
1795
3387
  const dbDir = join4(cwd, "src", "db");
1796
3388
  if (existsSync(configPath)) {
1797
- console.log("Config file already exists at bungres.config.ts");
3389
+ log.warn(import_picocolors4.default.yellow("Config file already exists at bungres.config.ts"));
3390
+ outro("Failed.");
1798
3391
  return;
1799
3392
  }
3393
+ const s = spinner();
3394
+ s.start("Creating project structure...");
1800
3395
  try {
1801
3396
  await mkdir(dbDir, { recursive: true });
1802
- console.log("Created src/db directory");
3397
+ log.step(`Created ${import_picocolors4.default.cyan("src/db")} directory`);
1803
3398
  } catch (e) {
1804
- console.error("Failed to create src/db directory:", e);
3399
+ s.stop("Failed");
3400
+ log.error(`Failed to create src/db directory: ${e}`);
3401
+ outro("Failed.");
1805
3402
  return;
1806
3403
  }
1807
3404
  const configContent = `import { defineConfig } from "@bungres/kit";
@@ -1816,9 +3413,11 @@ export default defineConfig({
1816
3413
  `;
1817
3414
  try {
1818
3415
  await Bun.write(configPath, configContent);
1819
- console.log("Created bungres.config.ts");
3416
+ log.step(`Created ${import_picocolors4.default.cyan("bungres.config.ts")}`);
1820
3417
  } catch (e) {
1821
- console.error("Failed to create config file:", e);
3418
+ s.stop("Failed");
3419
+ log.error(`Failed to create config file: ${e}`);
3420
+ outro("Failed.");
1822
3421
  return;
1823
3422
  }
1824
3423
  const schemaContent = `import {
@@ -1842,9 +3441,9 @@ export const users = table("users", {
1842
3441
  `;
1843
3442
  try {
1844
3443
  await Bun.write(join4(dbDir, "schema.ts"), schemaContent);
1845
- console.log("Created src/db/schema.ts with example table");
3444
+ log.step(`Created ${import_picocolors4.default.cyan("src/db/schema.ts")} with example table`);
1846
3445
  } catch (e) {
1847
- console.error("Failed to create schema file:", e);
3446
+ log.error(`Failed to create schema file: ${e}`);
1848
3447
  }
1849
3448
  const clientContent = `import { bungres } from "@bungres/orm";
1850
3449
  import * as schema from "./schema";
@@ -1855,18 +3454,17 @@ export const db = bungres({ url, schema });
1855
3454
  `;
1856
3455
  try {
1857
3456
  await Bun.write(join4(dbDir, "client.ts"), clientContent);
1858
- console.log("Created src/db/client.ts");
3457
+ log.step(`Created ${import_picocolors4.default.cyan("src/db/client.ts")}`);
1859
3458
  } 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");
3459
+ log.error(`Failed to create client file: ${e}`);
3460
+ }
3461
+ s.stop("Project initialized.");
3462
+ const nextSteps = `1. Set DATABASE_URL in your .env file
3463
+ 2. Edit src/db/schema.ts to define your tables
3464
+ 3. Run ${import_picocolors4.default.green("bungres generate")} to create migrations
3465
+ 4. Run ${import_picocolors4.default.green("bungres migrate")} to apply migrations`;
3466
+ note(nextSteps, "Next steps");
3467
+ outro(import_picocolors4.default.cyan("\u2728 Bungres project initialized!"));
1870
3468
  }
1871
3469
 
1872
3470
  // src/commands/pull.ts
@@ -1936,13 +3534,13 @@ function groupByTable(columns2, constraints, indexes) {
1936
3534
  map.set(col2.table_name, {
1937
3535
  tableName: col2.table_name,
1938
3536
  columns: [],
1939
- indexes: indexes.filter((i) => i.tablename === col2.table_name)
3537
+ indexes: indexes.filter((i2) => i2.tablename === col2.table_name)
1940
3538
  });
1941
3539
  }
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");
3540
+ const tableConstraints = constraints.filter((c2) => c2.table_name === col2.table_name && c2.column_name === col2.column_name);
3541
+ const pkConstraint = tableConstraints.find((c2) => c2.constraint_type === "PRIMARY KEY");
3542
+ const uniqueConstraint = tableConstraints.find((c2) => c2.constraint_type === "UNIQUE");
3543
+ const fkConstraint = tableConstraints.find((c2) => c2.constraint_type === "FOREIGN KEY");
1946
3544
  map.get(col2.table_name).columns.push({
1947
3545
  name: col2.column_name,
1948
3546
  dataType: col2.data_type,
@@ -1991,19 +3589,19 @@ function generateSchemaTS(tableMap, dbSchema) {
1991
3589
  if (table2.indexes.length > 0) {
1992
3590
  const idxLines = [];
1993
3591
  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, "");
3592
+ const m2 = idx.indexdef.match(/CREATE (UNIQUE )?INDEX (.+) ON (.+) USING (\w+) \((.+)\)(?: WHERE (.+))?/i);
3593
+ if (m2) {
3594
+ const isUnique = !!m2[1];
3595
+ const name = m2[2].trim().replace(/^"|"$/g, "");
1998
3596
  if (name.endsWith("_pkey") || name.endsWith("_key"))
1999
3597
  continue;
2000
- const using = m[4].toLowerCase();
2001
- const cols = m[5].split(",").map((c) => `"${c.trim().replace(/^"|"$/g, "")}"`);
3598
+ const using = m2[4].toLowerCase();
3599
+ const cols = m2[5].split(",").map((c2) => `"${c2.trim().replace(/^"|"$/g, "")}"`);
2002
3600
  let idxStr = `{ name: "${name}", columns: [${cols.join(", ")}], using: "${using}"`;
2003
3601
  if (isUnique)
2004
3602
  idxStr += `, unique: true`;
2005
- if (m[6])
2006
- idxStr += `, where: \`${m[6].trim()}\``;
3603
+ if (m2[6])
3604
+ idxStr += `, where: \`${m2[6].trim()}\``;
2007
3605
  idxStr += ` }`;
2008
3606
  idxLines.push(idxStr);
2009
3607
  }
@@ -2122,19 +3720,26 @@ function pgTypeToBungresBuilderName(col2) {
2122
3720
  return "text";
2123
3721
  }
2124
3722
  function toCamelCase(str) {
2125
- return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
3723
+ return str.replace(/_([a-z])/g, (_2, c2) => c2.toUpperCase());
2126
3724
  }
2127
3725
 
2128
3726
  // src/commands/push.ts
2129
- import * as fs from "fs";
3727
+ var import_picocolors5 = __toESM(require_picocolors(), 1);
2130
3728
  async function runPush(config2, opts = {}) {
2131
- console.log("@bungres/kit push: loading schemas...");
3729
+ intro(import_picocolors5.default.bgCyan(import_picocolors5.default.black(" @bungres/kit push ")));
3730
+ const s = spinner();
3731
+ s.start("Loading schemas...");
2132
3732
  const schemas2 = await loadSchemas(config2.schema);
2133
3733
  if (schemas2.length === 0) {
2134
- console.warn("No table definitions found. Check your schema glob pattern.");
3734
+ s.stop("No schemas found.");
3735
+ log.warn(import_picocolors5.default.yellow("No table definitions found. Check your schema glob pattern."));
3736
+ outro("Failed.");
2135
3737
  return;
2136
3738
  }
3739
+ s.stop(`Loaded ${schemas2.length} schemas.`);
2137
3740
  const sql2 = new Bun.SQL(config2.dbUrl);
3741
+ const ms = spinner();
3742
+ ms.start("Computing diff from database...");
2138
3743
  try {
2139
3744
  await sql2.unsafe(`CREATE SCHEMA IF NOT EXISTS "${config2.migrationsSchema}";`);
2140
3745
  const qualifiedPush = `"${config2.migrationsSchema}"."__bungres_push"`;
@@ -2149,56 +3754,62 @@ async function runPush(config2, opts = {}) {
2149
3754
  if (rows.length > 0) {
2150
3755
  prevSnapshot = typeof rows[0].snapshot === "string" ? JSON.parse(rows[0].snapshot) : rows[0].snapshot;
2151
3756
  }
2152
- const currentSnapshot = Object.fromEntries(schemas2.map((s) => [s.config.name, s.config]));
3757
+ const currentSnapshot = Object.fromEntries(schemas2.map((schemaEntry) => [schemaEntry.config.name, schemaEntry.config]));
2153
3758
  const diff = diffSchemas(prevSnapshot, currentSnapshot);
2154
3759
  if (diff.statements.length === 0) {
2155
- console.log(`
2156
- No schema changes detected. Database is up to date.`);
3760
+ ms.stop("Up to date.");
3761
+ log.success(import_picocolors5.default.green("No schema changes detected. Database is up to date."));
3762
+ outro("Done.");
2157
3763
  return;
2158
3764
  }
2159
- console.log(`
2160
- Changes to apply:`);
2161
- for (const s of diff.summary)
2162
- console.log(` + ${s}`);
3765
+ ms.stop(`Computed ${diff.statements.length} statements.`);
3766
+ log.message(import_picocolors5.default.bold("Changes to apply:"));
3767
+ for (const change of diff.summary) {
3768
+ if (change.startsWith("CREATE") || change.startsWith("ALTER TABLE") && change.includes("ADD")) {
3769
+ log.success(import_picocolors5.default.green(` + ${change}`));
3770
+ } else if (change.startsWith("DROP") || change.startsWith("ALTER TABLE") && change.includes("DROP")) {
3771
+ log.error(import_picocolors5.default.red(` - ${change}`));
3772
+ } else {
3773
+ log.info(import_picocolors5.default.blue(` ~ ${change}`));
3774
+ }
3775
+ }
2163
3776
  if (diff.warnings && diff.warnings.length > 0) {
2164
- console.warn(`
2165
- \u26A0\uFE0F WARNING: Data Loss Detected!`);
3777
+ log.warn(import_picocolors5.default.bgRed(import_picocolors5.default.white(" \u26A0\uFE0F DATA LOSS DETECTED ")));
2166
3778
  for (const w of diff.warnings)
2167
- console.warn(` ! ${w}`);
2168
- console.warn(`
2169
- These changes will be immediately executed against the database!`);
3779
+ log.warn(import_picocolors5.default.red(` ! ${w}`));
3780
+ log.warn(import_picocolors5.default.yellow("These changes will be immediately executed against the database!"));
2170
3781
  }
2171
3782
  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.");
3783
+ const confirm2 = await confirm({
3784
+ message: "Are you sure you want to push these changes?",
3785
+ initialValue: true
3786
+ });
3787
+ if (isCancel(confirm2) || !confirm2) {
3788
+ outro(import_picocolors5.default.gray("Push aborted."));
2177
3789
  return;
2178
3790
  }
2179
3791
  }
2180
- console.log(`
2181
- Pushing changes...`);
3792
+ const exSpinner = spinner();
3793
+ exSpinner.start("Pushing changes...");
2182
3794
  for (const stmt of diff.statements) {
2183
3795
  if (config2.verbose) {
2184
- console.log(`-- ${stmt}`);
3796
+ log.info(import_picocolors5.default.gray(`-- ${stmt}`));
2185
3797
  }
2186
3798
  await sql2.unsafe(stmt);
2187
3799
  }
2188
3800
  await sql2.unsafe(`INSERT INTO ${qualifiedPush} (snapshot) VALUES ($1);`, [JSON.stringify(currentSnapshot)]);
2189
- console.log(`
2190
- Push complete.`);
3801
+ exSpinner.stop(import_picocolors5.default.green("Changes applied successfully."));
3802
+ outro(import_picocolors5.default.cyan("\u2728 Push complete."));
3803
+ } catch (err) {
3804
+ log.error(import_picocolors5.default.red(`Push failed: ${err.message}`));
3805
+ outro("Failed.");
2191
3806
  } finally {
2192
3807
  await sql2.end();
2193
3808
  }
2194
3809
  }
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
3810
 
2201
3811
  // src/commands/refresh.ts
3812
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
2202
3813
  async function runRefresh(config2) {
2203
3814
  const schemas2 = await loadSchemas(config2.schema);
2204
3815
  if (schemas2.length === 0) {
@@ -2212,55 +3823,210 @@ async function runRefresh(config2) {
2212
3823
  for (const entry of schemas2) {
2213
3824
  const ddl = `TRUNCATE TABLE "${entry.config.name}" CASCADE;`;
2214
3825
  await sql2.unsafe(ddl);
2215
- console.log(colorize(` \u2713 truncated ${entry.config.name}`, "green"));
3826
+ console.log(import_picocolors6.default.green(` \u2713 truncated ${entry.config.name}`));
3827
+ }
3828
+ console.log(import_picocolors6.default.green(`
3829
+ Refresh complete. All tables are now empty.`));
3830
+ } finally {
3831
+ await sql2.end();
3832
+ }
3833
+ }
3834
+
3835
+ // src/commands/rollback.ts
3836
+ import { join as join6, resolve as resolve5 } from "path";
3837
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
3838
+ async function runRollback(config2) {
3839
+ intro(import_picocolors7.default.bgCyan(import_picocolors7.default.black(" @bungres/kit rollback ")));
3840
+ const migrationsDir = resolve5(config2.out);
3841
+ const sql2 = new Bun.SQL(config2.dbUrl);
3842
+ const table2 = config2.migrationsTable;
3843
+ const schema = config2.migrationsSchema;
3844
+ const qualifiedTable = `"${schema}"."${table2}"`;
3845
+ const s = spinner();
3846
+ s.start("Checking applied migrations...");
3847
+ try {
3848
+ const applied = await sql2.unsafe(`SELECT name FROM ${qualifiedTable} ORDER BY name DESC LIMIT 1`);
3849
+ if (applied.length === 0) {
3850
+ s.stop("No migrations to rollback.");
3851
+ log.warn(import_picocolors7.default.yellow("No applied migrations found in the database."));
3852
+ outro("Done.");
3853
+ return;
2216
3854
  }
2217
- console.log(colorize(`
2218
- Refresh complete. All tables are now empty.`, "green"));
3855
+ const lastMigration = applied[0].name;
3856
+ s.stop(`Found migration to rollback: ${import_picocolors7.default.cyan(lastMigration)}`);
3857
+ const shouldRollback = await confirm({
3858
+ message: `Are you sure you want to rollback ${import_picocolors7.default.cyan(lastMigration)}?`,
3859
+ initialValue: true
3860
+ });
3861
+ if (isCancel(shouldRollback) || !shouldRollback) {
3862
+ outro(import_picocolors7.default.gray("Rollback cancelled."));
3863
+ return;
3864
+ }
3865
+ const ms = spinner();
3866
+ ms.start(`Rolling back ${lastMigration}...`);
3867
+ const content = await Bun.file(join6(migrationsDir, lastMigration)).text();
3868
+ let downContent = "";
3869
+ const downMatch = content.match(/-- ==== DOWN ====([\s\S]*)/i);
3870
+ if (downMatch) {
3871
+ downContent = downMatch[1].trim();
3872
+ } else {
3873
+ ms.stop("Failed.");
3874
+ log.error(import_picocolors7.default.red(`No '-- ==== DOWN ====' section found in ${lastMigration}. Cannot rollback safely.`));
3875
+ outro("Failed.");
3876
+ return;
3877
+ }
3878
+ if (!downContent) {
3879
+ log.warn(import_picocolors7.default.yellow(`Down section is empty in ${lastMigration}. Nothing to execute.`));
3880
+ }
3881
+ if (config2.verbose) {
3882
+ log.info(import_picocolors7.default.gray(`-- ${lastMigration} (DOWN) --
3883
+ ${downContent}
3884
+ `));
3885
+ }
3886
+ await sql2.transaction(async (txSql) => {
3887
+ if (downContent) {
3888
+ 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);
3889
+ for (const stmt of statements) {
3890
+ await txSql.unsafe(stmt + ";");
3891
+ }
3892
+ }
3893
+ await txSql.unsafe(`DELETE FROM ${qualifiedTable} WHERE name = $1`, [lastMigration]);
3894
+ });
3895
+ ms.stop(import_picocolors7.default.green(`\u2713 Rolled back ${lastMigration}`));
3896
+ outro(import_picocolors7.default.cyan("\u2728 Rollback successful."));
3897
+ } catch (err) {
3898
+ log.error(import_picocolors7.default.red(`Rollback failed: ${err.message}`));
3899
+ outro("Failed.");
2219
3900
  } finally {
2220
3901
  await sql2.end();
2221
3902
  }
2222
3903
  }
2223
3904
 
2224
3905
  // src/commands/seed.ts
2225
- import { resolve as resolve5 } from "path";
3906
+ import { resolve as resolve6 } from "path";
3907
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
2226
3908
  async function runSeed(config2) {
2227
- if (!config2.seed) {
2228
- console.log(colorize(`No seed file configured in bungres.config.ts`, "yellow"));
3909
+ intro(import_picocolors8.default.bgCyan(import_picocolors8.default.black(" @bungres/kit seed ")));
3910
+ if (config2.seed) {
3911
+ const seedPath = resolve6(process.cwd(), config2.seed);
3912
+ const file = Bun.file(seedPath);
3913
+ if (await file.exists()) {
3914
+ const s2 = spinner();
3915
+ s2.start(`Running seeder: ${config2.seed}...`);
3916
+ const proc = Bun.spawn(["bun", "run", seedPath], {
3917
+ cwd: process.cwd(),
3918
+ stdout: "inherit",
3919
+ stderr: "inherit",
3920
+ env: { ...Bun.env, DATABASE_URL: config2.dbUrl }
3921
+ });
3922
+ const exitCode = await proc.exited;
3923
+ if (exitCode === 0) {
3924
+ s2.stop("Seed complete.");
3925
+ outro(import_picocolors8.default.cyan("\u2728 Seeding successful."));
3926
+ } else {
3927
+ s2.stop("Seed failed.");
3928
+ log.error(import_picocolors8.default.red(`Seed failed with exit code ${exitCode}.`));
3929
+ outro("Failed.");
3930
+ process.exit(exitCode);
3931
+ }
3932
+ return;
3933
+ }
3934
+ }
3935
+ log.info(import_picocolors8.default.blue("No custom seed script found. Initiating Auto-Seeder..."));
3936
+ let faker;
3937
+ try {
3938
+ const fakerModule = await import("@faker-js/faker");
3939
+ faker = fakerModule.faker;
3940
+ } catch (err) {
3941
+ log.error(import_picocolors8.default.red("Auto-seeder requires @faker-js/faker."));
3942
+ log.message(`Please install it: ${import_picocolors8.default.green("bun add -d @faker-js/faker")}`);
3943
+ outro("Failed.");
2229
3944
  return;
2230
3945
  }
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);
3946
+ const s = spinner();
3947
+ s.start("Loading schemas for auto-seeding...");
3948
+ const schemas2 = await loadSchemas(config2.schema);
3949
+ if (schemas2.length === 0) {
3950
+ s.stop("No schemas.");
3951
+ outro("Nothing to seed.");
3952
+ return;
2236
3953
  }
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);
3954
+ const tableConfigs = schemas2.map((s2) => s2.config);
3955
+ const sorted = topoSortConfigs(tableConfigs);
3956
+ s.stop(`Loaded ${sorted.length} tables in topological order.`);
3957
+ const sql2 = new Bun.SQL(config2.dbUrl);
3958
+ const insertedData = {};
3959
+ try {
3960
+ for (const table2 of sorted) {
3961
+ const ms = spinner();
3962
+ ms.start(`Seeding ${table2.name}...`);
3963
+ const rowsToInsert = 10;
3964
+ const rows = [];
3965
+ for (let i2 = 0;i2 < rowsToInsert; i2++) {
3966
+ const row = {};
3967
+ for (const [colName2, col2] of Object.entries(table2.columns)) {
3968
+ if (col2.references) {
3969
+ const parentTable = col2.references.table;
3970
+ const parentRows = insertedData[parentTable];
3971
+ if (parentRows && parentRows.length > 0) {
3972
+ const parentRow = parentRows[Math.floor(Math.random() * parentRows.length)];
3973
+ row[col2.name] = parentRow[col2.references.column];
3974
+ continue;
3975
+ }
3976
+ }
3977
+ if (col2.dataType === "uuid")
3978
+ row[col2.name] = faker.string.uuid();
3979
+ else if (col2.dataType === "varchar" || col2.dataType === "text")
3980
+ row[col2.name] = faker.lorem.word();
3981
+ else if (col2.dataType === "integer")
3982
+ row[col2.name] = faker.number.int({ min: 1, max: 1000 });
3983
+ else if (col2.dataType === "boolean")
3984
+ row[col2.name] = faker.datatype.boolean();
3985
+ else if (col2.dataType === "timestamptz" || col2.dataType === "timestamp")
3986
+ row[col2.name] = faker.date.recent().toISOString();
3987
+ else if (col2.dataType === "jsonb" || col2.dataType === "json")
3988
+ row[col2.name] = JSON.stringify({ key: faker.lorem.word() });
3989
+ else
3990
+ row[col2.name] = null;
3991
+ }
3992
+ rows.push(row);
3993
+ }
3994
+ const insertedRows = await sql2.transaction(async (tx) => {
3995
+ const results = [];
3996
+ for (const row of rows) {
3997
+ const keys = Object.keys(row).map((k) => `"${k}"`);
3998
+ const values = Object.values(row);
3999
+ const placeholders = values.map((_2, i2) => `$${i2 + 1}`);
4000
+ const q = `INSERT INTO "${table2.schema || "public"}"."${table2.name}" (${keys.join(", ")}) VALUES (${placeholders.join(", ")}) RETURNING *`;
4001
+ const res = await tx.unsafe(q, values);
4002
+ results.push(res[0]);
4003
+ }
4004
+ return results;
4005
+ });
4006
+ insertedData[table2.name] = insertedRows;
4007
+ ms.stop(import_picocolors8.default.green(`\u2713 Seeded ${rowsToInsert} rows into ${table2.name}`));
4008
+ }
4009
+ outro(import_picocolors8.default.cyan("\u2728 Auto-seeding complete."));
4010
+ } catch (err) {
4011
+ log.error(import_picocolors8.default.red(`Auto-seeding failed: ${err.message}`));
4012
+ outro("Failed.");
4013
+ } finally {
4014
+ await sql2.end();
2253
4015
  }
2254
4016
  }
2255
4017
 
2256
4018
  // src/commands/status.ts
2257
- import { resolve as resolve6 } from "path";
4019
+ import { resolve as resolve7 } from "path";
4020
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
2258
4021
  async function runStatus(config2) {
2259
- const migrationsDir = resolve6(config2.out);
4022
+ intro(import_picocolors9.default.bgCyan(import_picocolors9.default.black(" @bungres/kit status ")));
4023
+ const migrationsDir = resolve7(config2.out);
2260
4024
  const sql2 = new Bun.SQL(config2.dbUrl);
2261
4025
  const table2 = config2.migrationsTable;
2262
4026
  const schema = config2.migrationsSchema;
2263
4027
  const qualifiedTable = `"${schema}"."${table2}"`;
4028
+ const s = spinner();
4029
+ s.start("Checking migration status...");
2264
4030
  try {
2265
4031
  const tableCheck = await sql2.unsafe(`SELECT EXISTS (
2266
4032
  SELECT 1 FROM information_schema.tables
@@ -2274,33 +4040,247 @@ async function runStatus(config2) {
2274
4040
  }
2275
4041
  files.sort();
2276
4042
  if (files.length === 0) {
2277
- console.log(colorize("No migration files found. Run `bungres generate` first.", "yellow"));
4043
+ s.stop("No files found.");
4044
+ log.warn(import_picocolors9.default.yellow("No migration files found."));
4045
+ log.info(`Run ${import_picocolors9.default.green("bungres generate")} first.`);
4046
+ outro("Done.");
2278
4047
  return;
2279
4048
  }
2280
4049
  let appliedSet = new Set;
2281
4050
  if (trackingExists) {
2282
4051
  const applied = await sql2.unsafe(`SELECT name FROM ${qualifiedTable} ORDER BY applied_at`);
2283
- appliedSet = new Set(applied.map((r) => r.name));
4052
+ appliedSet = new Set(applied.map((r2) => r2.name));
2284
4053
  }
2285
- console.log(colorize(`
2286
- Migration status:
2287
- `, "cyan"));
4054
+ s.stop("Status check complete.");
4055
+ log.message(import_picocolors9.default.bold("Migration status:"));
2288
4056
  let pendingCount = 0;
2289
4057
  for (const file of files) {
2290
4058
  const isApplied = appliedSet.has(file);
2291
- const status = isApplied ? colorize("\u2713 applied ", "green") : colorize("\u2717 pending ", "yellow");
2292
- if (!isApplied)
4059
+ if (isApplied) {
4060
+ log.success(`${import_picocolors9.default.green("\u2713 applied ")} ${file}`);
4061
+ } else {
2293
4062
  pendingCount++;
2294
- console.log(` ${status} ${file}`);
4063
+ log.warn(`${import_picocolors9.default.yellow("\u2717 pending ")} ${file}`);
4064
+ }
2295
4065
  }
2296
- console.log(`
2297
- ${colorize(appliedSet.size.toString(), "green")} applied, ${colorize(pendingCount.toString(), "yellow")} pending.
2298
- `);
4066
+ log.info(`${import_picocolors9.default.green(appliedSet.size.toString())} applied, ${import_picocolors9.default.yellow(pendingCount.toString())} pending.`);
4067
+ outro("Done.");
4068
+ } catch (err) {
4069
+ log.error(import_picocolors9.default.red(`Status check failed: ${err.message}`));
4070
+ outro("Failed.");
2299
4071
  } finally {
2300
4072
  await sql2.end();
2301
4073
  }
2302
4074
  }
2303
4075
 
4076
+ // src/commands/studio.ts
4077
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
4078
+
4079
+ // src/commands/template.ts
4080
+ var indexHtml = `<!DOCTYPE html>
4081
+ <html lang="en">
4082
+ <head>
4083
+ <meta charset="UTF-8">
4084
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4085
+ <title>Bungres Studio</title>
4086
+
4087
+ <link rel="preconnect" href="https://fonts.googleapis.com">
4088
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
4089
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
4090
+ <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
4091
+
4092
+ <script src="https://unpkg.com/htmx.org@2.0.0" defer></script>
4093
+ <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
4094
+ <script src="https://cdn.tailwindcss.com"></script>
4095
+
4096
+ <script>
4097
+ tailwind.config = {
4098
+ darkMode: "class",
4099
+ theme: {
4100
+ extend: {
4101
+ fontFamily: {
4102
+ sans: ['Outfit', 'sans-serif'],
4103
+ mono: ['"Fira Code"', 'monospace'],
4104
+ },
4105
+ colors: {
4106
+ background: 'var(--background)',
4107
+ foreground: 'var(--foreground)',
4108
+ card: 'var(--card)',
4109
+ 'card-foreground': 'var(--card-foreground)',
4110
+ popover: 'var(--popover)',
4111
+ 'popover-foreground': 'var(--popover-foreground)',
4112
+ primary: 'var(--primary)',
4113
+ 'primary-foreground': 'var(--primary-foreground)',
4114
+ secondary: 'var(--secondary)',
4115
+ 'secondary-foreground': 'var(--secondary-foreground)',
4116
+ muted: 'var(--muted)',
4117
+ 'muted-foreground': 'var(--muted-foreground)',
4118
+ accent: 'var(--accent)',
4119
+ 'accent-foreground': 'var(--accent-foreground)',
4120
+ destructive: 'var(--destructive)',
4121
+ border: 'var(--border)',
4122
+ input: 'var(--input)',
4123
+ ring: 'var(--ring)',
4124
+ sidebar: {
4125
+ DEFAULT: 'var(--sidebar)',
4126
+ foreground: 'var(--sidebar-foreground)',
4127
+ primary: 'var(--sidebar-primary)',
4128
+ 'primary-foreground': 'var(--sidebar-primary-foreground)',
4129
+ accent: 'var(--sidebar-accent)',
4130
+ 'accent-foreground': 'var(--sidebar-accent-foreground)',
4131
+ border: 'var(--sidebar-border)',
4132
+ ring: 'var(--sidebar-ring)',
4133
+ }
4134
+ }
4135
+ }
4136
+ }
4137
+ }
4138
+ </script>
4139
+
4140
+ <style>
4141
+ :root {
4142
+ --background: oklch(1 0 0);
4143
+ --foreground: oklch(0.148 0.004 228.8);
4144
+ --card: oklch(1 0 0);
4145
+ --card-foreground: oklch(0.148 0.004 228.8);
4146
+ --popover: oklch(1 0 0);
4147
+ --popover-foreground: oklch(0.148 0.004 228.8);
4148
+ --primary: oklch(0.508 0.118 165.612);
4149
+ --primary-foreground: oklch(0.979 0.021 166.113);
4150
+ --secondary: oklch(0.967 0.001 286.375);
4151
+ --secondary-foreground: oklch(0.21 0.006 285.885);
4152
+ --muted: oklch(0.963 0.002 197.1);
4153
+ --muted-foreground: oklch(0.56 0.021 213.5);
4154
+ --accent: oklch(0.963 0.002 197.1);
4155
+ --accent-foreground: oklch(0.218 0.008 223.9);
4156
+ --destructive: oklch(0.577 0.245 27.325);
4157
+ --border: oklch(0.925 0.005 214.3);
4158
+ --input: oklch(0.925 0.005 214.3);
4159
+ --ring: oklch(0.723 0.014 214.4);
4160
+ --chart-1: oklch(0.845 0.143 164.978);
4161
+ --chart-2: oklch(0.696 0.17 162.48);
4162
+ --chart-3: oklch(0.596 0.145 163.225);
4163
+ --chart-4: oklch(0.508 0.118 165.612);
4164
+ --chart-5: oklch(0.432 0.095 166.913);
4165
+ --radius: 0.625rem;
4166
+ --sidebar: oklch(0.987 0.002 197.1);
4167
+ --sidebar-foreground: oklch(0.148 0.004 228.8);
4168
+ --sidebar-primary: oklch(0.596 0.145 163.225);
4169
+ --sidebar-primary-foreground: oklch(0.979 0.021 166.113);
4170
+ --sidebar-accent: oklch(0.963 0.002 197.1);
4171
+ --sidebar-accent-foreground: oklch(0.218 0.008 223.9);
4172
+ --sidebar-border: oklch(0.925 0.005 214.3);
4173
+ --sidebar-ring: oklch(0.723 0.014 214.4);
4174
+ }
4175
+
4176
+ .dark {
4177
+ --background: oklch(0.148 0.004 228.8);
4178
+ --foreground: oklch(0.987 0.002 197.1);
4179
+ --card: oklch(0.218 0.008 223.9);
4180
+ --card-foreground: oklch(0.987 0.002 197.1);
4181
+ --popover: oklch(0.218 0.008 223.9);
4182
+ --popover-foreground: oklch(0.987 0.002 197.1);
4183
+ --primary: oklch(0.432 0.095 166.913);
4184
+ --primary-foreground: oklch(0.979 0.021 166.113);
4185
+ --secondary: oklch(0.274 0.006 286.033);
4186
+ --secondary-foreground: oklch(0.985 0 0);
4187
+ --muted: oklch(0.275 0.011 216.9);
4188
+ --muted-foreground: oklch(0.723 0.014 214.4);
4189
+ --accent: oklch(0.275 0.011 216.9);
4190
+ --accent-foreground: oklch(0.987 0.002 197.1);
4191
+ --destructive: oklch(0.704 0.191 22.216);
4192
+ --border: oklch(1 0 0 / 10%);
4193
+ --input: oklch(1 0 0 / 15%);
4194
+ --ring: oklch(0.56 0.021 213.5);
4195
+ --chart-1: oklch(0.845 0.143 164.978);
4196
+ --chart-2: oklch(0.696 0.17 162.48);
4197
+ --chart-3: oklch(0.596 0.145 163.225);
4198
+ --chart-4: oklch(0.508 0.118 165.612);
4199
+ --chart-5: oklch(0.432 0.095 166.913);
4200
+ --sidebar: oklch(0.218 0.008 223.9);
4201
+ --sidebar-foreground: oklch(0.987 0.002 197.1);
4202
+ --sidebar-primary: oklch(0.696 0.17 162.48);
4203
+ --sidebar-primary-foreground: oklch(0.262 0.051 172.552);
4204
+ --sidebar-accent: oklch(0.275 0.011 216.9);
4205
+ --sidebar-accent-foreground: oklch(0.987 0.002 197.1);
4206
+ --sidebar-border: oklch(1 0 0 / 10%);
4207
+ --sidebar-ring: oklch(0.56 0.021 213.5);
4208
+ }
4209
+
4210
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
4211
+ ::-webkit-scrollbar-track { background: transparent; }
4212
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
4213
+ ::-webkit-scrollbar-thumb:hover { background: var(--muted-foreground); }
4214
+ ::-webkit-scrollbar-corner { background: transparent; }
4215
+ [x-cloak] { display: none !important; }
4216
+ .htmx-indicator { opacity: 0; transition: opacity 200ms ease-in; }
4217
+ .htmx-request .htmx-indicator { opacity: 1; }
4218
+ .htmx-request.htmx-indicator { opacity: 1; }
4219
+ </style>
4220
+ </head>
4221
+ <body class="dark bg-background text-foreground font-sans h-screen flex overflow-hidden selection:bg-primary/30">
4222
+ <div x-data="{ currentTable: null, pageSize: 25 }" class="flex h-screen w-full">
4223
+ <aside class="w-64 bg-sidebar border-r border-sidebar-border flex flex-col z-20 shrink-0 text-sidebar-foreground">
4224
+ <div class="p-4 border-b border-sidebar-border flex items-center gap-3 bg-sidebar">
4225
+ <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">
4226
+ <ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
4227
+ <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
4228
+ <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
4229
+ </svg>
4230
+ <h1 class="text-sm font-semibold tracking-tight m-0">Bungres Studio</h1>
4231
+ </div>
4232
+ <div class="flex-1 overflow-y-auto p-2" hx-get="/htmx/sidebar" hx-trigger="load">
4233
+ <div class="p-4 text-xs text-muted-foreground animate-pulse text-center">Loading schemas...</div>
4234
+ </div>
4235
+ </aside>
4236
+ <main class="flex-1 flex flex-col relative w-[calc(100vw-16rem)]">
4237
+ <header class="h-14 px-4 border-b border-border flex items-center justify-between bg-card z-10 shrink-0 text-card-foreground">
4238
+ <h2 class="text-sm font-semibold tracking-tight flex items-center gap-2">
4239
+ <template x-if="currentTable">
4240
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-primary">
4241
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
4242
+ <line x1="3" y1="9" x2="21" y2="9"></line>
4243
+ <line x1="9" y1="21" x2="9" y2="9"></line>
4244
+ </svg>
4245
+ </template>
4246
+ <template x-if="!currentTable">
4247
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-muted-foreground">
4248
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
4249
+ <line x1="3" y1="9" x2="21" y2="9"></line>
4250
+ <line x1="9" y1="21" x2="9" y2="9"></line>
4251
+ </svg>
4252
+ </template>
4253
+ <span x-text="currentTable ? currentTable : 'Select a table'"></span>
4254
+ </h2>
4255
+ <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')">
4256
+ <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">
4257
+ <polyline points="23 4 23 10 17 10"></polyline>
4258
+ <polyline points="1 20 1 14 7 14"></polyline>
4259
+ <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>
4260
+ </svg>
4261
+ Refresh
4262
+ </button>
4263
+ </header>
4264
+ <div class="flex-1 overflow-auto relative z-[1] text-left bg-background">
4265
+ <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'})">
4266
+ <div class="flex flex-col items-center justify-center h-full w-full text-muted-foreground animate-[fadeIn_0.5s_ease-out]">
4267
+ <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">
4268
+ <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>
4269
+ <polyline points="14 2 14 8 20 8"></polyline>
4270
+ </svg>
4271
+ <h3 class="text-foreground m-0 mb-2 text-lg">No Table Selected</h3>
4272
+ <p class="text-sm">Choose a table from the sidebar to view its data</p>
4273
+ </div>
4274
+ </div>
4275
+ </div>
4276
+ </main>
4277
+ </div>
4278
+ <script>
4279
+ document.addEventListener('alpine:init', () => {})
4280
+ </script>
4281
+ </body>
4282
+ </html>`;
4283
+
2304
4284
  // src/commands/studio.ts
2305
4285
  async function runStudio(config2) {
2306
4286
  const schemas2 = await loadSchemas(config2.schema);
@@ -2398,804 +4378,181 @@ async function runStudio(config2) {
2398
4378
  return new Response("Invalid request", { status: 400 });
2399
4379
  }
2400
4380
  }
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);
4381
+ if (req.method === "GET" && url.pathname === "/htmx/sidebar") {
4382
+ let html = '<ul class="flex flex-col gap-0.5 p-2 m-0 list-none">';
4383
+ schemas2.forEach((s) => {
4384
+ const tableName = s.config.name;
4385
+ html += `
4386
+ <li>
4387
+ <button
4388
+ hx-get="/htmx/tables/${tableName}?page=1&limit=25"
4389
+ hx-target="#data-container"
4390
+ @click="currentTable = '${tableName}'; pageSize = 25"
4391
+ :class="currentTable === '${tableName}' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground'"
4392
+ 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"
4393
+ >
4394
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4395
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
4396
+ <line x1="3" y1="9" x2="21" y2="9"></line>
4397
+ <line x1="9" y1="21" x2="9" y2="9"></line>
4398
+ </svg>
4399
+ ${tableName}
4400
+ </button>
4401
+ </li>
4402
+ `;
2955
4403
  });
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
- \`;
4404
+ html += "</ul>";
4405
+ return new Response(html, { headers: { "Content-Type": "text/html" } });
3004
4406
  }
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
- });
4407
+ if (req.method === "GET" && url.pathname.startsWith("/htmx/tables/")) {
4408
+ const tableName = url.pathname.split("/")[3];
4409
+ const schema = schemas2.find((s) => s.config.name === tableName);
4410
+ if (!schema) {
4411
+ return new Response(`<div class="p-4 text-red-500">Table not found</div>`, { status: 404, headers: { "Content-Type": "text/html" } });
3045
4412
  }
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
4413
+ try {
4414
+ const page = parseInt(url.searchParams.get("page") || "1", 10);
4415
+ const limit = parseInt(url.searchParams.get("limit") || "25", 10);
4416
+ const offset = (page - 1) * limit;
4417
+ const countResult = await db2.select({ count: schema.table }).from(schema.table);
4418
+ const total = Array.isArray(countResult) ? countResult.length : 0;
4419
+ const totalPages = Math.ceil(total / limit) || 1;
4420
+ const data = await db2.select().from(schema.table).limit(limit).offset(offset);
4421
+ const formatValue = (val) => {
4422
+ if (val === null || val === undefined)
4423
+ return '<span class="italic text-muted-foreground">null</span>';
4424
+ if (typeof val === "number")
4425
+ return `<span class="text-foreground">${val}</span>`;
4426
+ if (typeof val === "boolean")
4427
+ return `<span class="text-foreground">${val}</span>`;
4428
+ if (val instanceof Date)
4429
+ return `<span class="text-foreground">${val.toISOString()}</span>`;
4430
+ if (typeof val === "object")
4431
+ return `<span class="text-foreground font-mono text-xs">${JSON.stringify(val).replace(/&/g, "&amp;").replace(/</g, "&lt;")}</span>`;
4432
+ return `<span class="text-foreground">${String(val).replace(/&/g, "&amp;").replace(/</g, "&lt;")}</span>`;
3069
4433
  };
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>' : ''}
4434
+ if (data.length === 0) {
4435
+ return new Response(`
4436
+ <div class="flex flex-col items-center justify-center h-full w-full text-muted-foreground animate-[fadeIn_0.5s_ease-out]">
4437
+ <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">
4438
+ <circle cx="12" cy="12" r="10"></circle>
4439
+ <line x1="12" y1="8" x2="12" y2="12"></line>
4440
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
4441
+ </svg>
4442
+ <h3 class="text-foreground m-0 mb-2 text-lg">Table is Empty</h3>
4443
+ <p class="text-sm">No records found in "${tableName}"</p>
4444
+ </div>
4445
+ `, { headers: { "Content-Type": "text/html" } });
4446
+ }
4447
+ const columns2 = Object.keys(data[0] || {});
4448
+ let html = '<div class="flex flex-col h-full w-full bg-background">';
4449
+ html += '<div class="flex-1 overflow-auto">';
4450
+ html += '<table class="text-left border-collapse text-sm whitespace-nowrap">';
4451
+ html += "<thead><tr>";
4452
+ columns2.forEach((col2) => {
4453
+ const colConfig = schema.config.columns ? schema.config.columns[col2] : null;
4454
+ let typeLabel = colConfig ? colConfig.dataType : "unknown";
4455
+ let indexLabel = "";
4456
+ if (colConfig?.primaryKey) {
4457
+ indexLabel += '<span class="ml-1 text-[10px] bg-emerald-500/20 text-emerald-500 px-1 rounded uppercase" title="Primary Key">PK</span>';
4458
+ }
4459
+ if (colConfig?.unique) {
4460
+ indexLabel += '<span class="ml-1 text-[10px] bg-amber-500/20 text-amber-500 px-1 rounded uppercase" title="Unique">UQ</span>';
4461
+ }
4462
+ if (colConfig?.references) {
4463
+ indexLabel += '<span class="ml-1 text-[10px] bg-yellow-500/20 text-yellow-500 px-1 rounded uppercase" title="Foreign Key">FK</span>';
4464
+ }
4465
+ if (schema.config.indexes) {
4466
+ const isIndexed = schema.config.indexes.some((idx) => idx.columns.includes(col2));
4467
+ if (isIndexed) {
4468
+ indexLabel += '<span class="ml-1 text-[10px] bg-blue-500/20 text-blue-500 px-1 rounded uppercase" title="Indexed">IDX</span>';
4469
+ }
4470
+ }
4471
+ 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">
4472
+ <div class="flex flex-col gap-0.5">
4473
+ <span class="text-foreground">${col2}</span>
4474
+ <div class="flex items-center flex-wrap gap-1 mt-0.5">
4475
+ <span class="text-[10px] font-mono text-muted-foreground/70 mr-1">${typeLabel}</span>
4476
+ ${indexLabel}
3081
4477
  </div>
3082
- <div class="col-type">\${col.type}</div>
3083
4478
  </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>\`;
4479
+ </th>`;
3093
4480
  });
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';
4481
+ html += "</tr></thead><tbody>";
4482
+ data.forEach((row) => {
4483
+ html += '<tr class="hover:bg-muted/50 transition-colors">';
4484
+ columns2.forEach((col2) => {
4485
+ html += `<td class="border-b border-r border-border px-4 py-2 max-w-[300px] overflow-hidden text-ellipsis">${formatValue(row[col2])}</td>`;
4486
+ });
4487
+ html += "</tr>";
3112
4488
  });
4489
+ html += "</tbody></table></div>";
4490
+ const startRecord = (page - 1) * limit + 1;
4491
+ const endRecord = Math.min(page * limit, total);
4492
+ html += `
4493
+ <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">
4494
+ <div class="flex items-center gap-4">
4495
+ <span>${total} records</span>
4496
+ <span>${startRecord}-${endRecord}</span>
4497
+ </div>
4498
+ <div class="flex items-center gap-2">
4499
+ <select
4500
+ name="limit"
4501
+ 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"
4502
+ @change="pageSize = $event.target.value; htmx.ajax('GET', '/htmx/tables/${tableName}?page=1&limit=' + pageSize, {target: '#data-container'})"
4503
+ >
4504
+ <option value="10" ${limit === 10 ? "selected" : ""}>10</option>
4505
+ <option value="25" ${limit === 25 ? "selected" : ""}>25</option>
4506
+ <option value="50" ${limit === 50 ? "selected" : ""}>50</option>
4507
+ <option value="100" ${limit === 100 ? "selected" : ""}>100</option>
4508
+ </select>
4509
+
4510
+ <button
4511
+ ${page <= 1 ? "disabled" : ""}
4512
+ hx-get="/htmx/tables/${tableName}?page=${page - 1}&limit=${limit}"
4513
+ hx-target="#data-container"
4514
+ 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"
4515
+ >&larr;</button>
4516
+
4517
+ <span class="min-w-[40px] text-center">${page} / ${totalPages}</span>
4518
+
4519
+ <button
4520
+ ${page >= totalPages ? "disabled" : ""}
4521
+ hx-get="/htmx/tables/${tableName}?page=${page + 1}&limit=${limit}"
4522
+ hx-target="#data-container"
4523
+ 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"
4524
+ >&rarr;</button>
4525
+ </div>
4526
+ </div>
4527
+ `;
4528
+ html += "</div>";
4529
+ return new Response(html, { headers: { "Content-Type": "text/html" } });
4530
+ } catch (e) {
4531
+ return new Response(`
4532
+ <div class="p-6">
4533
+ <div class="bg-red-500/10 border border-red-500/20 text-red-400 p-4 rounded-lg">
4534
+ <h3 class="font-semibold mb-1">Error Loading Data</h3>
4535
+ <p class="text-sm opacity-80">${e.message}</p>
4536
+ </div>
4537
+ </div>
4538
+ `, { headers: { "Content-Type": "text/html" } });
3113
4539
  }
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
4540
  }
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);
4541
+ if (req.method === "GET" && url.pathname === "/") {
4542
+ return new Response(indexHtml, {
4543
+ headers: { "Content-Type": "text/html" }
4544
+ });
3187
4545
  }
3188
- });
3189
-
3190
- // Init
3191
- loadTables();
3192
- </script>
3193
- </body>
3194
- </html>
3195
- `;
4546
+ return new Response("Not found", { status: 404 });
4547
+ }
4548
+ });
4549
+ intro(import_picocolors10.default.bgCyan(import_picocolors10.default.black(" \uD83D\uDC18 Bungres Studio ")));
4550
+ log.success(`Studio is running at ${import_picocolors10.default.green(`http://localhost:${server.port}`)}`);
4551
+ outro(import_picocolors10.default.gray("Press Ctrl+C to stop"));
4552
+ }
3196
4553
 
3197
4554
  // src/commands/tusky.ts
3198
- import * as readline from "readline";
4555
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
3199
4556
  async function runTusky(config) {
3200
4557
  const schemas = await loadSchemas(config.schema);
3201
4558
  const schemaObj = {};
@@ -3203,40 +4560,23 @@ async function runTusky(config) {
3203
4560
  schemaObj[s.exportName] = s.table;
3204
4561
  }
3205
4562
  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
- });
4563
+ intro(import_picocolors11.default.bgCyan(import_picocolors11.default.black(" \uD83D\uDC18 Bungres REPL (Tusky) ")));
4564
+ log.success(import_picocolors11.default.green("Database connection established."));
4565
+ note(`Example: ${import_picocolors11.default.cyan("db.select().from(users);")}
4566
+ Exit: ${import_picocolors11.default.cyan("exit")}`, "Commands");
3226
4567
  globalThis.db = db;
3227
4568
  for (const s of schemas) {
3228
4569
  globalThis[s.exportName] = s.table;
3229
4570
  }
3230
- rl.prompt();
3231
- rl.on("line", async (line) => {
4571
+ process.stdout.write("bungres> ");
4572
+ for await (const line of console) {
3232
4573
  const input = line.trim();
3233
- if (input === ".exit") {
3234
- rl.close();
3235
- return;
4574
+ if (["exit", ".exit", "quit", ".quit", "exit()", "quit()"].includes(input.toLowerCase())) {
4575
+ break;
3236
4576
  }
3237
4577
  if (!input) {
3238
- rl.prompt();
3239
- return;
4578
+ process.stdout.write("bungres> ");
4579
+ continue;
3240
4580
  }
3241
4581
  try {
3242
4582
  let code = input;
@@ -3245,22 +4585,32 @@ Example query: await db.select().from(users)`);
3245
4585
  console.log(`(Running as raw SQL: await db.raw(\`${input}\`))`);
3246
4586
  code = `(async () => { return await db.raw(\`${input}\`); })()`;
3247
4587
  } else if (input.includes("await ")) {
3248
- code = `(async () => { return ${input}; })()`;
4588
+ code = `(async () => { return await ${input}; })()`;
3249
4589
  }
4590
+ const start = performance.now();
3250
4591
  const result = await eval(code);
3251
- console.log(result);
4592
+ const end = performance.now();
4593
+ const duration = (end - start).toFixed(2);
4594
+ if (Array.isArray(result) && result.length > 0 && typeof result[0] === "object") {
4595
+ console.table(result);
4596
+ } else {
4597
+ console.log(result);
4598
+ }
4599
+ console.log(`
4600
+ (Execution time: ${duration}ms)`);
3252
4601
  } catch (err) {
3253
4602
  console.error(err.message || err);
3254
4603
  }
3255
- rl.prompt();
3256
- });
3257
- rl.on("close", () => {
3258
- process.exit(0);
3259
- });
4604
+ process.stdout.write("bungres> ");
4605
+ }
4606
+ process.exit(0);
3260
4607
  }
3261
4608
 
4609
+ // src/cli.ts
4610
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
4611
+
3262
4612
  // src/config.ts
3263
- import { join as join6, resolve as resolve7 } from "path";
4613
+ import { join as join7, resolve as resolve8 } from "path";
3264
4614
  var CONFIG_FILES = [
3265
4615
  "bungres.config.ts",
3266
4616
  "bungres.config.js",
@@ -3270,9 +4620,9 @@ var CONFIG_FILES = [
3270
4620
  async function loadConfig(cwd = process.cwd()) {
3271
4621
  let userConfig = {};
3272
4622
  for (const file of CONFIG_FILES) {
3273
- const configPath = join6(cwd, file);
4623
+ const configPath = join7(cwd, file);
3274
4624
  if (await Bun.file(configPath).exists()) {
3275
- const mod = await import(resolve7(configPath));
4625
+ const mod = await import(resolve8(configPath));
3276
4626
  userConfig = mod.default ?? mod;
3277
4627
  break;
3278
4628
  }
@@ -3334,7 +4684,7 @@ async function ensureDatabase2(dbUrl) {
3334
4684
  // package.json
3335
4685
  var package_default = {
3336
4686
  name: "@bungres/kit",
3337
- version: "0.6.1",
4687
+ version: "0.7.0",
3338
4688
  description: "CLI toolkit for @bungres/orm \u2014 migrate, push, generate, pull",
3339
4689
  license: "MIT",
3340
4690
  engines: {
@@ -3384,7 +4734,9 @@ var package_default = {
3384
4734
  test: "bun test"
3385
4735
  },
3386
4736
  dependencies: {
3387
- "@bungres/orm": "workspace:*"
4737
+ "@bungres/orm": "^0.6.0",
4738
+ "@clack/prompts": "^1.7.0",
4739
+ picocolors: "^1.1.1"
3388
4740
  },
3389
4741
  devDependencies: {
3390
4742
  "bun-types": "latest",
@@ -3395,15 +4747,16 @@ var package_default = {
3395
4747
  // src/cli.ts
3396
4748
  var VERSION = package_default.version;
3397
4749
  var HELP = `
3398
- ${colorize("\uD83D\uDC18 Bungres ORM CLI", "cyan")} v${VERSION}
4750
+ ${import_picocolors12.default.cyan("\uD83D\uDC18 Bungres ORM CLI")} v${VERSION}
3399
4751
 
3400
- ${colorize("Usage:", "yellow")}
4752
+ ${import_picocolors12.default.yellow("Usage:")}
3401
4753
  bungres <command> [options]
3402
4754
 
3403
- ${colorize("Commands:", "yellow")}
4755
+ ${import_picocolors12.default.yellow("Commands:")}
3404
4756
  init Initialize bungres project with config file and db folder structure
3405
4757
  generate Generate SQL migration files from your schema definitions
3406
4758
  migrate Run pending migration files against the database
4759
+ rollback Revert the last applied migration
3407
4760
  push Apply schema directly to the DB (no migration files, dev mode)
3408
4761
  pull Introspect the database and generate TypeScript schema
3409
4762
  status Show applied vs. pending migrations
@@ -3414,17 +4767,18 @@ ${colorize("Commands:", "yellow")}
3414
4767
  tusky Boot up a Node REPL connected to the database with schema loaded
3415
4768
  drop Drop all tables defined in the schema (dev only)
3416
4769
 
3417
- ${colorize("Options:", "yellow")}
4770
+ ${import_picocolors12.default.yellow("Options:")}
3418
4771
  --config Path to config file (default: bungres.config.ts)
3419
4772
  --verbose Enable verbose SQL logging
3420
4773
  --force Skip confirmation prompts (use with drop)
3421
4774
  --version Show version
3422
4775
  --help Show this help
3423
4776
 
3424
- ${colorize("Examples:", "yellow")}
4777
+ ${import_picocolors12.default.yellow("Examples:")}
3425
4778
  bungres init
3426
4779
  bungres generate
3427
4780
  bungres migrate
4781
+ bungres rollback
3428
4782
  bungres push
3429
4783
  bungres pull
3430
4784
  bungres status
@@ -3454,7 +4808,7 @@ async function main() {
3454
4808
  const config2 = await loadConfig(process.cwd());
3455
4809
  if (flags.verbose)
3456
4810
  config2.verbose = true;
3457
- if (command && ["migrate", "push", "drop", "status", "fresh", "refresh"].includes(command)) {
4811
+ if (command && ["migrate", "rollback", "push", "drop", "status", "fresh", "refresh"].includes(command)) {
3458
4812
  await ensureDatabase2(config2.dbUrl);
3459
4813
  }
3460
4814
  switch (command) {
@@ -3464,6 +4818,9 @@ async function main() {
3464
4818
  case "migrate":
3465
4819
  await runMigrate(config2);
3466
4820
  break;
4821
+ case "rollback":
4822
+ await runRollback(config2);
4823
+ break;
3467
4824
  case "push":
3468
4825
  await runPush(config2, { force: !!flags.force });
3469
4826
  break;
@@ -3500,14 +4857,14 @@ async function main() {
3500
4857
  }
3501
4858
  function parseFlags(args) {
3502
4859
  const flags = {};
3503
- for (let i = 0;i < args.length; i++) {
3504
- const arg = args[i];
4860
+ for (let i2 = 0;i2 < args.length; i2++) {
4861
+ const arg = args[i2];
3505
4862
  if (arg?.startsWith("--")) {
3506
4863
  const key = arg.slice(2);
3507
- const next = args[i + 1];
4864
+ const next = args[i2 + 1];
3508
4865
  if (next && !next.startsWith("--")) {
3509
4866
  flags[key] = next;
3510
- i++;
4867
+ i2++;
3511
4868
  } else {
3512
4869
  flags[key] = true;
3513
4870
  }