@bungres/kit 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -73,6 +73,7 @@ function createTableFactory(casing) {
73
73
  var table = createTableFactory("snake");
74
74
  var snakeCase = { table: createTableFactory("snake") };
75
75
  var camelCase = { table: createTableFactory("camel") };
76
+ var noCasing = { table: createTableFactory("none") };
76
77
  // ../bungres-orm/src/schema/columns.ts
77
78
  function buildColumn(dataType, nameOrOpts, opts) {
78
79
  let name = "";
@@ -167,14 +168,14 @@ function sqlJoin(chunks, separator = ", ") {
167
168
  function rawSql(query) {
168
169
  return { sql: query, params: [] };
169
170
  }
170
- // ../bungres-orm/src/core/conditions.ts
171
- var colName = (c) => {
171
+ function colName(c) {
172
172
  if (typeof c === "string")
173
173
  return `"${c}"`;
174
174
  if (isSQLChunk(c))
175
175
  return c.sql;
176
176
  return c.tableName ? `${c.tableName}."${c.name}"` : `"${c.name}"`;
177
- };
177
+ }
178
+ // ../bungres-orm/src/core/conditions.ts
178
179
  function isColumnConfig(val) {
179
180
  return val !== null && typeof val === "object" && "name" in val && "dataType" in val;
180
181
  }
@@ -231,6 +232,14 @@ var inArray = (column, values) => {
231
232
  const placeholders = params.map((_, i) => `$${i + 1}`).join(", ");
232
233
  return { sql: `${colName(column)} = ANY(ARRAY[${placeholders}])`, params };
233
234
  };
235
+ var notInArray = (column, values) => {
236
+ if (values.length === 0)
237
+ return rawSql("TRUE");
238
+ const params = values;
239
+ const placeholders = params.map((_, i) => `$${i + 1}`).join(", ");
240
+ return { sql: `${colName(column)} != ALL(ARRAY[${placeholders}])`, params };
241
+ };
242
+ var between = (column, min, max) => sql`${rawSql(colName(column))} BETWEEN ${min} AND ${max}`;
234
243
  var and = (...conditions) => sqlJoin(conditions, " AND ");
235
244
  var or = (...conditions) => {
236
245
  const joined = sqlJoin(conditions, " OR ");
@@ -281,6 +290,10 @@ function parseWhereObject(tableConfig, whereObj) {
281
290
  conditions.push(lte(columnArg, opVal.lte));
282
291
  if (opVal.in !== undefined)
283
292
  conditions.push(inArray(columnArg, opVal.in));
293
+ if (opVal.notIn !== undefined)
294
+ conditions.push(notInArray(columnArg, opVal.notIn));
295
+ if (opVal.between !== undefined)
296
+ conditions.push(between(columnArg, opVal.between[0], opVal.between[1]));
284
297
  if (opVal.like !== undefined)
285
298
  conditions.push(like(columnArg, opVal.like));
286
299
  if (opVal.ilike !== undefined)
@@ -361,6 +374,9 @@ class DeleteBuilder {
361
374
  if (this._returning) {
362
375
  query += " RETURNING " + (this._returning[0] === "*" ? Object.keys(getTableConfig(this._table).columns).map((c) => `"${getTableConfig(this._table).columns[c].name}" AS "${c}"`).join(", ") : this._returning.map((c) => `"${getTableConfig(this._table).columns[c]?.name ?? c}" AS "${c}"`).join(", "));
363
376
  }
377
+ if (this._comment) {
378
+ query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
379
+ }
364
380
  return { sql: query, params };
365
381
  }
366
382
  }
@@ -471,7 +487,7 @@ class InsertBuilder {
471
487
  query += " RETURNING " + (this._returning[0] === "*" ? Object.keys(tConfig.columns).map((c) => `"${tConfig.columns[c].name}" AS "${c}"`).join(", ") : this._returning.map((c) => `"${tConfig.columns[c]?.name ?? c}" AS "${c}"`).join(", "));
472
488
  }
473
489
  if (this._comment) {
474
- query += ` /* ${this._comment} */`;
490
+ query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
475
491
  }
476
492
  return { sql: query, params };
477
493
  }
@@ -668,7 +684,7 @@ class SelectBuilder {
668
684
  query += ` OFFSET $${params.length}`;
669
685
  }
670
686
  if (this._comment) {
671
- query += ` /* ${this._comment} */`;
687
+ query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
672
688
  }
673
689
  return { sql: query, params };
674
690
  }
@@ -768,10 +784,22 @@ class UpdateBuilder {
768
784
  if (this._returning) {
769
785
  query += " RETURNING " + (this._returning[0] === "*" ? Object.keys(getTableConfig(this._table).columns).map((c) => `"${getTableConfig(this._table).columns[c].name}" AS "${c}"`).join(", ") : this._returning.map((c) => `"${getTableConfig(this._table).columns[c]?.name ?? c}" AS "${c}"`).join(", "));
770
786
  }
787
+ if (this._comment) {
788
+ query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
789
+ }
771
790
  return { sql: query, params };
772
791
  }
773
792
  }
774
793
  // ../bungres-orm/src/builders/relational.ts
794
+ function getPkColumn(tableConfig) {
795
+ if (tableConfig.primaryKeys?.length > 0)
796
+ return tableConfig.primaryKeys[0];
797
+ for (const [, col2] of Object.entries(tableConfig.columns)) {
798
+ if (col2.primaryKey)
799
+ return col2.name;
800
+ }
801
+ return "id";
802
+ }
775
803
  var _relationsCache = new WeakMap;
776
804
 
777
805
  class RelationalQueryBuilder {
@@ -922,14 +950,17 @@ class RelationalQueryBuilder {
922
950
  if (relations.ones[relKey]) {
923
951
  const rel = relations.ones[relKey];
924
952
  const subAlias = `${alias}_${relKey}`;
925
- const joinCond = `"${subAlias}"."${this._schema[rel.targetTable][TableConfigSymbol].columns.id?.name ?? "id"}" = "${alias}"."${rel.sourceColumn}"`;
953
+ const targetConfig = this._schema[rel.targetTable][TableConfigSymbol];
954
+ const targetPk = getPkColumn(targetConfig);
955
+ const joinCond = `"${subAlias}"."${targetPk}" = "${alias}"."${rel.sourceColumn}"`;
926
956
  const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias, joinCond);
927
957
  const subSql = `SELECT ${subQuery.sql} ${subQuery.from}`;
928
958
  jsonFields.push(`'${relKey}', (${subSql})`);
929
959
  } else if (relations.manys[relKey]) {
930
960
  const rel = relations.manys[relKey];
931
961
  const subAlias = `${alias}_${relKey}`;
932
- const joinCond = `"${subAlias}"."${rel.targetColumn}" = "${alias}"."${tableConfig.columns.id?.name ?? "id"}"`;
962
+ const thisPk = getPkColumn(tableConfig);
963
+ const joinCond = `"${subAlias}"."${rel.targetColumn}" = "${alias}"."${thisPk}"`;
933
964
  const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias, joinCond);
934
965
  const aggAlias = `${subAlias}_agg`;
935
966
  const aggSql = `
@@ -948,9 +979,9 @@ class RelationalQueryBuilder {
948
979
  const junctionTableConfig = this._schema[rel.junctionTable][TableConfigSymbol];
949
980
  const fromExtra = `
950
981
  INNER JOIN ${junctionTableConfig.qualifiedName} AS "${junctionAlias}"
951
- ON "${junctionAlias}"."${rel.joinTargetColumn}" = "${subAlias}"."${targetTableConfig.columns.id?.name ?? "id"}"
982
+ ON "${junctionAlias}"."${rel.joinTargetColumn}" = "${subAlias}"."${getPkColumn(targetTableConfig)}"
952
983
  `;
953
- const whereExtra = `"${junctionAlias}"."${rel.joinSourceColumn}" = "${alias}"."${tableConfig.columns.id?.name ?? "id"}"`;
984
+ const whereExtra = `"${junctionAlias}"."${rel.joinSourceColumn}" = "${alias}"."${getPkColumn(tableConfig)}"`;
954
985
  const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias, whereExtra, fromExtra);
955
986
  const aggSql = `
956
987
  SELECT COALESCE(json_agg(_sub.obj), '[]'::json)
@@ -1331,9 +1362,9 @@ function isTable(value) {
1331
1362
  }
1332
1363
 
1333
1364
  // src/utils/colors.ts
1334
- function colorize(text2, color) {
1365
+ function colorize(text, color) {
1335
1366
  const code2 = Bun.color(color, "ansi");
1336
- return code2 ? `${code2}${text2}\x1B[0m` : text2;
1367
+ return code2 ? `${code2}${text}\x1B[0m` : text;
1337
1368
  }
1338
1369
 
1339
1370
  // src/commands/drop.ts
@@ -1614,6 +1645,14 @@ function diffColumn(prev, next) {
1614
1645
  const col2 = `"${next.name}"`;
1615
1646
  if (prev.dataType !== next.dataType) {
1616
1647
  changes.push(`ALTER COLUMN ${col2} TYPE ${next.dataType.toUpperCase()} USING ${col2}::${next.dataType}`);
1648
+ } else {
1649
+ const prevLen = prev.length;
1650
+ const nextLen = next.length;
1651
+ if (prevLen !== nextLen && (next.dataType === "varchar" || next.dataType === "char")) {
1652
+ const typeName = next.dataType === "varchar" ? "VARCHAR" : "CHAR";
1653
+ const typeStr = nextLen ? `${typeName}(${nextLen})` : typeName;
1654
+ changes.push(`ALTER COLUMN ${col2} TYPE ${typeStr}`);
1655
+ }
1617
1656
  }
1618
1657
  if (!prev.notNull && next.notNull) {
1619
1658
  changes.push(`ALTER COLUMN ${col2} SET NOT NULL`);
@@ -1680,8 +1719,6 @@ async function runGenerate(config2, name) {
1680
1719
  if (isFirstMigration) {
1681
1720
  const sorted = topoSort(schemas2);
1682
1721
  statements = [
1683
- `CREATE EXTENSION IF NOT EXISTS "pgcrypto";`,
1684
- ``,
1685
1722
  ...sorted.flatMap((entry) => [
1686
1723
  `-- ${entry.exportName}`,
1687
1724
  generateCreateTable(entry.config, true),
@@ -2099,13 +2136,15 @@ async function runPush(config2, opts = {}) {
2099
2136
  }
2100
2137
  const sql2 = new Bun.SQL(config2.dbUrl);
2101
2138
  try {
2139
+ await sql2.unsafe(`CREATE SCHEMA IF NOT EXISTS "${config2.migrationsSchema}";`);
2140
+ const qualifiedPush = `"${config2.migrationsSchema}"."__bungres_push"`;
2102
2141
  await sql2.unsafe(`
2103
- CREATE TABLE IF NOT EXISTS public.__bungres_push (
2142
+ CREATE TABLE IF NOT EXISTS ${qualifiedPush} (
2104
2143
  id SERIAL PRIMARY KEY,
2105
2144
  snapshot JSONB NOT NULL
2106
2145
  );
2107
2146
  `);
2108
- const rows = await sql2.unsafe(`SELECT snapshot FROM public.__bungres_push ORDER BY id DESC LIMIT 1;`);
2147
+ const rows = await sql2.unsafe(`SELECT snapshot FROM ${qualifiedPush} ORDER BY id DESC LIMIT 1;`);
2109
2148
  let prevSnapshot = {};
2110
2149
  if (rows.length > 0) {
2111
2150
  prevSnapshot = typeof rows[0].snapshot === "string" ? JSON.parse(rows[0].snapshot) : rows[0].snapshot;
@@ -2140,14 +2179,13 @@ Are you sure you want to push these changes? Type YES to continue: `);
2140
2179
  }
2141
2180
  console.log(`
2142
2181
  Pushing changes...`);
2143
- await sql2.unsafe(`CREATE EXTENSION IF NOT EXISTS "pgcrypto";`);
2144
2182
  for (const stmt of diff.statements) {
2145
2183
  if (config2.verbose) {
2146
2184
  console.log(`-- ${stmt}`);
2147
2185
  }
2148
2186
  await sql2.unsafe(stmt);
2149
2187
  }
2150
- await sql2.unsafe(`INSERT INTO public.__bungres_push (snapshot) VALUES ($1);`, [JSON.stringify(currentSnapshot)]);
2188
+ await sql2.unsafe(`INSERT INTO ${qualifiedPush} (snapshot) VALUES ($1);`, [JSON.stringify(currentSnapshot)]);
2151
2189
  console.log(`
2152
2190
  Push complete.`);
2153
2191
  } finally {
@@ -2281,15 +2319,32 @@ async function runStudio(config2) {
2281
2319
  async fetch(req) {
2282
2320
  const url = new URL(req.url);
2283
2321
  if (req.method === "GET" && url.pathname === "/api/tables") {
2284
- const tables = schemas2.map((s) => ({
2285
- name: s.config.name,
2286
- exportName: s.exportName,
2287
- columns: Object.entries(s.config.columns).map(([key, col2]) => ({
2288
- name: col2.name,
2289
- type: col2.dataType,
2290
- primaryKey: col2.primaryKey
2291
- }))
2292
- }));
2322
+ const tables = schemas2.map((s) => {
2323
+ const fkColumns = new Set;
2324
+ if (s.config.foreignKeys) {
2325
+ s.config.foreignKeys.forEach((fk) => {
2326
+ fk.columns.forEach((col2) => fkColumns.add(col2));
2327
+ });
2328
+ }
2329
+ const columns2 = Object.entries(s.config.columns).map(([key, col2]) => {
2330
+ const fk = col2.references ? {
2331
+ table: col2.references.table,
2332
+ column: col2.references.column
2333
+ } : fkColumns.has(col2.name) ? { isForeignKey: true } : null;
2334
+ return {
2335
+ name: col2.name,
2336
+ type: col2.dataType,
2337
+ primaryKey: col2.primaryKey,
2338
+ foreignKey: fk
2339
+ };
2340
+ });
2341
+ return {
2342
+ name: s.config.name,
2343
+ exportName: s.exportName,
2344
+ columns: columns2,
2345
+ foreignKeys: s.config.foreignKeys || []
2346
+ };
2347
+ });
2293
2348
  return new Response(JSON.stringify(tables), {
2294
2349
  headers: { "Content-Type": "application/json" }
2295
2350
  });
@@ -2367,15 +2422,16 @@ var htmlTemplate = `
2367
2422
  <style>
2368
2423
  :root {
2369
2424
  --bg-color: #09090b;
2370
- --panel-bg: rgba(24, 24, 27, 0.7);
2425
+ --panel-bg: #18181b;
2371
2426
  --border-color: #27272a;
2372
- --text-main: #f4f4f5;
2427
+ --text-main: #fafafa;
2373
2428
  --text-muted: #a1a1aa;
2374
2429
  --accent-color: #8b5cf6;
2375
2430
  --accent-hover: #7c3aed;
2376
- --header-bg: rgba(9, 9, 11, 0.85);
2377
- --row-hover: rgba(39, 39, 42, 0.8);
2378
- --glass-border: rgba(255, 255, 255, 0.08);
2431
+ --header-bg: #09090b;
2432
+ --row-hover: #27272a;
2433
+ --input-bg: #18181b;
2434
+ --muted-bg: #27272a;
2379
2435
 
2380
2436
  --type-number: #f472b6;
2381
2437
  --type-string: #60a5fa;
@@ -2400,45 +2456,39 @@ var htmlTemplate = `
2400
2456
 
2401
2457
  /* Sidebar */
2402
2458
  .sidebar {
2403
- width: 280px;
2459
+ width: 240px;
2404
2460
  background-color: var(--panel-bg);
2405
- backdrop-filter: blur(12px);
2406
- -webkit-backdrop-filter: blur(12px);
2407
2461
  border-right: 1px solid var(--border-color);
2408
2462
  display: flex;
2409
2463
  flex-direction: column;
2410
- box-shadow: 4px 0 24px rgba(0,0,0,0.2);
2411
2464
  z-index: 20;
2412
2465
  }
2413
2466
 
2414
2467
  .sidebar-header {
2415
- padding: 24px;
2416
- border-bottom: 1px solid var(--glass-border);
2468
+ padding: 16px;
2469
+ border-bottom: 1px solid var(--border-color);
2417
2470
  display: flex;
2418
2471
  align-items: center;
2419
- gap: 12px;
2420
- background: linear-gradient(to bottom, rgba(255,255,255,0.03), transparent);
2472
+ gap: 8px;
2473
+ background-color: var(--panel-bg);
2421
2474
  }
2422
2475
 
2423
2476
  .sidebar-header h1 {
2424
2477
  margin: 0;
2425
- font-size: 18px;
2478
+ font-size: 14px;
2426
2479
  font-weight: 600;
2427
- background: linear-gradient(to right, #fff, #a1a1aa);
2428
- -webkit-background-clip: text;
2429
- -webkit-text-fill-color: transparent;
2430
- letter-spacing: -0.5px;
2480
+ color: var(--text-main);
2481
+ letter-spacing: -0.25px;
2431
2482
  }
2432
2483
 
2433
2484
  .sidebar-header svg {
2434
2485
  color: var(--accent-color);
2435
- filter: drop-shadow(0 0 8px rgba(139, 92, 246, 0.5));
2436
2486
  }
2437
2487
 
2438
2488
  .table-list {
2439
2489
  flex: 1;
2440
2490
  overflow-y: auto;
2441
- padding: 16px 12px;
2491
+ padding: 8px;
2442
2492
  list-style: none;
2443
2493
  margin: 0;
2444
2494
  }
@@ -2452,39 +2502,36 @@ var htmlTemplate = `
2452
2502
  }
2453
2503
 
2454
2504
  .table-item {
2455
- padding: 12px 16px;
2505
+ padding: 8px 12px;
2456
2506
  cursor: pointer;
2457
- font-size: 15px;
2507
+ font-size: 13px;
2458
2508
  font-weight: 500;
2459
2509
  color: var(--text-muted);
2460
- border-radius: 8px;
2461
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2510
+ border-radius: 6px;
2511
+ transition: all 0.15s ease;
2462
2512
  display: flex;
2463
2513
  align-items: center;
2464
- gap: 10px;
2465
- margin-bottom: 4px;
2514
+ gap: 8px;
2515
+ margin-bottom: 2px;
2466
2516
  border: 1px solid transparent;
2467
2517
  }
2468
2518
 
2469
2519
  .table-item:hover {
2470
- background-color: rgba(255, 255, 255, 0.05);
2520
+ background-color: var(--muted-bg);
2471
2521
  color: var(--text-main);
2472
- transform: translateX(4px);
2473
2522
  }
2474
2523
 
2475
2524
  .table-item.active {
2476
- background-color: rgba(139, 92, 246, 0.15);
2525
+ background-color: var(--accent-color);
2477
2526
  color: #fff;
2478
- border: 1px solid rgba(139, 92, 246, 0.3);
2479
- box-shadow: 0 4px 12px rgba(139, 92, 246, 0.1);
2527
+ border-color: var(--accent-color);
2480
2528
  }
2481
2529
 
2482
2530
  .table-item svg {
2483
- transition: transform 0.3s ease;
2531
+ flex-shrink: 0;
2484
2532
  }
2485
2533
  .table-item.active svg {
2486
- color: var(--accent-color);
2487
- transform: scale(1.1);
2534
+ color: #fff;
2488
2535
  }
2489
2536
 
2490
2537
  /* Main Content */
@@ -2496,31 +2543,29 @@ var htmlTemplate = `
2496
2543
  }
2497
2544
 
2498
2545
  .main-header {
2499
- height: 70px;
2500
- padding: 0 24px;
2546
+ height: 56px;
2547
+ padding: 0 16px;
2501
2548
  border-bottom: 1px solid var(--border-color);
2502
2549
  display: flex;
2503
2550
  align-items: center;
2504
2551
  justify-content: space-between;
2505
2552
  background-color: var(--header-bg);
2506
- backdrop-filter: blur(12px);
2507
- -webkit-backdrop-filter: blur(12px);
2508
2553
  z-index: 10;
2509
2554
  }
2510
2555
 
2511
2556
  .main-header h2 {
2512
2557
  margin: 0;
2513
- font-size: 20px;
2558
+ font-size: 14px;
2514
2559
  font-weight: 600;
2515
- color: white;
2516
- letter-spacing: -0.5px;
2560
+ color: var(--text-main);
2561
+ letter-spacing: -0.25px;
2517
2562
  display: flex;
2518
2563
  align-items: center;
2519
- gap: 10px;
2564
+ gap: 8px;
2520
2565
  }
2521
2566
 
2522
2567
  .btn {
2523
- background-color: rgba(255, 255, 255, 0.05);
2568
+ background-color: var(--input-bg);
2524
2569
  border: 1px solid var(--border-color);
2525
2570
  color: var(--text-main);
2526
2571
  padding: 6px 12px;
@@ -2529,14 +2574,14 @@ var htmlTemplate = `
2529
2574
  font-weight: 500;
2530
2575
  font-family: 'Outfit', sans-serif;
2531
2576
  cursor: pointer;
2532
- transition: all 0.2s ease;
2577
+ transition: all 0.15s ease;
2533
2578
  display: flex;
2534
2579
  align-items: center;
2535
- gap: 8px;
2580
+ gap: 6px;
2536
2581
  }
2537
2582
 
2538
2583
  .btn:hover:not(:disabled) {
2539
- background-color: rgba(255, 255, 255, 0.1);
2584
+ background-color: var(--muted-bg);
2540
2585
  }
2541
2586
 
2542
2587
  .btn:active:not(:disabled) {
@@ -2544,7 +2589,7 @@ var htmlTemplate = `
2544
2589
  }
2545
2590
 
2546
2591
  .btn:disabled {
2547
- opacity: 0.4;
2592
+ opacity: 0.5;
2548
2593
  cursor: not-allowed;
2549
2594
  }
2550
2595
 
@@ -2559,14 +2604,20 @@ var htmlTemplate = `
2559
2604
 
2560
2605
  /* Data Table */
2561
2606
  .data-grid-container {
2562
- background-color: rgba(24, 24, 27, 0.5);
2607
+ background-color: var(--bg-color);
2563
2608
  border: 1px solid var(--border-color);
2564
2609
  border-radius: 0;
2565
- overflow: auto;
2566
- box-shadow: 0 8px 32px rgba(0,0,0,0.2);
2567
- backdrop-filter: blur(8px);
2568
- max-height: 100%;
2610
+ overflow: hidden;
2611
+ height: 100%;
2569
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;
2570
2621
  }
2571
2622
 
2572
2623
  .data-grid-container::-webkit-scrollbar {
@@ -2581,6 +2632,7 @@ var htmlTemplate = `
2581
2632
  border-radius: 4px;
2582
2633
  }
2583
2634
 
2635
+
2584
2636
  table {
2585
2637
  width: auto;
2586
2638
  border-collapse: separate;
@@ -2591,11 +2643,11 @@ var htmlTemplate = `
2591
2643
  }
2592
2644
 
2593
2645
  th {
2594
- background-color: rgba(24, 24, 27, 0.95);
2595
- backdrop-filter: blur(4px);
2646
+ background-color: var(--panel-bg);
2596
2647
  color: var(--text-main);
2597
2648
  font-weight: 500;
2598
- padding: 12px 20px;
2649
+ font-size: 12px;
2650
+ padding: 10px 16px;
2599
2651
  border-bottom: 1px solid var(--border-color);
2600
2652
  border-right: 1px solid var(--border-color);
2601
2653
  white-space: nowrap;
@@ -2603,24 +2655,16 @@ var htmlTemplate = `
2603
2655
  top: 0;
2604
2656
  z-index: 10;
2605
2657
  }
2606
-
2607
-
2608
- th::after {
2609
- content: '';
2610
- position: absolute;
2611
- bottom: -1px; left: 0; right: 0;
2612
- height: 1px;
2613
- background: var(--border-color);
2614
- }
2615
2658
 
2616
2659
  .col-header {
2617
2660
  display: flex;
2618
2661
  flex-direction: column;
2619
- gap: 4px;
2662
+ gap: 2px;
2620
2663
  }
2621
2664
 
2622
2665
  .col-name {
2623
2666
  font-weight: 600;
2667
+ font-size: 13px;
2624
2668
  display: flex;
2625
2669
  align-items: center;
2626
2670
  gap: 6px;
@@ -2628,22 +2672,35 @@ var htmlTemplate = `
2628
2672
 
2629
2673
  .pk-badge {
2630
2674
  font-size: 10px;
2631
- background: rgba(251, 191, 36, 0.2);
2675
+ background: rgba(251, 191, 36, 0.15);
2632
2676
  color: #fbbf24;
2633
- padding: 2px 6px;
2677
+ padding: 2px 5px;
2634
2678
  border-radius: 4px;
2635
- font-weight: 600;
2636
- letter-spacing: 0.5px;
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);
2637
2693
  }
2638
2694
 
2639
2695
  .col-type {
2640
- font-size: 12px;
2696
+ font-size: 11px;
2641
2697
  color: var(--text-muted);
2642
- font-family: monospace;
2698
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
2699
+ text-transform: uppercase;
2643
2700
  }
2644
2701
 
2645
2702
  td {
2646
- padding: 12px 20px;
2703
+ padding: 10px 16px;
2647
2704
  border-bottom: 1px solid var(--border-color);
2648
2705
  border-right: 1px solid var(--border-color);
2649
2706
  color: var(--text-main);
@@ -2651,13 +2708,11 @@ var htmlTemplate = `
2651
2708
  overflow: hidden;
2652
2709
  text-overflow: ellipsis;
2653
2710
  white-space: nowrap;
2654
- transition: background-color 0.2s ease;
2711
+ transition: background-color 0.15s ease;
2712
+ font-size: 13px;
2655
2713
  }
2656
2714
 
2657
2715
 
2658
- tr:last-child td {
2659
- border-bottom: none;
2660
- }
2661
2716
 
2662
2717
  tr:hover td {
2663
2718
  background-color: var(--row-hover);
@@ -2717,45 +2772,45 @@ var htmlTemplate = `
2717
2772
  display: flex;
2718
2773
  align-items: center;
2719
2774
  justify-content: space-between;
2720
- padding: 16px 24px;
2721
- background-color: rgba(24, 24, 27, 0.95);
2775
+ padding: 12px 16px;
2776
+ background-color: var(--panel-bg);
2722
2777
  border-top: 1px solid var(--border-color);
2723
- font-size: 14px;
2778
+ font-size: 13px;
2724
2779
  color: var(--text-muted);
2725
- position: sticky;
2726
- bottom: 0;
2727
- z-index: 5;
2780
+ width: fit-content;
2781
+ min-width: 100%;
2782
+ flex-shrink: 0;
2728
2783
  }
2729
2784
 
2730
2785
  .pagination-info {
2731
2786
  display: flex;
2732
2787
  align-items: center;
2733
- gap: 16px;
2788
+ gap: 12px;
2734
2789
  }
2735
2790
 
2736
2791
  .pagination-controls {
2737
2792
  display: flex;
2738
2793
  align-items: center;
2739
- gap: 8px;
2794
+ gap: 6px;
2740
2795
  }
2741
2796
 
2742
2797
  .pagination-btn {
2743
- background-color: rgba(255, 255, 255, 0.05);
2798
+ background-color: var(--input-bg);
2744
2799
  border: 1px solid var(--border-color);
2745
2800
  color: var(--text-main);
2746
2801
  padding: 6px 12px;
2747
2802
  border-radius: 6px;
2748
2803
  cursor: pointer;
2749
2804
  font-size: 13px;
2750
- transition: all 0.2s ease;
2805
+ transition: all 0.15s ease;
2751
2806
  }
2752
2807
 
2753
2808
  .pagination-btn:hover:not(:disabled) {
2754
- background-color: rgba(255, 255, 255, 0.1);
2809
+ background-color: var(--muted-bg);
2755
2810
  }
2756
2811
 
2757
2812
  .pagination-btn:disabled {
2758
- opacity: 0.4;
2813
+ opacity: 0.5;
2759
2814
  cursor: not-allowed;
2760
2815
  }
2761
2816
 
@@ -2765,7 +2820,7 @@ var htmlTemplate = `
2765
2820
  }
2766
2821
 
2767
2822
  .page-input {
2768
- background-color: rgba(255, 255, 255, 0.05);
2823
+ background-color: var(--input-bg);
2769
2824
  border: 1px solid var(--border-color);
2770
2825
  color: var(--text-main);
2771
2826
  padding: 6px 8px;
@@ -2774,6 +2829,20 @@ var htmlTemplate = `
2774
2829
  text-align: center;
2775
2830
  font-size: 13px;
2776
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
+ }
2777
2846
  </style>
2778
2847
  </head>
2779
2848
  <body>
@@ -2830,7 +2899,7 @@ var htmlTemplate = `
2830
2899
  let currentPage = 1;
2831
2900
  let totalPages = 1;
2832
2901
  let totalRecords = 0;
2833
- let pageSize = 50;
2902
+ let pageSize = 25;
2834
2903
 
2835
2904
  // Format values for the data grid
2836
2905
  function formatValue(val) {
@@ -2910,21 +2979,29 @@ var htmlTemplate = `
2910
2979
  document.getElementById('refresh-btn').disabled = false;
2911
2980
 
2912
2981
  const container = document.getElementById('data-container');
2913
- container.innerHTML = \`
2914
- <div class="empty-state">
2915
- <svg class="spinning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2916
- <line x1="12" y1="2" x2="12" y2="6"></line>
2917
- <line x1="12" y1="18" x2="12" y2="22"></line>
2918
- <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
2919
- <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
2920
- <line x1="2" y1="12" x2="6" y2="12"></line>
2921
- <line x1="18" y1="12" x2="22" y2="12"></line>
2922
- <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
2923
- <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
2924
- </svg>
2925
- <p>Loading data...</p>
2926
- </div>
2927
- \`;
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
+ \`;
3004
+ }
2928
3005
 
2929
3006
  try {
2930
3007
  const res = await fetch(\`/api/tables/\${name}/data?page=\${page}&limit=\${pageSize}\`);
@@ -2967,13 +3044,32 @@ var htmlTemplate = `
2967
3044
  });
2968
3045
  }
2969
3046
 
2970
- columns = Array.from(allKeys).map(key => ({
2971
- name: key,
2972
- type: schemaColMap[key] ? schemaColMap[key].type : typeof rows[0][key],
2973
- primaryKey: schemaColMap[key] ? schemaColMap[key].primaryKey : false
2974
- }));
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
3069
+ };
3070
+ });
2975
3071
 
2976
- let html = '<div class="data-grid-container"><table><thead><tr>';
3072
+ let html = '<div class="data-grid-container"><div class="table-wrapper"><table><thead><tr>';
2977
3073
  columns.forEach(col => {
2978
3074
  html += \`
2979
3075
  <th>
@@ -2981,6 +3077,7 @@ var htmlTemplate = `
2981
3077
  <div class="col-name">
2982
3078
  \${col.name}
2983
3079
  \${col.primaryKey ? '<span class="pk-badge">PK</span>' : ''}
3080
+ \${col.foreignKey ? '<span class="fk-badge">FK</span>' : ''}
2984
3081
  </div>
2985
3082
  <div class="col-type">\${col.type}</div>
2986
3083
  </div>
@@ -2999,12 +3096,22 @@ var htmlTemplate = `
2999
3096
 
3000
3097
  html += '</tbody></table>';
3001
3098
 
3002
- // Add pagination
3099
+ // Add pagination inside the table wrapper to match table width
3003
3100
  html += renderPagination();
3004
3101
 
3005
- html += '</div>';
3102
+ html += '</div></div>';
3006
3103
  container.innerHTML = html;
3007
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';
3112
+ });
3113
+ }
3114
+
3008
3115
  } catch (e) {
3009
3116
  container.innerHTML = \`
3010
3117
  <div class="empty-state">
@@ -3026,18 +3133,27 @@ var htmlTemplate = `
3026
3133
 
3027
3134
  let html = '<div class="pagination">';
3028
3135
  html += '<div class="pagination-info">';
3029
- html += \`<span>Total: \${totalRecords} records</span>\`;
3030
- html += \`<span>Page \${currentPage} of \${totalPages}</span>\`;
3031
- html += \`<span>Showing \${startRecord}-\${endRecord}</span>\`;
3136
+ html += \`<span>\${totalRecords} records</span>\`;
3137
+ html += \`<span>\${startRecord}-\${endRecord}</span>\`;
3032
3138
  html += '</div>';
3033
3139
 
3034
3140
  html += '<div class="pagination-controls">';
3035
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
+
3036
3149
  // Previous button
3037
- html += \`<button class="pagination-btn" onclick="changePage(\${currentPage - 1})" \${currentPage === 1 ? 'disabled' : ''}>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>\`;
3038
3154
 
3039
3155
  // Next button
3040
- html += \`<button class="pagination-btn" onclick="changePage(\${currentPage + 1})" \${currentPage === totalPages ? 'disabled' : ''}>Next</button>\`;
3156
+ html += \`<button class="pagination-btn" onclick="changePage(\${currentPage + 1})" \${currentPage === totalPages ? 'disabled' : ''}>\u2192</button>\`;
3041
3157
 
3042
3158
  html += '</div>';
3043
3159
  html += '</div>';
@@ -3045,6 +3161,12 @@ var htmlTemplate = `
3045
3161
  return html;
3046
3162
  }
3047
3163
 
3164
+ function changePageSize(newSize) {
3165
+ pageSize = parseInt(newSize, 10);
3166
+ currentPage = 1;
3167
+ selectTable(currentTable, currentPage);
3168
+ }
3169
+
3048
3170
  function changePage(page) {
3049
3171
  if (page < 1 || page > totalPages) return;
3050
3172
  selectTable(currentTable, page);
@@ -3172,7 +3294,7 @@ async function loadConfig(cwd = process.cwd()) {
3172
3294
  migrationsSchema: userConfig.migrations?.schema ?? "bungres",
3173
3295
  breakpoints: userConfig.breakpoints ?? true,
3174
3296
  strict: userConfig.strict ?? false,
3175
- outDir: "./src/db/generated",
3297
+ outDir: userConfig.outDir ?? "./src/db/generated",
3176
3298
  verbose: userConfig.verbose ?? false
3177
3299
  };
3178
3300
  }
@@ -3189,6 +3311,9 @@ async function ensureDatabase2(dbUrl) {
3189
3311
  try {
3190
3312
  const parsed = new URL(dbUrl);
3191
3313
  const dbName = parsed.pathname.slice(1);
3314
+ if (!/^[a-zA-Z_][a-zA-Z0-9_$]*$/.test(dbName)) {
3315
+ throw new Error(`Invalid database name: "${dbName}"`);
3316
+ }
3192
3317
  parsed.pathname = "/postgres";
3193
3318
  const fallbackUrl = parsed.toString();
3194
3319
  const fallback = new Bun.SQL(fallbackUrl);
@@ -3206,9 +3331,69 @@ async function ensureDatabase2(dbUrl) {
3206
3331
  }
3207
3332
  }
3208
3333
  }
3334
+ // package.json
3335
+ var package_default = {
3336
+ name: "@bungres/kit",
3337
+ version: "0.6.1",
3338
+ description: "CLI toolkit for @bungres/orm \u2014 migrate, push, generate, pull",
3339
+ license: "MIT",
3340
+ engines: {
3341
+ node: ">=24.0.0",
3342
+ bun: ">=1.3.0"
3343
+ },
3344
+ type: "module",
3345
+ bin: {
3346
+ bungres: "./dist/cli.js"
3347
+ },
3348
+ main: "./dist/index.js",
3349
+ types: "./dist/index.d.ts",
3350
+ exports: {
3351
+ ".": {
3352
+ bun: "./src/index.ts",
3353
+ import: "./dist/index.js",
3354
+ types: "./dist/index.d.ts"
3355
+ }
3356
+ },
3357
+ files: [
3358
+ "dist",
3359
+ "README.md",
3360
+ "LICENSE"
3361
+ ],
3362
+ author: "aniket khote",
3363
+ repository: {
3364
+ type: "git",
3365
+ url: "https://github.com/bungres/bungres.git",
3366
+ directory: "packages/bungres-kit"
3367
+ },
3368
+ bugs: {
3369
+ url: "https://github.com/bungres/bungres/issues"
3370
+ },
3371
+ homepage: "https://github.com/bungres/bungres#readme",
3372
+ keywords: [
3373
+ "bun",
3374
+ "postgres",
3375
+ "orm",
3376
+ "cli",
3377
+ "migration",
3378
+ "generator"
3379
+ ],
3380
+ scripts: {
3381
+ "build:types": "tsc --emitDeclarationOnly",
3382
+ build: "bun build ./src/cli.ts ./src/index.ts --outdir ./dist --target bun --format esm && npm run build:types",
3383
+ dev: "bun --watch src/cli.ts",
3384
+ test: "bun test"
3385
+ },
3386
+ dependencies: {
3387
+ "@bungres/orm": "workspace:*"
3388
+ },
3389
+ devDependencies: {
3390
+ "bun-types": "latest",
3391
+ typescript: "latest"
3392
+ }
3393
+ };
3209
3394
 
3210
3395
  // src/cli.ts
3211
- var VERSION = "0.0.1";
3396
+ var VERSION = package_default.version;
3212
3397
  var HELP = `
3213
3398
  ${colorize("\uD83D\uDC18 Bungres ORM CLI", "cyan")} v${VERSION}
3214
3399
 
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAgBnD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAgGf"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAgBnD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CA+Ff"}
@@ -1 +1 @@
1
- {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAUnD,wBAAsB,OAAO,CAC3B,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,IAAI,CAAC,CAoFf"}
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAUnD,wBAAsB,OAAO,CAC3B,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,IAAI,CAAC,CAqFf"}
@@ -1 +1 @@
1
- {"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../../src/commands/studio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,wBAAsB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoHrE"}
1
+ {"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../../src/commands/studio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,wBAAsB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyIrE"}
package/dist/config.d.ts CHANGED
@@ -53,6 +53,11 @@ export interface BungresKitConfig {
53
53
  * Default: false
54
54
  */
55
55
  strict?: boolean;
56
+ /**
57
+ * Output directory for `bungres pull` generated TypeScript.
58
+ * Default: "./src/db/generated"
59
+ */
60
+ outDir?: string;
56
61
  /** Print every SQL statement executed. Default: false */
57
62
  verbose?: boolean;
58
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE1B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,aAAa,CAAC,EAAE;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IAEF;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE;QACX;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AASD,wBAAsB,UAAU,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CA0C7E"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE1B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,aAAa,CAAC,EAAE;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IAEF;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE;QACX;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AASD,wBAAsB,UAAU,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CA0C7E"}
@@ -1 +1 @@
1
- {"version":3,"file":"ensure-db.d.ts","sourceRoot":"","sources":["../src/ensure-db.ts"],"names":[],"mappings":"AAAA,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BjE"}
1
+ {"version":3,"file":"ensure-db.d.ts","sourceRoot":"","sources":["../src/ensure-db.ts"],"names":[],"mappings":"AAAA,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCjE"}
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ async function loadConfig(cwd = process.cwd()) {
34
34
  migrationsSchema: userConfig.migrations?.schema ?? "bungres",
35
35
  breakpoints: userConfig.breakpoints ?? true,
36
36
  strict: userConfig.strict ?? false,
37
- outDir: "./src/db/generated",
37
+ outDir: userConfig.outDir ?? "./src/db/generated",
38
38
  verbose: userConfig.verbose ?? false
39
39
  };
40
40
  }
@@ -110,6 +110,7 @@ function createTableFactory(casing) {
110
110
  var table = createTableFactory("snake");
111
111
  var snakeCase = { table: createTableFactory("snake") };
112
112
  var camelCase = { table: createTableFactory("camel") };
113
+ var noCasing = { table: createTableFactory("none") };
113
114
  // ../bungres-orm/src/schema/columns.ts
114
115
  function buildColumn(dataType, nameOrOpts, opts) {
115
116
  let name = "";
@@ -204,14 +205,14 @@ function sqlJoin(chunks, separator = ", ") {
204
205
  function rawSql(query) {
205
206
  return { sql: query, params: [] };
206
207
  }
207
- // ../bungres-orm/src/core/conditions.ts
208
- var colName = (c) => {
208
+ function colName(c) {
209
209
  if (typeof c === "string")
210
210
  return `"${c}"`;
211
211
  if (isSQLChunk(c))
212
212
  return c.sql;
213
213
  return c.tableName ? `${c.tableName}."${c.name}"` : `"${c.name}"`;
214
- };
214
+ }
215
+ // ../bungres-orm/src/core/conditions.ts
215
216
  function isColumnConfig(val) {
216
217
  return val !== null && typeof val === "object" && "name" in val && "dataType" in val;
217
218
  }
@@ -268,6 +269,14 @@ var inArray = (column, values) => {
268
269
  const placeholders = params.map((_, i) => `$${i + 1}`).join(", ");
269
270
  return { sql: `${colName(column)} = ANY(ARRAY[${placeholders}])`, params };
270
271
  };
272
+ var notInArray = (column, values) => {
273
+ if (values.length === 0)
274
+ return rawSql("TRUE");
275
+ const params = values;
276
+ const placeholders = params.map((_, i) => `$${i + 1}`).join(", ");
277
+ return { sql: `${colName(column)} != ALL(ARRAY[${placeholders}])`, params };
278
+ };
279
+ var between = (column, min, max) => sql`${rawSql(colName(column))} BETWEEN ${min} AND ${max}`;
271
280
  var and = (...conditions) => sqlJoin(conditions, " AND ");
272
281
  var or = (...conditions) => {
273
282
  const joined = sqlJoin(conditions, " OR ");
@@ -318,6 +327,10 @@ function parseWhereObject(tableConfig, whereObj) {
318
327
  conditions.push(lte(columnArg, opVal.lte));
319
328
  if (opVal.in !== undefined)
320
329
  conditions.push(inArray(columnArg, opVal.in));
330
+ if (opVal.notIn !== undefined)
331
+ conditions.push(notInArray(columnArg, opVal.notIn));
332
+ if (opVal.between !== undefined)
333
+ conditions.push(between(columnArg, opVal.between[0], opVal.between[1]));
321
334
  if (opVal.like !== undefined)
322
335
  conditions.push(like(columnArg, opVal.like));
323
336
  if (opVal.ilike !== undefined)
@@ -398,6 +411,9 @@ class DeleteBuilder {
398
411
  if (this._returning) {
399
412
  query += " RETURNING " + (this._returning[0] === "*" ? Object.keys(getTableConfig(this._table).columns).map((c) => `"${getTableConfig(this._table).columns[c].name}" AS "${c}"`).join(", ") : this._returning.map((c) => `"${getTableConfig(this._table).columns[c]?.name ?? c}" AS "${c}"`).join(", "));
400
413
  }
414
+ if (this._comment) {
415
+ query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
416
+ }
401
417
  return { sql: query, params };
402
418
  }
403
419
  }
@@ -508,7 +524,7 @@ class InsertBuilder {
508
524
  query += " RETURNING " + (this._returning[0] === "*" ? Object.keys(tConfig.columns).map((c) => `"${tConfig.columns[c].name}" AS "${c}"`).join(", ") : this._returning.map((c) => `"${tConfig.columns[c]?.name ?? c}" AS "${c}"`).join(", "));
509
525
  }
510
526
  if (this._comment) {
511
- query += ` /* ${this._comment} */`;
527
+ query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
512
528
  }
513
529
  return { sql: query, params };
514
530
  }
@@ -705,7 +721,7 @@ class SelectBuilder {
705
721
  query += ` OFFSET $${params.length}`;
706
722
  }
707
723
  if (this._comment) {
708
- query += ` /* ${this._comment} */`;
724
+ query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
709
725
  }
710
726
  return { sql: query, params };
711
727
  }
@@ -805,10 +821,22 @@ class UpdateBuilder {
805
821
  if (this._returning) {
806
822
  query += " RETURNING " + (this._returning[0] === "*" ? Object.keys(getTableConfig(this._table).columns).map((c) => `"${getTableConfig(this._table).columns[c].name}" AS "${c}"`).join(", ") : this._returning.map((c) => `"${getTableConfig(this._table).columns[c]?.name ?? c}" AS "${c}"`).join(", "));
807
823
  }
824
+ if (this._comment) {
825
+ query += ` /* ${this._comment.replace(/\*\//g, "")} */`;
826
+ }
808
827
  return { sql: query, params };
809
828
  }
810
829
  }
811
830
  // ../bungres-orm/src/builders/relational.ts
831
+ function getPkColumn(tableConfig) {
832
+ if (tableConfig.primaryKeys?.length > 0)
833
+ return tableConfig.primaryKeys[0];
834
+ for (const [, col2] of Object.entries(tableConfig.columns)) {
835
+ if (col2.primaryKey)
836
+ return col2.name;
837
+ }
838
+ return "id";
839
+ }
812
840
  var _relationsCache = new WeakMap;
813
841
 
814
842
  class RelationalQueryBuilder {
@@ -959,14 +987,17 @@ class RelationalQueryBuilder {
959
987
  if (relations.ones[relKey]) {
960
988
  const rel = relations.ones[relKey];
961
989
  const subAlias = `${alias}_${relKey}`;
962
- const joinCond = `"${subAlias}"."${this._schema[rel.targetTable][TableConfigSymbol].columns.id?.name ?? "id"}" = "${alias}"."${rel.sourceColumn}"`;
990
+ const targetConfig = this._schema[rel.targetTable][TableConfigSymbol];
991
+ const targetPk = getPkColumn(targetConfig);
992
+ const joinCond = `"${subAlias}"."${targetPk}" = "${alias}"."${rel.sourceColumn}"`;
963
993
  const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias, joinCond);
964
994
  const subSql = `SELECT ${subQuery.sql} ${subQuery.from}`;
965
995
  jsonFields.push(`'${relKey}', (${subSql})`);
966
996
  } else if (relations.manys[relKey]) {
967
997
  const rel = relations.manys[relKey];
968
998
  const subAlias = `${alias}_${relKey}`;
969
- const joinCond = `"${subAlias}"."${rel.targetColumn}" = "${alias}"."${tableConfig.columns.id?.name ?? "id"}"`;
999
+ const thisPk = getPkColumn(tableConfig);
1000
+ const joinCond = `"${subAlias}"."${rel.targetColumn}" = "${alias}"."${thisPk}"`;
970
1001
  const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias, joinCond);
971
1002
  const aggAlias = `${subAlias}_agg`;
972
1003
  const aggSql = `
@@ -985,9 +1016,9 @@ class RelationalQueryBuilder {
985
1016
  const junctionTableConfig = this._schema[rel.junctionTable][TableConfigSymbol];
986
1017
  const fromExtra = `
987
1018
  INNER JOIN ${junctionTableConfig.qualifiedName} AS "${junctionAlias}"
988
- ON "${junctionAlias}"."${rel.joinTargetColumn}" = "${subAlias}"."${targetTableConfig.columns.id?.name ?? "id"}"
1019
+ ON "${junctionAlias}"."${rel.joinTargetColumn}" = "${subAlias}"."${getPkColumn(targetTableConfig)}"
989
1020
  `;
990
- const whereExtra = `"${junctionAlias}"."${rel.joinSourceColumn}" = "${alias}"."${tableConfig.columns.id?.name ?? "id"}"`;
1021
+ const whereExtra = `"${junctionAlias}"."${rel.joinSourceColumn}" = "${alias}"."${getPkColumn(tableConfig)}"`;
991
1022
  const subQuery = this._buildSelectJson(rel.targetTable, rArgs, subAlias, params, alias, whereExtra, fromExtra);
992
1023
  const aggSql = `
993
1024
  SELECT COALESCE(json_agg(_sub.obj), '[]'::json)
@@ -1500,6 +1531,14 @@ function diffColumn(prev, next) {
1500
1531
  const col2 = `"${next.name}"`;
1501
1532
  if (prev.dataType !== next.dataType) {
1502
1533
  changes.push(`ALTER COLUMN ${col2} TYPE ${next.dataType.toUpperCase()} USING ${col2}::${next.dataType}`);
1534
+ } else {
1535
+ const prevLen = prev.length;
1536
+ const nextLen = next.length;
1537
+ if (prevLen !== nextLen && (next.dataType === "varchar" || next.dataType === "char")) {
1538
+ const typeName = next.dataType === "varchar" ? "VARCHAR" : "CHAR";
1539
+ const typeStr = nextLen ? `${typeName}(${nextLen})` : typeName;
1540
+ changes.push(`ALTER COLUMN ${col2} TYPE ${typeStr}`);
1541
+ }
1503
1542
  }
1504
1543
  if (!prev.notNull && next.notNull) {
1505
1544
  changes.push(`ALTER COLUMN ${col2} SET NOT NULL`);
@@ -1544,13 +1583,15 @@ async function runPush(config, opts = {}) {
1544
1583
  }
1545
1584
  const sql2 = new Bun.SQL(config.dbUrl);
1546
1585
  try {
1586
+ await sql2.unsafe(`CREATE SCHEMA IF NOT EXISTS "${config.migrationsSchema}";`);
1587
+ const qualifiedPush = `"${config.migrationsSchema}"."__bungres_push"`;
1547
1588
  await sql2.unsafe(`
1548
- CREATE TABLE IF NOT EXISTS public.__bungres_push (
1589
+ CREATE TABLE IF NOT EXISTS ${qualifiedPush} (
1549
1590
  id SERIAL PRIMARY KEY,
1550
1591
  snapshot JSONB NOT NULL
1551
1592
  );
1552
1593
  `);
1553
- const rows = await sql2.unsafe(`SELECT snapshot FROM public.__bungres_push ORDER BY id DESC LIMIT 1;`);
1594
+ const rows = await sql2.unsafe(`SELECT snapshot FROM ${qualifiedPush} ORDER BY id DESC LIMIT 1;`);
1554
1595
  let prevSnapshot = {};
1555
1596
  if (rows.length > 0) {
1556
1597
  prevSnapshot = typeof rows[0].snapshot === "string" ? JSON.parse(rows[0].snapshot) : rows[0].snapshot;
@@ -1585,14 +1626,13 @@ Are you sure you want to push these changes? Type YES to continue: `);
1585
1626
  }
1586
1627
  console.log(`
1587
1628
  Pushing changes...`);
1588
- await sql2.unsafe(`CREATE EXTENSION IF NOT EXISTS "pgcrypto";`);
1589
1629
  for (const stmt of diff.statements) {
1590
1630
  if (config.verbose) {
1591
1631
  console.log(`-- ${stmt}`);
1592
1632
  }
1593
1633
  await sql2.unsafe(stmt);
1594
1634
  }
1595
- await sql2.unsafe(`INSERT INTO public.__bungres_push (snapshot) VALUES ($1);`, [JSON.stringify(currentSnapshot)]);
1635
+ await sql2.unsafe(`INSERT INTO ${qualifiedPush} (snapshot) VALUES ($1);`, [JSON.stringify(currentSnapshot)]);
1596
1636
  console.log(`
1597
1637
  Push complete.`);
1598
1638
  } finally {
@@ -1608,9 +1648,9 @@ async function readLine() {
1608
1648
  import { join as join3, resolve as resolve3 } from "path";
1609
1649
 
1610
1650
  // src/utils/colors.ts
1611
- function colorize(text2, color) {
1651
+ function colorize(text, color) {
1612
1652
  const code = Bun.color(color, "ansi");
1613
- return code ? `${code}${text2}\x1B[0m` : text2;
1653
+ return code ? `${code}${text}\x1B[0m` : text;
1614
1654
  }
1615
1655
 
1616
1656
  // src/commands/generate.ts
@@ -1645,8 +1685,6 @@ async function runGenerate(config, name) {
1645
1685
  if (isFirstMigration) {
1646
1686
  const sorted = topoSort(schemas);
1647
1687
  statements = [
1648
- `CREATE EXTENSION IF NOT EXISTS "pgcrypto";`,
1649
- ``,
1650
1688
  ...sorted.flatMap((entry) => [
1651
1689
  `-- ${entry.exportName}`,
1652
1690
  generateCreateTable(entry.config, true),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bungres/kit",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "CLI toolkit for @bungres/orm — migrate, push, generate, pull",
5
5
  "license": "MIT",
6
6
  "engines": {