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
@@ -0,0 +1,109 @@
1
+ import type {
2
+ BuiltInHealthCheck,
3
+ DockerComposeServiceRaw,
4
+ DockerPresetName,
5
+ DockerPresetServiceDefinition,
6
+ ServiceConfig,
7
+ } from "../../types";
8
+
9
+ export interface DockerServiceFactoryInput {
10
+ serviceKey: string;
11
+ config: ServiceConfig;
12
+ }
13
+
14
+ export interface DockerServiceFactoryOutput {
15
+ service: DockerComposeServiceRaw;
16
+ volume?: string;
17
+ }
18
+
19
+ export type DockerServiceFactory = (
20
+ input: DockerServiceFactoryInput,
21
+ ) => DockerServiceFactoryOutput;
22
+
23
+ export type PresetServiceSharedOptions = Pick<
24
+ ServiceConfig,
25
+ "serviceName" | "database" | "user" | "password" | "expose"
26
+ > & {
27
+ port?: number;
28
+ healthCheck?: BuiltInHealthCheck | false;
29
+ docker?: DockerComposeServiceRaw;
30
+ };
31
+
32
+ export interface DockerServicePresetDefaults {
33
+ port: number;
34
+ healthCheck: BuiltInHealthCheck;
35
+ secondaryPort?: number;
36
+ }
37
+
38
+ export interface DockerServicePreset<
39
+ TOptions extends PresetServiceSharedOptions = PresetServiceSharedOptions,
40
+ TServiceConfig extends ServiceConfig = ServiceConfig,
41
+ > {
42
+ preset: DockerPresetName;
43
+ defaults: DockerServicePresetDefaults;
44
+ build: DockerServiceFactory;
45
+ createPresetDefinition(
46
+ service?: DockerComposeServiceRaw,
47
+ ): DockerPresetServiceDefinition;
48
+ toServiceConfig(options?: TOptions): TServiceConfig;
49
+ }
50
+
51
+ interface DefineDockerServiceInput<
52
+ TOptions extends PresetServiceSharedOptions = PresetServiceSharedOptions,
53
+ TServiceConfig extends ServiceConfig = ServiceConfig,
54
+ > {
55
+ preset: DockerPresetName;
56
+ defaults: DockerServicePresetDefaults;
57
+ build: DockerServiceFactory;
58
+ enhanceServiceConfig?: (
59
+ base: ServiceConfig,
60
+ options: TOptions,
61
+ ) => TServiceConfig;
62
+ }
63
+
64
+ /**
65
+ * Define a docker service preset as single source of truth.
66
+ * The same definition powers:
67
+ * - compose generation (`build`)
68
+ * - typed config helper defaults (`toServiceConfig`)
69
+ */
70
+ export function defineDockerService<
71
+ TOptions extends PresetServiceSharedOptions = PresetServiceSharedOptions,
72
+ TServiceConfig extends ServiceConfig = ServiceConfig,
73
+ >(
74
+ input: DefineDockerServiceInput<TOptions, TServiceConfig>,
75
+ ): DockerServicePreset<TOptions, TServiceConfig> {
76
+ function createPresetDefinition(
77
+ service?: DockerComposeServiceRaw,
78
+ ): DockerPresetServiceDefinition {
79
+ return {
80
+ kind: "preset",
81
+ preset: input.preset,
82
+ service,
83
+ };
84
+ }
85
+
86
+ function toServiceConfig(options = {} as TOptions): TServiceConfig {
87
+ const base: ServiceConfig = {
88
+ port: options.port ?? input.defaults.port,
89
+ expose: options.expose,
90
+ healthCheck: options.healthCheck ?? input.defaults.healthCheck,
91
+ database: options.database,
92
+ user: options.user,
93
+ password: options.password,
94
+ serviceName: options.serviceName,
95
+ docker: createPresetDefinition(options.docker),
96
+ };
97
+ return input.enhanceServiceConfig
98
+ ? input.enhanceServiceConfig(base, options)
99
+ : (base as TServiceConfig);
100
+ }
101
+
102
+ return {
103
+ preset: input.preset,
104
+ defaults: input.defaults,
105
+ build: input.build,
106
+ createPresetDefinition,
107
+ toServiceConfig,
108
+ };
109
+ }
@@ -0,0 +1,67 @@
1
+ import type {
2
+ DockerComposeServiceRaw,
3
+ DockerPresetName,
4
+ ServiceConfig,
5
+ } from "../../types";
6
+ import type { DockerServicePreset } from "./define-docker-service";
7
+
8
+ export type {
9
+ DockerServicePreset,
10
+ DockerServicePresetDefaults,
11
+ PresetServiceSharedOptions,
12
+ } from "./define-docker-service";
13
+
14
+ import {
15
+ type ClickhouseServiceOptions,
16
+ clickhouseDockerService,
17
+ } from "./clickhouse";
18
+ import { type PostgresServiceOptions, postgresDockerService } from "./postgres";
19
+ import { type RedisServiceOptions, redisDockerService } from "./redis";
20
+
21
+ const PRESET_SERVICES = {
22
+ postgres: postgresDockerService,
23
+ redis: redisDockerService,
24
+ clickhouse: clickhouseDockerService,
25
+ } satisfies Record<DockerPresetName, DockerServicePreset>;
26
+
27
+ export { clickhouseDockerService, postgresDockerService, redisDockerService };
28
+ export type {
29
+ ClickhouseServiceOptions,
30
+ PostgresServiceOptions,
31
+ RedisServiceOptions,
32
+ };
33
+
34
+ export type CustomServiceOptions = ServiceConfig & {
35
+ docker: DockerComposeServiceRaw;
36
+ };
37
+
38
+ /**
39
+ * Public service builders for dev.config.ts.
40
+ * Core owns this surface so defaults and preset mapping live in one place.
41
+ */
42
+ export const service = {
43
+ postgres: postgresDockerService.toServiceConfig,
44
+ redis: redisDockerService.toServiceConfig,
45
+ clickhouse: clickhouseDockerService.toServiceConfig,
46
+
47
+ custom(options: CustomServiceOptions): ServiceConfig {
48
+ return options;
49
+ },
50
+ };
51
+
52
+ export function inferDockerPreset(
53
+ serviceKey: string,
54
+ ): DockerPresetName | undefined {
55
+ const normalized = serviceKey.toLowerCase();
56
+ if (Object.hasOwn(PRESET_SERVICES, normalized)) {
57
+ return normalized as DockerPresetName;
58
+ }
59
+ return undefined;
60
+ }
61
+
62
+ export function buildPresetDockerService(
63
+ preset: DockerPresetName,
64
+ input: Parameters<DockerServicePreset["build"]>[0],
65
+ ): ReturnType<DockerServicePreset["build"]> {
66
+ return PRESET_SERVICES[preset].build(input);
67
+ }
@@ -0,0 +1,60 @@
1
+ import type {
2
+ BuiltInHealthCheck,
3
+ DockerComposeHealthcheckRaw,
4
+ DockerComposeServiceRaw,
5
+ } from "../../types";
6
+ import { defineDockerService } from "./define-docker-service";
7
+ import { getDefaultPortBindings, resolveHealthcheck } from "./shared";
8
+
9
+ export type PostgresServiceOptions = {
10
+ port?: number;
11
+ expose?: boolean;
12
+ healthCheck?: BuiltInHealthCheck | false;
13
+ serviceName?: string;
14
+ database?: string;
15
+ user?: string;
16
+ password?: string;
17
+ docker?: DockerComposeServiceRaw;
18
+ };
19
+
20
+ export const postgresDockerService =
21
+ defineDockerService<PostgresServiceOptions>({
22
+ preset: "postgres",
23
+ defaults: {
24
+ port: 5432,
25
+ healthCheck: "pg_isready",
26
+ },
27
+ build: ({ serviceKey, config }) => {
28
+ const user = config.user ?? "postgres";
29
+ const password = config.password ?? "postgres";
30
+ const database = config.database ?? "postgres";
31
+ const defaultHealthcheck: DockerComposeHealthcheckRaw = {
32
+ test: ["CMD-SHELL", `pg_isready -U ${user}`],
33
+ interval: "250ms",
34
+ timeout: "5s",
35
+ retries: 20,
36
+ };
37
+
38
+ return {
39
+ service: {
40
+ image: "pgvector/pgvector:pg16",
41
+ ports: getDefaultPortBindings(serviceKey, config, "postgres"),
42
+ volumes: [`${serviceKey}_data:/var/lib/postgresql/data`],
43
+ environment: {
44
+ POSTGRES_USER: user,
45
+ POSTGRES_PASSWORD: password,
46
+ POSTGRES_DB: database,
47
+ },
48
+ healthcheck: resolveHealthcheck(
49
+ config.healthCheck,
50
+ defaultHealthcheck,
51
+ {
52
+ internalPort: 5432,
53
+ user,
54
+ },
55
+ ),
56
+ },
57
+ volume: `${serviceKey}_data`,
58
+ };
59
+ },
60
+ });
@@ -0,0 +1,48 @@
1
+ import type {
2
+ BuiltInHealthCheck,
3
+ DockerComposeHealthcheckRaw,
4
+ DockerComposeServiceRaw,
5
+ } from "../../types";
6
+ import { defineDockerService } from "./define-docker-service";
7
+ import { getDefaultPortBindings, resolveHealthcheck } from "./shared";
8
+
9
+ export type RedisServiceOptions = {
10
+ port?: number;
11
+ expose?: boolean;
12
+ healthCheck?: BuiltInHealthCheck | false;
13
+ serviceName?: string;
14
+ database?: string;
15
+ user?: string;
16
+ password?: string;
17
+ docker?: DockerComposeServiceRaw;
18
+ };
19
+
20
+ export const redisDockerService = defineDockerService<RedisServiceOptions>({
21
+ preset: "redis",
22
+ defaults: {
23
+ port: 6379,
24
+ healthCheck: "redis-cli",
25
+ },
26
+ build: ({ serviceKey, config }) => {
27
+ const defaultHealthcheck: DockerComposeHealthcheckRaw = {
28
+ test: ["CMD", "redis-cli", "ping"],
29
+ interval: "250ms",
30
+ timeout: "5s",
31
+ retries: 20,
32
+ };
33
+
34
+ return {
35
+ service: {
36
+ image: "redis:7-alpine",
37
+ ports: getDefaultPortBindings(serviceKey, config, "redis"),
38
+ healthcheck: resolveHealthcheck(
39
+ config.healthCheck,
40
+ defaultHealthcheck,
41
+ {
42
+ internalPort: 6379,
43
+ },
44
+ ),
45
+ },
46
+ };
47
+ },
48
+ });
@@ -0,0 +1,79 @@
1
+ import type {
2
+ DockerComposeHealthcheckRaw,
3
+ DockerPresetName,
4
+ ServiceConfig,
5
+ } from "../../types";
6
+
7
+ const DEFAULT_HEALTHCHECK_SETTINGS = {
8
+ interval: "250ms",
9
+ timeout: "5s",
10
+ retries: 20,
11
+ } as const;
12
+
13
+ export function getPortEnvName(portKey: string): string {
14
+ return `${portKey.toUpperCase()}_PORT`;
15
+ }
16
+
17
+ export function getDefaultPortBindings(
18
+ serviceKey: string,
19
+ config: ServiceConfig,
20
+ preset?: DockerPresetName,
21
+ ): string[] {
22
+ const envName = getPortEnvName(serviceKey);
23
+ const bindings: string[] = [];
24
+
25
+ const defaultInternalPort =
26
+ preset === "postgres"
27
+ ? 5432
28
+ : preset === "redis"
29
+ ? 6379
30
+ : preset === "clickhouse"
31
+ ? 8123
32
+ : config.port;
33
+
34
+ bindings.push(`\${${envName}:-${config.port}}:${defaultInternalPort}`);
35
+
36
+ if (config.secondaryPort !== undefined) {
37
+ const secondaryEnv = getPortEnvName(`${serviceKey}Secondary`);
38
+ const secondaryInternal =
39
+ preset === "clickhouse" ? 9000 : config.secondaryPort;
40
+ bindings.push(
41
+ `\${${secondaryEnv}:-${config.secondaryPort}}:${secondaryInternal}`,
42
+ );
43
+ }
44
+
45
+ return bindings;
46
+ }
47
+
48
+ export function resolveHealthcheck(
49
+ healthCheck: ServiceConfig["healthCheck"] | undefined,
50
+ fallback: DockerComposeHealthcheckRaw | undefined,
51
+ options: { internalPort: number; user?: string },
52
+ ): DockerComposeHealthcheckRaw | undefined {
53
+ if (healthCheck === false) return undefined;
54
+ if (typeof healthCheck === "function") return fallback;
55
+ if (!healthCheck) return fallback;
56
+
57
+ switch (healthCheck) {
58
+ case "pg_isready":
59
+ return {
60
+ test: ["CMD-SHELL", `pg_isready -U ${options.user ?? "postgres"}`],
61
+ ...DEFAULT_HEALTHCHECK_SETTINGS,
62
+ };
63
+ case "redis-cli":
64
+ return {
65
+ test: ["CMD", "redis-cli", "ping"],
66
+ ...DEFAULT_HEALTHCHECK_SETTINGS,
67
+ };
68
+ case "http":
69
+ return {
70
+ test: [
71
+ "CMD-SHELL",
72
+ `wget -qO- http://127.0.0.1:${options.internalPort}/ping || exit 1`,
73
+ ],
74
+ ...DEFAULT_HEALTHCHECK_SETTINGS,
75
+ };
76
+ default:
77
+ return fallback;
78
+ }
79
+ }
@@ -0,0 +1,88 @@
1
+ import type { DockerComposeNode } from "../types";
2
+ import type { ComposeDocument } from "./model";
3
+
4
+ function isObject(
5
+ value: DockerComposeNode,
6
+ ): value is Record<string, DockerComposeNode | undefined> {
7
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8
+ }
9
+
10
+ function formatScalar(value: string | number | boolean | null): string {
11
+ if (value === null) return "null";
12
+ if (typeof value === "string") {
13
+ return JSON.stringify(value);
14
+ }
15
+ return String(value);
16
+ }
17
+
18
+ function formatKey(key: string): string {
19
+ return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key) ? key : JSON.stringify(key);
20
+ }
21
+
22
+ function sortNode(node: DockerComposeNode): DockerComposeNode {
23
+ if (Array.isArray(node)) {
24
+ return node.map(sortNode);
25
+ }
26
+ if (isObject(node)) {
27
+ const sorted: Record<string, DockerComposeNode | undefined> = {};
28
+ for (const key of Object.keys(node).sort()) {
29
+ const value = node[key];
30
+ if (value !== undefined) {
31
+ sorted[key] = sortNode(value);
32
+ }
33
+ }
34
+ return sorted;
35
+ }
36
+ return node;
37
+ }
38
+
39
+ function stringifyNode(node: DockerComposeNode, indent = 0): string {
40
+ const prefix = " ".repeat(indent);
41
+
42
+ if (
43
+ typeof node === "string" ||
44
+ typeof node === "number" ||
45
+ typeof node === "boolean" ||
46
+ node === null
47
+ ) {
48
+ return `${prefix}${formatScalar(node)}`;
49
+ }
50
+
51
+ if (Array.isArray(node)) {
52
+ if (node.length === 0) return `${prefix}[]`;
53
+ return node
54
+ .map((item) => {
55
+ const isNested = typeof item === "object" && item !== null;
56
+ if (!isNested) {
57
+ return `${prefix}- ${formatScalar(
58
+ item as string | number | boolean | null,
59
+ )}`;
60
+ }
61
+ return `${prefix}-\n${stringifyNode(item, indent + 2)}`;
62
+ })
63
+ .join("\n");
64
+ }
65
+
66
+ const entries = Object.entries(node).filter(
67
+ ([, value]) => value !== undefined,
68
+ ) as Array<[string, DockerComposeNode]>;
69
+ if (entries.length === 0) return `${prefix}{}`;
70
+
71
+ return entries
72
+ .map(([key, value]) => {
73
+ const formattedKey = formatKey(key);
74
+ const isNested = typeof value === "object" && value !== null;
75
+ if (!isNested) {
76
+ return `${prefix}${formattedKey}: ${formatScalar(
77
+ value as string | number | boolean | null,
78
+ )}`;
79
+ }
80
+ return `${prefix}${formattedKey}:\n${stringifyNode(value, indent + 2)}`;
81
+ })
82
+ .join("\n");
83
+ }
84
+
85
+ export function composeToYaml(document: ComposeDocument): string {
86
+ const sorted = sortNode(document as DockerComposeNode);
87
+ return `${stringifyNode(sorted)}\n`;
88
+ }