everything-dev 1.27.0 → 1.28.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/cli/infra.cjs +1 -1
- package/dist/cli/infra.mjs +1 -1
- package/dist/cli/init.cjs +7 -9
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +1 -1
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +1 -1
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +7 -9
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/prompts.cjs +28 -24
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +27 -24
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/sync.cjs +4 -1
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +4 -1
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli.cjs +187 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +186 -11
- package/dist/cli.mjs.map +1 -1
- package/dist/contract.cjs +1 -1
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +38 -34
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +38 -34
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +1 -0
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-session.cjs +0 -1
- package/dist/dev-session.mjs +1 -1
- package/dist/index.cjs +0 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +0 -1
- package/dist/near-cli.cjs +1 -1
- package/dist/near-cli.mjs +1 -1
- package/dist/orchestrator.cjs +1 -1
- package/dist/orchestrator.mjs +1 -1
- package/dist/plugin.cjs +163 -139
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +67 -34
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +66 -34
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +153 -130
- package/dist/plugin.mjs.map +1 -1
- package/dist/service-descriptor.d.cts +34 -0
- package/dist/service-descriptor.d.cts.map +1 -0
- package/dist/service-descriptor.d.mts +36 -0
- package/dist/service-descriptor.d.mts.map +1 -0
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/package.json +2 -2
- package/src/api-contract.ts +0 -623
- package/src/app.ts +0 -193
- package/src/cli/catalog.ts +0 -49
- package/src/cli/framework-version.ts +0 -61
- package/src/cli/help.ts +0 -13
- package/src/cli/infra.ts +0 -190
- package/src/cli/init.ts +0 -1145
- package/src/cli/parse.ts +0 -147
- package/src/cli/prompts.ts +0 -135
- package/src/cli/snapshot.ts +0 -46
- package/src/cli/status.ts +0 -99
- package/src/cli/sync.ts +0 -429
- package/src/cli/timing.ts +0 -63
- package/src/cli/upgrade.ts +0 -869
- package/src/cli.ts +0 -516
- package/src/components/dev-view.tsx +0 -352
- package/src/components/streaming-view.ts +0 -177
- package/src/config.ts +0 -893
- package/src/contract.meta.ts +0 -140
- package/src/contract.ts +0 -326
- package/src/dev-logs.ts +0 -92
- package/src/dev-session.ts +0 -283
- package/src/fastkv.ts +0 -181
- package/src/index.ts +0 -8
- package/src/integrity.ts +0 -138
- package/src/internal/manifest-normalizer.ts +0 -290
- package/src/merge.ts +0 -187
- package/src/mf.ts +0 -147
- package/src/near-cli.ts +0 -259
- package/src/network.ts +0 -3
- package/src/orchestrator.ts +0 -493
- package/src/plugin.ts +0 -1799
- package/src/sdk.ts +0 -14
- package/src/service-descriptor.ts +0 -281
- package/src/shared.ts +0 -249
- package/src/sidebar.ts +0 -140
- package/src/types.ts +0 -330
- package/src/ui/head.ts +0 -83
- package/src/ui/index.ts +0 -5
- package/src/ui/metadata.ts +0 -95
- package/src/ui/router.ts +0 -88
- package/src/ui/runtime.ts +0 -42
- package/src/ui/types.ts +0 -65
- package/src/utils/banner.ts +0 -21
- package/src/utils/linkify.ts +0 -11
- package/src/utils/path-match.ts +0 -16
- package/src/utils/run.ts +0 -31
- package/src/utils/save-config.ts +0 -20
- package/src/utils/theme.ts +0 -39
package/src/orchestrator.ts
DELETED
|
@@ -1,493 +0,0 @@
|
|
|
1
|
-
import { Command } from "@effect/platform";
|
|
2
|
-
import type { ExitCode } from "@effect/platform/CommandExecutor";
|
|
3
|
-
import { Deferred, Effect, Option, Ref, Stream } from "effect";
|
|
4
|
-
import { patchManifestFetchForSsrPublicPath } from "./mf";
|
|
5
|
-
import {
|
|
6
|
-
DevRuntimeConfig,
|
|
7
|
-
type ServiceDescriptor,
|
|
8
|
-
ServiceDescriptorMap,
|
|
9
|
-
} from "./service-descriptor";
|
|
10
|
-
import type { RuntimeConfig } from "./types";
|
|
11
|
-
|
|
12
|
-
process.on("unhandledRejection", (reason) => {
|
|
13
|
-
console.error("[Orchestrator] Unhandled rejection:", reason);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
process.on("uncaughtException", (err) => {
|
|
17
|
-
console.error("[Orchestrator] Uncaught exception:", err);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
export interface ProcessCallbacks {
|
|
21
|
-
onStatus: (name: string, status: ProcessStatus, message?: string) => void;
|
|
22
|
-
onLog: (name: string, line: string, isError?: boolean) => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface ProcessHandle {
|
|
26
|
-
name: string;
|
|
27
|
-
pid: number | undefined;
|
|
28
|
-
kill: Effect.Effect<void, unknown>;
|
|
29
|
-
waitForReady: Effect.Effect<void, Error>;
|
|
30
|
-
waitForExit: Effect.Effect<ExitCode, unknown>;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type ProcessStatus = "pending" | "starting" | "ready" | "error";
|
|
34
|
-
|
|
35
|
-
export interface ProcessState {
|
|
36
|
-
name: string;
|
|
37
|
-
status: ProcessStatus;
|
|
38
|
-
port: number;
|
|
39
|
-
message?: string;
|
|
40
|
-
source?: "local" | "remote";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const stripAnsi = (input: string): string => {
|
|
44
|
-
const ESC = String.fromCharCode(27);
|
|
45
|
-
const BEL = String.fromCharCode(7);
|
|
46
|
-
return input
|
|
47
|
-
.replace(new RegExp(`${ESC}\\][^${BEL}]*${BEL}`, "g"), "")
|
|
48
|
-
.replace(new RegExp(`${ESC}\\[[0-?]*[ -/]*[@-~]`, "g"), "");
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const probeHttpOk = (url: string, timeoutMs = 400) =>
|
|
52
|
-
Effect.tryPromise({
|
|
53
|
-
try: async () => {
|
|
54
|
-
const controller = new AbortController();
|
|
55
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
56
|
-
try {
|
|
57
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
58
|
-
return res.ok;
|
|
59
|
-
} catch {
|
|
60
|
-
return false;
|
|
61
|
-
} finally {
|
|
62
|
-
clearTimeout(timer);
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
catch: () => false,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const detectStatus = (
|
|
69
|
-
line: string,
|
|
70
|
-
descriptor: ServiceDescriptor,
|
|
71
|
-
): { status: ProcessStatus; isError: boolean } | null => {
|
|
72
|
-
const cleanLine = stripAnsi(line);
|
|
73
|
-
const errorPatterns = descriptor.errorPatterns ?? [];
|
|
74
|
-
const readyPatterns = descriptor.readyPatterns ?? [];
|
|
75
|
-
for (const pattern of errorPatterns) {
|
|
76
|
-
if (pattern.test(cleanLine)) {
|
|
77
|
-
return { status: "error", isError: true };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
for (const pattern of readyPatterns) {
|
|
81
|
-
if (pattern.test(cleanLine)) {
|
|
82
|
-
return { status: "ready", isError: false };
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return null;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
interface ServerHandle {
|
|
89
|
-
ready: Promise<void>;
|
|
90
|
-
shutdown: () => Promise<void>;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
interface ServerInput {
|
|
94
|
-
config: RuntimeConfig;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const patchConsole = (name: string, callbacks: ProcessCallbacks): (() => void) => {
|
|
98
|
-
const originalLog = console.log;
|
|
99
|
-
const originalError = console.error;
|
|
100
|
-
const originalWarn = console.warn;
|
|
101
|
-
const originalInfo = console.info;
|
|
102
|
-
|
|
103
|
-
const formatArgs = (args: unknown[], isError = false): string => {
|
|
104
|
-
return args
|
|
105
|
-
.map((arg) => {
|
|
106
|
-
if (arg instanceof Error) {
|
|
107
|
-
const parts = [`${arg.name}: ${arg.message}`];
|
|
108
|
-
if (arg.cause instanceof Error)
|
|
109
|
-
parts.push(`(cause: ${arg.cause.name}: ${arg.cause.message})`);
|
|
110
|
-
else if (arg.cause) parts.push(`(cause: ${String(arg.cause)})`);
|
|
111
|
-
if (isError && arg.stack) parts.push(arg.stack);
|
|
112
|
-
return parts.join("\n");
|
|
113
|
-
}
|
|
114
|
-
return typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg);
|
|
115
|
-
})
|
|
116
|
-
.join(" ");
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
console.log = (...args: unknown[]) => {
|
|
120
|
-
callbacks.onLog(name, formatArgs(args), false);
|
|
121
|
-
};
|
|
122
|
-
console.error = (...args: unknown[]) => {
|
|
123
|
-
callbacks.onLog(name, formatArgs(args, true), true);
|
|
124
|
-
};
|
|
125
|
-
console.warn = (...args: unknown[]) => {
|
|
126
|
-
callbacks.onLog(name, formatArgs(args), false);
|
|
127
|
-
};
|
|
128
|
-
console.info = (...args: unknown[]) => {
|
|
129
|
-
callbacks.onLog(name, formatArgs(args), false);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
return () => {
|
|
133
|
-
console.log = originalLog;
|
|
134
|
-
console.error = originalError;
|
|
135
|
-
console.warn = originalWarn;
|
|
136
|
-
console.info = originalInfo;
|
|
137
|
-
};
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const spawnRemoteHost = (descriptor: ServiceDescriptor, callbacks: ProcessCallbacks) =>
|
|
141
|
-
Effect.gen(function* () {
|
|
142
|
-
const runtimeConfig = yield* DevRuntimeConfig;
|
|
143
|
-
const remoteUrl = descriptor.remoteUrl;
|
|
144
|
-
if (!remoteUrl) {
|
|
145
|
-
return yield* Effect.fail(new Error("remoteUrl not provided on host descriptor"));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
callbacks.onStatus(descriptor.key, "starting");
|
|
149
|
-
callbacks.onLog(descriptor.key, `Remote: ${remoteUrl}`);
|
|
150
|
-
const restoreConsole = patchConsole(descriptor.key, callbacks);
|
|
151
|
-
callbacks.onLog(descriptor.key, "Loading Module Federation runtime...");
|
|
152
|
-
|
|
153
|
-
const mfRuntime = yield* Effect.tryPromise({
|
|
154
|
-
try: () => import("@module-federation/enhanced/runtime"),
|
|
155
|
-
catch: (e) => new Error(`Failed to load MF runtime: ${e}`),
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
const mfCore = yield* Effect.tryPromise({
|
|
159
|
-
try: () => import("@module-federation/runtime-core"),
|
|
160
|
-
catch: (e) => new Error(`Failed to load MF core: ${e}`),
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
let mf = mfRuntime.getInstance();
|
|
164
|
-
if (!mf) {
|
|
165
|
-
mf = mfRuntime.createInstance({ name: "cli-host", remotes: [] });
|
|
166
|
-
mfCore.setGlobalFederationInstance(mf);
|
|
167
|
-
}
|
|
168
|
-
patchManifestFetchForSsrPublicPath(mf as any);
|
|
169
|
-
|
|
170
|
-
const baseUrl = remoteUrl
|
|
171
|
-
.replace(/\/remoteEntry\.js$/, "")
|
|
172
|
-
.replace(/\/mf-manifest\.json$/, "")
|
|
173
|
-
.replace(/\/$/, "");
|
|
174
|
-
const remoteEntryUrl = `${baseUrl}/remoteEntry.js`;
|
|
175
|
-
const manifestUrl = `${baseUrl}/mf-manifest.json`;
|
|
176
|
-
|
|
177
|
-
const entryUrl = yield* Effect.tryPromise({
|
|
178
|
-
try: async () => {
|
|
179
|
-
try {
|
|
180
|
-
const res = await fetch(manifestUrl);
|
|
181
|
-
if (!res.ok) return remoteEntryUrl;
|
|
182
|
-
const json = (await res.json()) as Record<string, unknown>;
|
|
183
|
-
if (
|
|
184
|
-
json &&
|
|
185
|
-
typeof json === "object" &&
|
|
186
|
-
"metaData" in json &&
|
|
187
|
-
"exposes" in json &&
|
|
188
|
-
"shared" in json
|
|
189
|
-
) {
|
|
190
|
-
return manifestUrl;
|
|
191
|
-
}
|
|
192
|
-
} catch {}
|
|
193
|
-
return remoteEntryUrl;
|
|
194
|
-
},
|
|
195
|
-
catch: () => remoteEntryUrl,
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
(mf as any).registerRemotes([{ name: "host", entry: entryUrl }]);
|
|
199
|
-
callbacks.onLog(descriptor.key, `Loading host from ${entryUrl}...`);
|
|
200
|
-
|
|
201
|
-
const hostModule = yield* Effect.tryPromise({
|
|
202
|
-
try: () =>
|
|
203
|
-
(mf as any).loadRemote("host/Server") as Promise<{
|
|
204
|
-
runServer: (input: ServerInput) => ServerHandle;
|
|
205
|
-
}>,
|
|
206
|
-
catch: (e) => new Error(`Failed to load host module: ${e}`),
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
if (!hostModule?.runServer) {
|
|
210
|
-
return yield* Effect.fail(new Error("Host module does not export runServer function"));
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
callbacks.onLog(descriptor.key, "Starting server...");
|
|
214
|
-
const serverHandle = hostModule.runServer({ config: runtimeConfig });
|
|
215
|
-
yield* Effect.tryPromise({
|
|
216
|
-
try: () => serverHandle.ready,
|
|
217
|
-
catch: (e) => new Error(`Server failed to start: ${e}`),
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
callbacks.onStatus(descriptor.key, "ready");
|
|
221
|
-
|
|
222
|
-
return {
|
|
223
|
-
name: descriptor.key,
|
|
224
|
-
pid: process.pid,
|
|
225
|
-
kill: Effect.gen(function* () {
|
|
226
|
-
callbacks.onLog(descriptor.key, "Shutting down remote host...");
|
|
227
|
-
restoreConsole();
|
|
228
|
-
yield* Effect.tryPromise({
|
|
229
|
-
try: () => serverHandle.shutdown(),
|
|
230
|
-
catch: () => {},
|
|
231
|
-
}).pipe(Effect.ignore);
|
|
232
|
-
}),
|
|
233
|
-
waitForReady: Effect.succeed(undefined),
|
|
234
|
-
waitForExit: Effect.never,
|
|
235
|
-
} satisfies ProcessHandle;
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
const spawnDevProcess = (descriptor: ServiceDescriptor, callbacks: ProcessCallbacks) =>
|
|
239
|
-
Effect.gen(function* () {
|
|
240
|
-
const runtimeConfig = yield* DevRuntimeConfig;
|
|
241
|
-
|
|
242
|
-
if (!descriptor.localPath) {
|
|
243
|
-
return yield* Effect.fail(new Error(`No localPath for local service: ${descriptor.key}`));
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const fullCwd = descriptor.localPath;
|
|
247
|
-
const command = descriptor.command ?? "bun";
|
|
248
|
-
const args = descriptor.args ?? ["run", "dev"];
|
|
249
|
-
const port = descriptor.port ?? descriptor.defaultPort;
|
|
250
|
-
const name = descriptor.key;
|
|
251
|
-
|
|
252
|
-
const readyDeferred = yield* Deferred.make<void, Error>();
|
|
253
|
-
const statusRef = yield* Ref.make<ProcessStatus>("starting");
|
|
254
|
-
|
|
255
|
-
callbacks.onStatus(name, "starting");
|
|
256
|
-
|
|
257
|
-
const envVars: Record<string, string> = {
|
|
258
|
-
...(process.env as Record<string, string>),
|
|
259
|
-
FORCE_COLOR: "1",
|
|
260
|
-
...(port > 0 ? { PORT: String(port) } : {}),
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
if (name === "host") {
|
|
264
|
-
envVars.BOS_RUNTIME_CONFIG = JSON.stringify(runtimeConfig);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const cmd = Command.make(command, ...args).pipe(
|
|
268
|
-
Command.workingDirectory(fullCwd),
|
|
269
|
-
Command.env(envVars),
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
const proc = yield* Command.start(cmd);
|
|
273
|
-
|
|
274
|
-
const markReady = Effect.gen(function* () {
|
|
275
|
-
const currentStatus = yield* Ref.get(statusRef);
|
|
276
|
-
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
277
|
-
yield* Ref.set(statusRef, "ready");
|
|
278
|
-
callbacks.onStatus(name, "ready");
|
|
279
|
-
yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
if (port > 0) {
|
|
283
|
-
const readinessPath = descriptor.readinessPath;
|
|
284
|
-
const url = `http://127.0.0.1:${port}${readinessPath}`;
|
|
285
|
-
|
|
286
|
-
yield* Effect.forkScoped(
|
|
287
|
-
Effect.gen(function* () {
|
|
288
|
-
const deadline = Date.now() + 90_000;
|
|
289
|
-
while (Date.now() < deadline) {
|
|
290
|
-
const status = yield* Ref.get(statusRef);
|
|
291
|
-
if (status === "ready" || status === "error") return;
|
|
292
|
-
const ok = yield* probeHttpOk(url);
|
|
293
|
-
if (ok) {
|
|
294
|
-
yield* markReady;
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
yield* Effect.sleep("200 millis");
|
|
298
|
-
}
|
|
299
|
-
}),
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const pid = Number(proc.pid);
|
|
304
|
-
|
|
305
|
-
yield* Effect.forkScoped(
|
|
306
|
-
Effect.gen(function* () {
|
|
307
|
-
const exitCode = yield* proc.exitCode;
|
|
308
|
-
const currentStatus = yield* Ref.get(statusRef);
|
|
309
|
-
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
310
|
-
callbacks.onLog(name, `Process exited before ready (exit code: ${exitCode})`, true);
|
|
311
|
-
yield* Ref.set(statusRef, "error");
|
|
312
|
-
callbacks.onStatus(name, "error");
|
|
313
|
-
yield* Deferred.fail(readyDeferred, new Error(`Process exited before ready: ${name}`)).pipe(
|
|
314
|
-
Effect.ignore,
|
|
315
|
-
);
|
|
316
|
-
}),
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
const handleLine = (line: string, isStderr: boolean) =>
|
|
320
|
-
Effect.gen(function* () {
|
|
321
|
-
if (!line.trim()) return;
|
|
322
|
-
|
|
323
|
-
const cleanLine = stripAnsi(line);
|
|
324
|
-
const looksLikeError =
|
|
325
|
-
isStderr &&
|
|
326
|
-
/^(error|fail|fatal|exception|unhandled|reject)/i.test(cleanLine) &&
|
|
327
|
-
!/^\$/.test(cleanLine);
|
|
328
|
-
callbacks.onLog(name, line, looksLikeError);
|
|
329
|
-
|
|
330
|
-
const currentStatus = yield* Ref.get(statusRef);
|
|
331
|
-
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
332
|
-
|
|
333
|
-
const detected = detectStatus(line, descriptor);
|
|
334
|
-
if (detected) {
|
|
335
|
-
yield* Ref.set(statusRef, detected.status);
|
|
336
|
-
callbacks.onStatus(name, detected.status);
|
|
337
|
-
if (detected.status === "ready" || detected.status === "error") {
|
|
338
|
-
if (detected.status === "ready") {
|
|
339
|
-
yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);
|
|
340
|
-
} else {
|
|
341
|
-
yield* Deferred.fail(readyDeferred, new Error(`Process failed: ${name}`)).pipe(
|
|
342
|
-
Effect.ignore,
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
yield* Effect.forkScoped(
|
|
350
|
-
Stream.runForEach((line: string) => handleLine(line, false))(
|
|
351
|
-
Stream.splitLines(Stream.decodeText(proc.stdout, "utf-8")),
|
|
352
|
-
),
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
yield* Effect.forkScoped(
|
|
356
|
-
Stream.runForEach((line: string) => handleLine(line, true))(
|
|
357
|
-
Stream.splitLines(Stream.decodeText(proc.stderr, "utf-8")),
|
|
358
|
-
),
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
return {
|
|
362
|
-
name,
|
|
363
|
-
pid,
|
|
364
|
-
kill: Effect.gen(function* () {
|
|
365
|
-
const result = yield* proc.kill("SIGTERM").pipe(Effect.timeout("3 seconds"), Effect.option);
|
|
366
|
-
if (Option.isNone(result)) {
|
|
367
|
-
const pid = Number(proc.pid);
|
|
368
|
-
yield* Effect.try(() => process.kill(-pid, "SIGKILL")).pipe(Effect.ignore);
|
|
369
|
-
yield* Effect.sleep("250 millis");
|
|
370
|
-
}
|
|
371
|
-
}).pipe(Effect.ignore),
|
|
372
|
-
waitForReady: Deferred.await(readyDeferred),
|
|
373
|
-
waitForExit: proc.exitCode,
|
|
374
|
-
} satisfies ProcessHandle;
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
const spawnRemoteProbe = (
|
|
378
|
-
pkg: string,
|
|
379
|
-
descriptor: ServiceDescriptor,
|
|
380
|
-
callbacks: ProcessCallbacks,
|
|
381
|
-
) =>
|
|
382
|
-
Effect.gen(function* () {
|
|
383
|
-
callbacks.onStatus(pkg, "starting");
|
|
384
|
-
const readyDeferred = yield* Deferred.make<void, Error>();
|
|
385
|
-
const statusRef = yield* Ref.make<ProcessStatus>("starting");
|
|
386
|
-
|
|
387
|
-
const markReady = Effect.gen(function* () {
|
|
388
|
-
yield* Ref.set(statusRef, "ready");
|
|
389
|
-
yield* Deferred.succeed(readyDeferred, undefined);
|
|
390
|
-
callbacks.onStatus(pkg, "ready", "loaded");
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
const markError = Effect.gen(function* () {
|
|
394
|
-
yield* Ref.set(statusRef, "error");
|
|
395
|
-
yield* Deferred.fail(readyDeferred, new Error(`Remote ${pkg} unreachable`));
|
|
396
|
-
callbacks.onStatus(pkg, "error", "unreachable");
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
const baseUrl = descriptor.url.replace(/\/$/, "");
|
|
400
|
-
const manifestUrl = `${baseUrl}/mf-manifest.json`;
|
|
401
|
-
const entryUrl = `${baseUrl}${descriptor.readinessPath}`;
|
|
402
|
-
const probeUrl = descriptor.readinessPath === "/health" ? `${baseUrl}/health` : manifestUrl;
|
|
403
|
-
|
|
404
|
-
yield* Effect.forkScoped(
|
|
405
|
-
Effect.gen(function* () {
|
|
406
|
-
const deadline = Date.now() + 60_000;
|
|
407
|
-
while (Date.now() < deadline) {
|
|
408
|
-
const status = yield* Ref.get(statusRef);
|
|
409
|
-
if (status === "ready" || status === "error") return;
|
|
410
|
-
|
|
411
|
-
const ok = yield* probeHttpOk(probeUrl, 400);
|
|
412
|
-
|
|
413
|
-
if (ok) {
|
|
414
|
-
yield* markReady;
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
const fallbackOk = yield* probeHttpOk(entryUrl, 400);
|
|
419
|
-
|
|
420
|
-
if (fallbackOk) {
|
|
421
|
-
yield* markReady;
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
yield* Effect.sleep("500 millis");
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const status = yield* Ref.get(statusRef);
|
|
429
|
-
if (status !== "ready") {
|
|
430
|
-
yield* markError;
|
|
431
|
-
}
|
|
432
|
-
}),
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
return {
|
|
436
|
-
name: pkg,
|
|
437
|
-
pid: undefined,
|
|
438
|
-
kill: Effect.gen(function* () {
|
|
439
|
-
yield* Ref.set(statusRef, "error");
|
|
440
|
-
yield* Deferred.fail(readyDeferred, new Error("Killed")).pipe(Effect.ignore);
|
|
441
|
-
}),
|
|
442
|
-
waitForReady: Deferred.await(readyDeferred),
|
|
443
|
-
waitForExit: Effect.never,
|
|
444
|
-
} satisfies ProcessHandle;
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
export const makeDevProcess = (pkg: string, callbacks: ProcessCallbacks, portOverride?: number) =>
|
|
448
|
-
Effect.gen(function* () {
|
|
449
|
-
const services = yield* ServiceDescriptorMap;
|
|
450
|
-
const descriptor = services.get(pkg);
|
|
451
|
-
|
|
452
|
-
if (!descriptor) {
|
|
453
|
-
callbacks.onStatus(pkg, "ready", "Remote");
|
|
454
|
-
return {
|
|
455
|
-
name: pkg,
|
|
456
|
-
pid: undefined,
|
|
457
|
-
kill: Effect.void,
|
|
458
|
-
waitForReady: Effect.void,
|
|
459
|
-
waitForExit: Effect.never,
|
|
460
|
-
} satisfies ProcessHandle;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
if (pkg === "host" && descriptor.source === "remote") {
|
|
464
|
-
return yield* spawnRemoteHost(descriptor, callbacks);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (descriptor.source === "remote" || !descriptor.localPath) {
|
|
468
|
-
return yield* spawnRemoteProbe(pkg, descriptor, callbacks);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const resolvedDescriptor = portOverride ? { ...descriptor, port: portOverride } : descriptor;
|
|
472
|
-
|
|
473
|
-
return yield* spawnDevProcess(resolvedDescriptor, callbacks);
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
export function getProcessStates(
|
|
477
|
-
packages: string[],
|
|
478
|
-
services: Map<string, ServiceDescriptor>,
|
|
479
|
-
portOverride?: number,
|
|
480
|
-
): ProcessState[] {
|
|
481
|
-
return packages.map((pkg) => {
|
|
482
|
-
const descriptor = services.get(pkg);
|
|
483
|
-
return {
|
|
484
|
-
name: pkg,
|
|
485
|
-
status: "pending" as const,
|
|
486
|
-
port:
|
|
487
|
-
portOverride && pkg === "host"
|
|
488
|
-
? portOverride
|
|
489
|
-
: (descriptor?.port ?? descriptor?.defaultPort ?? 0),
|
|
490
|
-
source: descriptor?.source,
|
|
491
|
-
};
|
|
492
|
-
});
|
|
493
|
-
}
|