buncargo 1.0.29 → 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 (221) hide show
  1. package/dist/bin.d.ts +1 -12
  2. package/dist/bin.js +261 -253
  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 -83
  22. package/dist/core/docker.js +35 -32
  23. package/dist/core/index.d.ts +1 -1
  24. package/dist/core/index.js +123 -118
  25. package/dist/core/network.js +2 -2
  26. package/dist/core/ports.js +1 -1
  27. package/dist/core/process.js +1 -1
  28. package/dist/core/tunnel.d.ts +33 -0
  29. package/dist/core/utils.js +2 -2
  30. package/dist/core/watchdog-runner.js +45 -42
  31. package/dist/core/watchdog.d.ts +1 -0
  32. package/dist/core/watchdog.js +4 -2
  33. package/dist/docker/index.d.ts +1 -0
  34. package/dist/docker/index.js +38 -0
  35. package/dist/docker/runtime.d.ts +87 -0
  36. package/dist/docker/runtime.js +37 -0
  37. package/dist/docker-compose/compose.d.ts +1 -0
  38. package/dist/docker-compose/generated-file.d.ts +7 -0
  39. package/dist/docker-compose/index.d.ts +3 -0
  40. package/dist/docker-compose/index.js +15 -0
  41. package/dist/docker-compose/model.d.ts +6 -0
  42. package/dist/docker-compose/services/clickhouse.d.ts +16 -0
  43. package/dist/docker-compose/services/define-docker-service.d.ts +41 -0
  44. package/dist/docker-compose/services/index.d.ts +23 -0
  45. package/dist/docker-compose/services/index.js +17 -0
  46. package/dist/docker-compose/services/postgres.d.ts +12 -0
  47. package/dist/docker-compose/services/redis.d.ts +12 -0
  48. package/dist/docker-compose/services/shared.d.ts +7 -0
  49. package/dist/docker-compose/yaml.d.ts +2 -0
  50. package/dist/environment/create-dev-environment.d.ts +23 -0
  51. package/dist/environment/index.d.ts +1 -0
  52. package/dist/environment/index.js +15 -0
  53. package/dist/environment/logging.d.ts +17 -0
  54. package/dist/environment/seeding.d.ts +9 -0
  55. package/dist/environment.d.ts +1 -23
  56. package/dist/environment.js +12 -14
  57. package/dist/index-045jksh5.js +147 -0
  58. package/dist/index-08wa79cs.js +125 -117
  59. package/dist/index-0kxnae3z.js +335 -0
  60. package/dist/index-1mdrf7nz.js +51 -43
  61. package/dist/index-1yvbwj4k.js +262 -242
  62. package/dist/index-23ev345g.js +475 -0
  63. package/dist/index-2ckr49sf.js +228 -0
  64. package/dist/index-2f47khe5.js +376 -369
  65. package/dist/index-2fr3g85b.js +220 -183
  66. package/dist/index-38xnzpa6.js +450 -0
  67. package/dist/index-3h3dhtf2.js +51 -43
  68. package/dist/index-42x95209.js +51 -43
  69. package/dist/index-4gp0az1g.js +145 -0
  70. package/dist/index-4xrxh8yv.js +72 -0
  71. package/dist/index-5gmws6ah.js +181 -0
  72. package/dist/index-5hka0tff.js +78 -76
  73. package/dist/index-5rfqps4b.js +3 -0
  74. package/dist/index-5t9jxqm0.js +428 -0
  75. package/dist/index-6c1w1xk5.js +101 -0
  76. package/dist/index-6fm7mvwj.js +118 -97
  77. package/dist/index-6srpc523.js +127 -128
  78. package/dist/index-731rzzfp.js +157 -142
  79. package/dist/index-75y4cg2z.js +51 -43
  80. package/dist/index-7ja4ywyj.js +126 -127
  81. package/dist/index-8bw1cmz4.js +531 -0
  82. package/dist/index-8hbbj1mp.js +120 -121
  83. package/dist/index-8xj2p5n5.js +118 -97
  84. package/dist/index-bj79tw5w.js +0 -0
  85. package/dist/index-bnk6nr0g.js +73 -0
  86. package/dist/index-brbbzyks.js +72 -0
  87. package/dist/index-c0dr6mcv.js +123 -0
  88. package/dist/index-cty0bcry.js +235 -218
  89. package/dist/index-d8tyv5se.js +228 -0
  90. package/dist/index-d9efy0n4.js +176 -150
  91. package/dist/index-etfmqjjf.js +427 -0
  92. package/dist/index-fb29934k.js +172 -0
  93. package/dist/index-g50jw1yf.js +72 -0
  94. package/dist/index-g6eb5wdw.js +118 -117
  95. package/dist/index-ggq3yryx.js +99 -95
  96. package/dist/index-h70tce00.js +177 -0
  97. package/dist/index-hkxtfqtc.js +333 -0
  98. package/dist/index-kf3dhser.js +146 -143
  99. package/dist/index-ma6tgdb2.js +500 -0
  100. package/dist/index-mam0bcyz.js +123 -0
  101. package/dist/index-mm412dkp.js +274 -0
  102. package/dist/index-n8v18aeb.js +0 -0
  103. package/dist/index-ndnmnsej.js +378 -371
  104. package/dist/index-p8wty0e2.js +389 -379
  105. package/dist/index-qfphr2fd.js +78 -76
  106. package/dist/index-qqmms8rs.js +51 -43
  107. package/dist/index-qw4093g2.js +51 -43
  108. package/dist/index-qzwpzjbx.js +121 -122
  109. package/dist/index-segbnm0h.js +146 -143
  110. package/dist/index-t0fj6gg1.js +112 -0
  111. package/dist/index-thdkwnv7.js +122 -0
  112. package/dist/index-tjbx2r2t.js +270 -0
  113. package/dist/index-tjqw9vtj.js +62 -54
  114. package/dist/index-vbpb89jy.js +248 -0
  115. package/dist/index-vhs88xhe.js +99 -95
  116. package/dist/index-w8zxnjka.js +249 -0
  117. package/dist/index-wk2na3t9.js +385 -375
  118. package/dist/index-wz9x8g7z.js +383 -373
  119. package/dist/index-x249gyde.js +388 -378
  120. package/dist/index-xkvd0nsd.js +187 -0
  121. package/dist/index-yedqxm1z.js +80 -0
  122. package/dist/index-zfjzzjkf.js +240 -199
  123. package/dist/index.d.ts +12 -8
  124. package/dist/index.js +56 -35
  125. package/dist/lint.d.ts +1 -46
  126. package/dist/lint.js +3 -7
  127. package/dist/loader/cache.d.ts +4 -0
  128. package/dist/loader/find-config-file.d.ts +2 -0
  129. package/dist/loader/index.d.ts +5 -0
  130. package/dist/loader/index.js +24 -0
  131. package/dist/loader/load-dev-env.d.ts +5 -0
  132. package/dist/loader/loader.d.ts +1 -0
  133. package/dist/loader.d.ts +1 -45
  134. package/dist/loader.js +22 -20
  135. package/dist/prisma/index.d.ts +1 -0
  136. package/dist/prisma/prisma.d.ts +29 -0
  137. package/dist/prisma.d.ts +1 -29
  138. package/dist/prisma.js +6 -10
  139. package/dist/src/bin.js +309 -0
  140. package/dist/src/cli.js +5 -0
  141. package/dist/src/config.js +15 -0
  142. package/dist/src/core/docker.js +38 -0
  143. package/dist/src/core/index.js +130 -0
  144. package/dist/src/core/network.js +9 -0
  145. package/dist/src/core/ports.js +23 -0
  146. package/dist/src/core/process.js +31 -0
  147. package/dist/src/core/utils.js +11 -0
  148. package/dist/src/core/watchdog-runner.js +69 -0
  149. package/dist/src/core/watchdog.js +28 -0
  150. package/dist/src/docker/runtime.js +37 -0
  151. package/dist/src/docker-compose/index.js +16 -0
  152. package/dist/src/docker-compose/services/index.js +17 -0
  153. package/dist/src/environment.js +12 -0
  154. package/dist/src/index.js +122 -0
  155. package/dist/src/lint.js +3 -0
  156. package/dist/src/loader.js +25 -0
  157. package/dist/src/prisma.js +6 -0
  158. package/dist/src/types.js +0 -0
  159. package/dist/typecheck/index.d.ts +1 -0
  160. package/dist/typecheck/index.js +7 -0
  161. package/dist/typecheck/typecheck.d.ts +46 -0
  162. package/dist/types/all-types.d.ts +501 -0
  163. package/dist/types/cli.d.ts +1 -0
  164. package/dist/types/config.d.ts +6 -0
  165. package/dist/types/docker.d.ts +15 -0
  166. package/dist/types/environment.d.ts +8 -0
  167. package/dist/types/hooks.d.ts +9 -0
  168. package/dist/types/index.d.ts +1 -0
  169. package/dist/types/index.js +0 -0
  170. package/dist/types/prisma.d.ts +1 -0
  171. package/dist/types.d.ts +1 -399
  172. package/package.json +145 -140
  173. package/readme.md +349 -109
  174. package/src/cli/bin.ts +77 -0
  175. package/src/cli/commands/help.ts +39 -0
  176. package/src/cli/commands/runtime.ts +72 -0
  177. package/src/cli/commands/version.ts +4 -0
  178. package/src/cli/index.ts +1 -0
  179. package/{cli.ts → src/cli/run-cli.ts} +95 -6
  180. package/src/config/define-config.ts +30 -0
  181. package/src/config/index.ts +3 -0
  182. package/src/config/merge-configs.ts +33 -0
  183. package/src/config/validate-config.ts +136 -0
  184. package/{core → src/core}/index.ts +2 -2
  185. package/{core → src/core}/ports.ts +5 -2
  186. package/{core → src/core}/process.ts +6 -2
  187. package/src/core/tunnel.ts +151 -0
  188. package/{core → src/core}/utils.ts +1 -0
  189. package/{core → src/core}/watchdog.ts +5 -1
  190. package/src/docker/index.ts +1 -0
  191. package/{core/docker.ts → src/docker/runtime.ts} +11 -4
  192. package/src/docker-compose/generated-file.ts +45 -0
  193. package/src/docker-compose/index.ts +7 -0
  194. package/src/docker-compose/model.ts +197 -0
  195. package/src/docker-compose/services/clickhouse.ts +79 -0
  196. package/src/docker-compose/services/define-docker-service.ts +109 -0
  197. package/src/docker-compose/services/index.ts +67 -0
  198. package/src/docker-compose/services/postgres.ts +60 -0
  199. package/src/docker-compose/services/redis.ts +48 -0
  200. package/src/docker-compose/services/shared.ts +79 -0
  201. package/src/docker-compose/yaml.ts +88 -0
  202. package/{environment.ts → src/environment/create-dev-environment.ts} +93 -130
  203. package/src/environment/index.ts +1 -0
  204. package/src/environment/logging.ts +101 -0
  205. package/src/environment/seeding.ts +57 -0
  206. package/{index.ts → src/index.ts} +49 -20
  207. package/src/loader/cache.ts +23 -0
  208. package/src/loader/find-config-file.ts +29 -0
  209. package/src/loader/index.ts +17 -0
  210. package/src/loader/load-dev-env.ts +38 -0
  211. package/src/prisma/index.ts +1 -0
  212. package/{prisma.ts → src/prisma/prisma.ts} +4 -2
  213. package/src/typecheck/index.ts +1 -0
  214. package/{types.ts → src/types/all-types.ts} +130 -5
  215. package/src/types/index.ts +1 -0
  216. package/bin.ts +0 -192
  217. package/config.ts +0 -194
  218. package/loader.ts +0 -126
  219. /package/{core → src/core}/network.ts +0 -0
  220. /package/{core → src/core}/watchdog-runner.ts +0 -0
  221. /package/{lint.ts → src/typecheck/typecheck.ts} +0 -0
@@ -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,33 +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
- assertDockerRunning,
73
- DOCKER_NOT_RUNNING_MESSAGE,
74
- isContainerRunning,
75
- isDockerRunning,
76
- MAX_ATTEMPTS,
77
- POLL_INTERVAL,
78
- } from "./core/docker";
79
-
80
87
  export { getLocalIp, isPortAvailable, waitForServer } from "./core/network";
81
88
  export {
82
- computeDevIdentity,
83
89
  calculatePortOffset,
90
+ computeDevIdentity,
84
91
  findMonorepoRoot,
85
92
  getProjectName,
86
- getWorktreeProjectSuffix,
87
93
  getWorktreeName,
94
+ getWorktreeProjectSuffix,
88
95
  isWorktree,
89
96
  } from "./core/ports";
90
-
91
97
  export {
92
98
  getProcessOnPort,
93
99
  isPortInUse,
@@ -96,6 +102,13 @@ export {
96
102
  killProcessOnPort,
97
103
  killProcessOnPortAndWait,
98
104
  } from "./core/process";
105
+ export {
106
+ type PublicExposeTarget,
107
+ type PublicTunnel,
108
+ resolveExposeTargets,
109
+ startPublicTunnels,
110
+ stopPublicTunnels,
111
+ } from "./core/tunnel";
99
112
  export {
100
113
  getEnvVar,
101
114
  isCI,
@@ -113,3 +126,19 @@ export {
113
126
  stopHeartbeat,
114
127
  stopWatchdog,
115
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
+ }
@@ -0,0 +1,29 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+
4
+ export const CONFIG_FILES = [
5
+ "dev.config.ts",
6
+ "dev.config.js",
7
+ "dev-tools.config.ts",
8
+ "dev-tools.config.js",
9
+ ];
10
+
11
+ export function findConfigFile(startDir: string): string | null {
12
+ let currentDir = startDir;
13
+
14
+ while (true) {
15
+ for (const file of CONFIG_FILES) {
16
+ const configPath = join(currentDir, file);
17
+ if (existsSync(configPath)) {
18
+ return configPath;
19
+ }
20
+ }
21
+
22
+ const parentDir = dirname(currentDir);
23
+ if (parentDir === currentDir) {
24
+ return null;
25
+ }
26
+
27
+ currentDir = parentDir;
28
+ }
29
+ }
@@ -0,0 +1,17 @@
1
+ import type { AppConfig, DevEnvironment, ServiceConfig } from "../types";
2
+ import { getCachedDevEnv } from "./cache";
3
+
4
+ export { clearDevEnvCache } from "./cache";
5
+ export { CONFIG_FILES, findConfigFile } from "./find-config-file";
6
+ export { loadDevEnv } from "./load-dev-env";
7
+
8
+ export function getDevEnv(): DevEnvironment<
9
+ Record<string, ServiceConfig>,
10
+ Record<string, AppConfig>
11
+ > {
12
+ const env = getCachedDevEnv();
13
+ if (!env) {
14
+ throw new Error("Dev environment not loaded. Call loadDevEnv() first.");
15
+ }
16
+ return env;
17
+ }
@@ -0,0 +1,38 @@
1
+ import { createDevEnvironment } from "../environment";
2
+ import type { AppConfig, DevEnvironment, ServiceConfig } from "../types";
3
+ import { getCachedDevEnv, setCachedDevEnv } from "./cache";
4
+ import { findConfigFile } from "./find-config-file";
5
+
6
+ export async function loadDevEnv(options?: {
7
+ cwd?: string;
8
+ reload?: boolean;
9
+ }): Promise<
10
+ DevEnvironment<Record<string, ServiceConfig>, Record<string, AppConfig>>
11
+ > {
12
+ if (!options?.reload) {
13
+ const cached = getCachedDevEnv();
14
+ if (cached) return cached;
15
+ }
16
+
17
+ const cwd = options?.cwd ?? process.cwd();
18
+ const configPath = findConfigFile(cwd);
19
+
20
+ if (configPath) {
21
+ const mod = await import(configPath);
22
+ const config = mod.default;
23
+
24
+ if (!config?.projectPrefix || !config?.services) {
25
+ throw new Error(
26
+ `Invalid config in "${configPath}". Use defineDevConfig() and export as default.`,
27
+ );
28
+ }
29
+
30
+ const env = createDevEnvironment(config);
31
+ setCachedDevEnv(env);
32
+ return env;
33
+ }
34
+
35
+ throw new Error(
36
+ "No config file found. Create dev.config.ts with: export default defineDevConfig({ ... })",
37
+ );
38
+ }
@@ -0,0 +1 @@
1
+ export * from "./prisma";
@@ -28,7 +28,7 @@ import {
28
28
  isContainerRunning,
29
29
  startService,
30
30
  waitForServiceByType,
31
- } from "./core/docker";
31
+ } from "../docker/runtime";
32
32
  import type {
33
33
  AppConfig,
34
34
  BuiltInHealthCheck,
@@ -36,7 +36,7 @@ import type {
36
36
  PrismaConfig,
37
37
  PrismaRunner,
38
38
  ServiceConfig,
39
- } from "./types";
39
+ } from "../types";
40
40
 
41
41
  /**
42
42
  * Create a Prisma runner from config (used internally by createDevEnvironment).
@@ -80,9 +80,11 @@ export function createPrismaRunner<
80
80
 
81
81
  console.log(`🐳 Starting ${service}...`);
82
82
 
83
+ const composeFile = env.ensureComposeFile();
83
84
  const envVars = env.buildEnvVars();
84
85
  startService(env.root, env.projectName, service, envVars, {
85
86
  verbose: false,
87
+ composeFile,
86
88
  });
87
89
 
88
90
  const port = (env.ports as Record<string, number>)[service];
@@ -0,0 +1 @@
1
+ export * from "./typecheck";
@@ -27,12 +27,106 @@ export interface UrlBuilderContext {
27
27
  */
28
28
  export type UrlBuilderFn = (ctx: UrlBuilderContext) => string;
29
29
 
30
+ // ═══════════════════════════════════════════════════════════════════════════
31
+ // Docker Compose Configuration
32
+ // ═══════════════════════════════════════════════════════════════════════════
33
+
34
+ /**
35
+ * Recursive YAML-safe value used for Docker Compose objects.
36
+ */
37
+ export type DockerComposeNode =
38
+ | string
39
+ | number
40
+ | boolean
41
+ | null
42
+ | DockerComposeNode[]
43
+ | { [key: string]: DockerComposeNode | undefined };
44
+
45
+ /**
46
+ * Built-in Docker service presets.
47
+ */
48
+ export type DockerPresetName = "postgres" | "redis" | "clickhouse";
49
+
50
+ /**
51
+ * Docker Compose healthcheck object.
52
+ */
53
+ export interface DockerComposeHealthcheckRaw {
54
+ test?: string[] | string;
55
+ interval?: string;
56
+ timeout?: string;
57
+ retries?: number;
58
+ start_period?: string;
59
+ disable?: boolean;
60
+ [composeKey: string]: DockerComposeNode | undefined;
61
+ }
62
+
63
+ /**
64
+ * Docker Compose service (raw escape hatch).
65
+ * Includes common fields plus index signature for advanced keys.
66
+ */
67
+ export interface DockerComposeServiceRaw {
68
+ image?: string;
69
+ container_name?: string;
70
+ ports?: string[];
71
+ volumes?: string[];
72
+ environment?: Record<string, string | number | boolean>;
73
+ command?: string | string[];
74
+ entrypoint?: string | string[];
75
+ depends_on?: string[] | Record<string, DockerComposeNode>;
76
+ healthcheck?: DockerComposeHealthcheckRaw;
77
+ ulimits?: Record<string, number | { soft: number; hard: number }>;
78
+ restart?: string;
79
+ working_dir?: string;
80
+ [composeKey: string]: DockerComposeNode | undefined;
81
+ }
82
+
83
+ /**
84
+ * Docker Compose volume object.
85
+ */
86
+ export interface DockerComposeVolumeRaw {
87
+ driver?: string;
88
+ driver_opts?: Record<string, string | number | boolean>;
89
+ [composeKey: string]: DockerComposeNode | undefined;
90
+ }
91
+
92
+ /**
93
+ * Helper-friendly preset service definition.
94
+ */
95
+ export interface DockerPresetServiceDefinition {
96
+ kind: "preset";
97
+ preset: DockerPresetName;
98
+ service?: DockerComposeServiceRaw;
99
+ }
100
+
101
+ /**
102
+ * Docker service definition accepted by service config.
103
+ * - raw object is the manual escape hatch
104
+ * - helper mode returns `kind`-based definitions
105
+ */
106
+ export type DockerServiceDefinition =
107
+ | DockerComposeServiceRaw
108
+ | DockerPresetServiceDefinition;
109
+
110
+ /**
111
+ * Docker Compose generation configuration.
112
+ */
113
+ export interface DockerComposeGenerationOptions {
114
+ /** Path to generated compose file relative to root. Default: '.buncargo/docker-compose.generated.yml' */
115
+ generatedFile?: string;
116
+ /** Write strategy for generated compose file. Default: 'always' */
117
+ writeStrategy?: "always" | "if-missing";
118
+ /** Extra top-level named volumes */
119
+ volumes?: Record<string, DockerComposeVolumeRaw>;
120
+ }
121
+
30
122
  /**
31
123
  * Configuration for a Docker Compose service (e.g., postgres, redis).
32
124
  */
33
125
  export interface ServiceConfig {
34
126
  /** Base port for the service (before offset is applied) */
35
127
  port: number;
128
+ /** Whether this service can be exposed publicly via tunnel */
129
+ expose?: boolean;
36
130
  /** Optional secondary port (e.g., ClickHouse native protocol) */
37
131
  secondaryPort?: number;
38
132
  /** Health check: built-in name, custom function, or disabled (false) */
@@ -53,6 +147,8 @@ export interface ServiceConfig {
53
147
  user?: string;
54
148
  /** Password (default: 'postgres' for postgres, 'root' for mysql, 'clickhouse' for clickhouse) */
55
149
  password?: string;
150
+ /** Docker Compose service definition (preset helper or raw escape hatch) */
151
+ docker?: DockerServiceDefinition;
56
152
  }
57
153
 
58
154
  // ═══════════════════════════════════════════════════════════════════════════
@@ -65,6 +161,8 @@ export interface ServiceConfig {
65
161
  export interface AppConfig {
66
162
  /** Base port for the app (before offset is applied) */
67
163
  port: number;
164
+ /** Whether this app can be exposed publicly via tunnel */
165
+ expose?: boolean;
68
166
  /** Command to start the dev server */
69
167
  devCommand: string;
70
168
  /** Command to start production server (optional) */
@@ -110,6 +208,8 @@ export interface HookContext<
110
208
  ports: ComputedPorts<TServices, TApps>;
111
209
  /** Computed URLs for all services and apps */
112
210
  urls: ComputedUrls<TServices, TApps>;
211
+ /** Public tunnel URLs for exposed services/apps (when active) */
212
+ publicUrls: ComputedPublicUrls<TServices, TApps>;
113
213
  /** Execute a shell command with environment variables set */
114
214
  exec: (
115
215
  cmd: string,
@@ -269,8 +369,6 @@ export interface DevOptions {
269
369
  worktreeIsolation?: boolean;
270
370
  /** Auto-shutdown after idle time in ms. Set to false to disable. Default: false */
271
371
  autoShutdown?: number | false;
272
- /** Path to docker-compose.yml relative to root. Default: 'docker-compose.yml' */
273
- composeFile?: string;
274
372
  /** Default verbose setting for all operations. Default: true */
275
373
  verbose?: boolean;
276
374
  }
@@ -284,7 +382,12 @@ export type EnvVarsBuilder<
284
382
  > = (
285
383
  ports: ComputedPorts<TServices, TApps>,
286
384
  urls: ComputedUrls<TServices, TApps>,
287
- ctx: { projectName: string; localIp: string; portOffset: number },
385
+ ctx: {
386
+ projectName: string;
387
+ localIp: string;
388
+ portOffset: number;
389
+ publicUrls: ComputedPublicUrls<TServices, TApps>;
390
+ },
288
391
  ) => Record<string, string | number>;
289
392
 
290
393
  /**
@@ -308,11 +411,12 @@ export interface DevConfig<
308
411
  *
309
412
  * @example
310
413
  * ```typescript
311
- * envVars: (ports, urls, { localIp }) => ({
414
+ * envVars: (ports, urls, { localIp, publicUrls }) => ({
312
415
  * DATABASE_URL: urls.postgres,
313
416
  * BASE_URL: urls.api,
314
417
  * VITE_PORT: ports.platform,
315
- * EXPO_API_URL: `http://${localIp}:${ports.api}`
418
+ * EXPO_API_URL: `http://${localIp}:${ports.api}`,
419
+ * WEBHOOK_URL: publicUrls.api
316
420
  * })
317
421
  * ```
318
422
  */
@@ -327,6 +431,8 @@ export interface DevConfig<
327
431
  prisma?: PrismaConfig;
328
432
  /** Additional options (optional) */
329
433
  options?: DevOptions;
434
+ /** Docker Compose generation options (optional) */
435
+ docker?: DockerComposeGenerationOptions;
330
436
  }
331
437
 
332
438
  // ═══════════════════════════════════════════════════════════════════════════
@@ -366,6 +472,13 @@ export type ComputedUrls<
366
472
  [K in keyof TApps]: string;
367
473
  };
368
474
 
475
+ export type ComputedPublicUrls<
476
+ TServices extends Record<string, ServiceConfig>,
477
+ TApps extends Record<string, AppConfig>,
478
+ > = Partial<{
479
+ [K in keyof TServices | keyof TApps]: string;
480
+ }>;
481
+
369
482
  // ═══════════════════════════════════════════════════════════════════════════
370
483
  // Start/Stop Options
371
484
  // ═══════════════════════════════════════════════════════════════════════════
@@ -426,6 +539,10 @@ export interface DevEnvironment<
426
539
  readonly ports: ComputedPorts<TServices, TApps>;
427
540
  /** Computed URLs for all services and apps */
428
541
  readonly urls: ComputedUrls<TServices, TApps>;
542
+ /** Public tunnel URLs for exposed services/apps (when active) */
543
+ readonly publicUrls: ComputedPublicUrls<TServices, TApps>;
544
+ /** Services configuration */
545
+ readonly services: TServices;
429
546
  /** Apps configuration (for CLI to build commands) */
430
547
  readonly apps: TApps;
431
548
  /** Port offset applied (0 for main, > 0 for worktrees) */
@@ -436,6 +553,8 @@ export interface DevEnvironment<
436
553
  readonly localIp: string;
437
554
  /** Path to monorepo root */
438
555
  readonly root: string;
556
+ /** Path passed to docker compose -f */
557
+ readonly composeFile: string;
439
558
 
440
559
  // ─────────────────────────────────────────────────────────────────────────
441
560
  // Container Management
@@ -473,6 +592,12 @@ export interface DevEnvironment<
473
592
 
474
593
  /** Build environment variables for shell commands */
475
594
  buildEnvVars(production?: boolean): Record<string, string>;
595
+ /** Set public tunnel URLs used by envVars and *_PUBLIC_URL injection */
596
+ setPublicUrls(urls: ComputedPublicUrls<TServices, TApps>): void;
597
+ /** Clear all public tunnel URLs */
598
+ clearPublicUrls(): void;
599
+ /** Ensure generated docker compose file exists and return path used with -f */
600
+ ensureComposeFile(): string;
476
601
  /** Execute a command with environment variables set */
477
602
  exec(
478
603
  cmd: string,
@@ -0,0 +1 @@
1
+ export * from "./all-types";