everything-dev 1.7.2 → 1.8.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/api.cjs +1 -1
- package/dist/api.mjs +1 -1
- package/dist/app.cjs +82 -51
- package/dist/app.cjs.map +1 -1
- package/dist/app.mjs +82 -51
- package/dist/app.mjs.map +1 -1
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/components/dev-view.cjs +6 -3
- package/dist/components/dev-view.cjs.map +1 -1
- package/dist/components/dev-view.mjs +6 -3
- package/dist/components/dev-view.mjs.map +1 -1
- package/dist/components/streaming-view.cjs +5 -2
- package/dist/components/streaming-view.cjs.map +1 -1
- package/dist/components/streaming-view.mjs +5 -2
- package/dist/components/streaming-view.mjs.map +1 -1
- package/dist/config.cjs +28 -5
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +28 -5
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +1 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +14 -6
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +14 -6
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +1 -0
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-logs.cjs +6 -2
- package/dist/dev-logs.cjs.map +1 -1
- package/dist/dev-logs.mjs +7 -2
- package/dist/dev-logs.mjs.map +1 -1
- package/dist/dev-session.cjs +27 -23
- package/dist/dev-session.cjs.map +1 -1
- package/dist/dev-session.mjs +27 -24
- package/dist/dev-session.mjs.map +1 -1
- package/dist/federation.server.cjs +1 -1
- package/dist/federation.server.mjs +1 -1
- package/dist/host.cjs +4 -3
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts.map +1 -1
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +4 -3
- package/dist/host.mjs.map +1 -1
- package/dist/integrity.cjs +68 -2
- package/dist/integrity.cjs.map +1 -1
- package/dist/integrity.d.cts +14 -1
- package/dist/integrity.d.cts.map +1 -1
- package/dist/integrity.d.mts +14 -1
- package/dist/integrity.d.mts.map +1 -1
- package/dist/integrity.mjs +66 -3
- package/dist/integrity.mjs.map +1 -1
- package/dist/mf.cjs +32 -0
- package/dist/mf.cjs.map +1 -1
- package/dist/mf.d.cts +3 -1
- package/dist/mf.d.cts.map +1 -1
- package/dist/mf.d.mts +3 -1
- package/dist/mf.d.mts.map +1 -1
- package/dist/mf.mjs +32 -1
- package/dist/mf.mjs.map +1 -1
- package/dist/orchestrator.cjs +167 -317
- package/dist/orchestrator.cjs.map +1 -1
- package/dist/orchestrator.d.cts +24 -21
- package/dist/orchestrator.d.cts.map +1 -1
- package/dist/orchestrator.d.mts +24 -21
- package/dist/orchestrator.d.mts.map +1 -1
- package/dist/orchestrator.mjs +168 -316
- package/dist/orchestrator.mjs.map +1 -1
- package/dist/plugin.cjs +38 -107
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +19 -5
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +19 -5
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +39 -108
- package/dist/plugin.mjs.map +1 -1
- package/dist/service-descriptor.cjs +188 -0
- package/dist/service-descriptor.cjs.map +1 -0
- package/dist/service-descriptor.d.cts +107 -0
- package/dist/service-descriptor.d.cts.map +1 -0
- package/dist/service-descriptor.d.mts +107 -0
- package/dist/service-descriptor.d.mts.map +1 -0
- package/dist/service-descriptor.mjs +182 -0
- package/dist/service-descriptor.mjs.map +1 -0
- package/dist/types.cjs +8 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +18 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +18 -3
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +8 -1
- package/dist/types.mjs.map +1 -1
- package/dist/ui/index.cjs +1 -0
- package/dist/ui/index.d.cts +2 -2
- package/dist/ui/index.d.mts +2 -2
- package/dist/ui/index.mjs +2 -2
- package/dist/ui/runtime.cjs +4 -0
- package/dist/ui/runtime.cjs.map +1 -1
- package/dist/ui/runtime.d.cts +2 -1
- package/dist/ui/runtime.d.cts.map +1 -1
- package/dist/ui/runtime.d.mts +2 -1
- package/dist/ui/runtime.d.mts.map +1 -1
- package/dist/ui/runtime.mjs +4 -1
- package/dist/ui/runtime.mjs.map +1 -1
- package/package.json +12 -4
- package/skills/dev-workflow/SKILL.md +105 -0
- package/skills/publish-sync/SKILL.md +130 -0
- package/src/app.ts +98 -204
- package/src/cli/upgrade.ts +20 -4
- package/src/components/dev-view.tsx +8 -3
- package/src/components/streaming-view.ts +7 -2
- package/src/config.ts +40 -8
- package/src/contract.ts +1 -0
- package/src/dev-logs.ts +8 -1
- package/src/dev-session.ts +56 -79
- package/src/host.ts +4 -3
- package/src/integrity.ts +96 -10
- package/src/mf.ts +42 -0
- package/src/orchestrator.ts +232 -411
- package/src/plugin.ts +48 -136
- package/src/service-descriptor.ts +258 -0
- package/src/types.ts +8 -1
- package/src/ui/runtime.ts +5 -0
- package/dist/process-registry.cjs +0 -120
- package/dist/process-registry.cjs.map +0 -1
- package/dist/process-registry.d.cts +0 -25
- package/dist/process-registry.d.cts.map +0 -1
- package/dist/process-registry.d.mts +0 -25
- package/dist/process-registry.d.mts.map +0 -1
- package/dist/process-registry.mjs +0 -119
- package/dist/process-registry.mjs.map +0 -1
- package/src/process-registry.ts +0 -154
package/dist/orchestrator.mjs
CHANGED
|
@@ -1,204 +1,62 @@
|
|
|
1
|
-
import { getHostDevelopmentPort, getProjectRoot, parsePort } from "./config.mjs";
|
|
2
1
|
import { patchManifestFetchForSsrPublicPath } from "./mf.mjs";
|
|
3
|
-
import {
|
|
2
|
+
import { DevRuntimeConfig, ServiceDescriptorMap } from "./service-descriptor.mjs";
|
|
3
|
+
import { Deferred, Effect, Option, Ref, Stream } from "effect";
|
|
4
4
|
import { createConnection } from "node:net";
|
|
5
|
+
import { Command } from "@effect/platform";
|
|
5
6
|
|
|
6
7
|
//#region src/orchestrator.ts
|
|
7
|
-
const processConfigBases = {
|
|
8
|
-
"host-build": {
|
|
9
|
-
name: "host-build",
|
|
10
|
-
command: "bun",
|
|
11
|
-
args: ["run", "build"],
|
|
12
|
-
cwd: "host",
|
|
13
|
-
readyPatterns: [/built in/i, /compiled.*successfully/i],
|
|
14
|
-
errorPatterns: [
|
|
15
|
-
/error:/i,
|
|
16
|
-
/failed/i,
|
|
17
|
-
/exception/i
|
|
18
|
-
]
|
|
19
|
-
},
|
|
20
|
-
host: {
|
|
21
|
-
name: "host",
|
|
22
|
-
command: "bun",
|
|
23
|
-
args: ["run", "dev"],
|
|
24
|
-
cwd: "host",
|
|
25
|
-
readyPatterns: [/Host (dev|production) server running at/i, /Server running at/i],
|
|
26
|
-
errorPatterns: [
|
|
27
|
-
/error:/i,
|
|
28
|
-
/failed/i,
|
|
29
|
-
/exception/i
|
|
30
|
-
]
|
|
31
|
-
},
|
|
32
|
-
ui: {
|
|
33
|
-
name: "ui",
|
|
34
|
-
command: "bun",
|
|
35
|
-
args: ["run", "dev"],
|
|
36
|
-
cwd: "ui",
|
|
37
|
-
readyPatterns: [
|
|
38
|
-
/\bready\s+built in\b/i,
|
|
39
|
-
/\bLocal:\b/i,
|
|
40
|
-
/\bcompiled\b.*successfully/i
|
|
41
|
-
],
|
|
42
|
-
errorPatterns: [/error/i, /failed to compile/i]
|
|
43
|
-
},
|
|
44
|
-
"ui-ssr": {
|
|
45
|
-
name: "ui-ssr",
|
|
46
|
-
command: "bun",
|
|
47
|
-
args: ["run", "dev:ssr"],
|
|
48
|
-
cwd: "ui",
|
|
49
|
-
readyPatterns: [/\bready\s+built in\b/i, /\bcompiled\b.*successfully/i],
|
|
50
|
-
errorPatterns: [/error/i, /failed/i]
|
|
51
|
-
},
|
|
52
|
-
api: {
|
|
53
|
-
name: "api",
|
|
54
|
-
command: "bun",
|
|
55
|
-
args: ["run", "dev"],
|
|
56
|
-
cwd: "api",
|
|
57
|
-
readyPatterns: [
|
|
58
|
-
/ready in/i,
|
|
59
|
-
/compiled.*successfully/i,
|
|
60
|
-
/listening/i,
|
|
61
|
-
/started/i
|
|
62
|
-
],
|
|
63
|
-
errorPatterns: [/error/i, /failed/i]
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
function getProcessConfig(pkg, env, portOverride, bosConfig, runtimeConfig) {
|
|
67
|
-
if (pkg === "auth") {
|
|
68
|
-
const authConfig = runtimeConfig?.auth;
|
|
69
|
-
if (!authConfig?.localPath || authConfig.source !== "local") return null;
|
|
70
|
-
const port = portOverride ?? authConfig.port ?? (authConfig.url ? parsePort(authConfig.url) : 3020);
|
|
71
|
-
return {
|
|
72
|
-
name: "auth",
|
|
73
|
-
command: "bun",
|
|
74
|
-
args: ["run", "dev"],
|
|
75
|
-
cwd: authConfig.localPath,
|
|
76
|
-
port,
|
|
77
|
-
readyPatterns: [
|
|
78
|
-
/ready in/i,
|
|
79
|
-
/compiled.*successfully/i,
|
|
80
|
-
/listening/i,
|
|
81
|
-
/started/i
|
|
82
|
-
],
|
|
83
|
-
errorPatterns: [/error/i, /failed/i],
|
|
84
|
-
env
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
if (pkg.startsWith("plugin:")) {
|
|
88
|
-
const pluginId = pkg.slice(7);
|
|
89
|
-
const pluginConfig = runtimeConfig?.plugins?.[pluginId] ?? null;
|
|
90
|
-
const localPath = pluginConfig?.localPath;
|
|
91
|
-
if (!localPath || pluginConfig?.source !== "local") return null;
|
|
92
|
-
return {
|
|
93
|
-
name: pkg,
|
|
94
|
-
command: "bun",
|
|
95
|
-
args: ["run", "dev"],
|
|
96
|
-
cwd: localPath,
|
|
97
|
-
port: portOverride ?? pluginConfig?.port ?? (pluginConfig?.url ? parsePort(pluginConfig.url) : 0),
|
|
98
|
-
readyPatterns: [
|
|
99
|
-
/ready in/i,
|
|
100
|
-
/compiled.*successfully/i,
|
|
101
|
-
/listening/i,
|
|
102
|
-
/started/i
|
|
103
|
-
],
|
|
104
|
-
errorPatterns: [/error/i, /failed/i],
|
|
105
|
-
env
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
const base = processConfigBases[pkg];
|
|
109
|
-
if (!base) return null;
|
|
110
|
-
let port;
|
|
111
|
-
if (pkg === "host") port = portOverride ?? (runtimeConfig?.hostUrl ? parsePort(runtimeConfig.hostUrl) : bosConfig ? getHostDevelopmentPort(bosConfig.app.host.development) : 3e3);
|
|
112
|
-
else if (pkg === "ui") port = runtimeConfig?.ui.port ?? (runtimeConfig?.ui.url ? parsePort(runtimeConfig.ui.url) : 3002);
|
|
113
|
-
else if (pkg === "ui-ssr") port = runtimeConfig?.ui.ssrUrl ? parsePort(runtimeConfig.ui.ssrUrl) : runtimeConfig?.ui.port ? runtimeConfig.ui.port + 1 : 3003;
|
|
114
|
-
else if (pkg === "api") port = runtimeConfig?.api.port ?? (runtimeConfig?.api.url ? parsePort(runtimeConfig.api.url) : 3014);
|
|
115
|
-
else port = 0;
|
|
116
|
-
const cwd = pkg === "ui" ? runtimeConfig?.ui.localPath ?? base.cwd : pkg === "api" ? runtimeConfig?.api.localPath ?? base.cwd : base.cwd;
|
|
117
|
-
return {
|
|
118
|
-
...base,
|
|
119
|
-
cwd,
|
|
120
|
-
port,
|
|
121
|
-
env
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
8
|
const stripAnsi = (input) => {
|
|
125
9
|
const ESC = String.fromCharCode(27);
|
|
126
10
|
const BEL = String.fromCharCode(7);
|
|
127
11
|
return input.replace(new RegExp(`${ESC}\\][^${BEL}]*${BEL}`, "g"), "").replace(new RegExp(`${ESC}\\[[0-?]*[ -/]*[@-~]`, "g"), "");
|
|
128
12
|
};
|
|
129
|
-
const probeHttpOk =
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
const probeTcpOpen = async (port, timeoutMs = 250) => {
|
|
141
|
-
return new Promise((resolve) => {
|
|
142
|
-
const socket = createConnection({
|
|
143
|
-
host: "127.0.0.1",
|
|
144
|
-
port
|
|
145
|
-
});
|
|
146
|
-
const timer = setTimeout(() => {
|
|
147
|
-
socket.destroy();
|
|
148
|
-
resolve(false);
|
|
149
|
-
}, timeoutMs);
|
|
150
|
-
socket.once("connect", () => {
|
|
13
|
+
const probeHttpOk = (url, timeoutMs = 400) => Effect.tryPromise({
|
|
14
|
+
try: async () => {
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
17
|
+
try {
|
|
18
|
+
return (await fetch(url, { signal: controller.signal })).ok;
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
} finally {
|
|
151
22
|
clearTimeout(timer);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
catch: () => false
|
|
26
|
+
});
|
|
27
|
+
const probeTcpOpen = (port, timeoutMs = 250) => Effect.async((resume) => {
|
|
28
|
+
const socket = createConnection({
|
|
29
|
+
host: "127.0.0.1",
|
|
30
|
+
port
|
|
159
31
|
});
|
|
160
|
-
|
|
161
|
-
|
|
32
|
+
const timer = setTimeout(() => {
|
|
33
|
+
socket.destroy();
|
|
34
|
+
resume(Effect.succeed(false));
|
|
35
|
+
}, timeoutMs);
|
|
36
|
+
socket.once("connect", () => {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
socket.destroy();
|
|
39
|
+
resume(Effect.succeed(true));
|
|
40
|
+
});
|
|
41
|
+
socket.once("error", () => {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
resume(Effect.succeed(false));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
const detectStatus = (line, descriptor) => {
|
|
162
47
|
const cleanLine = stripAnsi(line);
|
|
163
|
-
|
|
48
|
+
const errorPatterns = descriptor.errorPatterns ?? [];
|
|
49
|
+
const readyPatterns = descriptor.readyPatterns ?? [];
|
|
50
|
+
for (const pattern of errorPatterns) if (pattern.test(cleanLine)) return {
|
|
164
51
|
status: "error",
|
|
165
52
|
isError: true
|
|
166
53
|
};
|
|
167
|
-
for (const pattern of
|
|
54
|
+
for (const pattern of readyPatterns) if (pattern.test(cleanLine)) return {
|
|
168
55
|
status: "ready",
|
|
169
56
|
isError: false
|
|
170
57
|
};
|
|
171
58
|
return null;
|
|
172
59
|
};
|
|
173
|
-
const killProcessTree = (pid) => Effect.gen(function* () {
|
|
174
|
-
const killSignal = (signal) => Effect.try({
|
|
175
|
-
try: () => {
|
|
176
|
-
process.kill(-pid, signal);
|
|
177
|
-
},
|
|
178
|
-
catch: () => null
|
|
179
|
-
}).pipe(Effect.ignore);
|
|
180
|
-
const killDirect = (signal) => Effect.try({
|
|
181
|
-
try: () => {
|
|
182
|
-
process.kill(pid, signal);
|
|
183
|
-
},
|
|
184
|
-
catch: () => null
|
|
185
|
-
}).pipe(Effect.ignore);
|
|
186
|
-
const isRunning = () => Effect.try({
|
|
187
|
-
try: () => {
|
|
188
|
-
process.kill(pid, 0);
|
|
189
|
-
return true;
|
|
190
|
-
},
|
|
191
|
-
catch: () => false
|
|
192
|
-
});
|
|
193
|
-
yield* killSignal("SIGTERM");
|
|
194
|
-
yield* killDirect("SIGTERM");
|
|
195
|
-
yield* Effect.sleep("200 millis");
|
|
196
|
-
if (yield* isRunning()) {
|
|
197
|
-
yield* killSignal("SIGKILL");
|
|
198
|
-
yield* killDirect("SIGKILL");
|
|
199
|
-
yield* Effect.sleep("100 millis");
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
60
|
const patchConsole = (name, callbacks) => {
|
|
203
61
|
const originalLog = console.log;
|
|
204
62
|
const originalError = console.error;
|
|
@@ -226,14 +84,14 @@ const patchConsole = (name, callbacks) => {
|
|
|
226
84
|
console.info = originalInfo;
|
|
227
85
|
};
|
|
228
86
|
};
|
|
229
|
-
const spawnRemoteHost = (
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
callbacks.onStatus(
|
|
234
|
-
callbacks.onLog(
|
|
235
|
-
const restoreConsole = patchConsole(
|
|
236
|
-
callbacks.onLog(
|
|
87
|
+
const spawnRemoteHost = (descriptor, callbacks) => Effect.gen(function* () {
|
|
88
|
+
const runtimeConfig = yield* DevRuntimeConfig;
|
|
89
|
+
const remoteUrl = descriptor.remoteUrl;
|
|
90
|
+
if (!remoteUrl) return yield* Effect.fail(/* @__PURE__ */ new Error("remoteUrl not provided on host descriptor"));
|
|
91
|
+
callbacks.onStatus(descriptor.key, "starting");
|
|
92
|
+
callbacks.onLog(descriptor.key, `Remote: ${remoteUrl}`);
|
|
93
|
+
const restoreConsole = patchConsole(descriptor.key, callbacks);
|
|
94
|
+
callbacks.onLog(descriptor.key, "Loading Module Federation runtime...");
|
|
237
95
|
const mfRuntime = yield* Effect.tryPromise({
|
|
238
96
|
try: () => import("@module-federation/enhanced/runtime"),
|
|
239
97
|
catch: (e) => /* @__PURE__ */ new Error(`Failed to load MF runtime: ${e}`)
|
|
@@ -270,24 +128,24 @@ const spawnRemoteHost = (config, callbacks, runtimeConfig) => Effect.gen(functio
|
|
|
270
128
|
name: "host",
|
|
271
129
|
entry: entryUrl
|
|
272
130
|
}]);
|
|
273
|
-
callbacks.onLog(
|
|
131
|
+
callbacks.onLog(descriptor.key, `Loading host from ${entryUrl}...`);
|
|
274
132
|
const hostModule = yield* Effect.tryPromise({
|
|
275
133
|
try: () => mf.loadRemote("host/Server"),
|
|
276
134
|
catch: (e) => /* @__PURE__ */ new Error(`Failed to load host module: ${e}`)
|
|
277
135
|
});
|
|
278
136
|
if (!hostModule?.runServer) return yield* Effect.fail(/* @__PURE__ */ new Error("Host module does not export runServer function"));
|
|
279
|
-
callbacks.onLog(
|
|
137
|
+
callbacks.onLog(descriptor.key, "Starting server...");
|
|
280
138
|
const serverHandle = hostModule.runServer({ config: runtimeConfig });
|
|
281
139
|
yield* Effect.tryPromise({
|
|
282
140
|
try: () => serverHandle.ready,
|
|
283
141
|
catch: (e) => /* @__PURE__ */ new Error(`Server failed to start: ${e}`)
|
|
284
142
|
});
|
|
285
|
-
callbacks.onStatus(
|
|
143
|
+
callbacks.onStatus(descriptor.key, "ready");
|
|
286
144
|
return {
|
|
287
|
-
name:
|
|
145
|
+
name: descriptor.key,
|
|
288
146
|
pid: process.pid,
|
|
289
147
|
kill: Effect.gen(function* () {
|
|
290
|
-
callbacks.onLog(
|
|
148
|
+
callbacks.onLog(descriptor.key, "Shutting down remote host...");
|
|
291
149
|
restoreConsole();
|
|
292
150
|
yield* Effect.tryPromise({
|
|
293
151
|
try: () => serverHandle.shutdown(),
|
|
@@ -298,56 +156,44 @@ const spawnRemoteHost = (config, callbacks, runtimeConfig) => Effect.gen(functio
|
|
|
298
156
|
waitForExit: Effect.never
|
|
299
157
|
};
|
|
300
158
|
});
|
|
301
|
-
const spawnDevProcess = (
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const
|
|
159
|
+
const spawnDevProcess = (descriptor, callbacks) => Effect.gen(function* () {
|
|
160
|
+
const runtimeConfig = yield* DevRuntimeConfig;
|
|
161
|
+
if (!descriptor.localPath) return yield* Effect.fail(/* @__PURE__ */ new Error(`No localPath for local service: ${descriptor.key}`));
|
|
162
|
+
const fullCwd = descriptor.localPath;
|
|
163
|
+
const command = descriptor.command ?? "bun";
|
|
164
|
+
const args = descriptor.args ?? ["run", "dev"];
|
|
165
|
+
const port = descriptor.port ?? descriptor.defaultPort;
|
|
166
|
+
const name = descriptor.key;
|
|
309
167
|
const readyDeferred = yield* Deferred.make();
|
|
310
168
|
const statusRef = yield* Ref.make("starting");
|
|
311
|
-
callbacks.onStatus(
|
|
169
|
+
callbacks.onStatus(name, "starting");
|
|
312
170
|
const envVars = {
|
|
313
171
|
...process.env,
|
|
314
|
-
...config.env,
|
|
315
172
|
FORCE_COLOR: "1",
|
|
316
|
-
...
|
|
173
|
+
...port > 0 ? { PORT: String(port) } : {}
|
|
317
174
|
};
|
|
318
|
-
if (
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
cwd: fullCwd,
|
|
322
|
-
env: envVars,
|
|
323
|
-
stdio: [
|
|
324
|
-
"inherit",
|
|
325
|
-
"pipe",
|
|
326
|
-
"pipe"
|
|
327
|
-
]
|
|
328
|
-
});
|
|
175
|
+
if (name === "host") envVars.BOS_RUNTIME_CONFIG = JSON.stringify(runtimeConfig);
|
|
176
|
+
const cmd = Command.make(command, ...args).pipe(Command.workingDirectory(fullCwd), Command.env(envVars));
|
|
177
|
+
const proc = yield* Command.start(cmd);
|
|
329
178
|
const markReady = Effect.gen(function* () {
|
|
330
179
|
const currentStatus = yield* Ref.get(statusRef);
|
|
331
180
|
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
332
181
|
yield* Ref.set(statusRef, "ready");
|
|
333
|
-
callbacks.onStatus(
|
|
182
|
+
callbacks.onStatus(name, "ready");
|
|
334
183
|
yield* Deferred.succeed(readyDeferred, void 0).pipe(Effect.ignore);
|
|
335
184
|
});
|
|
336
|
-
if (
|
|
337
|
-
const
|
|
338
|
-
const url = `http://127.0.0.1:${config.port}${readinessPath}`;
|
|
185
|
+
if (port > 0) {
|
|
186
|
+
const url = `http://127.0.0.1:${port}${descriptor.readinessPath}`;
|
|
339
187
|
yield* Effect.forkScoped(Effect.gen(function* () {
|
|
340
188
|
const deadline = Date.now() + 9e4;
|
|
341
189
|
while (Date.now() < deadline) {
|
|
342
190
|
const status = yield* Ref.get(statusRef);
|
|
343
191
|
if (status === "ready" || status === "error") return;
|
|
344
|
-
if (
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
catch: () => false
|
|
350
|
-
})) {
|
|
192
|
+
if (yield* probeHttpOk(url)) {
|
|
193
|
+
yield* markReady;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (yield* probeTcpOpen(port)) {
|
|
351
197
|
yield* markReady;
|
|
352
198
|
return;
|
|
353
199
|
}
|
|
@@ -355,117 +201,123 @@ const spawnDevProcess = (config, callbacks, runtimeConfig, registry) => Effect.g
|
|
|
355
201
|
}
|
|
356
202
|
}));
|
|
357
203
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
port: config.port,
|
|
362
|
-
startedAt: Date.now(),
|
|
363
|
-
command: [config.command, ...config.args].join(" ")
|
|
364
|
-
});
|
|
365
|
-
yield* Effect.forkScoped(Effect.promise(() => proc.exited).pipe(Effect.andThen((code) => Effect.gen(function* () {
|
|
366
|
-
if (registry && proc.pid) yield* registry.untrack(proc.pid).pipe(Effect.ignore);
|
|
204
|
+
const pid = Number(proc.pid);
|
|
205
|
+
yield* Effect.forkScoped(Effect.gen(function* () {
|
|
206
|
+
const exitCode = yield* proc.exitCode;
|
|
367
207
|
if ((yield* Ref.get(statusRef)) === "ready") return;
|
|
368
|
-
callbacks.onLog(
|
|
208
|
+
callbacks.onLog(name, `Process exited before ready (exit code: ${exitCode})`, true);
|
|
369
209
|
yield* Ref.set(statusRef, "error");
|
|
370
|
-
callbacks.onStatus(
|
|
371
|
-
yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Process exited before ready: ${
|
|
372
|
-
}))
|
|
210
|
+
callbacks.onStatus(name, "error");
|
|
211
|
+
yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Process exited before ready: ${name}`)).pipe(Effect.ignore);
|
|
212
|
+
}));
|
|
373
213
|
const handleLine = (line, isStderr) => Effect.gen(function* () {
|
|
374
214
|
if (!line.trim()) return;
|
|
375
|
-
|
|
215
|
+
const cleanLine = stripAnsi(line);
|
|
216
|
+
const looksLikeError = isStderr && /^(error|fail|fatal|exception|unhandled|reject)/i.test(cleanLine) && !/^\$/.test(cleanLine);
|
|
217
|
+
callbacks.onLog(name, line, looksLikeError);
|
|
376
218
|
if ((yield* Ref.get(statusRef)) === "ready") return;
|
|
377
|
-
const detected = detectStatus(line,
|
|
219
|
+
const detected = detectStatus(line, descriptor);
|
|
378
220
|
if (detected) {
|
|
379
221
|
yield* Ref.set(statusRef, detected.status);
|
|
380
|
-
callbacks.onStatus(
|
|
222
|
+
callbacks.onStatus(name, detected.status);
|
|
381
223
|
if (detected.status === "ready" || detected.status === "error") if (detected.status === "ready") yield* Deferred.succeed(readyDeferred, void 0).pipe(Effect.ignore);
|
|
382
|
-
else yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Process failed: ${
|
|
224
|
+
else yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Process failed: ${name}`)).pipe(Effect.ignore);
|
|
383
225
|
}
|
|
384
226
|
});
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
227
|
+
yield* Effect.forkScoped(Stream.runForEach((line) => handleLine(line, false))(Stream.splitLines(Stream.decodeText(proc.stdout, "utf-8"))));
|
|
228
|
+
yield* Effect.forkScoped(Stream.runForEach((line) => handleLine(line, true))(Stream.splitLines(Stream.decodeText(proc.stderr, "utf-8"))));
|
|
229
|
+
return {
|
|
230
|
+
name,
|
|
231
|
+
pid,
|
|
232
|
+
kill: Effect.gen(function* () {
|
|
233
|
+
const result = yield* proc.kill("SIGTERM").pipe(Effect.timeout("3 seconds"), Effect.option);
|
|
234
|
+
if (Option.isNone(result)) {
|
|
235
|
+
const pid = Number(proc.pid);
|
|
236
|
+
yield* Effect.try(() => process.kill(-pid, "SIGKILL")).pipe(Effect.ignore);
|
|
237
|
+
yield* Effect.sleep("250 millis");
|
|
238
|
+
}
|
|
239
|
+
}).pipe(Effect.ignore),
|
|
240
|
+
waitForReady: Deferred.await(readyDeferred),
|
|
241
|
+
waitForExit: proc.exitCode
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
const spawnRemoteProbe = (pkg, descriptor, callbacks) => Effect.gen(function* () {
|
|
245
|
+
callbacks.onStatus(pkg, "starting");
|
|
246
|
+
const readyDeferred = yield* Deferred.make();
|
|
247
|
+
const statusRef = yield* Ref.make("starting");
|
|
248
|
+
const markReady = Effect.gen(function* () {
|
|
249
|
+
yield* Ref.set(statusRef, "ready");
|
|
250
|
+
yield* Deferred.succeed(readyDeferred, void 0);
|
|
251
|
+
callbacks.onStatus(pkg, "ready", "loaded");
|
|
252
|
+
});
|
|
253
|
+
const markError = Effect.gen(function* () {
|
|
254
|
+
yield* Ref.set(statusRef, "error");
|
|
255
|
+
yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Remote ${pkg} unreachable`));
|
|
256
|
+
callbacks.onStatus(pkg, "error", "unreachable");
|
|
257
|
+
});
|
|
258
|
+
const baseUrl = descriptor.url.replace(/\/$/, "");
|
|
259
|
+
const manifestUrl = `${baseUrl}/mf-manifest.json`;
|
|
260
|
+
const entryUrl = `${baseUrl}${descriptor.readinessPath}`;
|
|
261
|
+
const probeUrl = descriptor.readinessPath === "/health" ? `${baseUrl}/health` : manifestUrl;
|
|
262
|
+
yield* Effect.forkScoped(Effect.gen(function* () {
|
|
263
|
+
const deadline = Date.now() + 6e4;
|
|
264
|
+
while (Date.now() < deadline) {
|
|
265
|
+
const status = yield* Ref.get(statusRef);
|
|
266
|
+
if (status === "ready" || status === "error") return;
|
|
267
|
+
if (yield* probeHttpOk(probeUrl, 400)) {
|
|
268
|
+
yield* markReady;
|
|
398
269
|
return;
|
|
399
270
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
buffer = lines.pop() ?? "";
|
|
403
|
-
for (const line of lines) Effect.runSync(handleLine(line, false));
|
|
404
|
-
return pump();
|
|
405
|
-
});
|
|
406
|
-
pump().then(() => {
|
|
407
|
-
if (active) resume(Effect.void);
|
|
408
|
-
});
|
|
409
|
-
return Effect.sync(() => {
|
|
410
|
-
active = false;
|
|
411
|
-
reader.cancel();
|
|
412
|
-
});
|
|
413
|
-
}));
|
|
414
|
-
const stderrFiber = yield* Effect.forkScoped(Effect.async((resume) => {
|
|
415
|
-
if (!proc.stderr) {
|
|
416
|
-
resume(Effect.void);
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
const reader = proc.stderr.getReader();
|
|
420
|
-
let buffer = "";
|
|
421
|
-
let active = true;
|
|
422
|
-
const pump = () => reader.read().then(({ done, value }) => {
|
|
423
|
-
if (!active) return;
|
|
424
|
-
if (done) {
|
|
425
|
-
if (buffer) Effect.runSync(handleLine(buffer, true));
|
|
271
|
+
if (yield* probeHttpOk(entryUrl, 400)) {
|
|
272
|
+
yield* markReady;
|
|
426
273
|
return;
|
|
427
274
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
for (const line of lines) Effect.runSync(handleLine(line, true));
|
|
432
|
-
return pump();
|
|
433
|
-
});
|
|
434
|
-
pump().then(() => {
|
|
435
|
-
if (active) resume(Effect.void);
|
|
436
|
-
});
|
|
437
|
-
return Effect.sync(() => {
|
|
438
|
-
active = false;
|
|
439
|
-
reader.cancel();
|
|
440
|
-
});
|
|
275
|
+
yield* Effect.sleep("500 millis");
|
|
276
|
+
}
|
|
277
|
+
if ((yield* Ref.get(statusRef)) !== "ready") yield* markError;
|
|
441
278
|
}));
|
|
442
279
|
return {
|
|
443
|
-
name:
|
|
444
|
-
pid:
|
|
445
|
-
kill:
|
|
446
|
-
|
|
447
|
-
yield*
|
|
448
|
-
try {
|
|
449
|
-
proc.kill("SIGKILL");
|
|
450
|
-
} catch {}
|
|
280
|
+
name: pkg,
|
|
281
|
+
pid: void 0,
|
|
282
|
+
kill: Effect.gen(function* () {
|
|
283
|
+
yield* Ref.set(statusRef, "error");
|
|
284
|
+
yield* Deferred.fail(readyDeferred, /* @__PURE__ */ new Error("Killed")).pipe(Effect.ignore);
|
|
451
285
|
}),
|
|
452
286
|
waitForReady: Deferred.await(readyDeferred),
|
|
453
|
-
waitForExit: Effect.
|
|
454
|
-
yield* Fiber.joinAll([stdoutFiber, stderrFiber]);
|
|
455
|
-
return yield* Effect.promise(() => proc.exited);
|
|
456
|
-
})
|
|
287
|
+
waitForExit: Effect.never
|
|
457
288
|
};
|
|
458
289
|
});
|
|
459
|
-
const makeDevProcess = (pkg,
|
|
460
|
-
const
|
|
461
|
-
if (!
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
290
|
+
const makeDevProcess = (pkg, callbacks, portOverride) => Effect.gen(function* () {
|
|
291
|
+
const descriptor = (yield* ServiceDescriptorMap).get(pkg);
|
|
292
|
+
if (!descriptor) {
|
|
293
|
+
callbacks.onStatus(pkg, "ready", "Remote");
|
|
294
|
+
return {
|
|
295
|
+
name: pkg,
|
|
296
|
+
pid: void 0,
|
|
297
|
+
kill: Effect.void,
|
|
298
|
+
waitForReady: Effect.void,
|
|
299
|
+
waitForExit: Effect.never
|
|
300
|
+
};
|
|
465
301
|
}
|
|
466
|
-
return yield*
|
|
302
|
+
if (pkg === "host" && descriptor.source === "remote") return yield* spawnRemoteHost(descriptor, callbacks);
|
|
303
|
+
if (descriptor.source === "remote" || !descriptor.localPath) return yield* spawnRemoteProbe(pkg, descriptor, callbacks);
|
|
304
|
+
return yield* spawnDevProcess(portOverride ? {
|
|
305
|
+
...descriptor,
|
|
306
|
+
port: portOverride
|
|
307
|
+
} : descriptor, callbacks);
|
|
467
308
|
});
|
|
309
|
+
function getProcessStates(packages, services, portOverride) {
|
|
310
|
+
return packages.map((pkg) => {
|
|
311
|
+
const descriptor = services.get(pkg);
|
|
312
|
+
return {
|
|
313
|
+
name: pkg,
|
|
314
|
+
status: "pending",
|
|
315
|
+
port: portOverride && pkg === "host" ? portOverride : descriptor?.port ?? descriptor?.defaultPort ?? 0,
|
|
316
|
+
source: descriptor?.source
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
}
|
|
468
320
|
|
|
469
321
|
//#endregion
|
|
470
|
-
export {
|
|
322
|
+
export { getProcessStates, makeDevProcess };
|
|
471
323
|
//# sourceMappingURL=orchestrator.mjs.map
|