everything-dev 1.7.2 → 1.8.1

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