buncargo 1.0.29 → 3.2.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 +317 -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 +30 -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/quick-tunnel/cloudflared-process.d.ts +10 -0
- package/dist/core/quick-tunnel/constants.d.ts +9 -0
- package/dist/core/quick-tunnel/index.d.ts +17 -0
- package/dist/core/quick-tunnel/install.d.ts +1 -0
- package/dist/core/tunnel.d.ts +34 -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/only-apps.d.ts +10 -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-3eyrdxw9.js +577 -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-5aq985p4.js +250 -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-6cmex7m5.js +72 -0
- package/dist/index-6d6x175r.js +572 -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-7v19es2e.js +666 -0
- package/dist/index-8bw1cmz4.js +531 -0
- package/dist/index-8hbbj1mp.js +120 -121
- package/dist/index-8xj2p5n5.js +118 -97
- package/dist/index-9wyhzw0h.js +574 -0
- package/dist/index-ag90ry8t.js +576 -0
- package/dist/index-bj79tw5w.js +0 -0
- package/dist/index-bnk6nr0g.js +73 -0
- package/dist/index-brbbzyks.js +72 -0
- package/dist/index-byeqyjrz.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-enj4zdma.js +574 -0
- 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-k370bech.js +72 -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-qa8akv6y.js +666 -0
- 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-vg55rq0y.js +250 -0
- package/dist/index-vhs88xhe.js +99 -95
- package/dist/index-vs81yaks.js +244 -0
- 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-x54nbgs7.js +355 -0
- package/dist/index-xkvd0nsd.js +187 -0
- package/dist/index-yedqxm1z.js +80 -0
- package/dist/index-yz4jfz7z.js +338 -0
- package/dist/index-zfjzzjkf.js +240 -199
- package/dist/index.d.ts +12 -8
- package/dist/index.js +56 -34
- 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 +544 -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 +55 -48
- package/readme.md +365 -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} +114 -10
- 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/quick-tunnel/cloudflared-process.ts +83 -0
- package/src/core/quick-tunnel/constants.ts +31 -0
- package/src/core/quick-tunnel/index.ts +96 -0
- package/src/core/quick-tunnel/install.ts +160 -0
- package/src/core/tunnel.ts +165 -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} +214 -141
- package/src/environment/index.ts +1 -0
- package/src/environment/logging.ts +115 -0
- package/src/environment/only-apps.ts +34 -0
- package/src/environment/seeding.ts +57 -0
- package/{index.ts → src/index.ts} +52 -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} +186 -8
- 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,666 @@
|
|
|
1
|
+
import {
|
|
2
|
+
areContainersRunning,
|
|
3
|
+
isContainerRunning,
|
|
4
|
+
startContainers,
|
|
5
|
+
startService,
|
|
6
|
+
stopContainers,
|
|
7
|
+
waitForServiceByType
|
|
8
|
+
} from "./index-d8tyv5se.js";
|
|
9
|
+
import {
|
|
10
|
+
getLocalIp,
|
|
11
|
+
isCI,
|
|
12
|
+
logExpoApiUrl,
|
|
13
|
+
logFrontendPort,
|
|
14
|
+
waitForDevServers,
|
|
15
|
+
waitForServer
|
|
16
|
+
} from "./index-c0dr6mcv.js";
|
|
17
|
+
import {
|
|
18
|
+
computeDevIdentity,
|
|
19
|
+
computePorts,
|
|
20
|
+
computeUrls,
|
|
21
|
+
findMonorepoRoot
|
|
22
|
+
} from "./index-fb29934k.js";
|
|
23
|
+
import {
|
|
24
|
+
getGeneratedComposePath,
|
|
25
|
+
writeGeneratedComposeFile
|
|
26
|
+
} from "./index-5t9jxqm0.js";
|
|
27
|
+
import {
|
|
28
|
+
resolveExposeTargets,
|
|
29
|
+
startPublicTunnels,
|
|
30
|
+
stopPublicTunnels
|
|
31
|
+
} from "./index-x54nbgs7.js";
|
|
32
|
+
import {
|
|
33
|
+
spawnWatchdog,
|
|
34
|
+
startHeartbeat,
|
|
35
|
+
stopHeartbeat,
|
|
36
|
+
stopWatchdog
|
|
37
|
+
} from "./index-mam0bcyz.js";
|
|
38
|
+
import {
|
|
39
|
+
buildApps,
|
|
40
|
+
execAsync,
|
|
41
|
+
startDevServers,
|
|
42
|
+
stopProcess
|
|
43
|
+
} from "./index-mm412dkp.js";
|
|
44
|
+
import {
|
|
45
|
+
assertValidConfig
|
|
46
|
+
} from "./index-t0fj6gg1.js";
|
|
47
|
+
|
|
48
|
+
// src/prisma/prisma.ts
|
|
49
|
+
import { spawn } from "node:child_process";
|
|
50
|
+
import { join } from "node:path";
|
|
51
|
+
function createPrismaRunner(env, config) {
|
|
52
|
+
const {
|
|
53
|
+
cwd = "packages/prisma",
|
|
54
|
+
service = "postgres",
|
|
55
|
+
urlEnvVar = "DATABASE_URL"
|
|
56
|
+
} = config;
|
|
57
|
+
const healthCheckTypes = {
|
|
58
|
+
postgres: "pg_isready",
|
|
59
|
+
redis: "redis-cli",
|
|
60
|
+
clickhouse: "http"
|
|
61
|
+
};
|
|
62
|
+
function getDatabaseUrl() {
|
|
63
|
+
const envVars = env.buildEnvVars();
|
|
64
|
+
const url = envVars[urlEnvVar];
|
|
65
|
+
if (!url) {
|
|
66
|
+
throw new Error(`Environment variable ${urlEnvVar} not found. Make sure your dev config defines it in envVars.`);
|
|
67
|
+
}
|
|
68
|
+
return url;
|
|
69
|
+
}
|
|
70
|
+
async function ensureDatabase() {
|
|
71
|
+
const alreadyRunning = await isContainerRunning(env.projectName, service);
|
|
72
|
+
if (alreadyRunning) {
|
|
73
|
+
console.log(`✓ ${service} already running`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
console.log(`\uD83D\uDC33 Starting ${service}...`);
|
|
77
|
+
const composeFile = env.ensureComposeFile();
|
|
78
|
+
const envVars = env.buildEnvVars();
|
|
79
|
+
startService(env.root, env.projectName, service, envVars, {
|
|
80
|
+
verbose: false,
|
|
81
|
+
composeFile
|
|
82
|
+
});
|
|
83
|
+
const port = env.ports[service];
|
|
84
|
+
if (!port) {
|
|
85
|
+
throw new Error(`Service ${service} not found in dev environment ports`);
|
|
86
|
+
}
|
|
87
|
+
const healthCheckType = healthCheckTypes[service] ?? "tcp";
|
|
88
|
+
console.log(`⏳ Waiting for ${service} to be healthy...`);
|
|
89
|
+
await waitForServiceByType(service, healthCheckType, port, {
|
|
90
|
+
verbose: true
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function run(args) {
|
|
94
|
+
if (args.length === 0) {
|
|
95
|
+
console.log(`
|
|
96
|
+
Usage: bun prisma <command> [args...]
|
|
97
|
+
|
|
98
|
+
Examples:
|
|
99
|
+
bun prisma migrate dev # Create new migration
|
|
100
|
+
bun prisma migrate deploy # Apply migrations
|
|
101
|
+
bun prisma db push # Push schema changes
|
|
102
|
+
bun prisma studio # Open Prisma Studio
|
|
103
|
+
bun prisma migrate reset # Reset database
|
|
104
|
+
`);
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
const port = env.ports[service];
|
|
108
|
+
console.log(`
|
|
109
|
+
\uD83D\uDD27 Prisma CLI
|
|
110
|
+
Project: ${env.projectName}
|
|
111
|
+
Database: localhost:${port}
|
|
112
|
+
${env.portOffset > 0 ? `(port offset +${env.portOffset})` : ""}
|
|
113
|
+
`);
|
|
114
|
+
await ensureDatabase();
|
|
115
|
+
const envVars = env.buildEnvVars();
|
|
116
|
+
const workingDir = join(env.root, cwd);
|
|
117
|
+
const fullEnv = {
|
|
118
|
+
...process.env,
|
|
119
|
+
...envVars,
|
|
120
|
+
[urlEnvVar]: getDatabaseUrl()
|
|
121
|
+
};
|
|
122
|
+
console.log(`\uD83D\uDD04 Running: prisma ${args.join(" ")}
|
|
123
|
+
`);
|
|
124
|
+
return new Promise((resolve) => {
|
|
125
|
+
const proc = spawn("bunx", ["prisma", ...args], {
|
|
126
|
+
cwd: workingDir,
|
|
127
|
+
env: fullEnv,
|
|
128
|
+
stdio: "inherit"
|
|
129
|
+
});
|
|
130
|
+
proc.on("close", (code) => {
|
|
131
|
+
resolve(code ?? 0);
|
|
132
|
+
});
|
|
133
|
+
proc.on("error", () => {
|
|
134
|
+
resolve(1);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return { run, getDatabaseUrl, ensureDatabase };
|
|
139
|
+
}
|
|
140
|
+
// src/environment/logging.ts
|
|
141
|
+
import pc from "picocolors";
|
|
142
|
+
function formatUrl(url) {
|
|
143
|
+
return pc.cyan(url.replace(/:(\d+)(\/?)/, (_, port, slash) => `:${pc.bold(port)}${slash}`));
|
|
144
|
+
}
|
|
145
|
+
function formatLabel(label, value, arrow = "➜") {
|
|
146
|
+
return ` ${pc.green(arrow)} ${pc.bold(label.padEnd(10))} ${value}`;
|
|
147
|
+
}
|
|
148
|
+
function formatDimLabel(label, value) {
|
|
149
|
+
return ` ${pc.dim("•")} ${pc.dim(label.padEnd(10))} ${pc.dim(value)}`;
|
|
150
|
+
}
|
|
151
|
+
function tunnelFor(tunnels, name, kind) {
|
|
152
|
+
return tunnels?.find((t) => t.name === name && t.kind === kind);
|
|
153
|
+
}
|
|
154
|
+
function logEnvironmentInfo(input) {
|
|
155
|
+
const {
|
|
156
|
+
label,
|
|
157
|
+
projectName,
|
|
158
|
+
services,
|
|
159
|
+
apps,
|
|
160
|
+
ports,
|
|
161
|
+
localIp,
|
|
162
|
+
worktree,
|
|
163
|
+
portOffset,
|
|
164
|
+
projectSuffix,
|
|
165
|
+
tunnels
|
|
166
|
+
} = input;
|
|
167
|
+
const serviceNames = Object.keys(services);
|
|
168
|
+
const appNames = Object.keys(apps);
|
|
169
|
+
console.log("");
|
|
170
|
+
console.log(` ${pc.cyan(pc.bold(`\uD83D\uDC33 ${label}`))}`);
|
|
171
|
+
console.log(formatLabel("Project:", pc.white(projectName)));
|
|
172
|
+
if (serviceNames.length > 0) {
|
|
173
|
+
console.log("");
|
|
174
|
+
console.log(` ${pc.dim("─── Services ───")}`);
|
|
175
|
+
for (const name of serviceNames) {
|
|
176
|
+
const port = ports[name];
|
|
177
|
+
const url = `localhost:${port}`;
|
|
178
|
+
console.log(formatLabel(`${name}:`, formatUrl(`http://${url}`)));
|
|
179
|
+
const t = tunnelFor(tunnels, name, "service");
|
|
180
|
+
if (t) {
|
|
181
|
+
console.log(` ${pc.dim("Public:")} ${formatUrl(t.publicUrl)} ${pc.dim("(tunnel)")}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (appNames.length > 0) {
|
|
186
|
+
console.log("");
|
|
187
|
+
console.log(` ${pc.dim("─── Applications ───")}`);
|
|
188
|
+
for (const name of appNames) {
|
|
189
|
+
const port = ports[name];
|
|
190
|
+
const localUrl = `http://localhost:${port}`;
|
|
191
|
+
const networkUrl = `http://${localIp}:${port}`;
|
|
192
|
+
console.log(` ${pc.green("➜")} ${pc.bold(pc.cyan(name))}`);
|
|
193
|
+
console.log(` ${pc.dim("Local:")} ${formatUrl(localUrl)}`);
|
|
194
|
+
console.log(` ${pc.dim("Network:")} ${formatUrl(networkUrl)}`);
|
|
195
|
+
const t = tunnelFor(tunnels, name, "app");
|
|
196
|
+
if (t) {
|
|
197
|
+
console.log(` ${pc.dim("Public:")} ${formatUrl(t.publicUrl)} ${pc.dim("(tunnel)")}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
console.log("");
|
|
202
|
+
console.log(` ${pc.dim("─── Environment ───")}`);
|
|
203
|
+
console.log(formatDimLabel("Worktree:", worktree ? "yes" : "no"));
|
|
204
|
+
console.log(formatDimLabel("Port offset:", portOffset > 0 ? `+${portOffset}` : "none"));
|
|
205
|
+
if (projectSuffix) {
|
|
206
|
+
console.log(formatDimLabel("Suffix:", projectSuffix));
|
|
207
|
+
}
|
|
208
|
+
console.log(formatDimLabel("Local IP:", localIp));
|
|
209
|
+
console.log("");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/environment/only-apps.ts
|
|
213
|
+
function assertOnlyAppNames(appKeys, onlyApps) {
|
|
214
|
+
if (onlyApps === undefined)
|
|
215
|
+
return;
|
|
216
|
+
const unknown = onlyApps.filter((n) => !appKeys.includes(n));
|
|
217
|
+
if (unknown.length > 0) {
|
|
218
|
+
throw new Error(`Unknown app name(s) in onlyApps: ${unknown.join(", ")}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function pickApps(apps, onlyApps) {
|
|
222
|
+
if (onlyApps === undefined)
|
|
223
|
+
return apps;
|
|
224
|
+
const out = {};
|
|
225
|
+
for (const name of onlyApps) {
|
|
226
|
+
const config = apps[name];
|
|
227
|
+
if (config !== undefined) {
|
|
228
|
+
out[name] = config;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/environment/seeding.ts
|
|
235
|
+
function createCheckTableHelper(urls, exec) {
|
|
236
|
+
return async (tableName, service) => {
|
|
237
|
+
const serviceName = service ?? "postgres";
|
|
238
|
+
const serviceUrl = urls[serviceName];
|
|
239
|
+
if (!serviceUrl) {
|
|
240
|
+
console.warn(`⚠️ Service "${serviceName}" not found for checkTable`);
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
const checkResult = await exec(`psql "${serviceUrl}" -tAc 'SELECT COUNT(*) FROM "${tableName}" LIMIT 1'`, { throwOnError: false });
|
|
244
|
+
const count = checkResult.stdout.trim();
|
|
245
|
+
const shouldSeed = checkResult.exitCode !== 0 || count === "0" || count === "";
|
|
246
|
+
if (!shouldSeed) {
|
|
247
|
+
console.log(` \uD83D\uDCCA Table "${tableName}" has ${count} rows`);
|
|
248
|
+
}
|
|
249
|
+
return shouldSeed;
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function createSeedCheckContext(baseContext, checkTable) {
|
|
253
|
+
return {
|
|
254
|
+
...baseContext,
|
|
255
|
+
checkTable
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/environment/create-dev-environment.ts
|
|
260
|
+
function createDevEnvironment(config, options = {}) {
|
|
261
|
+
assertValidConfig(config);
|
|
262
|
+
const root = findMonorepoRoot();
|
|
263
|
+
const suffix = options.suffix;
|
|
264
|
+
const identity = computeDevIdentity({
|
|
265
|
+
projectPrefix: config.projectPrefix,
|
|
266
|
+
suffix,
|
|
267
|
+
root,
|
|
268
|
+
worktreeIsolation: config.options?.worktreeIsolation
|
|
269
|
+
});
|
|
270
|
+
const { worktree, projectSuffix, portOffset, projectName } = identity;
|
|
271
|
+
const localIp = getLocalIp();
|
|
272
|
+
const services = config.services;
|
|
273
|
+
const apps = config.apps ?? {};
|
|
274
|
+
const composeFile = getGeneratedComposePath(root, config.docker).composeFileArg;
|
|
275
|
+
function ensureComposeFile() {
|
|
276
|
+
return writeGeneratedComposeFile(root, services, config.docker);
|
|
277
|
+
}
|
|
278
|
+
const ports = computePorts(services, apps, portOffset);
|
|
279
|
+
const urls = computeUrls(services, apps, ports, localIp);
|
|
280
|
+
const publicUrls = {};
|
|
281
|
+
function setPublicUrls(urlsInput) {
|
|
282
|
+
for (const key of Object.keys(publicUrls)) {
|
|
283
|
+
delete publicUrls[key];
|
|
284
|
+
}
|
|
285
|
+
for (const [key, value] of Object.entries(urlsInput)) {
|
|
286
|
+
publicUrls[key] = value;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function clearPublicUrls() {
|
|
290
|
+
for (const key of Object.keys(publicUrls)) {
|
|
291
|
+
delete publicUrls[key];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function buildEnvVars(production = false) {
|
|
295
|
+
const baseEnv = {
|
|
296
|
+
COMPOSE_PROJECT_NAME: projectName,
|
|
297
|
+
NODE_ENV: production ? "production" : "development"
|
|
298
|
+
};
|
|
299
|
+
for (const [name, port] of Object.entries(ports)) {
|
|
300
|
+
const envName = `${name.toUpperCase()}_PORT`;
|
|
301
|
+
baseEnv[envName] = String(port);
|
|
302
|
+
}
|
|
303
|
+
for (const [name, url] of Object.entries(urls)) {
|
|
304
|
+
const envName = `${name.toUpperCase()}_URL`;
|
|
305
|
+
baseEnv[envName] = url;
|
|
306
|
+
}
|
|
307
|
+
for (const [name, url] of Object.entries(publicUrls)) {
|
|
308
|
+
const envName = `${name.toUpperCase()}_PUBLIC_URL`;
|
|
309
|
+
baseEnv[envName] = url;
|
|
310
|
+
}
|
|
311
|
+
if (config.envVars) {
|
|
312
|
+
const userEnv = config.envVars(ports, urls, {
|
|
313
|
+
projectName,
|
|
314
|
+
localIp,
|
|
315
|
+
portOffset,
|
|
316
|
+
publicUrls
|
|
317
|
+
});
|
|
318
|
+
for (const [key, value] of Object.entries(userEnv)) {
|
|
319
|
+
baseEnv[key] = String(value);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return baseEnv;
|
|
323
|
+
}
|
|
324
|
+
let hookContext = null;
|
|
325
|
+
function getHookContext() {
|
|
326
|
+
if (!hookContext) {
|
|
327
|
+
hookContext = {
|
|
328
|
+
projectName,
|
|
329
|
+
ports,
|
|
330
|
+
urls,
|
|
331
|
+
publicUrls,
|
|
332
|
+
root,
|
|
333
|
+
isCI: isCI(),
|
|
334
|
+
portOffset,
|
|
335
|
+
localIp,
|
|
336
|
+
exec: async (cmd, opts) => {
|
|
337
|
+
const envVars = buildEnvVars();
|
|
338
|
+
return execAsync(cmd, root, envVars, opts);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
return hookContext;
|
|
343
|
+
}
|
|
344
|
+
function exec(cmd, options2) {
|
|
345
|
+
const envVars = buildEnvVars();
|
|
346
|
+
return execAsync(cmd, root, envVars, options2);
|
|
347
|
+
}
|
|
348
|
+
async function start(startOptions = {}) {
|
|
349
|
+
const isCI2 = process.env.CI === "true";
|
|
350
|
+
const {
|
|
351
|
+
verbose = config.options?.verbose ?? true,
|
|
352
|
+
wait = true,
|
|
353
|
+
startServers: shouldStartServers = true,
|
|
354
|
+
productionBuild = isCI2,
|
|
355
|
+
skipSeed = false,
|
|
356
|
+
skipEnvironmentLog = false,
|
|
357
|
+
onlyApps
|
|
358
|
+
} = startOptions;
|
|
359
|
+
assertOnlyAppNames(Object.keys(apps), onlyApps);
|
|
360
|
+
const appsToStart = pickApps(apps, onlyApps);
|
|
361
|
+
const envVars = buildEnvVars(productionBuild);
|
|
362
|
+
ensureComposeFile();
|
|
363
|
+
if (verbose && !skipEnvironmentLog) {
|
|
364
|
+
logInfo(productionBuild ? "Production Environment" : "Dev Environment");
|
|
365
|
+
}
|
|
366
|
+
const serviceCount = Object.keys(services).length;
|
|
367
|
+
const alreadyRunning = await areContainersRunning(projectName, serviceCount);
|
|
368
|
+
if (alreadyRunning) {
|
|
369
|
+
if (verbose)
|
|
370
|
+
console.log("✓ Containers already running");
|
|
371
|
+
} else {
|
|
372
|
+
startContainers(root, projectName, envVars, {
|
|
373
|
+
verbose,
|
|
374
|
+
wait,
|
|
375
|
+
composeFile
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const allMigrations = [
|
|
379
|
+
...config.prisma ? [
|
|
380
|
+
{
|
|
381
|
+
name: "prisma",
|
|
382
|
+
command: "bunx prisma migrate deploy",
|
|
383
|
+
cwd: config.prisma.cwd ?? "packages/prisma"
|
|
384
|
+
}
|
|
385
|
+
] : [],
|
|
386
|
+
...config.migrations ?? []
|
|
387
|
+
];
|
|
388
|
+
if (allMigrations.length > 0) {
|
|
389
|
+
if (verbose)
|
|
390
|
+
console.log("\uD83D\uDCE6 Running migrations...");
|
|
391
|
+
const migrationResults = await Promise.all(allMigrations.map(async (migration) => {
|
|
392
|
+
const result = await exec(migration.command, {
|
|
393
|
+
cwd: migration.cwd,
|
|
394
|
+
throwOnError: false
|
|
395
|
+
});
|
|
396
|
+
return { name: migration.name, result };
|
|
397
|
+
}));
|
|
398
|
+
for (const { name, result } of migrationResults) {
|
|
399
|
+
if (result.exitCode !== 0) {
|
|
400
|
+
console.error(`❌ Migration "${name}" failed`);
|
|
401
|
+
if (result.stdout) {
|
|
402
|
+
console.error(result.stdout);
|
|
403
|
+
}
|
|
404
|
+
if (result.stderr) {
|
|
405
|
+
console.error(result.stderr);
|
|
406
|
+
}
|
|
407
|
+
throw new Error(`Migration "${name}" failed`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (verbose)
|
|
411
|
+
console.log("✓ Migrations complete");
|
|
412
|
+
}
|
|
413
|
+
if (config.hooks?.afterContainersReady) {
|
|
414
|
+
await config.hooks.afterContainersReady(getHookContext());
|
|
415
|
+
}
|
|
416
|
+
if (config.seed && !skipSeed) {
|
|
417
|
+
let shouldSeed = true;
|
|
418
|
+
if (config.seed.check) {
|
|
419
|
+
const checkTable = createCheckTableHelper(urls, exec);
|
|
420
|
+
const seedCheckContext = createSeedCheckContext(getHookContext(), checkTable);
|
|
421
|
+
shouldSeed = await config.seed.check(seedCheckContext);
|
|
422
|
+
}
|
|
423
|
+
if (shouldSeed) {
|
|
424
|
+
if (verbose)
|
|
425
|
+
console.log("\uD83C\uDF31 Running seeders...");
|
|
426
|
+
const seedResult = await exec(config.seed.command, {
|
|
427
|
+
cwd: config.seed.cwd,
|
|
428
|
+
verbose,
|
|
429
|
+
throwOnError: false
|
|
430
|
+
});
|
|
431
|
+
if (seedResult.exitCode !== 0) {
|
|
432
|
+
console.error("❌ Seeding failed");
|
|
433
|
+
console.error(seedResult.stderr);
|
|
434
|
+
} else {
|
|
435
|
+
if (verbose)
|
|
436
|
+
console.log("✓ Seeding complete");
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
if (verbose)
|
|
440
|
+
console.log("✓ Database already has data, skipping seeders");
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (shouldStartServers && Object.keys(appsToStart).length > 0) {
|
|
444
|
+
if (config.hooks?.beforeServers) {
|
|
445
|
+
await config.hooks.beforeServers(getHookContext());
|
|
446
|
+
}
|
|
447
|
+
if (productionBuild) {
|
|
448
|
+
buildApps(appsToStart, root, envVars, { verbose });
|
|
449
|
+
}
|
|
450
|
+
const pids = await startDevServers(appsToStart, root, envVars, ports, {
|
|
451
|
+
verbose,
|
|
452
|
+
productionBuild,
|
|
453
|
+
isCI: isCI2
|
|
454
|
+
});
|
|
455
|
+
if (verbose)
|
|
456
|
+
console.log("⏳ Waiting for servers to be ready...");
|
|
457
|
+
await waitForDevServers(appsToStart, ports, {
|
|
458
|
+
timeout: isCI2 ? 120000 : 60000,
|
|
459
|
+
verbose,
|
|
460
|
+
productionBuild
|
|
461
|
+
});
|
|
462
|
+
if (config.hooks?.afterServers) {
|
|
463
|
+
await config.hooks.afterServers(getHookContext());
|
|
464
|
+
}
|
|
465
|
+
if (verbose)
|
|
466
|
+
console.log(`✅ Environment ready
|
|
467
|
+
`);
|
|
468
|
+
return pids;
|
|
469
|
+
}
|
|
470
|
+
if (verbose)
|
|
471
|
+
console.log(`✅ Containers ready
|
|
472
|
+
`);
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
async function stop(stopOptions = {}) {
|
|
476
|
+
const { verbose = true, removeVolumes = false } = stopOptions;
|
|
477
|
+
ensureComposeFile();
|
|
478
|
+
if (config.hooks?.beforeStop) {
|
|
479
|
+
await config.hooks.beforeStop(getHookContext());
|
|
480
|
+
}
|
|
481
|
+
stopContainers(root, projectName, {
|
|
482
|
+
verbose,
|
|
483
|
+
removeVolumes,
|
|
484
|
+
composeFile
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
async function restart() {
|
|
488
|
+
await stop();
|
|
489
|
+
await start({ startServers: false });
|
|
490
|
+
}
|
|
491
|
+
async function isRunning() {
|
|
492
|
+
const serviceCount = Object.keys(services).length;
|
|
493
|
+
return areContainersRunning(projectName, serviceCount);
|
|
494
|
+
}
|
|
495
|
+
async function startServersOnly(options2 = {}) {
|
|
496
|
+
const { productionBuild = false, verbose = true, onlyApps } = options2;
|
|
497
|
+
assertOnlyAppNames(Object.keys(apps), onlyApps);
|
|
498
|
+
const appsToStart = pickApps(apps, onlyApps);
|
|
499
|
+
const envVars = buildEnvVars(productionBuild);
|
|
500
|
+
const isCI2 = process.env.CI === "true";
|
|
501
|
+
if (Object.keys(appsToStart).length === 0) {
|
|
502
|
+
return {};
|
|
503
|
+
}
|
|
504
|
+
if (productionBuild) {
|
|
505
|
+
buildApps(appsToStart, root, envVars, { verbose });
|
|
506
|
+
}
|
|
507
|
+
const pids = await startDevServers(appsToStart, root, envVars, ports, {
|
|
508
|
+
verbose,
|
|
509
|
+
productionBuild,
|
|
510
|
+
isCI: isCI2
|
|
511
|
+
});
|
|
512
|
+
if (verbose)
|
|
513
|
+
console.log("⏳ Waiting for servers to be ready...");
|
|
514
|
+
await waitForDevServers(appsToStart, ports, {
|
|
515
|
+
timeout: isCI2 ? 120000 : 60000,
|
|
516
|
+
verbose,
|
|
517
|
+
productionBuild
|
|
518
|
+
});
|
|
519
|
+
return pids;
|
|
520
|
+
}
|
|
521
|
+
async function waitForServersReady(options2 = {}) {
|
|
522
|
+
const { timeout = 60000, productionBuild = false, onlyApps } = options2;
|
|
523
|
+
assertOnlyAppNames(Object.keys(apps), onlyApps);
|
|
524
|
+
const appsToWait = pickApps(apps, onlyApps);
|
|
525
|
+
await waitForDevServers(appsToWait, ports, { timeout, productionBuild });
|
|
526
|
+
}
|
|
527
|
+
async function openPublicTunnels(options2 = {}) {
|
|
528
|
+
const { names, waitForHealthy } = options2;
|
|
529
|
+
const exposeList = names?.length ? names.join(",") : undefined;
|
|
530
|
+
if (waitForHealthy?.length) {
|
|
531
|
+
assertOnlyAppNames(Object.keys(apps), waitForHealthy);
|
|
532
|
+
const appsWait = pickApps(apps, waitForHealthy);
|
|
533
|
+
const isCI2 = process.env.CI === "true";
|
|
534
|
+
await waitForDevServers(appsWait, ports, {
|
|
535
|
+
timeout: isCI2 ? 120000 : 60000,
|
|
536
|
+
verbose: config.options?.verbose ?? true,
|
|
537
|
+
productionBuild: false
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
const { targets, unknownNames, notEnabledNames } = resolveExposeTargets({
|
|
541
|
+
services,
|
|
542
|
+
apps,
|
|
543
|
+
ports
|
|
544
|
+
}, exposeList);
|
|
545
|
+
if (unknownNames.length > 0) {
|
|
546
|
+
throw new Error(`Unknown expose target(s): ${unknownNames.join(", ")}`);
|
|
547
|
+
}
|
|
548
|
+
if (notEnabledNames.length > 0) {
|
|
549
|
+
throw new Error(`Target(s) missing expose: true: ${notEnabledNames.join(", ")}`);
|
|
550
|
+
}
|
|
551
|
+
if (targets.length === 0) {
|
|
552
|
+
throw new Error("No expose targets selected. Add expose: true to services/apps or pass names that have expose: true.");
|
|
553
|
+
}
|
|
554
|
+
const tunnels = await startPublicTunnels(targets);
|
|
555
|
+
setPublicUrls(Object.fromEntries(tunnels.map((t) => [t.name, t.publicUrl])));
|
|
556
|
+
let closed = false;
|
|
557
|
+
async function close() {
|
|
558
|
+
if (closed)
|
|
559
|
+
return;
|
|
560
|
+
closed = true;
|
|
561
|
+
await stopPublicTunnels(tunnels);
|
|
562
|
+
clearPublicUrls();
|
|
563
|
+
}
|
|
564
|
+
return {
|
|
565
|
+
publicUrls: { ...publicUrls },
|
|
566
|
+
tunnels,
|
|
567
|
+
close
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function logInfo(label = "Docker Dev", tunnels) {
|
|
571
|
+
const tunnelRows = tunnels?.map(({ kind, name, localUrl, publicUrl }) => ({
|
|
572
|
+
kind,
|
|
573
|
+
name,
|
|
574
|
+
localUrl,
|
|
575
|
+
publicUrl
|
|
576
|
+
}));
|
|
577
|
+
logEnvironmentInfo({
|
|
578
|
+
label,
|
|
579
|
+
projectName,
|
|
580
|
+
services,
|
|
581
|
+
apps,
|
|
582
|
+
ports,
|
|
583
|
+
localIp,
|
|
584
|
+
worktree,
|
|
585
|
+
portOffset,
|
|
586
|
+
projectSuffix,
|
|
587
|
+
tunnels: tunnelRows
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
async function waitForServerUrl(url, timeout) {
|
|
591
|
+
await waitForServer(url, { timeout });
|
|
592
|
+
}
|
|
593
|
+
function startHeartbeat2(intervalMs) {
|
|
594
|
+
startHeartbeat(projectName, intervalMs);
|
|
595
|
+
}
|
|
596
|
+
function stopHeartbeat2() {
|
|
597
|
+
stopHeartbeat();
|
|
598
|
+
}
|
|
599
|
+
async function spawnWatchdog2(timeoutMinutes) {
|
|
600
|
+
await spawnWatchdog(projectName, root, {
|
|
601
|
+
timeoutMinutes,
|
|
602
|
+
verbose: true,
|
|
603
|
+
composeFile
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
function stopWatchdog2() {
|
|
607
|
+
stopWatchdog(projectName);
|
|
608
|
+
}
|
|
609
|
+
function getExpoApiUrl() {
|
|
610
|
+
const apiPort = ports.api;
|
|
611
|
+
const url = `http://${localIp}:${apiPort}`;
|
|
612
|
+
logExpoApiUrl(url);
|
|
613
|
+
return url;
|
|
614
|
+
}
|
|
615
|
+
function getFrontendPort() {
|
|
616
|
+
const port = ports.platform;
|
|
617
|
+
logFrontendPort(port);
|
|
618
|
+
return port;
|
|
619
|
+
}
|
|
620
|
+
function withSuffix(newSuffix) {
|
|
621
|
+
return createDevEnvironment(config, { suffix: newSuffix });
|
|
622
|
+
}
|
|
623
|
+
const env = {
|
|
624
|
+
projectName,
|
|
625
|
+
ports,
|
|
626
|
+
urls,
|
|
627
|
+
publicUrls,
|
|
628
|
+
services,
|
|
629
|
+
apps,
|
|
630
|
+
portOffset,
|
|
631
|
+
isWorktree: worktree,
|
|
632
|
+
localIp,
|
|
633
|
+
root,
|
|
634
|
+
composeFile,
|
|
635
|
+
start,
|
|
636
|
+
stop,
|
|
637
|
+
restart,
|
|
638
|
+
isRunning,
|
|
639
|
+
startServers: startServersOnly,
|
|
640
|
+
stopProcess,
|
|
641
|
+
waitForServers: waitForServersReady,
|
|
642
|
+
buildEnvVars,
|
|
643
|
+
setPublicUrls: (urlsInput) => {
|
|
644
|
+
setPublicUrls(urlsInput);
|
|
645
|
+
},
|
|
646
|
+
clearPublicUrls,
|
|
647
|
+
ensureComposeFile,
|
|
648
|
+
exec,
|
|
649
|
+
waitForServer: waitForServerUrl,
|
|
650
|
+
logInfo,
|
|
651
|
+
openPublicTunnels,
|
|
652
|
+
getExpoApiUrl,
|
|
653
|
+
getFrontendPort,
|
|
654
|
+
startHeartbeat: startHeartbeat2,
|
|
655
|
+
stopHeartbeat: stopHeartbeat2,
|
|
656
|
+
spawnWatchdog: spawnWatchdog2,
|
|
657
|
+
stopWatchdog: stopWatchdog2,
|
|
658
|
+
prisma: undefined,
|
|
659
|
+
withSuffix
|
|
660
|
+
};
|
|
661
|
+
if (config.prisma) {
|
|
662
|
+
env.prisma = createPrismaRunner(env, config.prisma);
|
|
663
|
+
}
|
|
664
|
+
return env;
|
|
665
|
+
}
|
|
666
|
+
export { createDevEnvironment };
|