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.
- package/dist/bin.d.ts +1 -12
- package/dist/bin.js +261 -253
- package/dist/cli/bin.d.ts +13 -0
- package/dist/cli/bin.js +315 -0
- package/dist/cli/commands/help.d.ts +1 -0
- package/dist/cli/commands/runtime.d.ts +5 -0
- package/dist/cli/commands/version.d.ts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/run-cli.d.ts +22 -0
- package/dist/cli.d.ts +1 -22
- package/dist/cli.js +5 -13
- package/dist/config/config.d.ts +1 -0
- package/dist/config/define-config.d.ts +13 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +15 -0
- package/dist/config/merge-configs.d.ts +3 -0
- package/dist/config/validate-config.d.ts +3 -0
- package/dist/config.d.ts +1 -72
- package/dist/config.js +12 -12
- package/dist/core/docker.d.ts +1 -83
- package/dist/core/docker.js +35 -32
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +123 -118
- package/dist/core/network.js +2 -2
- package/dist/core/ports.js +1 -1
- package/dist/core/process.js +1 -1
- package/dist/core/tunnel.d.ts +33 -0
- package/dist/core/utils.js +2 -2
- package/dist/core/watchdog-runner.js +45 -42
- package/dist/core/watchdog.d.ts +1 -0
- package/dist/core/watchdog.js +4 -2
- package/dist/docker/index.d.ts +1 -0
- package/dist/docker/index.js +38 -0
- package/dist/docker/runtime.d.ts +87 -0
- package/dist/docker/runtime.js +37 -0
- package/dist/docker-compose/compose.d.ts +1 -0
- package/dist/docker-compose/generated-file.d.ts +7 -0
- package/dist/docker-compose/index.d.ts +3 -0
- package/dist/docker-compose/index.js +15 -0
- package/dist/docker-compose/model.d.ts +6 -0
- package/dist/docker-compose/services/clickhouse.d.ts +16 -0
- package/dist/docker-compose/services/define-docker-service.d.ts +41 -0
- package/dist/docker-compose/services/index.d.ts +23 -0
- package/dist/docker-compose/services/index.js +17 -0
- package/dist/docker-compose/services/postgres.d.ts +12 -0
- package/dist/docker-compose/services/redis.d.ts +12 -0
- package/dist/docker-compose/services/shared.d.ts +7 -0
- package/dist/docker-compose/yaml.d.ts +2 -0
- package/dist/environment/create-dev-environment.d.ts +23 -0
- package/dist/environment/index.d.ts +1 -0
- package/dist/environment/index.js +15 -0
- package/dist/environment/logging.d.ts +17 -0
- package/dist/environment/seeding.d.ts +9 -0
- package/dist/environment.d.ts +1 -23
- package/dist/environment.js +12 -14
- package/dist/index-045jksh5.js +147 -0
- package/dist/index-08wa79cs.js +125 -117
- package/dist/index-0kxnae3z.js +335 -0
- package/dist/index-1mdrf7nz.js +51 -43
- package/dist/index-1yvbwj4k.js +262 -242
- package/dist/index-23ev345g.js +475 -0
- package/dist/index-2ckr49sf.js +228 -0
- package/dist/index-2f47khe5.js +376 -369
- package/dist/index-2fr3g85b.js +220 -183
- package/dist/index-38xnzpa6.js +450 -0
- package/dist/index-3h3dhtf2.js +51 -43
- package/dist/index-42x95209.js +51 -43
- package/dist/index-4gp0az1g.js +145 -0
- package/dist/index-4xrxh8yv.js +72 -0
- package/dist/index-5gmws6ah.js +181 -0
- package/dist/index-5hka0tff.js +78 -76
- package/dist/index-5rfqps4b.js +3 -0
- package/dist/index-5t9jxqm0.js +428 -0
- package/dist/index-6c1w1xk5.js +101 -0
- package/dist/index-6fm7mvwj.js +118 -97
- package/dist/index-6srpc523.js +127 -128
- package/dist/index-731rzzfp.js +157 -142
- package/dist/index-75y4cg2z.js +51 -43
- package/dist/index-7ja4ywyj.js +126 -127
- package/dist/index-8bw1cmz4.js +531 -0
- package/dist/index-8hbbj1mp.js +120 -121
- package/dist/index-8xj2p5n5.js +118 -97
- package/dist/index-bj79tw5w.js +0 -0
- package/dist/index-bnk6nr0g.js +73 -0
- package/dist/index-brbbzyks.js +72 -0
- package/dist/index-c0dr6mcv.js +123 -0
- package/dist/index-cty0bcry.js +235 -218
- package/dist/index-d8tyv5se.js +228 -0
- package/dist/index-d9efy0n4.js +176 -150
- package/dist/index-etfmqjjf.js +427 -0
- package/dist/index-fb29934k.js +172 -0
- package/dist/index-g50jw1yf.js +72 -0
- package/dist/index-g6eb5wdw.js +118 -117
- package/dist/index-ggq3yryx.js +99 -95
- package/dist/index-h70tce00.js +177 -0
- package/dist/index-hkxtfqtc.js +333 -0
- package/dist/index-kf3dhser.js +146 -143
- package/dist/index-ma6tgdb2.js +500 -0
- package/dist/index-mam0bcyz.js +123 -0
- package/dist/index-mm412dkp.js +274 -0
- package/dist/index-n8v18aeb.js +0 -0
- package/dist/index-ndnmnsej.js +378 -371
- package/dist/index-p8wty0e2.js +389 -379
- package/dist/index-qfphr2fd.js +78 -76
- package/dist/index-qqmms8rs.js +51 -43
- package/dist/index-qw4093g2.js +51 -43
- package/dist/index-qzwpzjbx.js +121 -122
- package/dist/index-segbnm0h.js +146 -143
- package/dist/index-t0fj6gg1.js +112 -0
- package/dist/index-thdkwnv7.js +122 -0
- package/dist/index-tjbx2r2t.js +270 -0
- package/dist/index-tjqw9vtj.js +62 -54
- package/dist/index-vbpb89jy.js +248 -0
- package/dist/index-vhs88xhe.js +99 -95
- package/dist/index-w8zxnjka.js +249 -0
- package/dist/index-wk2na3t9.js +385 -375
- package/dist/index-wz9x8g7z.js +383 -373
- package/dist/index-x249gyde.js +388 -378
- package/dist/index-xkvd0nsd.js +187 -0
- package/dist/index-yedqxm1z.js +80 -0
- package/dist/index-zfjzzjkf.js +240 -199
- package/dist/index.d.ts +12 -8
- package/dist/index.js +56 -35
- package/dist/lint.d.ts +1 -46
- package/dist/lint.js +3 -7
- package/dist/loader/cache.d.ts +4 -0
- package/dist/loader/find-config-file.d.ts +2 -0
- package/dist/loader/index.d.ts +5 -0
- package/dist/loader/index.js +24 -0
- package/dist/loader/load-dev-env.d.ts +5 -0
- package/dist/loader/loader.d.ts +1 -0
- package/dist/loader.d.ts +1 -45
- package/dist/loader.js +22 -20
- package/dist/prisma/index.d.ts +1 -0
- package/dist/prisma/prisma.d.ts +29 -0
- package/dist/prisma.d.ts +1 -29
- package/dist/prisma.js +6 -10
- package/dist/src/bin.js +309 -0
- package/dist/src/cli.js +5 -0
- package/dist/src/config.js +15 -0
- package/dist/src/core/docker.js +38 -0
- package/dist/src/core/index.js +130 -0
- package/dist/src/core/network.js +9 -0
- package/dist/src/core/ports.js +23 -0
- package/dist/src/core/process.js +31 -0
- package/dist/src/core/utils.js +11 -0
- package/dist/src/core/watchdog-runner.js +69 -0
- package/dist/src/core/watchdog.js +28 -0
- package/dist/src/docker/runtime.js +37 -0
- package/dist/src/docker-compose/index.js +16 -0
- package/dist/src/docker-compose/services/index.js +17 -0
- package/dist/src/environment.js +12 -0
- package/dist/src/index.js +122 -0
- package/dist/src/lint.js +3 -0
- package/dist/src/loader.js +25 -0
- package/dist/src/prisma.js +6 -0
- package/dist/src/types.js +0 -0
- package/dist/typecheck/index.d.ts +1 -0
- package/dist/typecheck/index.js +7 -0
- package/dist/typecheck/typecheck.d.ts +46 -0
- package/dist/types/all-types.d.ts +501 -0
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/config.d.ts +6 -0
- package/dist/types/docker.d.ts +15 -0
- package/dist/types/environment.d.ts +8 -0
- package/dist/types/hooks.d.ts +9 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +0 -0
- package/dist/types/prisma.d.ts +1 -0
- package/dist/types.d.ts +1 -399
- package/package.json +145 -140
- package/readme.md +349 -109
- package/src/cli/bin.ts +77 -0
- package/src/cli/commands/help.ts +39 -0
- package/src/cli/commands/runtime.ts +72 -0
- package/src/cli/commands/version.ts +4 -0
- package/src/cli/index.ts +1 -0
- package/{cli.ts → src/cli/run-cli.ts} +95 -6
- package/src/config/define-config.ts +30 -0
- package/src/config/index.ts +3 -0
- package/src/config/merge-configs.ts +33 -0
- package/src/config/validate-config.ts +136 -0
- package/{core → src/core}/index.ts +2 -2
- package/{core → src/core}/ports.ts +5 -2
- package/{core → src/core}/process.ts +6 -2
- package/src/core/tunnel.ts +151 -0
- package/{core → src/core}/utils.ts +1 -0
- package/{core → src/core}/watchdog.ts +5 -1
- package/src/docker/index.ts +1 -0
- package/{core/docker.ts → src/docker/runtime.ts} +11 -4
- package/src/docker-compose/generated-file.ts +45 -0
- package/src/docker-compose/index.ts +7 -0
- package/src/docker-compose/model.ts +197 -0
- package/src/docker-compose/services/clickhouse.ts +79 -0
- package/src/docker-compose/services/define-docker-service.ts +109 -0
- package/src/docker-compose/services/index.ts +67 -0
- package/src/docker-compose/services/postgres.ts +60 -0
- package/src/docker-compose/services/redis.ts +48 -0
- package/src/docker-compose/services/shared.ts +79 -0
- package/src/docker-compose/yaml.ts +88 -0
- package/{environment.ts → src/environment/create-dev-environment.ts} +93 -130
- package/src/environment/index.ts +1 -0
- package/src/environment/logging.ts +101 -0
- package/src/environment/seeding.ts +57 -0
- package/{index.ts → src/index.ts} +49 -20
- package/src/loader/cache.ts +23 -0
- package/src/loader/find-config-file.ts +29 -0
- package/src/loader/index.ts +17 -0
- package/src/loader/load-dev-env.ts +38 -0
- package/src/prisma/index.ts +1 -0
- package/{prisma.ts → src/prisma/prisma.ts} +4 -2
- package/src/typecheck/index.ts +1 -0
- package/{types.ts → src/types/all-types.ts} +130 -5
- package/src/types/index.ts +1 -0
- package/bin.ts +0 -192
- package/config.ts +0 -194
- package/loader.ts +0 -126
- /package/{core → src/core}/network.ts +0 -0
- /package/{core → src/core}/watchdog-runner.ts +0 -0
- /package/{lint.ts → src/typecheck/typecheck.ts} +0 -0
|
@@ -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
|
+
}
|
|
@@ -1,34 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
areContainersRunning,
|
|
5
|
-
startContainers,
|
|
6
|
-
stopContainers,
|
|
7
|
-
} from "./core/docker";
|
|
8
|
-
import { getLocalIp, waitForDevServers, waitForServer } from "./core/network";
|
|
1
|
+
import { assertValidConfig } from "../config";
|
|
2
|
+
import { getLocalIp, waitForDevServers, waitForServer } from "../core/network";
|
|
9
3
|
import {
|
|
10
4
|
computeDevIdentity,
|
|
11
5
|
computePorts,
|
|
12
6
|
computeUrls,
|
|
13
7
|
findMonorepoRoot,
|
|
14
|
-
} from "
|
|
8
|
+
} from "../core/ports";
|
|
15
9
|
import {
|
|
16
10
|
buildApps,
|
|
17
11
|
execAsync,
|
|
18
12
|
startDevServers,
|
|
19
13
|
stopProcess as stopProcessFn,
|
|
20
|
-
} from "
|
|
21
|
-
import { isCI as isCIEnv, logExpoApiUrl, logFrontendPort } from "
|
|
14
|
+
} from "../core/process";
|
|
15
|
+
import { isCI as isCIEnv, logExpoApiUrl, logFrontendPort } from "../core/utils";
|
|
22
16
|
import {
|
|
23
17
|
spawnWatchdog as spawnWatchdogFn,
|
|
24
18
|
startHeartbeat as startHeartbeatFn,
|
|
25
19
|
stopHeartbeat as stopHeartbeatFn,
|
|
26
20
|
stopWatchdog as stopWatchdogFn,
|
|
27
|
-
} from "
|
|
28
|
-
import {
|
|
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";
|
|
29
32
|
import type {
|
|
30
33
|
AppConfig,
|
|
31
34
|
ComputedPorts,
|
|
35
|
+
ComputedPublicUrls,
|
|
32
36
|
ComputedUrls,
|
|
33
37
|
DevConfig,
|
|
34
38
|
DevEnvironment,
|
|
@@ -39,34 +43,9 @@ import type {
|
|
|
39
43
|
ServiceConfig,
|
|
40
44
|
StartOptions,
|
|
41
45
|
StopOptions,
|
|
42
|
-
} from "
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Console Output Formatting (Vite-inspired)
|
|
46
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Format a URL with colored port number (Vite-style).
|
|
50
|
-
*/
|
|
51
|
-
function formatUrl(url: string): string {
|
|
52
|
-
return pc.cyan(
|
|
53
|
-
url.replace(/:(\d+)(\/?)/, (_, port, slash) => `:${pc.bold(port)}${slash}`),
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Format a label with arrow prefix (Vite-style).
|
|
59
|
-
*/
|
|
60
|
-
function formatLabel(label: string, value: string, arrow = "➜"): string {
|
|
61
|
-
return ` ${pc.green(arrow)} ${pc.bold(label.padEnd(10))} ${value}`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Format a dim label (for secondary info).
|
|
66
|
-
*/
|
|
67
|
-
function formatDimLabel(label: string, value: string): string {
|
|
68
|
-
return ` ${pc.dim("•")} ${pc.dim(label.padEnd(10))} ${pc.dim(value)}`;
|
|
69
|
-
}
|
|
46
|
+
} from "../types";
|
|
47
|
+
import { logEnvironmentInfo } from "./logging";
|
|
48
|
+
import { createCheckTableHelper, createSeedCheckContext } from "./seeding";
|
|
70
49
|
|
|
71
50
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
72
51
|
// Environment Factory
|
|
@@ -115,6 +94,14 @@ export function createDevEnvironment<
|
|
|
115
94
|
|
|
116
95
|
const services = config.services;
|
|
117
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
|
+
}
|
|
118
105
|
|
|
119
106
|
// Compute ports and URLs
|
|
120
107
|
const ports = computePorts(services, apps, portOffset) as ComputedPorts<
|
|
@@ -125,6 +112,22 @@ export function createDevEnvironment<
|
|
|
125
112
|
TServices,
|
|
126
113
|
TApps
|
|
127
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
|
+
}
|
|
128
131
|
|
|
129
132
|
// Build environment variables
|
|
130
133
|
function buildEnvVars(production = false): Record<string, string> {
|
|
@@ -145,12 +148,19 @@ export function createDevEnvironment<
|
|
|
145
148
|
baseEnv[envName] = url;
|
|
146
149
|
}
|
|
147
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
|
+
|
|
148
157
|
// Call user's envVars function if provided
|
|
149
158
|
if (config.envVars) {
|
|
150
159
|
const userEnv = config.envVars(ports, urls, {
|
|
151
160
|
projectName,
|
|
152
161
|
localIp,
|
|
153
162
|
portOffset,
|
|
163
|
+
publicUrls: publicUrls as ComputedPublicUrls<TServices, TApps>,
|
|
154
164
|
});
|
|
155
165
|
for (const [key, value] of Object.entries(userEnv)) {
|
|
156
166
|
baseEnv[key] = String(value);
|
|
@@ -169,6 +179,7 @@ export function createDevEnvironment<
|
|
|
169
179
|
projectName,
|
|
170
180
|
ports,
|
|
171
181
|
urls,
|
|
182
|
+
publicUrls: publicUrls as ComputedPublicUrls<TServices, TApps>,
|
|
172
183
|
root,
|
|
173
184
|
isCI: isCIEnv(),
|
|
174
185
|
portOffset,
|
|
@@ -205,6 +216,7 @@ export function createDevEnvironment<
|
|
|
205
216
|
} = startOptions;
|
|
206
217
|
|
|
207
218
|
const envVars = buildEnvVars(productionBuild);
|
|
219
|
+
ensureComposeFile();
|
|
208
220
|
|
|
209
221
|
// Log environment info
|
|
210
222
|
if (verbose) {
|
|
@@ -224,7 +236,7 @@ export function createDevEnvironment<
|
|
|
224
236
|
startContainers(root, projectName, envVars, {
|
|
225
237
|
verbose,
|
|
226
238
|
wait,
|
|
227
|
-
composeFile
|
|
239
|
+
composeFile,
|
|
228
240
|
});
|
|
229
241
|
}
|
|
230
242
|
|
|
@@ -258,19 +270,19 @@ export function createDevEnvironment<
|
|
|
258
270
|
}),
|
|
259
271
|
);
|
|
260
272
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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`);
|
|
270
284
|
}
|
|
271
|
-
throw new Error(`Migration "${name}" failed`);
|
|
272
285
|
}
|
|
273
|
-
}
|
|
274
286
|
|
|
275
287
|
if (verbose) console.log("✓ Migrations complete");
|
|
276
288
|
}
|
|
@@ -286,35 +298,14 @@ export function createDevEnvironment<
|
|
|
286
298
|
|
|
287
299
|
// Check if seeding is needed using check function
|
|
288
300
|
if (config.seed.check) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
service?: keyof TServices,
|
|
293
|
-
): Promise<boolean> => {
|
|
294
|
-
const serviceName = (service ?? "postgres") as string;
|
|
295
|
-
const serviceUrl = (urls as Record<string, string>)[serviceName];
|
|
296
|
-
if (!serviceUrl) {
|
|
297
|
-
console.warn(`⚠️ Service "${serviceName}" not found for checkTable`);
|
|
298
|
-
return true; // Default to seeding if service not found
|
|
299
|
-
}
|
|
300
|
-
const checkResult = await exec(
|
|
301
|
-
`psql "${serviceUrl}" -tAc 'SELECT COUNT(*) FROM "${tableName}" LIMIT 1'`,
|
|
302
|
-
{ throwOnError: false },
|
|
301
|
+
const checkTable = createCheckTableHelper<TServices, TApps>(
|
|
302
|
+
urls as Record<string, string>,
|
|
303
|
+
exec,
|
|
303
304
|
);
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
if (!shouldSeed) {
|
|
307
|
-
console.log(` 📊 Table "${tableName}" has ${count} rows`);
|
|
308
|
-
}
|
|
309
|
-
return shouldSeed;
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// Build seed check context with helpers
|
|
313
|
-
const seedCheckContext = {
|
|
314
|
-
...getHookContext(),
|
|
305
|
+
const seedCheckContext = createSeedCheckContext(
|
|
306
|
+
getHookContext(),
|
|
315
307
|
checkTable,
|
|
316
|
-
|
|
317
|
-
|
|
308
|
+
);
|
|
318
309
|
shouldSeed = await config.seed.check(seedCheckContext);
|
|
319
310
|
}
|
|
320
311
|
|
|
@@ -380,6 +371,7 @@ export function createDevEnvironment<
|
|
|
380
371
|
|
|
381
372
|
async function stop(stopOptions: StopOptions = {}): Promise<void> {
|
|
382
373
|
const { verbose = true, removeVolumes = false } = stopOptions;
|
|
374
|
+
ensureComposeFile();
|
|
383
375
|
|
|
384
376
|
// Run beforeStop hook
|
|
385
377
|
if (config.hooks?.beforeStop) {
|
|
@@ -389,7 +381,7 @@ export function createDevEnvironment<
|
|
|
389
381
|
stopContainers(root, projectName, {
|
|
390
382
|
verbose,
|
|
391
383
|
removeVolumes,
|
|
392
|
-
composeFile
|
|
384
|
+
composeFile,
|
|
393
385
|
});
|
|
394
386
|
}
|
|
395
387
|
|
|
@@ -438,54 +430,17 @@ export function createDevEnvironment<
|
|
|
438
430
|
// ─────────────────────────────────────────────────────────────────────────
|
|
439
431
|
|
|
440
432
|
function logInfo(label = "Docker Dev"): void {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
for (const name of serviceNames) {
|
|
453
|
-
const port = (ports as Record<string, number>)[name];
|
|
454
|
-
const url = `localhost:${port}`;
|
|
455
|
-
console.log(formatLabel(`${name}:`, formatUrl(`http://${url}`)));
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Apps section (Dev servers)
|
|
460
|
-
if (appNames.length > 0) {
|
|
461
|
-
console.log("");
|
|
462
|
-
console.log(` ${pc.dim("─── Applications ───")}`);
|
|
463
|
-
for (const name of appNames) {
|
|
464
|
-
const port = (ports as Record<string, number>)[name];
|
|
465
|
-
const localUrl = `http://localhost:${port}`;
|
|
466
|
-
const networkUrl = `http://${localIp}:${port}`;
|
|
467
|
-
|
|
468
|
-
console.log(` ${pc.green("➜")} ${pc.bold(pc.cyan(name))}`);
|
|
469
|
-
console.log(` ${pc.dim("Local:")} ${formatUrl(localUrl)}`);
|
|
470
|
-
console.log(` ${pc.dim("Network:")} ${formatUrl(networkUrl)}`);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Environment info
|
|
475
|
-
console.log("");
|
|
476
|
-
console.log(` ${pc.dim("─── Environment ───")}`);
|
|
477
|
-
console.log(formatDimLabel("Worktree:", worktree ? "yes" : "no"));
|
|
478
|
-
console.log(
|
|
479
|
-
formatDimLabel(
|
|
480
|
-
"Port offset:",
|
|
481
|
-
portOffset > 0 ? `+${portOffset}` : "none",
|
|
482
|
-
),
|
|
483
|
-
);
|
|
484
|
-
if (projectSuffix) {
|
|
485
|
-
console.log(formatDimLabel("Suffix:", projectSuffix));
|
|
486
|
-
}
|
|
487
|
-
console.log(formatDimLabel("Local IP:", localIp));
|
|
488
|
-
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
|
+
});
|
|
489
444
|
}
|
|
490
445
|
|
|
491
446
|
async function waitForServerUrl(
|
|
@@ -511,7 +466,7 @@ export function createDevEnvironment<
|
|
|
511
466
|
await spawnWatchdogFn(projectName, root, {
|
|
512
467
|
timeoutMinutes,
|
|
513
468
|
verbose: true,
|
|
514
|
-
composeFile
|
|
469
|
+
composeFile,
|
|
515
470
|
});
|
|
516
471
|
}
|
|
517
472
|
|
|
@@ -554,11 +509,14 @@ export function createDevEnvironment<
|
|
|
554
509
|
projectName,
|
|
555
510
|
ports,
|
|
556
511
|
urls,
|
|
512
|
+
publicUrls: publicUrls as ComputedPublicUrls<TServices, TApps>,
|
|
513
|
+
services,
|
|
557
514
|
apps,
|
|
558
515
|
portOffset,
|
|
559
516
|
isWorktree: worktree,
|
|
560
517
|
localIp,
|
|
561
518
|
root,
|
|
519
|
+
composeFile,
|
|
562
520
|
|
|
563
521
|
// Container management
|
|
564
522
|
start,
|
|
@@ -573,6 +531,11 @@ export function createDevEnvironment<
|
|
|
573
531
|
|
|
574
532
|
// Utilities
|
|
575
533
|
buildEnvVars,
|
|
534
|
+
setPublicUrls: (urlsInput) => {
|
|
535
|
+
setPublicUrls(urlsInput as Record<string, string>);
|
|
536
|
+
},
|
|
537
|
+
clearPublicUrls,
|
|
538
|
+
ensureComposeFile,
|
|
576
539
|
exec,
|
|
577
540
|
waitForServer: waitForServerUrl,
|
|
578
541
|
logInfo,
|