@buildepicshit/cli 0.0.2 → 0.0.4
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/package.json +5 -3
- package/src/bes.ts +124 -0
- package/src/config-loader.ts +68 -0
- package/src/daemon.ts +127 -0
- package/src/orchestrator/core/__tests__/command.test.ts +264 -0
- package/src/orchestrator/core/__tests__/token.test.ts +145 -0
- package/src/orchestrator/core/command.ts +334 -0
- package/src/orchestrator/core/compound.ts +84 -0
- package/src/orchestrator/core/health-check.ts +38 -0
- package/src/orchestrator/core/index.ts +39 -0
- package/src/orchestrator/core/token.ts +88 -0
- package/src/orchestrator/core/types.ts +102 -0
- package/src/orchestrator/index.ts +67 -0
- package/src/orchestrator/logger/logger.ts +123 -0
- package/src/orchestrator/presets/docker.ts +164 -0
- package/src/orchestrator/presets/drizzle.ts +93 -0
- package/src/orchestrator/presets/esbuild.ts +12 -0
- package/src/orchestrator/presets/hono.ts +7 -0
- package/src/orchestrator/presets/index.ts +6 -0
- package/src/orchestrator/presets/nextjs.ts +97 -0
- package/src/orchestrator/presets/node.ts +12 -0
- package/src/orchestrator/runner/__tests__/event-bus.test.ts +97 -0
- package/src/orchestrator/runner/event-bus.ts +55 -0
- package/src/orchestrator/runner/health-runner.ts +129 -0
- package/src/orchestrator/runner/index.ts +17 -0
- package/src/orchestrator/runner/process.ts +167 -0
- package/src/orchestrator/runner/runtime-context.ts +51 -0
- package/src/orchestrator/utils/__tests__/dna.test.ts +133 -0
- package/src/orchestrator/utils/dna.ts +85 -0
- package/src/project.ts +40 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import type { ComputeContext, LazyEnvSource, Token } from "./types.js";
|
|
4
|
+
|
|
5
|
+
export function compute(fn: (ctx: ComputeContext) => Promise<string>): Token {
|
|
6
|
+
return fn;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function fromEnv(key: string, fallback?: string): Token {
|
|
10
|
+
return async ({ env }: ComputeContext) => {
|
|
11
|
+
const value = env[key];
|
|
12
|
+
if (value !== undefined) return value;
|
|
13
|
+
if (fallback !== undefined) return fallback;
|
|
14
|
+
throw new Error(
|
|
15
|
+
`Environment variable "${key}" is not set and no fallback provided`,
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function fromPackageJson(path: string, fallback?: string): Token {
|
|
21
|
+
return async ({ cwd }: ComputeContext) => {
|
|
22
|
+
const raw = await readFile(join(cwd, "package.json"), "utf8");
|
|
23
|
+
const pkg = JSON.parse(raw);
|
|
24
|
+
|
|
25
|
+
const value = path.split(".").reduce<unknown>((obj, key) => {
|
|
26
|
+
if (obj != null && typeof obj === "object") {
|
|
27
|
+
return (obj as Record<string, unknown>)[key];
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}, pkg);
|
|
31
|
+
|
|
32
|
+
if (value !== undefined) return String(value);
|
|
33
|
+
if (fallback !== undefined) return fallback;
|
|
34
|
+
throw new Error(
|
|
35
|
+
`package.json path "${path}" not found in ${cwd}/package.json and no fallback provided`,
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parse .env file(s) relative to the command's cwd.
|
|
42
|
+
* Multiple files can be passed — later files override earlier ones.
|
|
43
|
+
*/
|
|
44
|
+
export function fromFile(...paths: string[]): LazyEnvSource {
|
|
45
|
+
return {
|
|
46
|
+
__type: "env-source",
|
|
47
|
+
resolve: async (ctx: ComputeContext) => {
|
|
48
|
+
const env: Record<string, string> = {};
|
|
49
|
+
|
|
50
|
+
for (const filePath of paths) {
|
|
51
|
+
const fullPath = resolve(ctx.cwd, filePath);
|
|
52
|
+
let content: string;
|
|
53
|
+
try {
|
|
54
|
+
content = await readFile(fullPath, "utf8");
|
|
55
|
+
} catch {
|
|
56
|
+
continue; // skip missing files silently
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const line of content.split("\n")) {
|
|
60
|
+
const trimmed = line.trim();
|
|
61
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
62
|
+
const eqIdx = trimmed.indexOf("=");
|
|
63
|
+
if (eqIdx === -1) continue;
|
|
64
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
65
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
66
|
+
// Strip surrounding quotes
|
|
67
|
+
if (
|
|
68
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
69
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
70
|
+
) {
|
|
71
|
+
value = value.slice(1, -1);
|
|
72
|
+
}
|
|
73
|
+
env[key] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return env;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function resolveToken(
|
|
83
|
+
token: Token,
|
|
84
|
+
ctx: ComputeContext,
|
|
85
|
+
): Promise<string> {
|
|
86
|
+
if (typeof token === "string") return token;
|
|
87
|
+
return token(ctx);
|
|
88
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Identity } from "../utils/dna.js";
|
|
2
|
+
|
|
3
|
+
// ─── Token ───────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
export type ComputeContext = {
|
|
6
|
+
cwd: string;
|
|
7
|
+
env: Record<string, string>;
|
|
8
|
+
root: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type Token = string | ((ctx: ComputeContext) => Promise<string>);
|
|
12
|
+
|
|
13
|
+
// ─── Env Source ──────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export interface LazyEnvSource {
|
|
16
|
+
__type: "env-source";
|
|
17
|
+
resolve: (ctx: ComputeContext) => Promise<Record<string, string>>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type EnvCallback = (
|
|
21
|
+
self: ICommand,
|
|
22
|
+
) => Record<string, Token> | LazyEnvSource;
|
|
23
|
+
|
|
24
|
+
export type EnvSource = Record<string, Token> | LazyEnvSource | EnvCallback;
|
|
25
|
+
|
|
26
|
+
export function isLazyEnvSource(
|
|
27
|
+
source: Record<string, Token> | LazyEnvSource,
|
|
28
|
+
): source is LazyEnvSource {
|
|
29
|
+
return (
|
|
30
|
+
typeof source === "object" &&
|
|
31
|
+
"__type" in source &&
|
|
32
|
+
source.__type === "env-source"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function isEnvCallback(source: EnvSource): source is EnvCallback {
|
|
37
|
+
return typeof source === "function";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ─── Command ─────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export interface BuiltCommand {
|
|
43
|
+
args: string[];
|
|
44
|
+
command: string;
|
|
45
|
+
cwd: string;
|
|
46
|
+
env: Record<string, string>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface IdentityAccessor extends Identity {
|
|
50
|
+
(suffix: string): ICommand;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ICommand {
|
|
54
|
+
readonly __type: "command";
|
|
55
|
+
arg(value: Token): ICommand;
|
|
56
|
+
as(name: string): ICommand;
|
|
57
|
+
build(ctx: ComputeContext): Promise<BuiltCommand>;
|
|
58
|
+
collectNames(): string[];
|
|
59
|
+
dependsOn(...deps: Runnable[]): ICommand;
|
|
60
|
+
dir(path: string): ICommand;
|
|
61
|
+
env(source: EnvSource): ICommand;
|
|
62
|
+
flag(name: string, value?: Token): ICommand;
|
|
63
|
+
readonly identity: IdentityAccessor;
|
|
64
|
+
run(): Promise<void>;
|
|
65
|
+
waitFor(check: HealthCheck | HealthCheckCallback): ICommand;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Compound ────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
export interface ICompound {
|
|
71
|
+
readonly __type: "compound";
|
|
72
|
+
collectNames(): string[];
|
|
73
|
+
dir(path: string): ICompound;
|
|
74
|
+
env(source: EnvSource): ICompound;
|
|
75
|
+
readonly mode: "par" | "seq";
|
|
76
|
+
run(): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Runnable ────────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
export type Runnable = ICommand | ICompound;
|
|
82
|
+
|
|
83
|
+
// ─── Health Check ────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
export type HealthCheckCallback = (self: ICommand) => HealthCheck;
|
|
86
|
+
|
|
87
|
+
export type HealthCheck =
|
|
88
|
+
| { type: "tcp"; host: Token; port: Token }
|
|
89
|
+
| { type: "http"; url: Token; status?: number }
|
|
90
|
+
| { type: "stdout"; pattern: RegExp }
|
|
91
|
+
| { type: "exec"; command: ICommand }
|
|
92
|
+
| { type: "custom"; check: () => Promise<boolean> };
|
|
93
|
+
|
|
94
|
+
// ─── Process ─────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
export type ProcessStatus =
|
|
97
|
+
| "completed"
|
|
98
|
+
| "failed"
|
|
99
|
+
| "healthy"
|
|
100
|
+
| "pending"
|
|
101
|
+
| "running"
|
|
102
|
+
| "starting";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Core API
|
|
2
|
+
export {
|
|
3
|
+
cmd,
|
|
4
|
+
compute,
|
|
5
|
+
customHealthCheck,
|
|
6
|
+
execHealthCheck,
|
|
7
|
+
fromEnv,
|
|
8
|
+
fromFile,
|
|
9
|
+
fromPackageJson,
|
|
10
|
+
health,
|
|
11
|
+
httpHealthCheck,
|
|
12
|
+
par,
|
|
13
|
+
postgresHealthCheck,
|
|
14
|
+
seq,
|
|
15
|
+
stdoutHealthCheck,
|
|
16
|
+
tcpHealthCheck,
|
|
17
|
+
} from "./core/index.js";
|
|
18
|
+
// Types
|
|
19
|
+
export type {
|
|
20
|
+
BuiltCommand,
|
|
21
|
+
ComputeContext,
|
|
22
|
+
EnvCallback,
|
|
23
|
+
EnvSource,
|
|
24
|
+
HealthCheck,
|
|
25
|
+
HealthCheckCallback,
|
|
26
|
+
ICommand,
|
|
27
|
+
ICompound,
|
|
28
|
+
IdentityAccessor,
|
|
29
|
+
ProcessStatus,
|
|
30
|
+
Runnable,
|
|
31
|
+
Token,
|
|
32
|
+
} from "./core/types.js";
|
|
33
|
+
// Logger
|
|
34
|
+
export type { Logger } from "./logger/logger.js";
|
|
35
|
+
export { createLazyLogger, createLogger } from "./logger/logger.js";
|
|
36
|
+
// Presets
|
|
37
|
+
export type { IDockerCompose, IDrizzle, INextjs, NextPreset } from "./presets/index.js";
|
|
38
|
+
export {
|
|
39
|
+
docker,
|
|
40
|
+
dockerCompose,
|
|
41
|
+
drizzle,
|
|
42
|
+
esbuild,
|
|
43
|
+
esbuildWatch,
|
|
44
|
+
hono,
|
|
45
|
+
next,
|
|
46
|
+
nextjs,
|
|
47
|
+
nodemon,
|
|
48
|
+
tsx,
|
|
49
|
+
} from "./presets/index.js";
|
|
50
|
+
// Runner
|
|
51
|
+
export type {
|
|
52
|
+
DevEvent,
|
|
53
|
+
IEventBus,
|
|
54
|
+
LogEntry,
|
|
55
|
+
OrchestratorProcess,
|
|
56
|
+
ProcessMeta,
|
|
57
|
+
RuntimeContext,
|
|
58
|
+
} from "./runner/index.js";
|
|
59
|
+
export {
|
|
60
|
+
clearRuntimeContext,
|
|
61
|
+
createEventBus,
|
|
62
|
+
getRuntimeContext,
|
|
63
|
+
setRuntimeContext,
|
|
64
|
+
} from "./runner/index.js";
|
|
65
|
+
export type { Identity } from "./utils/dna.js";
|
|
66
|
+
// Utils
|
|
67
|
+
export { createIdentity, identity } from "./utils/dna.js";
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import chalk, { type ChalkInstance } from "chalk";
|
|
2
|
+
|
|
3
|
+
const COLORS: ChalkInstance[] = [
|
|
4
|
+
chalk.cyan,
|
|
5
|
+
chalk.magenta,
|
|
6
|
+
chalk.yellow,
|
|
7
|
+
chalk.green,
|
|
8
|
+
chalk.blue,
|
|
9
|
+
chalk.red,
|
|
10
|
+
chalk.white,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export interface Logger {
|
|
14
|
+
completed(serviceName: string): void;
|
|
15
|
+
error(serviceName: string, message: string): void;
|
|
16
|
+
healthy(serviceName: string): void;
|
|
17
|
+
output(serviceName: string, line: string, stream: "stderr" | "stdout"): void;
|
|
18
|
+
ready(serviceName: string): void;
|
|
19
|
+
starting(serviceName: string, nodeId: string): void;
|
|
20
|
+
system(message: string): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createLogger(serviceNames: string[]): Logger {
|
|
24
|
+
// Compute max prefix width for alignment
|
|
25
|
+
const maxLen = Math.max(...serviceNames.map((n) => n.length), 6);
|
|
26
|
+
const colorMap = new Map<string, ChalkInstance>();
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < serviceNames.length; i++) {
|
|
29
|
+
colorMap.set(serviceNames[i], COLORS[i % COLORS.length]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function prefix(name: string): string {
|
|
33
|
+
const color = colorMap.get(name) ?? chalk.white;
|
|
34
|
+
return color(`[${name.padEnd(maxLen)}]`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
completed(serviceName) {
|
|
39
|
+
console.log(`${prefix(serviceName)} ${chalk.green("completed")} ✓`);
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
error(serviceName, message) {
|
|
43
|
+
console.error(
|
|
44
|
+
`${prefix(serviceName)} ${chalk.red("error")} ✗ ${message}`,
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
healthy(serviceName) {
|
|
49
|
+
console.log(`${prefix(serviceName)} ${chalk.green("healthy")} ✓`);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
output(serviceName, line, _stream) {
|
|
53
|
+
console.log(`${prefix(serviceName)} ${line}`);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
ready(serviceName) {
|
|
57
|
+
console.log(`${prefix(serviceName)} ${chalk.green("ready")} ✓`);
|
|
58
|
+
},
|
|
59
|
+
starting(serviceName, nodeId) {
|
|
60
|
+
console.log(`${prefix(serviceName)} Starting ${nodeId}...`);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
system(message) {
|
|
64
|
+
console.log(chalk.gray(message));
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Lazy logger that auto-registers service names on first use.
|
|
71
|
+
* Adjusts column padding dynamically as new names appear.
|
|
72
|
+
*/
|
|
73
|
+
export function createLazyLogger(): Logger {
|
|
74
|
+
const colorMap = new Map<string, ChalkInstance>();
|
|
75
|
+
let maxLen = 6;
|
|
76
|
+
let nextColorIdx = 0;
|
|
77
|
+
|
|
78
|
+
function ensureName(name: string) {
|
|
79
|
+
if (!colorMap.has(name)) {
|
|
80
|
+
colorMap.set(name, COLORS[nextColorIdx % COLORS.length]);
|
|
81
|
+
nextColorIdx++;
|
|
82
|
+
maxLen = Math.max(maxLen, name.length);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function prefix(name: string): string {
|
|
87
|
+
ensureName(name);
|
|
88
|
+
const color = colorMap.get(name) as ChalkInstance;
|
|
89
|
+
return color(`[${name.padEnd(maxLen)}]`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
completed(serviceName) {
|
|
94
|
+
console.log(`${prefix(serviceName)} ${chalk.green("completed")} ✓`);
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
error(serviceName, message) {
|
|
98
|
+
console.error(
|
|
99
|
+
`${prefix(serviceName)} ${chalk.red("error")} ✗ ${message}`,
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
healthy(serviceName) {
|
|
104
|
+
console.log(`${prefix(serviceName)} ${chalk.green("healthy")} ✓`);
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
output(serviceName, line, _stream) {
|
|
108
|
+
console.log(`${prefix(serviceName)} ${line}`);
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
ready(serviceName) {
|
|
112
|
+
console.log(`${prefix(serviceName)} ${chalk.green("ready")} ✓`);
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
starting(serviceName, _nodeId) {
|
|
116
|
+
console.log(`${prefix(serviceName)} Starting...`);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
system(message) {
|
|
120
|
+
console.log(chalk.gray(message));
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { cmd } from "../core/command.js";
|
|
2
|
+
import type {
|
|
3
|
+
BuiltCommand,
|
|
4
|
+
ComputeContext,
|
|
5
|
+
EnvSource,
|
|
6
|
+
HealthCheck,
|
|
7
|
+
HealthCheckCallback,
|
|
8
|
+
ICommand,
|
|
9
|
+
IdentityAccessor,
|
|
10
|
+
Runnable,
|
|
11
|
+
Token,
|
|
12
|
+
} from "../core/types.js";
|
|
13
|
+
|
|
14
|
+
export interface IDockerCompose extends ICommand {
|
|
15
|
+
file(path: string): IDockerCompose;
|
|
16
|
+
up(options?: { detach?: boolean }): IDockerCompose;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface DockerComposeState {
|
|
20
|
+
readonly detach: boolean;
|
|
21
|
+
readonly filePath: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class DockerCompose implements IDockerCompose {
|
|
25
|
+
readonly __type = "command" as const;
|
|
26
|
+
private readonly inner: ICommand;
|
|
27
|
+
private readonly dcState: DockerComposeState;
|
|
28
|
+
|
|
29
|
+
constructor(inner: ICommand, dcState: DockerComposeState) {
|
|
30
|
+
this.inner = inner;
|
|
31
|
+
this.dcState = dcState;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ─── Docker-compose-specific methods ─────────────────────────────────
|
|
35
|
+
|
|
36
|
+
up(options?: { detach?: boolean }): IDockerCompose {
|
|
37
|
+
return new DockerCompose(this.inner, {
|
|
38
|
+
...this.dcState,
|
|
39
|
+
detach: options?.detach ?? true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
file(path: string): IDockerCompose {
|
|
44
|
+
return new DockerCompose(this.inner, {
|
|
45
|
+
...this.dcState,
|
|
46
|
+
filePath: path,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── ICommand delegation (returns IDockerCompose for chaining) ───────
|
|
51
|
+
|
|
52
|
+
get identity(): IdentityAccessor {
|
|
53
|
+
const innerAccessor = this.inner.identity;
|
|
54
|
+
const self = this;
|
|
55
|
+
const setter = (suffix: string): IDockerCompose => {
|
|
56
|
+
return new DockerCompose(
|
|
57
|
+
self.inner.identity(suffix),
|
|
58
|
+
self.dcState,
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
Object.defineProperty(setter, "name", {
|
|
62
|
+
configurable: true,
|
|
63
|
+
value: innerAccessor.name,
|
|
64
|
+
});
|
|
65
|
+
return Object.assign(setter, {
|
|
66
|
+
localhostUrl: innerAccessor.localhostUrl,
|
|
67
|
+
port: innerAccessor.port,
|
|
68
|
+
url: innerAccessor.url,
|
|
69
|
+
}) as IdentityAccessor;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
flag(name: string, value?: Token): IDockerCompose {
|
|
73
|
+
return new DockerCompose(this.inner.flag(name, value), this.dcState);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
arg(value: Token): IDockerCompose {
|
|
77
|
+
return new DockerCompose(this.inner.arg(value), this.dcState);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
env(source: EnvSource): IDockerCompose {
|
|
81
|
+
return new DockerCompose(this.inner.env(source), this.dcState);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
dir(path: string): IDockerCompose {
|
|
85
|
+
return new DockerCompose(this.inner.dir(path), this.dcState);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
waitFor(check: HealthCheck | HealthCheckCallback): IDockerCompose {
|
|
89
|
+
return new DockerCompose(this.inner.waitFor(check), this.dcState);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
as(name: string): IDockerCompose {
|
|
93
|
+
return new DockerCompose(this.inner.as(name), this.dcState);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
dependsOn(...deps: Runnable[]): IDockerCompose {
|
|
97
|
+
return new DockerCompose(
|
|
98
|
+
this.inner.dependsOn(...deps),
|
|
99
|
+
this.dcState,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
collectNames(): string[] {
|
|
104
|
+
return this.inner.collectNames();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async build(ctx: ComputeContext): Promise<BuiltCommand> {
|
|
108
|
+
// Build the final docker compose command from state
|
|
109
|
+
let command = this.inner;
|
|
110
|
+
if (this.dcState.filePath) {
|
|
111
|
+
command = command.flag("file", this.dcState.filePath);
|
|
112
|
+
}
|
|
113
|
+
command = command.arg("up");
|
|
114
|
+
if (this.dcState.detach) {
|
|
115
|
+
command = command.flag("detach");
|
|
116
|
+
}
|
|
117
|
+
return command.build(ctx);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async run(): Promise<void> {
|
|
121
|
+
// Build a resolved inner command with docker-compose args applied
|
|
122
|
+
let command = this.inner;
|
|
123
|
+
if (this.dcState.filePath) {
|
|
124
|
+
command = command.flag("file", this.dcState.filePath);
|
|
125
|
+
}
|
|
126
|
+
command = command.arg("up");
|
|
127
|
+
if (this.dcState.detach) {
|
|
128
|
+
command = command.flag("detach");
|
|
129
|
+
}
|
|
130
|
+
return command.run();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function dockerCompose(): IDockerCompose {
|
|
135
|
+
return new DockerCompose(cmd("docker compose"), {
|
|
136
|
+
detach: false,
|
|
137
|
+
filePath: null,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** @deprecated Use `dockerCompose()` instead */
|
|
142
|
+
export function docker(options?: {
|
|
143
|
+
detach?: boolean;
|
|
144
|
+
file?: Token;
|
|
145
|
+
service?: string;
|
|
146
|
+
}): ICommand {
|
|
147
|
+
let command = cmd("docker compose");
|
|
148
|
+
|
|
149
|
+
if (options?.file) {
|
|
150
|
+
command = command.flag("file", options.file);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
command = command.arg("up");
|
|
154
|
+
|
|
155
|
+
if (options?.detach) {
|
|
156
|
+
command = command.flag("detach");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (options?.service) {
|
|
160
|
+
command = command.arg(options.service);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return command;
|
|
164
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { cmd } from "../core/command.js";
|
|
2
|
+
import type {
|
|
3
|
+
EnvSource,
|
|
4
|
+
ICommand,
|
|
5
|
+
LazyEnvSource,
|
|
6
|
+
Runnable,
|
|
7
|
+
Token,
|
|
8
|
+
} from "../core/types.js";
|
|
9
|
+
|
|
10
|
+
export interface IDrizzle {
|
|
11
|
+
database(port: Token): IDrizzle;
|
|
12
|
+
dependsOn(...deps: Runnable[]): IDrizzle;
|
|
13
|
+
dir(path: string): IDrizzle;
|
|
14
|
+
envFile(source: LazyEnvSource): IDrizzle;
|
|
15
|
+
readonly migrate: ICommand;
|
|
16
|
+
readonly studio: ICommand;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface DrizzleState {
|
|
20
|
+
readonly databasePort: Token | null;
|
|
21
|
+
readonly dependencies: readonly Runnable[];
|
|
22
|
+
readonly dirPath: string | null;
|
|
23
|
+
readonly envSources: readonly EnvSource[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class Drizzle implements IDrizzle {
|
|
27
|
+
private readonly state: DrizzleState;
|
|
28
|
+
|
|
29
|
+
constructor(state: DrizzleState) {
|
|
30
|
+
this.state = state;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
database(port: Token): IDrizzle {
|
|
34
|
+
return new Drizzle({ ...this.state, databasePort: port });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
dependsOn(...deps: Runnable[]): IDrizzle {
|
|
38
|
+
return new Drizzle({
|
|
39
|
+
...this.state,
|
|
40
|
+
dependencies: [...this.state.dependencies, ...deps],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
dir(path: string): IDrizzle {
|
|
45
|
+
return new Drizzle({ ...this.state, dirPath: path });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
envFile(source: LazyEnvSource): IDrizzle {
|
|
49
|
+
return new Drizzle({
|
|
50
|
+
...this.state,
|
|
51
|
+
envSources: [...this.state.envSources, source],
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get migrate(): ICommand {
|
|
56
|
+
return this.buildCommand("drizzle-kit migrate");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get studio(): ICommand {
|
|
60
|
+
return this.buildCommand("drizzle-kit studio");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private buildCommand(base: string): ICommand {
|
|
64
|
+
let command: ICommand = cmd(base);
|
|
65
|
+
|
|
66
|
+
if (this.state.dirPath) {
|
|
67
|
+
command = command.dir(this.state.dirPath);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const source of this.state.envSources) {
|
|
71
|
+
command = command.env(source);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (this.state.databasePort) {
|
|
75
|
+
command = command.env({ DATABASE_PORT: this.state.databasePort });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (this.state.dependencies.length > 0) {
|
|
79
|
+
command = command.dependsOn(...this.state.dependencies);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return command;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function drizzle(): IDrizzle {
|
|
87
|
+
return new Drizzle({
|
|
88
|
+
databasePort: null,
|
|
89
|
+
dependencies: [],
|
|
90
|
+
dirPath: null,
|
|
91
|
+
envSources: [],
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { cmd } from "../core/command.js";
|
|
2
|
+
import type { ICommand } from "../core/types.js";
|
|
3
|
+
|
|
4
|
+
export function esbuild(configFile?: string): ICommand {
|
|
5
|
+
return cmd("node").arg(configFile ?? "esbuild.config.js");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function esbuildWatch(configFile?: string): ICommand {
|
|
9
|
+
return cmd("node")
|
|
10
|
+
.arg(configFile ?? "esbuild.config.js")
|
|
11
|
+
.flag("watch");
|
|
12
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { type IDockerCompose, docker, dockerCompose } from "./docker.js";
|
|
2
|
+
export { type IDrizzle, drizzle } from "./drizzle.js";
|
|
3
|
+
export { esbuild, esbuildWatch } from "./esbuild.js";
|
|
4
|
+
export { hono } from "./hono.js";
|
|
5
|
+
export { type INextjs, type NextPreset, next, nextjs } from "./nextjs.js";
|
|
6
|
+
export { nodemon, tsx } from "./node.js";
|