everything-dev 0.3.2 → 1.3.2

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 (308) hide show
  1. package/README.md +64 -0
  2. package/cli.js +10 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +29 -0
  4. package/dist/api-contract.cjs +172 -0
  5. package/dist/api-contract.cjs.map +1 -0
  6. package/dist/api-contract.mjs +171 -0
  7. package/dist/api-contract.mjs.map +1 -0
  8. package/dist/api.cjs +124 -0
  9. package/dist/api.cjs.map +1 -0
  10. package/dist/api.d.cts +36 -0
  11. package/dist/api.d.cts.map +1 -0
  12. package/dist/api.d.mts +36 -0
  13. package/dist/api.d.mts.map +1 -0
  14. package/dist/api.mjs +119 -0
  15. package/dist/api.mjs.map +1 -0
  16. package/dist/app.cjs +156 -0
  17. package/dist/app.cjs.map +1 -0
  18. package/dist/app.mjs +153 -0
  19. package/dist/app.mjs.map +1 -0
  20. package/dist/cli/catalog.cjs +30 -0
  21. package/dist/cli/catalog.cjs.map +1 -0
  22. package/dist/cli/catalog.mjs +29 -0
  23. package/dist/cli/catalog.mjs.map +1 -0
  24. package/dist/cli/help.cjs +16 -0
  25. package/dist/cli/help.cjs.map +1 -0
  26. package/dist/cli/help.mjs +16 -0
  27. package/dist/cli/help.mjs.map +1 -0
  28. package/dist/cli/init.cjs +317 -0
  29. package/dist/cli/init.cjs.map +1 -0
  30. package/dist/cli/init.d.cts +36 -0
  31. package/dist/cli/init.d.cts.map +1 -0
  32. package/dist/cli/init.d.mts +36 -0
  33. package/dist/cli/init.d.mts.map +1 -0
  34. package/dist/cli/init.mjs +309 -0
  35. package/dist/cli/init.mjs.map +1 -0
  36. package/dist/cli/parse.cjs +96 -0
  37. package/dist/cli/parse.cjs.map +1 -0
  38. package/dist/cli/parse.mjs +95 -0
  39. package/dist/cli/parse.mjs.map +1 -0
  40. package/dist/cli/prompts.cjs +42 -0
  41. package/dist/cli/prompts.cjs.map +1 -0
  42. package/dist/cli/prompts.mjs +41 -0
  43. package/dist/cli/prompts.mjs.map +1 -0
  44. package/dist/cli.cjs +167 -0
  45. package/dist/cli.cjs.map +1 -0
  46. package/dist/cli.d.cts +1 -0
  47. package/dist/cli.d.mts +1 -0
  48. package/dist/cli.mjs +166 -0
  49. package/dist/cli.mjs.map +1 -0
  50. package/dist/components/dev-view.cjs +307 -0
  51. package/dist/components/dev-view.cjs.map +1 -0
  52. package/dist/components/dev-view.mjs +306 -0
  53. package/dist/components/dev-view.mjs.map +1 -0
  54. package/dist/components/streaming-view.cjs +146 -0
  55. package/dist/components/streaming-view.cjs.map +1 -0
  56. package/dist/components/streaming-view.mjs +144 -0
  57. package/dist/components/streaming-view.mjs.map +1 -0
  58. package/dist/config.cjs +280 -0
  59. package/dist/config.cjs.map +1 -0
  60. package/dist/config.d.cts +35 -0
  61. package/dist/config.d.cts.map +1 -0
  62. package/dist/config.d.mts +35 -0
  63. package/dist/config.d.mts.map +1 -0
  64. package/dist/config.mjs +266 -0
  65. package/dist/config.mjs.map +1 -0
  66. package/dist/contract.cjs +209 -0
  67. package/dist/contract.cjs.map +1 -0
  68. package/dist/contract.d.cts +490 -0
  69. package/dist/contract.d.cts.map +1 -0
  70. package/dist/contract.d.mts +490 -0
  71. package/dist/contract.d.mts.map +1 -0
  72. package/dist/contract.meta.cjs +104 -0
  73. package/dist/contract.meta.cjs.map +1 -0
  74. package/dist/contract.meta.d.cts +141 -0
  75. package/dist/contract.meta.d.cts.map +1 -0
  76. package/dist/contract.meta.d.mts +141 -0
  77. package/dist/contract.meta.d.mts.map +1 -0
  78. package/dist/contract.meta.mjs +102 -0
  79. package/dist/contract.meta.mjs.map +1 -0
  80. package/dist/contract.mjs +186 -0
  81. package/dist/contract.mjs.map +1 -0
  82. package/dist/dev-logs.cjs +53 -0
  83. package/dist/dev-logs.cjs.map +1 -0
  84. package/dist/dev-logs.mjs +51 -0
  85. package/dist/dev-logs.mjs.map +1 -0
  86. package/dist/dev-session.cjs +195 -0
  87. package/dist/dev-session.cjs.map +1 -0
  88. package/dist/dev-session.mjs +194 -0
  89. package/dist/dev-session.mjs.map +1 -0
  90. package/dist/fastkv.cjs +89 -0
  91. package/dist/fastkv.cjs.map +1 -0
  92. package/dist/fastkv.d.cts +11 -0
  93. package/dist/fastkv.d.cts.map +1 -0
  94. package/dist/fastkv.d.mts +11 -0
  95. package/dist/fastkv.d.mts.map +1 -0
  96. package/dist/fastkv.mjs +82 -0
  97. package/dist/fastkv.mjs.map +1 -0
  98. package/dist/federation.server.cjs +27 -0
  99. package/dist/federation.server.cjs.map +1 -0
  100. package/dist/federation.server.mjs +27 -0
  101. package/dist/federation.server.mjs.map +1 -0
  102. package/dist/host.cjs +367 -0
  103. package/dist/host.cjs.map +1 -0
  104. package/dist/host.d.cts +22 -0
  105. package/dist/host.d.cts.map +1 -0
  106. package/dist/host.d.mts +22 -0
  107. package/dist/host.d.mts.map +1 -0
  108. package/dist/host.mjs +364 -0
  109. package/dist/host.mjs.map +1 -0
  110. package/dist/index.cjs +122 -0
  111. package/dist/index.d.cts +7 -0
  112. package/dist/index.d.mts +7 -0
  113. package/dist/index.mjs +9 -0
  114. package/dist/integrity.cjs +39 -0
  115. package/dist/integrity.cjs.map +1 -0
  116. package/dist/integrity.d.cts +7 -0
  117. package/dist/integrity.d.cts.map +1 -0
  118. package/dist/integrity.d.mts +7 -0
  119. package/dist/integrity.d.mts.map +1 -0
  120. package/dist/integrity.mjs +35 -0
  121. package/dist/integrity.mjs.map +1 -0
  122. package/dist/mf.cjs +77 -0
  123. package/dist/mf.cjs.map +1 -0
  124. package/dist/mf.d.cts +19 -0
  125. package/dist/mf.d.cts.map +1 -0
  126. package/dist/mf.d.mts +19 -0
  127. package/dist/mf.d.mts.map +1 -0
  128. package/dist/mf.mjs +71 -0
  129. package/dist/mf.mjs.map +1 -0
  130. package/dist/near-cli.cjs +196 -0
  131. package/dist/near-cli.cjs.map +1 -0
  132. package/dist/near-cli.mjs +193 -0
  133. package/dist/near-cli.mjs.map +1 -0
  134. package/dist/network.cjs +9 -0
  135. package/dist/network.cjs.map +1 -0
  136. package/dist/network.mjs +8 -0
  137. package/dist/network.mjs.map +1 -0
  138. package/dist/orchestrator.cjs +441 -0
  139. package/dist/orchestrator.cjs.map +1 -0
  140. package/dist/orchestrator.d.cts +40 -0
  141. package/dist/orchestrator.d.cts.map +1 -0
  142. package/dist/orchestrator.d.mts +40 -0
  143. package/dist/orchestrator.d.mts.map +1 -0
  144. package/dist/orchestrator.mjs +436 -0
  145. package/dist/orchestrator.mjs.map +1 -0
  146. package/dist/plugin.cjs +825 -0
  147. package/dist/plugin.cjs.map +1 -0
  148. package/dist/plugin.d.cts +347 -0
  149. package/dist/plugin.d.cts.map +1 -0
  150. package/dist/plugin.d.mts +348 -0
  151. package/dist/plugin.d.mts.map +1 -0
  152. package/dist/plugin.mjs +822 -0
  153. package/dist/plugin.mjs.map +1 -0
  154. package/dist/process-registry.cjs +120 -0
  155. package/dist/process-registry.cjs.map +1 -0
  156. package/dist/process-registry.d.cts +25 -0
  157. package/dist/process-registry.d.cts.map +1 -0
  158. package/dist/process-registry.d.mts +25 -0
  159. package/dist/process-registry.d.mts.map +1 -0
  160. package/dist/process-registry.mjs +119 -0
  161. package/dist/process-registry.mjs.map +1 -0
  162. package/dist/sdk.cjs +61 -0
  163. package/dist/sdk.d.cts +5 -0
  164. package/dist/sdk.d.mts +5 -0
  165. package/dist/sdk.mjs +6 -0
  166. package/dist/shared.cjs +143 -0
  167. package/dist/shared.cjs.map +1 -0
  168. package/dist/shared.d.cts +33 -0
  169. package/dist/shared.d.cts.map +1 -0
  170. package/dist/shared.d.mts +33 -0
  171. package/dist/shared.d.mts.map +1 -0
  172. package/dist/shared.mjs +140 -0
  173. package/dist/shared.mjs.map +1 -0
  174. package/dist/types.cjs +160 -0
  175. package/dist/types.cjs.map +1 -0
  176. package/dist/types.d.cts +269 -0
  177. package/dist/types.d.cts.map +1 -0
  178. package/dist/types.d.mts +269 -0
  179. package/dist/types.d.mts.map +1 -0
  180. package/dist/types.mjs +144 -0
  181. package/dist/types.mjs.map +1 -0
  182. package/dist/ui/head.cjs +67 -0
  183. package/dist/ui/head.cjs.map +1 -0
  184. package/dist/ui/head.d.cts +19 -0
  185. package/dist/ui/head.d.cts.map +1 -0
  186. package/dist/ui/head.d.mts +19 -0
  187. package/dist/ui/head.d.mts.map +1 -0
  188. package/dist/ui/head.mjs +61 -0
  189. package/dist/ui/head.mjs.map +1 -0
  190. package/dist/ui/index.cjs +32 -0
  191. package/dist/ui/index.d.cts +7 -0
  192. package/dist/ui/index.d.mts +7 -0
  193. package/dist/ui/index.mjs +6 -0
  194. package/dist/ui/metadata.cjs +106 -0
  195. package/dist/ui/metadata.cjs.map +1 -0
  196. package/dist/ui/metadata.d.cts +35 -0
  197. package/dist/ui/metadata.d.cts.map +1 -0
  198. package/dist/ui/metadata.d.mts +35 -0
  199. package/dist/ui/metadata.d.mts.map +1 -0
  200. package/dist/ui/metadata.mjs +100 -0
  201. package/dist/ui/metadata.mjs.map +1 -0
  202. package/dist/ui/router.cjs +56 -0
  203. package/dist/ui/router.cjs.map +1 -0
  204. package/dist/ui/router.d.cts +11 -0
  205. package/dist/ui/router.d.cts.map +1 -0
  206. package/dist/ui/router.d.mts +11 -0
  207. package/dist/ui/router.d.mts.map +1 -0
  208. package/dist/ui/router.mjs +51 -0
  209. package/dist/ui/router.mjs.map +1 -0
  210. package/dist/ui/runtime.cjs +65 -0
  211. package/dist/ui/runtime.cjs.map +1 -0
  212. package/dist/ui/runtime.d.cts +29 -0
  213. package/dist/ui/runtime.d.cts.map +1 -0
  214. package/dist/ui/runtime.d.mts +29 -0
  215. package/dist/ui/runtime.d.mts.map +1 -0
  216. package/dist/ui/runtime.mjs +53 -0
  217. package/dist/ui/runtime.mjs.map +1 -0
  218. package/dist/ui/types.cjs +0 -0
  219. package/dist/ui/types.d.cts +52 -0
  220. package/dist/ui/types.d.cts.map +1 -0
  221. package/dist/ui/types.d.mts +52 -0
  222. package/dist/ui/types.d.mts.map +1 -0
  223. package/dist/ui/types.mjs +1 -0
  224. package/dist/utils/banner.cjs +24 -0
  225. package/dist/utils/banner.cjs.map +1 -0
  226. package/dist/utils/banner.mjs +23 -0
  227. package/dist/utils/banner.mjs.map +1 -0
  228. package/dist/utils/linkify.cjs +15 -0
  229. package/dist/utils/linkify.cjs.map +1 -0
  230. package/dist/utils/linkify.mjs +14 -0
  231. package/dist/utils/linkify.mjs.map +1 -0
  232. package/dist/utils/run.cjs +40 -0
  233. package/dist/utils/run.cjs.map +1 -0
  234. package/dist/utils/run.mjs +39 -0
  235. package/dist/utils/run.mjs.map +1 -0
  236. package/dist/utils/theme.cjs +44 -0
  237. package/dist/utils/theme.cjs.map +1 -0
  238. package/dist/utils/theme.mjs +37 -0
  239. package/dist/utils/theme.mjs.map +1 -0
  240. package/package.json +269 -80
  241. package/src/api-contract.ts +309 -0
  242. package/src/api.ts +181 -0
  243. package/src/app.ts +346 -0
  244. package/src/cli/catalog.ts +49 -0
  245. package/src/cli/help.ts +13 -0
  246. package/src/cli/init.ts +415 -0
  247. package/src/cli/parse.ts +130 -0
  248. package/src/cli/prompts.ts +64 -0
  249. package/src/cli.ts +203 -1507
  250. package/src/components/dev-view.tsx +104 -41
  251. package/src/components/streaming-view.ts +89 -22
  252. package/src/config.ts +462 -532
  253. package/src/contract.meta.ts +96 -0
  254. package/src/contract.ts +164 -561
  255. package/src/dev-logs.ts +85 -0
  256. package/src/dev-session.ts +318 -0
  257. package/src/fastkv.ts +153 -0
  258. package/src/federation.server.ts +43 -0
  259. package/src/host.ts +526 -0
  260. package/src/index.ts +6 -3
  261. package/src/integrity.ts +54 -0
  262. package/src/mf.ts +105 -0
  263. package/src/near-cli.ts +284 -0
  264. package/src/network.ts +3 -0
  265. package/src/orchestrator.ts +648 -0
  266. package/src/plugin.ts +1116 -2303
  267. package/src/process-registry.ts +154 -0
  268. package/src/scripts/sync-api-contract.ts +24 -0
  269. package/src/sdk.ts +14 -0
  270. package/src/shared.ts +206 -0
  271. package/src/types.ts +152 -206
  272. package/src/ui/head.ts +34 -27
  273. package/src/ui/index.ts +3 -3
  274. package/src/ui/metadata.ts +95 -0
  275. package/src/ui/router.ts +22 -6
  276. package/src/ui/runtime.ts +55 -6
  277. package/src/ui/types.ts +24 -11
  278. package/src/utils/banner.ts +10 -6
  279. package/src/utils/run.ts +26 -27
  280. package/src/utils/theme.ts +3 -66
  281. package/src/components/monitor-view.tsx +0 -475
  282. package/src/components/status-view.tsx +0 -173
  283. package/src/lib/env.ts +0 -109
  284. package/src/lib/near-cli.ts +0 -289
  285. package/src/lib/nova.ts +0 -266
  286. package/src/lib/orchestrator.ts +0 -276
  287. package/src/lib/process-registry.ts +0 -166
  288. package/src/lib/process.ts +0 -549
  289. package/src/lib/resource-monitor/assertions.ts +0 -234
  290. package/src/lib/resource-monitor/command.ts +0 -283
  291. package/src/lib/resource-monitor/diff.ts +0 -157
  292. package/src/lib/resource-monitor/errors.ts +0 -127
  293. package/src/lib/resource-monitor/index.ts +0 -305
  294. package/src/lib/resource-monitor/platform/darwin.ts +0 -306
  295. package/src/lib/resource-monitor/platform/index.ts +0 -35
  296. package/src/lib/resource-monitor/platform/linux.ts +0 -332
  297. package/src/lib/resource-monitor/platform/windows.ts +0 -298
  298. package/src/lib/resource-monitor/snapshot.ts +0 -217
  299. package/src/lib/resource-monitor/types.ts +0 -74
  300. package/src/lib/session-recorder/errors.ts +0 -102
  301. package/src/lib/session-recorder/flows/login.ts +0 -210
  302. package/src/lib/session-recorder/index.ts +0 -361
  303. package/src/lib/session-recorder/playwright.ts +0 -257
  304. package/src/lib/session-recorder/report.ts +0 -353
  305. package/src/lib/session-recorder/server.ts +0 -267
  306. package/src/lib/session-recorder/types.ts +0 -115
  307. package/src/lib/sync.ts +0 -1
  308. package/src/ui/files.ts +0 -134
@@ -0,0 +1,648 @@
1
+ import { createConnection } from "node:net";
2
+ import { Deferred, Effect, Fiber, Ref } from "effect";
3
+ import { getHostDevelopmentPort, getProjectRoot, parsePort } from "./config";
4
+ import { patchManifestFetchForSsrPublicPath } from "./mf";
5
+ import type { ProcessRegistry } from "./process-registry";
6
+ import type { BosConfig, RuntimeConfig } from "./types";
7
+
8
+ export interface DevProcess {
9
+ name: string;
10
+ command: string;
11
+ args: string[];
12
+ cwd: string;
13
+ env?: Record<string, string>;
14
+ port: number;
15
+ readyPatterns: RegExp[];
16
+ errorPatterns: RegExp[];
17
+ }
18
+
19
+ export interface ProcessCallbacks {
20
+ onStatus: (name: string, status: ProcessStatus, message?: string) => void;
21
+ onLog: (name: string, line: string, isError?: boolean) => void;
22
+ }
23
+
24
+ export interface ProcessHandle {
25
+ name: string;
26
+ pid: number | undefined;
27
+ kill: () => Promise<void>;
28
+ waitForReady: Effect.Effect<void, Error>;
29
+ waitForExit: Effect.Effect<unknown>;
30
+ }
31
+
32
+ export type ProcessStatus = "pending" | "starting" | "ready" | "error";
33
+
34
+ interface ProcessConfigBase {
35
+ name: string;
36
+ command: string;
37
+ args: string[];
38
+ cwd: string;
39
+ readyPatterns: RegExp[];
40
+ errorPatterns: RegExp[];
41
+ }
42
+
43
+ const processConfigBases: Record<string, ProcessConfigBase> = {
44
+ "host-build": {
45
+ name: "host-build",
46
+ command: "bun",
47
+ args: ["run", "build"],
48
+ cwd: "host",
49
+ readyPatterns: [/built in/i, /compiled.*successfully/i],
50
+ errorPatterns: [/error:/i, /failed/i, /exception/i],
51
+ },
52
+ host: {
53
+ name: "host",
54
+ command: "bun",
55
+ args: ["run", "dev"],
56
+ cwd: "host",
57
+ readyPatterns: [/Host (dev|production) server running at/i, /Server running at/i],
58
+ errorPatterns: [/error:/i, /failed/i, /exception/i],
59
+ },
60
+ ui: {
61
+ name: "ui",
62
+ command: "bun",
63
+ args: ["run", "dev"],
64
+ cwd: "ui",
65
+ // Wait for the client build (mf) specifically, not just SSR.
66
+ readyPatterns: [/\bready\s+built in\b/i, /\bLocal:\b/i, /\bcompiled\b.*successfully/i],
67
+ errorPatterns: [/error/i, /failed to compile/i],
68
+ },
69
+ "ui-ssr": {
70
+ name: "ui-ssr",
71
+ command: "bun",
72
+ args: ["run", "dev:ssr"],
73
+ cwd: "ui",
74
+ readyPatterns: [/\bready\s+built in\b/i, /\bcompiled\b.*successfully/i],
75
+ errorPatterns: [/error/i, /failed/i],
76
+ },
77
+ api: {
78
+ name: "api",
79
+ command: "bun",
80
+ args: ["run", "dev"],
81
+ cwd: "api",
82
+ readyPatterns: [/ready in/i, /compiled.*successfully/i, /listening/i, /started/i],
83
+ errorPatterns: [/error/i, /failed/i],
84
+ },
85
+ };
86
+
87
+ export function getProcessConfig(
88
+ pkg: string,
89
+ env?: Record<string, string>,
90
+ portOverride?: number,
91
+ bosConfig?: BosConfig,
92
+ runtimeConfig?: RuntimeConfig,
93
+ ): DevProcess | null {
94
+ if (pkg.startsWith("plugin:")) {
95
+ const pluginId = pkg.slice("plugin:".length);
96
+ const pluginConfig = runtimeConfig?.plugins?.[pluginId] ?? null;
97
+ const localPath = pluginConfig?.localPath;
98
+
99
+ if (!localPath || pluginConfig?.source !== "local") return null;
100
+
101
+ const port =
102
+ portOverride ?? pluginConfig?.port ?? (pluginConfig?.url ? parsePort(pluginConfig.url) : 0);
103
+
104
+ return {
105
+ name: pkg,
106
+ command: "bun",
107
+ args: ["run", "dev"],
108
+ cwd: localPath,
109
+ port,
110
+ readyPatterns: [/ready in/i, /compiled.*successfully/i, /listening/i, /started/i],
111
+ errorPatterns: [/error/i, /failed/i],
112
+ env,
113
+ };
114
+ }
115
+
116
+ const base = processConfigBases[pkg];
117
+ if (!base) return null;
118
+
119
+ let port: number;
120
+ if (pkg === "host") {
121
+ port =
122
+ portOverride ??
123
+ (runtimeConfig?.hostUrl
124
+ ? parsePort(runtimeConfig.hostUrl)
125
+ : bosConfig
126
+ ? getHostDevelopmentPort(bosConfig.app.host.development)
127
+ : 3000);
128
+ } else if (pkg === "ui") {
129
+ port =
130
+ runtimeConfig?.ui.port ?? (runtimeConfig?.ui.url ? parsePort(runtimeConfig.ui.url) : 3002);
131
+ } else if (pkg === "ui-ssr") {
132
+ const uiPort = runtimeConfig?.ui.ssrUrl
133
+ ? parsePort(runtimeConfig.ui.ssrUrl)
134
+ : runtimeConfig?.ui.port
135
+ ? runtimeConfig.ui.port + 1
136
+ : 3003;
137
+ port = uiPort;
138
+ } else if (pkg === "api") {
139
+ port =
140
+ runtimeConfig?.api.port ?? (runtimeConfig?.api.url ? parsePort(runtimeConfig.api.url) : 3014);
141
+ } else {
142
+ port = 0;
143
+ }
144
+
145
+ const cwd =
146
+ pkg === "ui"
147
+ ? (runtimeConfig?.ui.localPath ?? base.cwd)
148
+ : pkg === "api"
149
+ ? (runtimeConfig?.api.localPath ?? base.cwd)
150
+ : base.cwd;
151
+
152
+ return { ...base, cwd, port, env };
153
+ }
154
+
155
+ const stripAnsi = (input: string): string => {
156
+ const ESC = String.fromCharCode(27);
157
+ const BEL = String.fromCharCode(7);
158
+ return input
159
+ .replace(new RegExp(`${ESC}\\][^${BEL}]*${BEL}`, "g"), "")
160
+ .replace(new RegExp(`${ESC}\\[[0-?]*[ -/]*[@-~]`, "g"), "");
161
+ };
162
+
163
+ const probeHttpOk = async (url: string, timeoutMs = 400): Promise<boolean> => {
164
+ const controller = new AbortController();
165
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
166
+ try {
167
+ const res = await fetch(url, { signal: controller.signal });
168
+ return res.ok;
169
+ } catch {
170
+ return false;
171
+ } finally {
172
+ clearTimeout(timer);
173
+ }
174
+ };
175
+
176
+ const probeTcpOpen = async (port: number, timeoutMs = 250): Promise<boolean> => {
177
+ return new Promise((resolve) => {
178
+ const socket = createConnection({ host: "127.0.0.1", port });
179
+ const timer = setTimeout(() => {
180
+ socket.destroy();
181
+ resolve(false);
182
+ }, timeoutMs);
183
+ socket.once("connect", () => {
184
+ clearTimeout(timer);
185
+ socket.destroy();
186
+ resolve(true);
187
+ });
188
+ socket.once("error", () => {
189
+ clearTimeout(timer);
190
+ resolve(false);
191
+ });
192
+ });
193
+ };
194
+
195
+ const detectStatus = (
196
+ line: string,
197
+ config: DevProcess,
198
+ ): { status: ProcessStatus; isError: boolean } | null => {
199
+ const cleanLine = stripAnsi(line);
200
+ for (const pattern of config.errorPatterns) {
201
+ if (pattern.test(cleanLine)) {
202
+ return { status: "error", isError: true };
203
+ }
204
+ }
205
+ for (const pattern of config.readyPatterns) {
206
+ if (pattern.test(cleanLine)) {
207
+ return { status: "ready", isError: false };
208
+ }
209
+ }
210
+ return null;
211
+ };
212
+
213
+ const killProcessTree = (pid: number) =>
214
+ Effect.gen(function* () {
215
+ const killSignal = (signal: NodeJS.Signals) =>
216
+ Effect.try({
217
+ try: () => {
218
+ process.kill(-pid, signal);
219
+ },
220
+ catch: () => null,
221
+ }).pipe(Effect.ignore);
222
+
223
+ const killDirect = (signal: NodeJS.Signals) =>
224
+ Effect.try({
225
+ try: () => {
226
+ process.kill(pid, signal);
227
+ },
228
+ catch: () => null,
229
+ }).pipe(Effect.ignore);
230
+
231
+ const isRunning = () =>
232
+ Effect.try({
233
+ try: () => {
234
+ process.kill(pid, 0);
235
+ return true;
236
+ },
237
+ catch: () => false,
238
+ });
239
+
240
+ yield* killSignal("SIGTERM");
241
+ yield* killDirect("SIGTERM");
242
+
243
+ yield* Effect.sleep("200 millis");
244
+
245
+ const stillRunning = yield* isRunning();
246
+ if (stillRunning) {
247
+ yield* killSignal("SIGKILL");
248
+ yield* killDirect("SIGKILL");
249
+ yield* Effect.sleep("100 millis");
250
+ }
251
+ });
252
+
253
+ interface ServerHandle {
254
+ ready: Promise<void>;
255
+ shutdown: () => Promise<void>;
256
+ }
257
+
258
+ interface ServerInput {
259
+ config: RuntimeConfig;
260
+ }
261
+
262
+ const patchConsole = (name: string, callbacks: ProcessCallbacks): (() => void) => {
263
+ const originalLog = console.log;
264
+ const originalError = console.error;
265
+ const originalWarn = console.warn;
266
+ const originalInfo = console.info;
267
+
268
+ const formatArgs = (args: unknown[]): string => {
269
+ return args
270
+ .map((arg) => (typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)))
271
+ .join(" ");
272
+ };
273
+
274
+ console.log = (...args: unknown[]) => {
275
+ callbacks.onLog(name, formatArgs(args), false);
276
+ };
277
+ console.error = (...args: unknown[]) => {
278
+ callbacks.onLog(name, formatArgs(args), true);
279
+ };
280
+ console.warn = (...args: unknown[]) => {
281
+ callbacks.onLog(name, formatArgs(args), false);
282
+ };
283
+ console.info = (...args: unknown[]) => {
284
+ callbacks.onLog(name, formatArgs(args), false);
285
+ };
286
+
287
+ return () => {
288
+ console.log = originalLog;
289
+ console.error = originalError;
290
+ console.warn = originalWarn;
291
+ console.info = originalInfo;
292
+ };
293
+ };
294
+
295
+ export const spawnRemoteHost = (
296
+ config: DevProcess,
297
+ callbacks: ProcessCallbacks,
298
+ runtimeConfig: RuntimeConfig,
299
+ ) =>
300
+ Effect.gen(function* () {
301
+ const remoteUrl = config.env?.HOST_REMOTE_URL;
302
+ if (!remoteUrl) {
303
+ return yield* Effect.fail(new Error("HOST_REMOTE_URL not provided for remote host"));
304
+ }
305
+
306
+ if (config.env) {
307
+ for (const [key, value] of Object.entries(config.env)) {
308
+ process.env[key] = value;
309
+ }
310
+ }
311
+
312
+ callbacks.onStatus(config.name, "starting");
313
+ callbacks.onLog(config.name, `Remote: ${remoteUrl}`);
314
+ const restoreConsole = patchConsole(config.name, callbacks);
315
+ callbacks.onLog(config.name, "Loading Module Federation runtime...");
316
+
317
+ const mfRuntime = yield* Effect.tryPromise({
318
+ try: () => import("@module-federation/enhanced/runtime"),
319
+ catch: (e) => new Error(`Failed to load MF runtime: ${e}`),
320
+ });
321
+
322
+ const mfCore = yield* Effect.tryPromise({
323
+ try: () => import("@module-federation/runtime-core"),
324
+ catch: (e) => new Error(`Failed to load MF core: ${e}`),
325
+ });
326
+
327
+ let mf = mfRuntime.getInstance();
328
+ if (!mf) {
329
+ mf = mfRuntime.createInstance({ name: "cli-host", remotes: [] });
330
+ mfCore.setGlobalFederationInstance(mf);
331
+ }
332
+ patchManifestFetchForSsrPublicPath(mf as any);
333
+
334
+ const baseUrl = remoteUrl
335
+ .replace(/\/remoteEntry\.js$/, "")
336
+ .replace(/\/mf-manifest\.json$/, "")
337
+ .replace(/\/$/, "");
338
+ const remoteEntryUrl = `${baseUrl}/remoteEntry.js`;
339
+ const manifestUrl = `${baseUrl}/mf-manifest.json`;
340
+
341
+ const entryUrl = yield* Effect.tryPromise({
342
+ try: async () => {
343
+ try {
344
+ const res = await fetch(manifestUrl);
345
+ if (!res.ok) return remoteEntryUrl;
346
+ const json = (await res.json()) as Record<string, unknown>;
347
+ if (
348
+ json &&
349
+ typeof json === "object" &&
350
+ "metaData" in json &&
351
+ "exposes" in json &&
352
+ "shared" in json
353
+ ) {
354
+ return manifestUrl;
355
+ }
356
+ } catch {}
357
+ return remoteEntryUrl;
358
+ },
359
+ catch: () => remoteEntryUrl,
360
+ });
361
+
362
+ (mf as any).registerRemotes([{ name: "host", entry: entryUrl }]);
363
+ callbacks.onLog(config.name, `Loading host from ${entryUrl}...`);
364
+
365
+ const hostModule = yield* Effect.tryPromise({
366
+ try: () =>
367
+ (mf as any).loadRemote("host/Server") as Promise<{
368
+ runServer: (input: ServerInput) => ServerHandle;
369
+ }>,
370
+ catch: (e) => new Error(`Failed to load host module: ${e}`),
371
+ });
372
+
373
+ if (!hostModule?.runServer) {
374
+ return yield* Effect.fail(new Error("Host module does not export runServer function"));
375
+ }
376
+
377
+ callbacks.onLog(config.name, "Starting server...");
378
+ const serverHandle = hostModule.runServer({ config: runtimeConfig });
379
+ yield* Effect.tryPromise({
380
+ try: () => serverHandle.ready,
381
+ catch: (e) => new Error(`Server failed to start: ${e}`),
382
+ });
383
+
384
+ callbacks.onStatus(config.name, "ready");
385
+
386
+ return {
387
+ name: config.name,
388
+ pid: process.pid,
389
+ kill: async () => {
390
+ callbacks.onLog(config.name, "Shutting down remote host...");
391
+ restoreConsole();
392
+ await serverHandle.shutdown();
393
+ },
394
+ waitForReady: Effect.succeed(undefined),
395
+ waitForExit: Effect.never,
396
+ } satisfies ProcessHandle;
397
+ });
398
+
399
+ export const spawnDevProcess = (
400
+ config: DevProcess,
401
+ callbacks: ProcessCallbacks,
402
+ runtimeConfig?: RuntimeConfig,
403
+ registry?: ProcessRegistry,
404
+ ) =>
405
+ Effect.gen(function* () {
406
+ let configDir: string;
407
+ try {
408
+ configDir = getProjectRoot();
409
+ } catch {
410
+ configDir = process.cwd();
411
+ }
412
+ const fullCwd = config.cwd.startsWith("/") ? config.cwd : `${configDir}/${config.cwd}`;
413
+ const readyDeferred = yield* Deferred.make<void, Error>();
414
+ const statusRef = yield* Ref.make<ProcessStatus>("starting");
415
+
416
+ callbacks.onStatus(config.name, "starting");
417
+
418
+ const envVars: Record<string, string> = {
419
+ ...(process.env as Record<string, string>),
420
+ ...config.env,
421
+ FORCE_COLOR: "1",
422
+ ...(config.port > 0 ? { PORT: String(config.port) } : {}),
423
+ };
424
+
425
+ if (runtimeConfig && config.name === "host") {
426
+ envVars.BOS_RUNTIME_CONFIG = JSON.stringify(runtimeConfig);
427
+ }
428
+
429
+ const proc = Bun.spawn({
430
+ cmd: [config.command, ...config.args],
431
+ cwd: fullCwd,
432
+ env: envVars,
433
+ stdio: ["inherit", "pipe", "pipe"],
434
+ });
435
+
436
+ const markReady = Effect.gen(function* () {
437
+ const currentStatus = yield* Ref.get(statusRef);
438
+ if (currentStatus === "ready" || currentStatus === "error") return;
439
+ yield* Ref.set(statusRef, "ready");
440
+ callbacks.onStatus(config.name, "ready");
441
+ yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);
442
+ });
443
+
444
+ // Prefer probe-based readiness to avoid brittle log regexes.
445
+ // This is best-effort and complements log detection.
446
+ if (config.port > 0) {
447
+ const readinessPath =
448
+ config.name === "host" ? "/health" : config.name === "ui-ssr" ? "/" : "/remoteEntry.js";
449
+ const url = `http://127.0.0.1:${config.port}${readinessPath}`;
450
+
451
+ yield* Effect.fork(
452
+ Effect.gen(function* () {
453
+ const deadline = Date.now() + 90_000;
454
+ while (Date.now() < deadline) {
455
+ const status = yield* Ref.get(statusRef);
456
+ if (status === "ready" || status === "error") return;
457
+ const ok = url
458
+ ? yield* Effect.tryPromise({
459
+ try: () => probeHttpOk(url),
460
+ catch: () => false,
461
+ })
462
+ : yield* Effect.tryPromise({
463
+ try: () => probeTcpOpen(config.port),
464
+ catch: () => false,
465
+ });
466
+ if (ok) {
467
+ yield* markReady;
468
+ return;
469
+ }
470
+ yield* Effect.sleep("200 millis");
471
+ }
472
+ }),
473
+ );
474
+ }
475
+
476
+ if (registry && proc.pid) {
477
+ yield* registry.track({
478
+ pid: proc.pid,
479
+ name: config.name,
480
+ port: config.port,
481
+ startedAt: Date.now(),
482
+ command: [config.command, ...config.args].join(" "),
483
+ });
484
+ }
485
+
486
+ yield* Effect.fork(
487
+ Effect.promise(() => proc.exited).pipe(
488
+ Effect.andThen((code) =>
489
+ Effect.gen(function* () {
490
+ if (registry && proc.pid) {
491
+ yield* registry.untrack(proc.pid).pipe(Effect.ignore);
492
+ }
493
+ const currentStatus = yield* Ref.get(statusRef);
494
+ if (currentStatus === "ready") return;
495
+ callbacks.onLog(config.name, `Process exited before ready (exit code: ${code})`, true);
496
+ yield* Ref.set(statusRef, "error");
497
+ callbacks.onStatus(config.name, "error");
498
+ yield* Deferred.fail(
499
+ readyDeferred,
500
+ new Error(`Process exited before ready: ${config.name}`),
501
+ ).pipe(Effect.ignore);
502
+ }),
503
+ ),
504
+ ),
505
+ );
506
+
507
+ const handleLine = (line: string, isStderr: boolean) =>
508
+ Effect.gen(function* () {
509
+ if (!line.trim()) return;
510
+
511
+ callbacks.onLog(config.name, line, isStderr);
512
+
513
+ const currentStatus = yield* Ref.get(statusRef);
514
+ if (currentStatus === "ready") return;
515
+
516
+ const detected = detectStatus(line, config);
517
+ if (detected) {
518
+ yield* Ref.set(statusRef, detected.status);
519
+ callbacks.onStatus(config.name, detected.status);
520
+ if (detected.status === "ready" || detected.status === "error") {
521
+ if (detected.status === "ready") {
522
+ yield* Deferred.succeed(readyDeferred, undefined).pipe(Effect.ignore);
523
+ } else {
524
+ yield* Deferred.fail(readyDeferred, new Error(`Process failed: ${config.name}`)).pipe(
525
+ Effect.ignore,
526
+ );
527
+ }
528
+ }
529
+ }
530
+ });
531
+
532
+ const decoder = new TextDecoder();
533
+
534
+ const stdoutFiber = yield* Effect.fork(
535
+ Effect.async<void>((resume) => {
536
+ if (!proc.stdout) {
537
+ resume(Effect.void);
538
+ return;
539
+ }
540
+ const reader = proc.stdout.getReader();
541
+ let buffer = "";
542
+
543
+ const pump = (): Promise<void> =>
544
+ reader.read().then(({ done, value }) => {
545
+ if (done) {
546
+ if (buffer) {
547
+ Effect.runSync(handleLine(buffer, false));
548
+ }
549
+ return;
550
+ }
551
+ buffer += decoder
552
+ .decode(value, { stream: true })
553
+ .replace(/\r\n/g, "\n")
554
+ .replace(/\r/g, "\n");
555
+ const lines = buffer.split("\n");
556
+ buffer = lines.pop() ?? "";
557
+ for (const line of lines) {
558
+ Effect.runSync(handleLine(line, false));
559
+ }
560
+ return pump();
561
+ });
562
+
563
+ pump().then(() => resume(Effect.void));
564
+ }),
565
+ );
566
+
567
+ const stderrFiber = yield* Effect.fork(
568
+ Effect.async<void>((resume) => {
569
+ if (!proc.stderr) {
570
+ resume(Effect.void);
571
+ return;
572
+ }
573
+ const reader = proc.stderr.getReader();
574
+ let buffer = "";
575
+
576
+ const pump = (): Promise<void> =>
577
+ reader.read().then(({ done, value }) => {
578
+ if (done) {
579
+ if (buffer) {
580
+ Effect.runSync(handleLine(buffer, true));
581
+ }
582
+ return;
583
+ }
584
+ buffer += decoder
585
+ .decode(value, { stream: true })
586
+ .replace(/\r\n/g, "\n")
587
+ .replace(/\r/g, "\n");
588
+ const lines = buffer.split("\n");
589
+ buffer = lines.pop() ?? "";
590
+ for (const line of lines) {
591
+ Effect.runSync(handleLine(line, true));
592
+ }
593
+ return pump();
594
+ });
595
+
596
+ pump().then(() => resume(Effect.void));
597
+ }),
598
+ );
599
+
600
+ const handle: ProcessHandle = {
601
+ name: config.name,
602
+ pid: proc.pid,
603
+ kill: async () => {
604
+ const pid = proc.pid;
605
+ if (pid) {
606
+ await Effect.runPromise(killProcessTree(pid));
607
+ } else {
608
+ proc.kill("SIGTERM");
609
+ await new Promise((r) => setTimeout(r, 100));
610
+ try {
611
+ proc.kill("SIGKILL");
612
+ } catch {}
613
+ }
614
+ },
615
+ waitForReady: Deferred.await(readyDeferred),
616
+ waitForExit: Effect.gen(function* () {
617
+ yield* Fiber.joinAll([stdoutFiber, stderrFiber]);
618
+ return yield* Effect.promise(() => proc.exited);
619
+ }),
620
+ };
621
+
622
+ return handle;
623
+ });
624
+
625
+ export const makeDevProcess = (
626
+ pkg: string,
627
+ env: Record<string, string> | undefined,
628
+ callbacks: ProcessCallbacks,
629
+ portOverride?: number,
630
+ bosConfig?: BosConfig,
631
+ runtimeConfig?: RuntimeConfig,
632
+ registry?: ProcessRegistry,
633
+ ) =>
634
+ Effect.gen(function* () {
635
+ const config = getProcessConfig(pkg, env, portOverride, bosConfig, runtimeConfig);
636
+ if (!config) {
637
+ return yield* Effect.fail(new Error(`Unknown package: ${pkg}`));
638
+ }
639
+
640
+ if (pkg === "host" && runtimeConfig) {
641
+ if (env?.HOST_SOURCE === "remote") {
642
+ return yield* spawnRemoteHost(config, callbacks, runtimeConfig);
643
+ }
644
+ return yield* spawnDevProcess(config, callbacks, runtimeConfig, registry);
645
+ }
646
+
647
+ return yield* spawnDevProcess(config, callbacks, runtimeConfig, registry);
648
+ });