buncargo 1.0.26 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/dist/bin.d.ts +1 -12
  2. package/dist/bin.js +261 -252
  3. package/dist/cli/bin.d.ts +13 -0
  4. package/dist/cli/bin.js +315 -0
  5. package/dist/cli/commands/help.d.ts +1 -0
  6. package/dist/cli/commands/runtime.d.ts +5 -0
  7. package/dist/cli/commands/version.d.ts +1 -0
  8. package/dist/cli/index.d.ts +1 -0
  9. package/dist/cli/index.js +14 -0
  10. package/dist/cli/run-cli.d.ts +22 -0
  11. package/dist/cli.d.ts +1 -22
  12. package/dist/cli.js +5 -13
  13. package/dist/config/config.d.ts +1 -0
  14. package/dist/config/define-config.d.ts +13 -0
  15. package/dist/config/index.d.ts +3 -0
  16. package/dist/config/index.js +15 -0
  17. package/dist/config/merge-configs.d.ts +3 -0
  18. package/dist/config/validate-config.d.ts +3 -0
  19. package/dist/config.d.ts +1 -72
  20. package/dist/config.js +12 -12
  21. package/dist/core/docker.d.ts +1 -74
  22. package/dist/core/docker.js +35 -26
  23. package/dist/core/index.d.ts +1 -1
  24. package/dist/core/index.js +123 -108
  25. package/dist/core/network.js +2 -2
  26. package/dist/core/ports.d.ts +22 -0
  27. package/dist/core/ports.js +5 -1
  28. package/dist/core/process.js +1 -1
  29. package/dist/core/tunnel.d.ts +33 -0
  30. package/dist/core/utils.js +2 -2
  31. package/dist/core/watchdog-runner.js +45 -42
  32. package/dist/core/watchdog.d.ts +1 -0
  33. package/dist/core/watchdog.js +4 -2
  34. package/dist/docker/index.d.ts +1 -0
  35. package/dist/docker/index.js +38 -0
  36. package/dist/docker/runtime.d.ts +87 -0
  37. package/dist/docker/runtime.js +37 -0
  38. package/dist/docker-compose/compose.d.ts +1 -0
  39. package/dist/docker-compose/generated-file.d.ts +7 -0
  40. package/dist/docker-compose/index.d.ts +3 -0
  41. package/dist/docker-compose/index.js +15 -0
  42. package/dist/docker-compose/model.d.ts +6 -0
  43. package/dist/docker-compose/services/clickhouse.d.ts +16 -0
  44. package/dist/docker-compose/services/define-docker-service.d.ts +41 -0
  45. package/dist/docker-compose/services/index.d.ts +23 -0
  46. package/dist/docker-compose/services/index.js +17 -0
  47. package/dist/docker-compose/services/postgres.d.ts +12 -0
  48. package/dist/docker-compose/services/redis.d.ts +12 -0
  49. package/dist/docker-compose/services/shared.d.ts +7 -0
  50. package/dist/docker-compose/yaml.d.ts +2 -0
  51. package/dist/environment/create-dev-environment.d.ts +23 -0
  52. package/dist/environment/index.d.ts +1 -0
  53. package/dist/environment/index.js +15 -0
  54. package/dist/environment/logging.d.ts +17 -0
  55. package/dist/environment/seeding.d.ts +9 -0
  56. package/dist/environment.d.ts +1 -23
  57. package/dist/environment.js +12 -14
  58. package/dist/index-045jksh5.js +147 -0
  59. package/dist/index-08wa79cs.js +125 -117
  60. package/dist/index-0kxnae3z.js +335 -0
  61. package/dist/index-1mdrf7nz.js +66 -0
  62. package/dist/index-1yvbwj4k.js +262 -242
  63. package/dist/index-23ev345g.js +475 -0
  64. package/dist/index-2ckr49sf.js +228 -0
  65. package/dist/index-2f47khe5.js +376 -369
  66. package/dist/index-2fr3g85b.js +220 -183
  67. package/dist/index-38xnzpa6.js +450 -0
  68. package/dist/index-3h3dhtf2.js +51 -43
  69. package/dist/index-42x95209.js +51 -43
  70. package/dist/index-4gp0az1g.js +145 -0
  71. package/dist/index-4xrxh8yv.js +72 -0
  72. package/dist/index-5gmws6ah.js +181 -0
  73. package/dist/index-5hka0tff.js +78 -76
  74. package/dist/index-5rfqps4b.js +3 -0
  75. package/dist/index-5t9jxqm0.js +428 -0
  76. package/dist/index-6c1w1xk5.js +101 -0
  77. package/dist/index-6fm7mvwj.js +118 -97
  78. package/dist/index-6srpc523.js +127 -128
  79. package/dist/index-731rzzfp.js +187 -0
  80. package/dist/index-75y4cg2z.js +51 -43
  81. package/dist/index-7ja4ywyj.js +126 -127
  82. package/dist/index-8bw1cmz4.js +531 -0
  83. package/dist/index-8hbbj1mp.js +120 -121
  84. package/dist/index-8xj2p5n5.js +145 -0
  85. package/dist/index-bj79tw5w.js +0 -0
  86. package/dist/index-bnk6nr0g.js +73 -0
  87. package/dist/index-brbbzyks.js +72 -0
  88. package/dist/index-c0dr6mcv.js +123 -0
  89. package/dist/index-cty0bcry.js +235 -218
  90. package/dist/index-d8tyv5se.js +228 -0
  91. package/dist/index-d9efy0n4.js +176 -150
  92. package/dist/index-etfmqjjf.js +427 -0
  93. package/dist/index-fb29934k.js +172 -0
  94. package/dist/index-g50jw1yf.js +72 -0
  95. package/dist/index-g6eb5wdw.js +118 -117
  96. package/dist/index-ggq3yryx.js +99 -95
  97. package/dist/index-h70tce00.js +177 -0
  98. package/dist/index-hkxtfqtc.js +333 -0
  99. package/dist/index-kf3dhser.js +146 -143
  100. package/dist/index-ma6tgdb2.js +500 -0
  101. package/dist/index-mam0bcyz.js +123 -0
  102. package/dist/index-mm412dkp.js +274 -0
  103. package/dist/index-n8v18aeb.js +0 -0
  104. package/dist/index-ndnmnsej.js +378 -371
  105. package/dist/index-p8wty0e2.js +389 -379
  106. package/dist/index-qfphr2fd.js +100 -0
  107. package/dist/index-qqmms8rs.js +51 -43
  108. package/dist/index-qw4093g2.js +51 -43
  109. package/dist/index-qzwpzjbx.js +121 -122
  110. package/dist/index-segbnm0h.js +146 -143
  111. package/dist/index-t0fj6gg1.js +112 -0
  112. package/dist/index-thdkwnv7.js +122 -0
  113. package/dist/index-tjbx2r2t.js +270 -0
  114. package/dist/index-tjqw9vtj.js +62 -54
  115. package/dist/index-vbpb89jy.js +248 -0
  116. package/dist/index-vhs88xhe.js +99 -95
  117. package/dist/index-w8zxnjka.js +249 -0
  118. package/dist/index-wk2na3t9.js +404 -0
  119. package/dist/index-wz9x8g7z.js +383 -373
  120. package/dist/index-x249gyde.js +388 -378
  121. package/dist/index-xkvd0nsd.js +187 -0
  122. package/dist/index-yedqxm1z.js +80 -0
  123. package/dist/index-zfjzzjkf.js +266 -0
  124. package/dist/index.d.ts +12 -8
  125. package/dist/index.js +66 -35
  126. package/dist/lint.d.ts +1 -46
  127. package/dist/lint.js +3 -7
  128. package/dist/loader/cache.d.ts +4 -0
  129. package/dist/loader/find-config-file.d.ts +2 -0
  130. package/dist/loader/index.d.ts +5 -0
  131. package/dist/loader/index.js +24 -0
  132. package/dist/loader/load-dev-env.d.ts +5 -0
  133. package/dist/loader/loader.d.ts +1 -0
  134. package/dist/loader.d.ts +1 -45
  135. package/dist/loader.js +22 -20
  136. package/dist/prisma/index.d.ts +1 -0
  137. package/dist/prisma/prisma.d.ts +29 -0
  138. package/dist/prisma.d.ts +1 -29
  139. package/dist/prisma.js +6 -10
  140. package/dist/src/bin.js +309 -0
  141. package/dist/src/cli.js +5 -0
  142. package/dist/src/config.js +15 -0
  143. package/dist/src/core/docker.js +38 -0
  144. package/dist/src/core/index.js +130 -0
  145. package/dist/src/core/network.js +9 -0
  146. package/dist/src/core/ports.js +23 -0
  147. package/dist/src/core/process.js +31 -0
  148. package/dist/src/core/utils.js +11 -0
  149. package/dist/src/core/watchdog-runner.js +69 -0
  150. package/dist/src/core/watchdog.js +28 -0
  151. package/dist/src/docker/runtime.js +37 -0
  152. package/dist/src/docker-compose/index.js +16 -0
  153. package/dist/src/docker-compose/services/index.js +17 -0
  154. package/dist/src/environment.js +12 -0
  155. package/dist/src/index.js +122 -0
  156. package/dist/src/lint.js +3 -0
  157. package/dist/src/loader.js +25 -0
  158. package/dist/src/prisma.js +6 -0
  159. package/dist/src/types.js +0 -0
  160. package/dist/typecheck/index.d.ts +1 -0
  161. package/dist/typecheck/index.js +7 -0
  162. package/dist/typecheck/typecheck.d.ts +46 -0
  163. package/dist/types/all-types.d.ts +501 -0
  164. package/dist/types/cli.d.ts +1 -0
  165. package/dist/types/config.d.ts +6 -0
  166. package/dist/types/docker.d.ts +15 -0
  167. package/dist/types/environment.d.ts +8 -0
  168. package/dist/types/hooks.d.ts +9 -0
  169. package/dist/types/index.d.ts +1 -0
  170. package/dist/types/index.js +0 -0
  171. package/dist/types/prisma.d.ts +1 -0
  172. package/dist/types.d.ts +1 -393
  173. package/package.json +145 -140
  174. package/readme.md +358 -105
  175. package/src/cli/bin.ts +77 -0
  176. package/src/cli/commands/help.ts +39 -0
  177. package/src/cli/commands/runtime.ts +72 -0
  178. package/src/cli/commands/version.ts +4 -0
  179. package/src/cli/index.ts +1 -0
  180. package/{cli.ts → src/cli/run-cli.ts} +95 -6
  181. package/src/config/define-config.ts +30 -0
  182. package/src/config/index.ts +3 -0
  183. package/src/config/merge-configs.ts +33 -0
  184. package/src/config/validate-config.ts +136 -0
  185. package/{core → src/core}/index.ts +2 -2
  186. package/{core → src/core}/ports.ts +68 -1
  187. package/{core → src/core}/process.ts +6 -2
  188. package/src/core/tunnel.ts +151 -0
  189. package/{core → src/core}/utils.ts +1 -0
  190. package/{core → src/core}/watchdog.ts +5 -1
  191. package/src/docker/index.ts +1 -0
  192. package/{core/docker.ts → src/docker/runtime.ts} +40 -4
  193. package/src/docker-compose/generated-file.ts +45 -0
  194. package/src/docker-compose/index.ts +7 -0
  195. package/src/docker-compose/model.ts +197 -0
  196. package/src/docker-compose/services/clickhouse.ts +79 -0
  197. package/src/docker-compose/services/define-docker-service.ts +109 -0
  198. package/src/docker-compose/services/index.ts +67 -0
  199. package/src/docker-compose/services/postgres.ts +60 -0
  200. package/src/docker-compose/services/redis.ts +48 -0
  201. package/src/docker-compose/services/shared.ts +79 -0
  202. package/src/docker-compose/yaml.ts +88 -0
  203. package/{environment.ts → src/environment/create-dev-environment.ts} +101 -146
  204. package/src/environment/index.ts +1 -0
  205. package/src/environment/logging.ts +101 -0
  206. package/src/environment/seeding.ts +57 -0
  207. package/{index.ts → src/index.ts} +49 -15
  208. package/src/loader/cache.ts +23 -0
  209. package/src/loader/find-config-file.ts +29 -0
  210. package/src/loader/index.ts +17 -0
  211. package/src/loader/load-dev-env.ts +38 -0
  212. package/src/prisma/index.ts +1 -0
  213. package/{prisma.ts → src/prisma/prisma.ts} +4 -2
  214. package/src/typecheck/index.ts +1 -0
  215. package/{types.ts → src/types/all-types.ts} +137 -6
  216. package/src/types/index.ts +1 -0
  217. package/bin.ts +0 -191
  218. package/config.ts +0 -194
  219. package/loader.ts +0 -126
  220. /package/{core → src/core}/network.ts +0 -0
  221. /package/{core → src/core}/watchdog-runner.ts +0 -0
  222. /package/{lint.ts → src/typecheck/typecheck.ts} +0 -0
@@ -1,37 +1,38 @@
1
- import pc from "picocolors";
2
- import { assertValidConfig } from "./config";
1
+ import { assertValidConfig } from "../config";
2
+ import { getLocalIp, waitForDevServers, waitForServer } from "../core/network";
3
3
  import {
4
- areContainersRunning,
5
- startContainers,
6
- stopContainers,
7
- waitForAllServices,
8
- } from "./core/docker";
9
- import { getLocalIp, waitForDevServers, waitForServer } from "./core/network";
10
- import {
11
- calculatePortOffset,
4
+ computeDevIdentity,
12
5
  computePorts,
13
6
  computeUrls,
14
7
  findMonorepoRoot,
15
- getProjectName,
16
- isWorktree,
17
- } from "./core/ports";
8
+ } from "../core/ports";
18
9
  import {
19
10
  buildApps,
20
11
  execAsync,
21
12
  startDevServers,
22
13
  stopProcess as stopProcessFn,
23
- } from "./core/process";
24
- import { isCI as isCIEnv, logExpoApiUrl, logFrontendPort } from "./core/utils";
14
+ } from "../core/process";
15
+ import { isCI as isCIEnv, logExpoApiUrl, logFrontendPort } from "../core/utils";
25
16
  import {
26
17
  spawnWatchdog as spawnWatchdogFn,
27
18
  startHeartbeat as startHeartbeatFn,
28
19
  stopHeartbeat as stopHeartbeatFn,
29
20
  stopWatchdog as stopWatchdogFn,
30
- } from "./core/watchdog";
31
- import { createPrismaRunner } from "./prisma";
21
+ } from "../core/watchdog";
22
+ import {
23
+ areContainersRunning,
24
+ startContainers,
25
+ stopContainers,
26
+ } from "../docker/runtime";
27
+ import {
28
+ getGeneratedComposePath,
29
+ writeGeneratedComposeFile,
30
+ } from "../docker-compose";
31
+ import { createPrismaRunner } from "../prisma";
32
32
  import type {
33
33
  AppConfig,
34
34
  ComputedPorts,
35
+ ComputedPublicUrls,
35
36
  ComputedUrls,
36
37
  DevConfig,
37
38
  DevEnvironment,
@@ -42,34 +43,9 @@ import type {
42
43
  ServiceConfig,
43
44
  StartOptions,
44
45
  StopOptions,
45
- } from "./types";
46
-
47
- // ═══════════════════════════════════════════════════════════════════════════
48
- // Console Output Formatting (Vite-inspired)
49
- // ═══════════════════════════════════════════════════════════════════════════
50
-
51
- /**
52
- * Format a URL with colored port number (Vite-style).
53
- */
54
- function formatUrl(url: string): string {
55
- return pc.cyan(
56
- url.replace(/:(\d+)(\/?)/, (_, port, slash) => `:${pc.bold(port)}${slash}`),
57
- );
58
- }
59
-
60
- /**
61
- * Format a label with arrow prefix (Vite-style).
62
- */
63
- function formatLabel(label: string, value: string, arrow = "➜"): string {
64
- return ` ${pc.green(arrow)} ${pc.bold(label.padEnd(10))} ${value}`;
65
- }
66
-
67
- /**
68
- * Format a dim label (for secondary info).
69
- */
70
- function formatDimLabel(label: string, value: string): string {
71
- return ` ${pc.dim("•")} ${pc.dim(label.padEnd(10))} ${pc.dim(value)}`;
72
- }
46
+ } from "../types";
47
+ import { logEnvironmentInfo } from "./logging";
48
+ import { createCheckTableHelper, createSeedCheckContext } from "./seeding";
73
49
 
74
50
  // ═══════════════════════════════════════════════════════════════════════════
75
51
  // Environment Factory
@@ -107,13 +83,25 @@ export function createDevEnvironment<
107
83
  // Compute environment values
108
84
  const root = findMonorepoRoot();
109
85
  const suffix = options.suffix;
110
- const worktree = isWorktree(root);
111
- const portOffset = calculatePortOffset(suffix, root);
112
- const projectName = getProjectName(config.projectPrefix, suffix, root);
86
+ const identity = computeDevIdentity({
87
+ projectPrefix: config.projectPrefix,
88
+ suffix,
89
+ root,
90
+ worktreeIsolation: config.options?.worktreeIsolation,
91
+ });
92
+ const { worktree, projectSuffix, portOffset, projectName } = identity;
113
93
  const localIp = getLocalIp();
114
94
 
115
95
  const services = config.services;
116
96
  const apps = (config.apps ?? {}) as TApps;
97
+ const composeFile = getGeneratedComposePath(
98
+ root,
99
+ config.docker,
100
+ ).composeFileArg;
101
+
102
+ function ensureComposeFile(): string {
103
+ return writeGeneratedComposeFile(root, services, config.docker);
104
+ }
117
105
 
118
106
  // Compute ports and URLs
119
107
  const ports = computePorts(services, apps, portOffset) as ComputedPorts<
@@ -124,6 +112,22 @@ export function createDevEnvironment<
124
112
  TServices,
125
113
  TApps
126
114
  >;
115
+ const publicUrls: Record<string, string> = {};
116
+
117
+ function setPublicUrls(urlsInput: Record<string, string>): void {
118
+ for (const key of Object.keys(publicUrls)) {
119
+ delete publicUrls[key];
120
+ }
121
+ for (const [key, value] of Object.entries(urlsInput)) {
122
+ publicUrls[key] = value;
123
+ }
124
+ }
125
+
126
+ function clearPublicUrls(): void {
127
+ for (const key of Object.keys(publicUrls)) {
128
+ delete publicUrls[key];
129
+ }
130
+ }
127
131
 
128
132
  // Build environment variables
129
133
  function buildEnvVars(production = false): Record<string, string> {
@@ -144,12 +148,19 @@ export function createDevEnvironment<
144
148
  baseEnv[envName] = url;
145
149
  }
146
150
 
151
+ // Add public URL environment variables when tunnels are active
152
+ for (const [name, url] of Object.entries(publicUrls)) {
153
+ const envName = `${name.toUpperCase()}_PUBLIC_URL`;
154
+ baseEnv[envName] = url;
155
+ }
156
+
147
157
  // Call user's envVars function if provided
148
158
  if (config.envVars) {
149
159
  const userEnv = config.envVars(ports, urls, {
150
160
  projectName,
151
161
  localIp,
152
162
  portOffset,
163
+ publicUrls: publicUrls as ComputedPublicUrls<TServices, TApps>,
153
164
  });
154
165
  for (const [key, value] of Object.entries(userEnv)) {
155
166
  baseEnv[key] = String(value);
@@ -168,6 +179,7 @@ export function createDevEnvironment<
168
179
  projectName,
169
180
  ports,
170
181
  urls,
182
+ publicUrls: publicUrls as ComputedPublicUrls<TServices, TApps>,
171
183
  root,
172
184
  isCI: isCIEnv(),
173
185
  portOffset,
@@ -204,6 +216,7 @@ export function createDevEnvironment<
204
216
  } = startOptions;
205
217
 
206
218
  const envVars = buildEnvVars(productionBuild);
219
+ ensureComposeFile();
207
220
 
208
221
  // Log environment info
209
222
  if (verbose) {
@@ -223,16 +236,7 @@ export function createDevEnvironment<
223
236
  startContainers(root, projectName, envVars, {
224
237
  verbose,
225
238
  wait,
226
- composeFile: config.options?.composeFile,
227
- });
228
- }
229
-
230
- // Wait for services to be healthy
231
- if (wait) {
232
- await waitForAllServices(services, ports, {
233
- verbose,
234
- projectName,
235
- root,
239
+ composeFile,
236
240
  });
237
241
  }
238
242
 
@@ -266,19 +270,19 @@ export function createDevEnvironment<
266
270
  }),
267
271
  );
268
272
 
269
- // Check for failures
270
- for (const { name, result } of migrationResults) {
271
- if (result.exitCode !== 0) {
272
- console.error(`❌ Migration "${name}" failed`);
273
- if (result.stdout) {
274
- console.error(result.stdout);
275
- }
276
- if (result.stderr) {
277
- console.error(result.stderr);
273
+ // Check for failures
274
+ for (const { name, result } of migrationResults) {
275
+ if (result.exitCode !== 0) {
276
+ console.error(`❌ Migration "${name}" failed`);
277
+ if (result.stdout) {
278
+ console.error(result.stdout);
279
+ }
280
+ if (result.stderr) {
281
+ console.error(result.stderr);
282
+ }
283
+ throw new Error(`Migration "${name}" failed`);
278
284
  }
279
- throw new Error(`Migration "${name}" failed`);
280
285
  }
281
- }
282
286
 
283
287
  if (verbose) console.log("✓ Migrations complete");
284
288
  }
@@ -294,35 +298,14 @@ export function createDevEnvironment<
294
298
 
295
299
  // Check if seeding is needed using check function
296
300
  if (config.seed.check) {
297
- // Create checkTable helper function with typed service parameter
298
- const checkTable = async (
299
- tableName: string,
300
- service?: keyof TServices,
301
- ): Promise<boolean> => {
302
- const serviceName = (service ?? "postgres") as string;
303
- const serviceUrl = (urls as Record<string, string>)[serviceName];
304
- if (!serviceUrl) {
305
- console.warn(`⚠️ Service "${serviceName}" not found for checkTable`);
306
- return true; // Default to seeding if service not found
307
- }
308
- const checkResult = await exec(
309
- `psql "${serviceUrl}" -tAc 'SELECT COUNT(*) FROM "${tableName}" LIMIT 1'`,
310
- { throwOnError: false },
301
+ const checkTable = createCheckTableHelper<TServices, TApps>(
302
+ urls as Record<string, string>,
303
+ exec,
311
304
  );
312
- const count = checkResult.stdout.trim();
313
- const shouldSeed = checkResult.exitCode !== 0 || count === "0" || count === "";
314
- if (!shouldSeed) {
315
- console.log(` 📊 Table "${tableName}" has ${count} rows`);
316
- }
317
- return shouldSeed;
318
- };
319
-
320
- // Build seed check context with helpers
321
- const seedCheckContext = {
322
- ...getHookContext(),
305
+ const seedCheckContext = createSeedCheckContext(
306
+ getHookContext(),
323
307
  checkTable,
324
- };
325
-
308
+ );
326
309
  shouldSeed = await config.seed.check(seedCheckContext);
327
310
  }
328
311
 
@@ -388,6 +371,7 @@ export function createDevEnvironment<
388
371
 
389
372
  async function stop(stopOptions: StopOptions = {}): Promise<void> {
390
373
  const { verbose = true, removeVolumes = false } = stopOptions;
374
+ ensureComposeFile();
391
375
 
392
376
  // Run beforeStop hook
393
377
  if (config.hooks?.beforeStop) {
@@ -397,7 +381,7 @@ export function createDevEnvironment<
397
381
  stopContainers(root, projectName, {
398
382
  verbose,
399
383
  removeVolumes,
400
- composeFile: config.options?.composeFile,
384
+ composeFile,
401
385
  });
402
386
  }
403
387
 
@@ -446,54 +430,17 @@ export function createDevEnvironment<
446
430
  // ─────────────────────────────────────────────────────────────────────────
447
431
 
448
432
  function logInfo(label = "Docker Dev"): void {
449
- const serviceNames = Object.keys(services);
450
- const appNames = Object.keys(apps);
451
-
452
- console.log("");
453
- console.log(` ${pc.cyan(pc.bold(`🐳 ${label}`))}`);
454
- console.log(formatLabel("Project:", pc.white(projectName)));
455
-
456
- // Services section (Docker containers)
457
- if (serviceNames.length > 0) {
458
- console.log("");
459
- console.log(` ${pc.dim("─── Services ───")}`);
460
- for (const name of serviceNames) {
461
- const port = (ports as Record<string, number>)[name];
462
- const url = `localhost:${port}`;
463
- console.log(formatLabel(`${name}:`, formatUrl(`http://${url}`)));
464
- }
465
- }
466
-
467
- // Apps section (Dev servers)
468
- if (appNames.length > 0) {
469
- console.log("");
470
- console.log(` ${pc.dim("─── Applications ───")}`);
471
- for (const name of appNames) {
472
- const port = (ports as Record<string, number>)[name];
473
- const localUrl = `http://localhost:${port}`;
474
- const networkUrl = `http://${localIp}:${port}`;
475
-
476
- console.log(` ${pc.green("➜")} ${pc.bold(pc.cyan(name))}`);
477
- console.log(` ${pc.dim("Local:")} ${formatUrl(localUrl)}`);
478
- console.log(` ${pc.dim("Network:")} ${formatUrl(networkUrl)}`);
479
- }
480
- }
481
-
482
- // Environment info
483
- console.log("");
484
- console.log(` ${pc.dim("─── Environment ───")}`);
485
- console.log(formatDimLabel("Worktree:", worktree ? "yes" : "no"));
486
- console.log(
487
- formatDimLabel(
488
- "Port offset:",
489
- portOffset > 0 ? `+${portOffset}` : "none",
490
- ),
491
- );
492
- if (suffix) {
493
- console.log(formatDimLabel("Suffix:", suffix));
494
- }
495
- console.log(formatDimLabel("Local IP:", localIp));
496
- console.log("");
433
+ logEnvironmentInfo({
434
+ label,
435
+ projectName,
436
+ services,
437
+ apps,
438
+ ports: ports as Record<string, number>,
439
+ localIp,
440
+ worktree,
441
+ portOffset,
442
+ projectSuffix,
443
+ });
497
444
  }
498
445
 
499
446
  async function waitForServerUrl(
@@ -519,7 +466,7 @@ export function createDevEnvironment<
519
466
  await spawnWatchdogFn(projectName, root, {
520
467
  timeoutMinutes,
521
468
  verbose: true,
522
- composeFile: config.options?.composeFile,
469
+ composeFile,
523
470
  });
524
471
  }
525
472
 
@@ -562,11 +509,14 @@ export function createDevEnvironment<
562
509
  projectName,
563
510
  ports,
564
511
  urls,
512
+ publicUrls: publicUrls as ComputedPublicUrls<TServices, TApps>,
513
+ services,
565
514
  apps,
566
515
  portOffset,
567
516
  isWorktree: worktree,
568
517
  localIp,
569
518
  root,
519
+ composeFile,
570
520
 
571
521
  // Container management
572
522
  start,
@@ -581,6 +531,11 @@ export function createDevEnvironment<
581
531
 
582
532
  // Utilities
583
533
  buildEnvVars,
534
+ setPublicUrls: (urlsInput) => {
535
+ setPublicUrls(urlsInput as Record<string, string>);
536
+ },
537
+ clearPublicUrls,
538
+ ensureComposeFile,
584
539
  exec,
585
540
  waitForServer: waitForServerUrl,
586
541
  logInfo,
@@ -0,0 +1 @@
1
+ export { createDevEnvironment } from "./create-dev-environment";
@@ -0,0 +1,101 @@
1
+ import pc from "picocolors";
2
+
3
+ function formatUrl(url: string): string {
4
+ return pc.cyan(
5
+ url.replace(/:(\d+)(\/?)/, (_, port, slash) => `:${pc.bold(port)}${slash}`),
6
+ );
7
+ }
8
+
9
+ function formatLabel(label: string, value: string, arrow = "➜"): string {
10
+ return ` ${pc.green(arrow)} ${pc.bold(label.padEnd(10))} ${value}`;
11
+ }
12
+
13
+ function formatDimLabel(label: string, value: string): string {
14
+ return ` ${pc.dim("•")} ${pc.dim(label.padEnd(10))} ${pc.dim(value)}`;
15
+ }
16
+
17
+ export function logEnvironmentInfo(input: {
18
+ label: string;
19
+ projectName: string;
20
+ services: Record<string, unknown>;
21
+ apps: Record<string, unknown>;
22
+ ports: Record<string, number>;
23
+ localIp: string;
24
+ worktree: boolean;
25
+ portOffset: number;
26
+ projectSuffix?: string;
27
+ }): void {
28
+ const {
29
+ label,
30
+ projectName,
31
+ services,
32
+ apps,
33
+ ports,
34
+ localIp,
35
+ worktree,
36
+ portOffset,
37
+ projectSuffix,
38
+ } = input;
39
+ const serviceNames = Object.keys(services);
40
+ const appNames = Object.keys(apps);
41
+
42
+ console.log("");
43
+ console.log(` ${pc.cyan(pc.bold(`🐳 ${label}`))}`);
44
+ console.log(formatLabel("Project:", pc.white(projectName)));
45
+
46
+ if (serviceNames.length > 0) {
47
+ console.log("");
48
+ console.log(` ${pc.dim("─── Services ───")}`);
49
+ for (const name of serviceNames) {
50
+ const port = ports[name];
51
+ const url = `localhost:${port}`;
52
+ console.log(formatLabel(`${name}:`, formatUrl(`http://${url}`)));
53
+ }
54
+ }
55
+
56
+ if (appNames.length > 0) {
57
+ console.log("");
58
+ console.log(` ${pc.dim("─── Applications ───")}`);
59
+ for (const name of appNames) {
60
+ const port = ports[name];
61
+ const localUrl = `http://localhost:${port}`;
62
+ const networkUrl = `http://${localIp}:${port}`;
63
+
64
+ console.log(` ${pc.green("➜")} ${pc.bold(pc.cyan(name))}`);
65
+ console.log(` ${pc.dim("Local:")} ${formatUrl(localUrl)}`);
66
+ console.log(` ${pc.dim("Network:")} ${formatUrl(networkUrl)}`);
67
+ }
68
+ }
69
+
70
+ console.log("");
71
+ console.log(` ${pc.dim("─── Environment ───")}`);
72
+ console.log(formatDimLabel("Worktree:", worktree ? "yes" : "no"));
73
+ console.log(
74
+ formatDimLabel("Port offset:", portOffset > 0 ? `+${portOffset}` : "none"),
75
+ );
76
+ if (projectSuffix) {
77
+ console.log(formatDimLabel("Suffix:", projectSuffix));
78
+ }
79
+ console.log(formatDimLabel("Local IP:", localIp));
80
+ console.log("");
81
+ }
82
+
83
+ export function logPublicUrls(
84
+ tunnels: Array<{
85
+ kind: "service" | "app";
86
+ name: string;
87
+ publicUrl: string;
88
+ localUrl: string;
89
+ }>,
90
+ ): void {
91
+ if (tunnels.length === 0) return;
92
+
93
+ console.log("");
94
+ console.log(` ${pc.dim("─── Public URLs (Quick Tunnel) ───")}`);
95
+ for (const tunnel of tunnels) {
96
+ const label = `${tunnel.name} (${tunnel.kind})`;
97
+ console.log(formatLabel(`${label}:`, formatUrl(tunnel.publicUrl), "🌐"));
98
+ console.log(formatDimLabel("Local target:", tunnel.localUrl));
99
+ }
100
+ console.log("");
101
+ }
@@ -0,0 +1,57 @@
1
+ import type {
2
+ AppConfig,
3
+ HookContext,
4
+ SeedCheckContext,
5
+ ServiceConfig,
6
+ } from "../types";
7
+
8
+ export function createCheckTableHelper<
9
+ TServices extends Record<string, ServiceConfig>,
10
+ TApps extends Record<string, AppConfig>,
11
+ >(
12
+ urls: Record<string, string>,
13
+ exec: (
14
+ cmd: string,
15
+ options?: { throwOnError?: boolean },
16
+ ) => Promise<{
17
+ exitCode: number;
18
+ stdout: string;
19
+ stderr: string;
20
+ }>,
21
+ ): SeedCheckContext<TServices, TApps>["checkTable"] {
22
+ return async (
23
+ tableName: string,
24
+ service?: keyof TServices,
25
+ ): Promise<boolean> => {
26
+ const serviceName = (service ?? "postgres") as string;
27
+ const serviceUrl = urls[serviceName];
28
+ if (!serviceUrl) {
29
+ console.warn(`⚠️ Service "${serviceName}" not found for checkTable`);
30
+ return true;
31
+ }
32
+ const checkResult = await exec(
33
+ `psql "${serviceUrl}" -tAc 'SELECT COUNT(*) FROM "${tableName}" LIMIT 1'`,
34
+ { throwOnError: false },
35
+ );
36
+ const count = checkResult.stdout.trim();
37
+ const shouldSeed =
38
+ checkResult.exitCode !== 0 || count === "0" || count === "";
39
+ if (!shouldSeed) {
40
+ console.log(` 📊 Table "${tableName}" has ${count} rows`);
41
+ }
42
+ return shouldSeed;
43
+ };
44
+ }
45
+
46
+ export function createSeedCheckContext<
47
+ TServices extends Record<string, ServiceConfig>,
48
+ TApps extends Record<string, AppConfig>,
49
+ >(
50
+ baseContext: HookContext<TServices, TApps>,
51
+ checkTable: SeedCheckContext<TServices, TApps>["checkTable"],
52
+ ): SeedCheckContext<TServices, TApps> {
53
+ return {
54
+ ...baseContext,
55
+ checkTable,
56
+ };
57
+ }
@@ -3,25 +3,33 @@
3
3
  // ═══════════════════════════════════════════════════════════════════════════
4
4
 
5
5
  // CLI runner
6
- export { getFlagValue, hasFlag, runCli } from "./cli";
6
+ export { getFlagValue, hasFlag, runCli } from "./cli/run-cli";
7
7
  // Config factory
8
8
  export {
9
9
  assertValidConfig,
10
10
  defineDevConfig,
11
11
  mergeConfigs,
12
12
  validateConfig,
13
- } from "./config";
13
+ } from "./config/index";
14
+ export type {
15
+ ClickhouseServiceOptions,
16
+ CustomServiceOptions,
17
+ PostgresServiceOptions,
18
+ RedisServiceOptions,
19
+ } from "./docker-compose/services";
20
+ // Service helpers
21
+ export { service } from "./docker-compose/services";
14
22
  // Environment factory
15
- export { createDevEnvironment } from "./environment";
23
+ export { createDevEnvironment } from "./environment/index";
24
+ // Config loader (for programmatic access)
25
+ export { clearDevEnvCache, getDevEnv, loadDevEnv } from "./loader/index";
16
26
  // Lint / Typecheck
17
27
  export {
18
28
  runWorkspaceTypecheck,
19
29
  type TypecheckResult,
20
30
  type WorkspaceTypecheckOptions,
21
31
  type WorkspaceTypecheckResult,
22
- } from "./lint";
23
- // Config loader (for programmatic access)
24
- export { clearDevEnvCache, getDevEnv, loadDevEnv } from "./loader";
32
+ } from "./typecheck/index";
25
33
 
26
34
  // ═══════════════════════════════════════════════════════════════════════════
27
35
  // Types
@@ -34,6 +42,7 @@ export type {
34
42
  CliOptions,
35
43
  // Computed types
36
44
  ComputedPorts,
45
+ ComputedPublicUrls,
37
46
  ComputedUrls,
38
47
  // Main config
39
48
  DevConfig,
@@ -42,6 +51,14 @@ export type {
42
51
  DevHooks,
43
52
  DevOptions,
44
53
  DevServerPids,
54
+ DockerComposeGenerationOptions,
55
+ DockerComposeHealthcheckRaw,
56
+ DockerComposeNode,
57
+ DockerComposeServiceRaw,
58
+ DockerComposeVolumeRaw,
59
+ DockerPresetName,
60
+ DockerPresetServiceDefinition,
61
+ DockerServiceDefinition,
45
62
  EnvVarsBuilder,
46
63
  ExecOptions,
47
64
  HealthCheckFn,
@@ -61,28 +78,22 @@ export type {
61
78
  StopOptions,
62
79
  UrlBuilderContext,
63
80
  UrlBuilderFn,
64
- } from "./types";
81
+ } from "./types/index";
65
82
 
66
83
  // ═══════════════════════════════════════════════════════════════════════════
67
84
  // Core Utilities (for advanced use cases)
68
85
  // ═══════════════════════════════════════════════════════════════════════════
69
86
 
70
- export {
71
- areContainersRunning,
72
- isContainerRunning,
73
- MAX_ATTEMPTS,
74
- POLL_INTERVAL,
75
- } from "./core/docker";
76
-
77
87
  export { getLocalIp, isPortAvailable, waitForServer } from "./core/network";
78
88
  export {
79
89
  calculatePortOffset,
90
+ computeDevIdentity,
80
91
  findMonorepoRoot,
81
92
  getProjectName,
82
93
  getWorktreeName,
94
+ getWorktreeProjectSuffix,
83
95
  isWorktree,
84
96
  } from "./core/ports";
85
-
86
97
  export {
87
98
  getProcessOnPort,
88
99
  isPortInUse,
@@ -91,6 +102,13 @@ export {
91
102
  killProcessOnPort,
92
103
  killProcessOnPortAndWait,
93
104
  } from "./core/process";
105
+ export {
106
+ type PublicExposeTarget,
107
+ type PublicTunnel,
108
+ resolveExposeTargets,
109
+ startPublicTunnels,
110
+ stopPublicTunnels,
111
+ } from "./core/tunnel";
94
112
  export {
95
113
  getEnvVar,
96
114
  isCI,
@@ -108,3 +126,19 @@ export {
108
126
  stopHeartbeat,
109
127
  stopWatchdog,
110
128
  } from "./core/watchdog";
129
+ export {
130
+ areContainersRunning,
131
+ assertDockerRunning,
132
+ DOCKER_NOT_RUNNING_MESSAGE,
133
+ isContainerRunning,
134
+ isDockerRunning,
135
+ MAX_ATTEMPTS,
136
+ POLL_INTERVAL,
137
+ } from "./docker/index";
138
+ export {
139
+ buildComposeModel,
140
+ composeToYaml,
141
+ DEFAULT_GENERATED_COMPOSE_FILE,
142
+ getGeneratedComposePath,
143
+ writeGeneratedComposeFile,
144
+ } from "./docker-compose/index";
@@ -0,0 +1,23 @@
1
+ import type { AppConfig, DevEnvironment, ServiceConfig } from "../types";
2
+
3
+ let cachedEnv: DevEnvironment<
4
+ Record<string, ServiceConfig>,
5
+ Record<string, AppConfig>
6
+ > | null = null;
7
+
8
+ export function setCachedDevEnv(
9
+ env: DevEnvironment<Record<string, ServiceConfig>, Record<string, AppConfig>>,
10
+ ): void {
11
+ cachedEnv = env;
12
+ }
13
+
14
+ export function getCachedDevEnv(): DevEnvironment<
15
+ Record<string, ServiceConfig>,
16
+ Record<string, AppConfig>
17
+ > | null {
18
+ return cachedEnv;
19
+ }
20
+
21
+ export function clearDevEnvCache(): void {
22
+ cachedEnv = null;
23
+ }