everything-dev 1.5.0 → 1.7.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-contract.cjs +55 -8
- package/dist/api-contract.cjs.map +1 -1
- package/dist/api-contract.mjs +55 -8
- package/dist/api-contract.mjs.map +1 -1
- package/dist/app.cjs +26 -2
- package/dist/app.cjs.map +1 -1
- package/dist/app.mjs +27 -3
- package/dist/app.mjs.map +1 -1
- package/dist/cli/init.cjs +4 -4
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.mjs +4 -4
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/sync.cjs +4 -3
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +4 -3
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli.cjs +0 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +0 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/components/streaming-view.cjs +0 -18
- package/dist/components/streaming-view.cjs.map +1 -1
- package/dist/components/streaming-view.mjs +0 -18
- package/dist/components/streaming-view.mjs.map +1 -1
- package/dist/config.cjs +21 -5
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +2 -1
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +2 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +21 -6
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +8 -1
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +44 -8
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +44 -8
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.meta.cjs +1 -1
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +1 -1
- package/dist/contract.meta.d.mts +1 -1
- package/dist/contract.meta.mjs +1 -1
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +8 -1
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-session.cjs +51 -66
- package/dist/dev-session.cjs.map +1 -1
- package/dist/dev-session.mjs +52 -67
- package/dist/dev-session.mjs.map +1 -1
- package/dist/fastkv.cjs +56 -0
- package/dist/fastkv.cjs.map +1 -1
- package/dist/fastkv.d.cts +45 -1
- package/dist/fastkv.d.cts.map +1 -1
- package/dist/fastkv.d.mts +45 -1
- package/dist/fastkv.d.mts.map +1 -1
- package/dist/fastkv.mjs +54 -1
- package/dist/fastkv.mjs.map +1 -1
- package/dist/host.cjs +1 -1
- package/dist/host.cjs.map +1 -1
- package/dist/host.mjs +1 -1
- package/dist/host.mjs.map +1 -1
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +3 -3
- package/dist/near-cli.cjs +1 -1
- package/dist/near-cli.mjs +1 -1
- package/dist/orchestrator.cjs +55 -20
- package/dist/orchestrator.cjs.map +1 -1
- package/dist/orchestrator.d.cts +5 -4
- package/dist/orchestrator.d.cts.map +1 -1
- package/dist/orchestrator.d.mts +5 -4
- package/dist/orchestrator.d.mts.map +1 -1
- package/dist/orchestrator.mjs +55 -20
- package/dist/orchestrator.mjs.map +1 -1
- package/dist/plugin.cjs +135 -9
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +50 -9
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +50 -9
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +137 -11
- package/dist/plugin.mjs.map +1 -1
- package/dist/types.cjs +15 -5
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +62 -11
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +62 -11
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +15 -5
- package/dist/types.mjs.map +1 -1
- package/package.json +2 -2
- package/src/api-contract.ts +88 -9
- package/src/app.ts +55 -7
- package/src/cli/init.ts +6 -6
- package/src/cli/sync.ts +11 -4
- package/src/cli.ts +0 -1
- package/src/components/streaming-view.ts +0 -20
- package/src/config.ts +39 -23
- package/src/contract.meta.ts +4 -1
- package/src/contract.ts +7 -0
- package/src/dev-session.ts +85 -83
- package/src/fastkv.ts +95 -0
- package/src/host.ts +1 -1
- package/src/orchestrator.ts +61 -31
- package/src/plugin.ts +202 -5
- package/src/types.ts +38 -4
package/src/dev-session.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Deferred, Effect, Exit } from "effect";
|
|
2
2
|
import {
|
|
3
3
|
type DevViewHandle,
|
|
4
4
|
type LogEntry,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type ProcessCallbacks,
|
|
15
15
|
type ProcessHandle,
|
|
16
16
|
} from "./orchestrator";
|
|
17
|
-
import { makeProcessRegistry } from "./process-registry";
|
|
17
|
+
import { makeProcessRegistry, type ProcessRegistry } from "./process-registry";
|
|
18
18
|
import type { BosConfig, RuntimeConfig, SourceMode } from "./types";
|
|
19
19
|
|
|
20
20
|
export interface AppConfig {
|
|
@@ -87,9 +87,23 @@ function formatLogLine(entry: LogEntry): string {
|
|
|
87
87
|
return `[${ts}] [${entry.source}] [${prefix}] ${entry.line}`;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
const scopedProcess = (
|
|
91
|
+
pkg: string,
|
|
92
|
+
env: Record<string, string> | undefined,
|
|
93
|
+
callbacks: ProcessCallbacks,
|
|
94
|
+
portOverride: number | undefined,
|
|
95
|
+
bosConfig: BosConfig | undefined,
|
|
96
|
+
runtimeConfig: RuntimeConfig | undefined,
|
|
97
|
+
registry: ProcessRegistry | undefined,
|
|
98
|
+
) =>
|
|
99
|
+
Effect.acquireRelease(
|
|
100
|
+
makeDevProcess(pkg, env, callbacks, portOverride, bosConfig, runtimeConfig, registry),
|
|
101
|
+
(handle) => handle.kill.pipe(Effect.ignore),
|
|
102
|
+
);
|
|
103
|
+
|
|
90
104
|
export const runDevSession = (
|
|
91
105
|
orchestrator: AppOrchestrator,
|
|
92
|
-
|
|
106
|
+
onShutdownReady?: (requestShutdown: () => void) => void,
|
|
93
107
|
) =>
|
|
94
108
|
Effect.gen(function* () {
|
|
95
109
|
const configDir = getProjectRoot();
|
|
@@ -125,62 +139,36 @@ export const runDevSession = (
|
|
|
125
139
|
const logger = yield* Effect.promise(() =>
|
|
126
140
|
createDevLogger(configDir, orchestrator.description),
|
|
127
141
|
);
|
|
128
|
-
|
|
142
|
+
|
|
143
|
+
const shutdown = yield* Deferred.make<void>();
|
|
144
|
+
|
|
145
|
+
onShutdownReady?.(() => {
|
|
146
|
+
void Effect.runPromise(Deferred.succeed(shutdown, undefined));
|
|
147
|
+
});
|
|
148
|
+
|
|
129
149
|
const allLogs: LogEntry[] = [];
|
|
130
150
|
let view: DevViewHandle | null = null;
|
|
131
|
-
let
|
|
132
|
-
|
|
133
|
-
const killAll = async () => {
|
|
134
|
-
const reversed = [...handles].reverse();
|
|
135
|
-
for (const handle of reversed) {
|
|
136
|
-
try {
|
|
137
|
-
await handle.kill();
|
|
138
|
-
} catch {}
|
|
139
|
-
}
|
|
140
|
-
await Effect.runPromise(registry.killAll(true)).catch(() => {});
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const exportLogs = async () => {
|
|
144
|
-
console.log("\n");
|
|
145
|
-
console.log("═".repeat(70));
|
|
146
|
-
console.log(` SESSION LOGS: ${orchestrator.description}`);
|
|
147
|
-
console.log(` Started: ${new Date(allLogs[0]?.timestamp || Date.now()).toISOString()}`);
|
|
148
|
-
console.log(` Total entries: ${allLogs.length}`);
|
|
149
|
-
console.log("═".repeat(70));
|
|
150
|
-
console.log("");
|
|
151
|
-
for (const entry of allLogs) {
|
|
152
|
-
console.log(formatLogLine(entry));
|
|
153
|
-
}
|
|
154
|
-
console.log("");
|
|
155
|
-
console.log("═".repeat(70));
|
|
156
|
-
console.log(` Full logs saved to: ${logger.logFile}`);
|
|
157
|
-
console.log("═".repeat(70));
|
|
158
|
-
console.log("");
|
|
159
|
-
};
|
|
151
|
+
let shouldExportLogs = false;
|
|
160
152
|
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
view?.unmount();
|
|
165
|
-
await killAll();
|
|
166
|
-
if (showLogs) {
|
|
167
|
-
await exportLogs();
|
|
168
|
-
}
|
|
153
|
+
const requestShutdownAndExport = () => {
|
|
154
|
+
shouldExportLogs = true;
|
|
155
|
+
void Effect.runPromise(Deferred.succeed(shutdown, undefined));
|
|
169
156
|
};
|
|
170
157
|
|
|
171
|
-
onCleanupReady?.(cleanup);
|
|
172
|
-
|
|
173
158
|
const useInteractive = orchestrator.interactive ?? isInteractiveSupported();
|
|
174
159
|
view = useInteractive
|
|
175
160
|
? renderDevView(
|
|
176
161
|
initialProcesses,
|
|
177
162
|
orchestrator.description,
|
|
178
163
|
orchestrator.env,
|
|
179
|
-
() =>
|
|
180
|
-
|
|
164
|
+
() => void Effect.runPromise(Deferred.succeed(shutdown, undefined)),
|
|
165
|
+
requestShutdownAndExport,
|
|
181
166
|
)
|
|
182
|
-
: renderStreamingView(
|
|
183
|
-
|
|
167
|
+
: renderStreamingView(
|
|
168
|
+
initialProcesses,
|
|
169
|
+
orchestrator.description,
|
|
170
|
+
orchestrator.env,
|
|
171
|
+
() => void Effect.runPromise(Deferred.succeed(shutdown, undefined)),
|
|
184
172
|
);
|
|
185
173
|
|
|
186
174
|
const callbacks: ProcessCallbacks = {
|
|
@@ -207,7 +195,7 @@ export const runDevSession = (
|
|
|
207
195
|
|
|
208
196
|
const startProcess = (pkg: string) => {
|
|
209
197
|
const portOverride = pkg === "host" ? orchestrator.port : undefined;
|
|
210
|
-
return
|
|
198
|
+
return scopedProcess(
|
|
211
199
|
pkg,
|
|
212
200
|
orchestrator.env,
|
|
213
201
|
callbacks,
|
|
@@ -237,7 +225,6 @@ export const runDevSession = (
|
|
|
237
225
|
const hostPackages = orderedPackages.filter((pkg) => pkg === "host");
|
|
238
226
|
|
|
239
227
|
const nonHostHandles = yield* startGroup(nonHostPackages);
|
|
240
|
-
handles.push(...nonHostHandles);
|
|
241
228
|
|
|
242
229
|
yield* Effect.forEach(
|
|
243
230
|
nonHostHandles.map((handle, index) => ({
|
|
@@ -249,7 +236,6 @@ export const runDevSession = (
|
|
|
249
236
|
);
|
|
250
237
|
|
|
251
238
|
const hostHandles = yield* startGroup(hostPackages);
|
|
252
|
-
handles.push(...hostHandles);
|
|
253
239
|
|
|
254
240
|
yield* Effect.forEach(
|
|
255
241
|
hostHandles.map((handle, index) => ({ handle, pkg: hostPackages[index] ?? handle.name })),
|
|
@@ -257,16 +243,48 @@ export const runDevSession = (
|
|
|
257
243
|
{ concurrency: "unbounded" },
|
|
258
244
|
);
|
|
259
245
|
|
|
260
|
-
yield* Effect.addFinalizer(() =>
|
|
261
|
-
|
|
246
|
+
yield* Effect.addFinalizer(() =>
|
|
247
|
+
Effect.gen(function* () {
|
|
248
|
+
view?.unmount();
|
|
249
|
+
|
|
250
|
+
if (shouldExportLogs) {
|
|
251
|
+
console.log("\n");
|
|
252
|
+
console.log("═".repeat(70));
|
|
253
|
+
console.log(` SESSION LOGS: ${orchestrator.description}`);
|
|
254
|
+
console.log(` Started: ${new Date(allLogs[0]?.timestamp || Date.now()).toISOString()}`);
|
|
255
|
+
console.log(` Total entries: ${allLogs.length}`);
|
|
256
|
+
console.log("═".repeat(70));
|
|
257
|
+
console.log("");
|
|
258
|
+
for (const entry of allLogs) {
|
|
259
|
+
console.log(formatLogLine(entry));
|
|
260
|
+
}
|
|
261
|
+
console.log("");
|
|
262
|
+
console.log("═".repeat(70));
|
|
263
|
+
console.log(` Full logs saved to: ${logger.logFile}`);
|
|
264
|
+
console.log("═".repeat(70));
|
|
265
|
+
console.log("");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
yield* registry.killAll(true).pipe(Effect.ignore);
|
|
269
|
+
}),
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
yield* Deferred.await(shutdown);
|
|
262
273
|
});
|
|
263
274
|
|
|
264
275
|
export const startApp = (orchestrator: AppOrchestrator) => {
|
|
265
|
-
let
|
|
276
|
+
let requestShutdown: (() => void) | null = null;
|
|
277
|
+
let signalCount = 0;
|
|
278
|
+
let forceExitTimer: ReturnType<typeof setTimeout> | null = null;
|
|
279
|
+
|
|
280
|
+
const forceExit = () => {
|
|
281
|
+
console.log("\n[Dev] Force exit");
|
|
282
|
+
process.exit(0);
|
|
283
|
+
};
|
|
266
284
|
|
|
267
285
|
const program = Effect.scoped(
|
|
268
|
-
runDevSession(orchestrator, (
|
|
269
|
-
|
|
286
|
+
runDevSession(orchestrator, (shutdown) => {
|
|
287
|
+
requestShutdown = shutdown;
|
|
270
288
|
}),
|
|
271
289
|
).pipe(
|
|
272
290
|
Effect.catchAll((e) =>
|
|
@@ -281,38 +299,22 @@ export const startApp = (orchestrator: AppOrchestrator) => {
|
|
|
281
299
|
),
|
|
282
300
|
);
|
|
283
301
|
|
|
284
|
-
const handleSignal =
|
|
285
|
-
if (activeCleanup) await activeCleanup();
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
const forceExit = () => {
|
|
289
|
-
console.log("\n[Dev] Force exit");
|
|
290
|
-
process.exit(0);
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
let signalCount = 0;
|
|
294
|
-
process.on("SIGINT", () => {
|
|
295
|
-
signalCount++;
|
|
296
|
-
if (signalCount > 1) {
|
|
297
|
-
forceExit();
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
const timeout = setTimeout(forceExit, 5000);
|
|
301
|
-
void handleSignal().finally(() => {
|
|
302
|
-
clearTimeout(timeout);
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
process.on("SIGTERM", () => {
|
|
302
|
+
const handleSignal = () => {
|
|
306
303
|
signalCount++;
|
|
307
304
|
if (signalCount > 1) {
|
|
308
305
|
forceExit();
|
|
309
306
|
return;
|
|
310
307
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
});
|
|
308
|
+
console.log("\n[Dev] Shutting down...");
|
|
309
|
+
forceExitTimer = setTimeout(forceExit, 8000);
|
|
310
|
+
requestShutdown?.();
|
|
311
|
+
};
|
|
316
312
|
|
|
317
|
-
|
|
313
|
+
process.on("SIGINT", handleSignal);
|
|
314
|
+
process.on("SIGTERM", handleSignal);
|
|
315
|
+
|
|
316
|
+
Effect.runPromiseExit(program).then((exit) => {
|
|
317
|
+
if (forceExitTimer) clearTimeout(forceExitTimer);
|
|
318
|
+
process.exit(Exit.isSuccess(exit) ? 0 : 0);
|
|
319
|
+
});
|
|
318
320
|
};
|
package/src/fastkv.ts
CHANGED
|
@@ -127,6 +127,101 @@ export async function fetchBosConfigFromFastKv<T>(bosUrl: string): Promise<T> {
|
|
|
127
127
|
return value as T;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
export interface PluginManifest {
|
|
131
|
+
schemaVersion: number;
|
|
132
|
+
kind: string;
|
|
133
|
+
plugin: { name: string; version: string };
|
|
134
|
+
runtime: { remoteEntry: string };
|
|
135
|
+
contract: {
|
|
136
|
+
kind: string;
|
|
137
|
+
types: { path: string; exportName: string; typeName: string; sha256: string };
|
|
138
|
+
};
|
|
139
|
+
additionalExports?: Array<{ path: string; exports: string[]; sha256: string }>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface PluginMetadata {
|
|
143
|
+
title: string | null;
|
|
144
|
+
description: string | null;
|
|
145
|
+
repoUrl: string | null;
|
|
146
|
+
version: string;
|
|
147
|
+
publishedAt: string;
|
|
148
|
+
cdnUrl: string;
|
|
149
|
+
integrity: string | null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface PluginRegistryEntry {
|
|
153
|
+
manifest: PluginManifest;
|
|
154
|
+
metadata: PluginMetadata;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function parsePluginBosUrl(
|
|
158
|
+
source: string,
|
|
159
|
+
): { accountId: string; pluginName: string } | null {
|
|
160
|
+
if (!source.startsWith("bos://")) return null;
|
|
161
|
+
const match = source.match(/^bos:\/\/([^/]+)\/plugins\/([^/]+)$/);
|
|
162
|
+
if (!match?.[1] || !match[2]) return null;
|
|
163
|
+
return { accountId: match[1], pluginName: match[2] };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function fetchKvValue(accountId: string, key: string): Promise<unknown | null> {
|
|
167
|
+
const payload = await fetchJson<FastKvListResponse>(
|
|
168
|
+
`${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId))}/${encodeURIComponent(accountId)}`,
|
|
169
|
+
{
|
|
170
|
+
method: "POST",
|
|
171
|
+
body: JSON.stringify({ key, limit: 1 }),
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
const value = payload?.entries?.find(Boolean)?.value;
|
|
175
|
+
if (value == null) return null;
|
|
176
|
+
if (typeof value === "string") {
|
|
177
|
+
try {
|
|
178
|
+
return JSON.parse(value);
|
|
179
|
+
} catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export async function fetchPluginFromRegistry(
|
|
187
|
+
accountId: string,
|
|
188
|
+
pluginName: string,
|
|
189
|
+
): Promise<PluginRegistryEntry | null> {
|
|
190
|
+
const manifestKey = `plugins/${accountId}/${pluginName}/manifest.json`;
|
|
191
|
+
const metadataKey = `plugins/${accountId}/${pluginName}/metadata`;
|
|
192
|
+
|
|
193
|
+
const [rawManifest, rawMetadata] = await Promise.all([
|
|
194
|
+
fetchKvValue(accountId, manifestKey),
|
|
195
|
+
fetchKvValue(accountId, metadataKey),
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
if (!rawManifest || typeof rawManifest !== "object") return null;
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
manifest: rawManifest as PluginManifest,
|
|
202
|
+
metadata: (rawMetadata ?? {
|
|
203
|
+
title: null,
|
|
204
|
+
description: null,
|
|
205
|
+
repoUrl: null,
|
|
206
|
+
version: "",
|
|
207
|
+
publishedAt: "",
|
|
208
|
+
cdnUrl: "",
|
|
209
|
+
integrity: null,
|
|
210
|
+
}) as PluginMetadata,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function fetchRemotePluginManifest(cdnUrl: string): Promise<PluginManifest | null> {
|
|
215
|
+
try {
|
|
216
|
+
const baseUrl = cdnUrl.replace(/\/$/, "");
|
|
217
|
+
const response = await fetch(`${baseUrl}/plugin.manifest.json`);
|
|
218
|
+
if (!response.ok) return null;
|
|
219
|
+
return (await response.json()) as PluginManifest;
|
|
220
|
+
} catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
130
225
|
async function fetchJson<T>(url: string, init?: RequestInit): Promise<T | null> {
|
|
131
226
|
const controller = new AbortController();
|
|
132
227
|
const timeout = setTimeout(() => controller.abort(), FASTKV_TIMEOUT_MS);
|
package/src/host.ts
CHANGED
|
@@ -258,7 +258,7 @@ async function runHostServer(opts: {
|
|
|
258
258
|
}),
|
|
259
259
|
);
|
|
260
260
|
|
|
261
|
-
app.use("*", secureHeaders());
|
|
261
|
+
app.use("*", secureHeaders({ crossOriginOpenerPolicy: "same-origin-allow-popups" }));
|
|
262
262
|
|
|
263
263
|
app.get("/health", (c) => c.text("OK"));
|
|
264
264
|
app.get("/ready", async (c) => {
|
package/src/orchestrator.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface ProcessCallbacks {
|
|
|
24
24
|
export interface ProcessHandle {
|
|
25
25
|
name: string;
|
|
26
26
|
pid: number | undefined;
|
|
27
|
-
kill:
|
|
27
|
+
kill: Effect.Effect<void, unknown>;
|
|
28
28
|
waitForReady: Effect.Effect<void, Error>;
|
|
29
29
|
waitForExit: Effect.Effect<unknown>;
|
|
30
30
|
}
|
|
@@ -62,7 +62,6 @@ const processConfigBases: Record<string, ProcessConfigBase> = {
|
|
|
62
62
|
command: "bun",
|
|
63
63
|
args: ["run", "dev"],
|
|
64
64
|
cwd: "ui",
|
|
65
|
-
// Wait for the client build (mf) specifically, not just SSR.
|
|
66
65
|
readyPatterns: [/\bready\s+built in\b/i, /\bLocal:\b/i, /\bcompiled\b.*successfully/i],
|
|
67
66
|
errorPatterns: [/error/i, /failed to compile/i],
|
|
68
67
|
},
|
|
@@ -91,6 +90,25 @@ export function getProcessConfig(
|
|
|
91
90
|
bosConfig?: BosConfig,
|
|
92
91
|
runtimeConfig?: RuntimeConfig,
|
|
93
92
|
): DevProcess | null {
|
|
93
|
+
if (pkg === "auth") {
|
|
94
|
+
const authConfig = runtimeConfig?.auth;
|
|
95
|
+
if (!authConfig?.localPath || authConfig.source !== "local") return null;
|
|
96
|
+
|
|
97
|
+
const port =
|
|
98
|
+
portOverride ?? authConfig.port ?? (authConfig.url ? parsePort(authConfig.url) : 3020);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
name: "auth",
|
|
102
|
+
command: "bun",
|
|
103
|
+
args: ["run", "dev"],
|
|
104
|
+
cwd: authConfig.localPath,
|
|
105
|
+
port,
|
|
106
|
+
readyPatterns: [/ready in/i, /compiled.*successfully/i, /listening/i, /started/i],
|
|
107
|
+
errorPatterns: [/error/i, /failed/i],
|
|
108
|
+
env,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
if (pkg.startsWith("plugin:")) {
|
|
95
113
|
const pluginId = pkg.slice("plugin:".length);
|
|
96
114
|
const pluginConfig = runtimeConfig?.plugins?.[pluginId] ?? null;
|
|
@@ -386,11 +404,14 @@ export const spawnRemoteHost = (
|
|
|
386
404
|
return {
|
|
387
405
|
name: config.name,
|
|
388
406
|
pid: process.pid,
|
|
389
|
-
kill:
|
|
407
|
+
kill: Effect.gen(function* () {
|
|
390
408
|
callbacks.onLog(config.name, "Shutting down remote host...");
|
|
391
409
|
restoreConsole();
|
|
392
|
-
|
|
393
|
-
|
|
410
|
+
yield* Effect.tryPromise({
|
|
411
|
+
try: () => serverHandle.shutdown(),
|
|
412
|
+
catch: () => {},
|
|
413
|
+
}).pipe(Effect.ignore);
|
|
414
|
+
}),
|
|
394
415
|
waitForReady: Effect.succeed(undefined),
|
|
395
416
|
waitForExit: Effect.never,
|
|
396
417
|
} satisfies ProcessHandle;
|
|
@@ -441,14 +462,12 @@ export const spawnDevProcess = (
|
|
|
441
462
|
yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);
|
|
442
463
|
});
|
|
443
464
|
|
|
444
|
-
// Prefer probe-based readiness to avoid brittle log regexes.
|
|
445
|
-
// This is best-effort and complements log detection.
|
|
446
465
|
if (config.port > 0) {
|
|
447
466
|
const readinessPath =
|
|
448
467
|
config.name === "host" ? "/health" : config.name === "ui-ssr" ? "/" : "/remoteEntry.js";
|
|
449
468
|
const url = `http://127.0.0.1:${config.port}${readinessPath}`;
|
|
450
469
|
|
|
451
|
-
yield* Effect.
|
|
470
|
+
yield* Effect.forkScoped(
|
|
452
471
|
Effect.gen(function* () {
|
|
453
472
|
const deadline = Date.now() + 90_000;
|
|
454
473
|
while (Date.now() < deadline) {
|
|
@@ -483,7 +502,7 @@ export const spawnDevProcess = (
|
|
|
483
502
|
});
|
|
484
503
|
}
|
|
485
504
|
|
|
486
|
-
yield* Effect.
|
|
505
|
+
yield* Effect.forkScoped(
|
|
487
506
|
Effect.promise(() => proc.exited).pipe(
|
|
488
507
|
Effect.andThen((code) =>
|
|
489
508
|
Effect.gen(function* () {
|
|
@@ -531,7 +550,7 @@ export const spawnDevProcess = (
|
|
|
531
550
|
|
|
532
551
|
const decoder = new TextDecoder();
|
|
533
552
|
|
|
534
|
-
const stdoutFiber = yield* Effect.
|
|
553
|
+
const stdoutFiber = yield* Effect.forkScoped(
|
|
535
554
|
Effect.async<void>((resume) => {
|
|
536
555
|
if (!proc.stdout) {
|
|
537
556
|
resume(Effect.void);
|
|
@@ -539,13 +558,13 @@ export const spawnDevProcess = (
|
|
|
539
558
|
}
|
|
540
559
|
const reader = proc.stdout.getReader();
|
|
541
560
|
let buffer = "";
|
|
561
|
+
let active = true;
|
|
542
562
|
|
|
543
563
|
const pump = (): Promise<void> =>
|
|
544
564
|
reader.read().then(({ done, value }) => {
|
|
565
|
+
if (!active) return;
|
|
545
566
|
if (done) {
|
|
546
|
-
if (buffer)
|
|
547
|
-
Effect.runSync(handleLine(buffer, false));
|
|
548
|
-
}
|
|
567
|
+
if (buffer) Effect.runSync(handleLine(buffer, false));
|
|
549
568
|
return;
|
|
550
569
|
}
|
|
551
570
|
buffer += decoder
|
|
@@ -560,11 +579,18 @@ export const spawnDevProcess = (
|
|
|
560
579
|
return pump();
|
|
561
580
|
});
|
|
562
581
|
|
|
563
|
-
pump().then(() =>
|
|
582
|
+
pump().then(() => {
|
|
583
|
+
if (active) resume(Effect.void);
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
return Effect.sync(() => {
|
|
587
|
+
active = false;
|
|
588
|
+
reader.cancel();
|
|
589
|
+
});
|
|
564
590
|
}),
|
|
565
591
|
);
|
|
566
592
|
|
|
567
|
-
const stderrFiber = yield* Effect.
|
|
593
|
+
const stderrFiber = yield* Effect.forkScoped(
|
|
568
594
|
Effect.async<void>((resume) => {
|
|
569
595
|
if (!proc.stderr) {
|
|
570
596
|
resume(Effect.void);
|
|
@@ -572,13 +598,13 @@ export const spawnDevProcess = (
|
|
|
572
598
|
}
|
|
573
599
|
const reader = proc.stderr.getReader();
|
|
574
600
|
let buffer = "";
|
|
601
|
+
let active = true;
|
|
575
602
|
|
|
576
603
|
const pump = (): Promise<void> =>
|
|
577
604
|
reader.read().then(({ done, value }) => {
|
|
605
|
+
if (!active) return;
|
|
578
606
|
if (done) {
|
|
579
|
-
if (buffer)
|
|
580
|
-
Effect.runSync(handleLine(buffer, true));
|
|
581
|
-
}
|
|
607
|
+
if (buffer) Effect.runSync(handleLine(buffer, true));
|
|
582
608
|
return;
|
|
583
609
|
}
|
|
584
610
|
buffer += decoder
|
|
@@ -593,25 +619,29 @@ export const spawnDevProcess = (
|
|
|
593
619
|
return pump();
|
|
594
620
|
});
|
|
595
621
|
|
|
596
|
-
pump().then(() =>
|
|
622
|
+
pump().then(() => {
|
|
623
|
+
if (active) resume(Effect.void);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
return Effect.sync(() => {
|
|
627
|
+
active = false;
|
|
628
|
+
reader.cancel();
|
|
629
|
+
});
|
|
597
630
|
}),
|
|
598
631
|
);
|
|
599
632
|
|
|
600
633
|
const handle: ProcessHandle = {
|
|
601
634
|
name: config.name,
|
|
602
635
|
pid: proc.pid,
|
|
603
|
-
kill:
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
} catch {}
|
|
613
|
-
}
|
|
614
|
-
},
|
|
636
|
+
kill: proc.pid
|
|
637
|
+
? killProcessTree(proc.pid)
|
|
638
|
+
: Effect.gen(function* () {
|
|
639
|
+
proc.kill("SIGTERM");
|
|
640
|
+
yield* Effect.sleep("100 millis");
|
|
641
|
+
try {
|
|
642
|
+
proc.kill("SIGKILL");
|
|
643
|
+
} catch {}
|
|
644
|
+
}),
|
|
615
645
|
waitForReady: Deferred.await(readyDeferred),
|
|
616
646
|
waitForExit: Effect.gen(function* () {
|
|
617
647
|
yield* Fiber.joinAll([stdoutFiber, stderrFiber]);
|