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,85 @@
1
+ import { existsSync } from "node:fs";
2
+ import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+
5
+ export interface LogEntry {
6
+ timestamp: number;
7
+ source: string;
8
+ line: string;
9
+ isError?: boolean;
10
+ }
11
+
12
+ export interface DevLogger {
13
+ logFile: string;
14
+ latestFile: string;
15
+ write: (entry: LogEntry) => Promise<void>;
16
+ readLatest: (opts?: { tail?: number }) => Promise<string>;
17
+ }
18
+
19
+ export function getBosDir(configDir: string): string {
20
+ return join(configDir, ".bos");
21
+ }
22
+
23
+ export function getLogsDir(configDir: string): string {
24
+ return join(getBosDir(configDir), "logs");
25
+ }
26
+
27
+ function formatLogLine(entry: LogEntry): string {
28
+ const ts = new Date(entry.timestamp).toISOString();
29
+ const prefix = entry.isError ? "ERR" : "OUT";
30
+ return `[${ts}] [${entry.source}] [${prefix}] ${entry.line}`;
31
+ }
32
+
33
+ export async function createDevLogger(configDir: string, description: string): Promise<DevLogger> {
34
+ const dir = getLogsDir(configDir);
35
+ if (!existsSync(dir)) {
36
+ await mkdir(dir, { recursive: true });
37
+ }
38
+
39
+ const now = new Date();
40
+ const ts = now.toISOString().replace(/[:.]/g, "-").slice(0, 19);
41
+ const logFile = join(dir, `dev-${ts}.log`);
42
+ const latestFile = join(dir, "dev-latest.log");
43
+
44
+ const header =
45
+ `# everything-dev dev session: ${description}\n` + `# Started: ${now.toISOString()}\n\n`;
46
+ // Overwrite each run so dev-latest.log is always actionable.
47
+ await writeFile(logFile, header, "utf8");
48
+ await writeFile(latestFile, header, "utf8");
49
+
50
+ let chain = Promise.resolve();
51
+ const enqueue = (fn: () => Promise<void>) => {
52
+ chain = chain.then(fn, fn);
53
+ return chain;
54
+ };
55
+
56
+ return {
57
+ logFile,
58
+ latestFile,
59
+ write: (entry) =>
60
+ enqueue(async () => {
61
+ const line = `${formatLogLine(entry)}\n`;
62
+ await appendFile(logFile, line);
63
+ await appendFile(latestFile, line);
64
+ }),
65
+ readLatest: async (opts) => {
66
+ const text = await readFile(latestFile, "utf8").catch(() => "");
67
+ const tail = opts?.tail;
68
+ if (!tail || tail <= 0) return text;
69
+ const lines = text.split("\n");
70
+ return lines.slice(Math.max(0, lines.length - tail)).join("\n");
71
+ },
72
+ };
73
+ }
74
+
75
+ export async function readDevLatestLog(
76
+ configDir: string,
77
+ opts?: { tail?: number },
78
+ ): Promise<string> {
79
+ const latestFile = join(getLogsDir(configDir), "dev-latest.log");
80
+ const text = await readFile(latestFile, "utf8").catch(() => "");
81
+ const tail = opts?.tail;
82
+ if (!tail || tail <= 0) return text;
83
+ const lines = text.split("\n");
84
+ return lines.slice(Math.max(0, lines.length - tail)).join("\n");
85
+ }
@@ -0,0 +1,318 @@
1
+ import { Effect } from "effect";
2
+ import {
3
+ type DevViewHandle,
4
+ type LogEntry,
5
+ type ProcessState,
6
+ renderDevView,
7
+ } from "./components/dev-view";
8
+ import { renderStreamingView } from "./components/streaming-view";
9
+ import { getProjectRoot } from "./config";
10
+ import { createDevLogger } from "./dev-logs";
11
+ import {
12
+ getProcessConfig,
13
+ makeDevProcess,
14
+ type ProcessCallbacks,
15
+ type ProcessHandle,
16
+ } from "./orchestrator";
17
+ import { makeProcessRegistry } from "./process-registry";
18
+ import type { BosConfig, RuntimeConfig, SourceMode } from "./types";
19
+
20
+ export interface AppConfig {
21
+ host: SourceMode;
22
+ ui: SourceMode;
23
+ api: SourceMode;
24
+ proxy?: boolean;
25
+ ssr?: boolean;
26
+ }
27
+
28
+ export interface AppOrchestrator {
29
+ packages: string[];
30
+ env: Record<string, string>;
31
+ description: string;
32
+ appConfig: AppConfig;
33
+ bosConfig: BosConfig;
34
+ runtimeConfig: RuntimeConfig;
35
+ port?: number;
36
+ interactive?: boolean;
37
+ noLogs?: boolean;
38
+ }
39
+
40
+ const LOG_NOISE_PATTERNS = [
41
+ /\[ Federation Runtime \] Version .* from (host|ui) of shared singleton module/,
42
+ /Executing an Effect versioned \d+\.\d+\.\d+ with a Runtime of version/,
43
+ /you may want to dedupe the effect dependencies/,
44
+ ];
45
+
46
+ const SSR_LOG_ALLOWLIST = [
47
+ /\bready\s+built in\b/i,
48
+ /\bcompiled\b.*successfully/i,
49
+ /\berror\b/i,
50
+ /\bfailed\b/i,
51
+ /\bexception\b/i,
52
+ ];
53
+
54
+ const shouldDisplayLog = (source: string, line: string, isError?: boolean): boolean => {
55
+ if (process.env.DEBUG === "true" || process.env.DEBUG === "1") return true;
56
+ if (source === "ui-ssr") {
57
+ if (isError) return true;
58
+ return SSR_LOG_ALLOWLIST.some((pattern) => pattern.test(line));
59
+ }
60
+ return !LOG_NOISE_PATTERNS.some((pattern) => pattern.test(line));
61
+ };
62
+
63
+ const isInteractiveSupported = (): boolean => {
64
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
65
+ };
66
+
67
+ const STARTUP_ORDER = ["ui-ssr", "ui", "api", "plugin", "host-build", "host"];
68
+
69
+ const sortByOrder = (packages: string[]): string[] => {
70
+ return [...packages].sort((a, b) => {
71
+ const aIdx = a.startsWith("plugin:")
72
+ ? STARTUP_ORDER.indexOf("plugin")
73
+ : STARTUP_ORDER.indexOf(a);
74
+ const bIdx = b.startsWith("plugin:")
75
+ ? STARTUP_ORDER.indexOf("plugin")
76
+ : STARTUP_ORDER.indexOf(b);
77
+ if (aIdx === -1 && bIdx === -1) return 0;
78
+ if (aIdx === -1) return 1;
79
+ if (bIdx === -1) return -1;
80
+ return aIdx - bIdx;
81
+ });
82
+ };
83
+
84
+ function formatLogLine(entry: LogEntry): string {
85
+ const ts = new Date(entry.timestamp).toISOString();
86
+ const prefix = entry.isError ? "ERR" : "OUT";
87
+ return `[${ts}] [${entry.source}] [${prefix}] ${entry.line}`;
88
+ }
89
+
90
+ export const runDevSession = (
91
+ orchestrator: AppOrchestrator,
92
+ onCleanupReady?: (cleanup: () => Promise<void>) => void,
93
+ ) =>
94
+ Effect.gen(function* () {
95
+ const configDir = getProjectRoot();
96
+ const orderedPackages = sortByOrder(orchestrator.packages);
97
+ const initialProcesses: ProcessState[] = orderedPackages.map((pkg) => {
98
+ const portOverride = pkg === "host" ? orchestrator.port : undefined;
99
+ const config = getProcessConfig(
100
+ pkg,
101
+ undefined,
102
+ portOverride,
103
+ orchestrator.bosConfig,
104
+ orchestrator.runtimeConfig,
105
+ );
106
+ const source =
107
+ pkg === "host"
108
+ ? orchestrator.appConfig.host
109
+ : pkg === "ui"
110
+ ? orchestrator.appConfig.ui
111
+ : pkg === "api"
112
+ ? orchestrator.appConfig.api
113
+ : undefined;
114
+ return {
115
+ name: pkg,
116
+ status: "pending" as const,
117
+ port: config?.port ?? 0,
118
+ source,
119
+ };
120
+ });
121
+
122
+ const registry = yield* makeProcessRegistry(configDir);
123
+ yield* registry.killAll().pipe(Effect.ignore);
124
+
125
+ const logger = yield* Effect.promise(() =>
126
+ createDevLogger(configDir, orchestrator.description),
127
+ );
128
+ const handles: ProcessHandle[] = [];
129
+ const allLogs: LogEntry[] = [];
130
+ let view: DevViewHandle | null = null;
131
+ let shuttingDown = false;
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
+ };
160
+
161
+ const cleanup = async (showLogs = false) => {
162
+ if (shuttingDown) return;
163
+ shuttingDown = true;
164
+ view?.unmount();
165
+ await killAll();
166
+ if (showLogs) {
167
+ await exportLogs();
168
+ }
169
+ };
170
+
171
+ onCleanupReady?.(cleanup);
172
+
173
+ const useInteractive = orchestrator.interactive ?? isInteractiveSupported();
174
+ view = useInteractive
175
+ ? renderDevView(
176
+ initialProcesses,
177
+ orchestrator.description,
178
+ orchestrator.env,
179
+ () => cleanup(false),
180
+ () => cleanup(true),
181
+ )
182
+ : renderStreamingView(initialProcesses, orchestrator.description, orchestrator.env, () =>
183
+ cleanup(false),
184
+ );
185
+
186
+ const callbacks: ProcessCallbacks = {
187
+ onStatus: (name, status, message) => {
188
+ view?.updateProcess(name, status, message);
189
+ },
190
+ onLog: (name, line, isError) => {
191
+ const entry: LogEntry = {
192
+ id: `${Date.now()}-${allLogs.length + 1}`,
193
+ source: name,
194
+ line,
195
+ timestamp: Date.now(),
196
+ isError,
197
+ };
198
+ allLogs.push(entry);
199
+ if (shouldDisplayLog(name, line, isError)) {
200
+ view?.addLog(name, line, isError);
201
+ }
202
+ if (!orchestrator.noLogs) {
203
+ void logger.write(entry);
204
+ }
205
+ },
206
+ };
207
+
208
+ const startProcess = (pkg: string) => {
209
+ const portOverride = pkg === "host" ? orchestrator.port : undefined;
210
+ return makeDevProcess(
211
+ pkg,
212
+ orchestrator.env,
213
+ callbacks,
214
+ portOverride,
215
+ orchestrator.bosConfig,
216
+ orchestrator.runtimeConfig,
217
+ registry,
218
+ );
219
+ };
220
+
221
+ const startGroup = (packages: string[]) =>
222
+ Effect.forEach(packages, startProcess, { concurrency: "unbounded" });
223
+
224
+ const awaitReady = (pkg: string, handle: ProcessHandle) =>
225
+ Effect.race(
226
+ handle.waitForReady,
227
+ Effect.sleep("30 seconds").pipe(
228
+ Effect.andThen(
229
+ Effect.sync(() => {
230
+ callbacks.onLog(pkg, "Timeout waiting for ready, continuing...", true);
231
+ }),
232
+ ),
233
+ ),
234
+ );
235
+
236
+ const nonHostPackages = orderedPackages.filter((pkg) => pkg !== "host");
237
+ const hostPackages = orderedPackages.filter((pkg) => pkg === "host");
238
+
239
+ const nonHostHandles = yield* startGroup(nonHostPackages);
240
+ handles.push(...nonHostHandles);
241
+
242
+ yield* Effect.forEach(
243
+ nonHostHandles.map((handle, index) => ({
244
+ handle,
245
+ pkg: nonHostPackages[index] ?? handle.name,
246
+ })),
247
+ ({ handle, pkg }) => awaitReady(pkg, handle),
248
+ { concurrency: "unbounded" },
249
+ );
250
+
251
+ const hostHandles = yield* startGroup(hostPackages);
252
+ handles.push(...hostHandles);
253
+
254
+ yield* Effect.forEach(
255
+ hostHandles.map((handle, index) => ({ handle, pkg: hostPackages[index] ?? handle.name })),
256
+ ({ handle, pkg }) => awaitReady(pkg, handle),
257
+ { concurrency: "unbounded" },
258
+ );
259
+
260
+ yield* Effect.addFinalizer(() => Effect.promise(() => cleanup(false)));
261
+ yield* Effect.never;
262
+ });
263
+
264
+ export const startApp = (orchestrator: AppOrchestrator) => {
265
+ let activeCleanup: (() => Promise<void>) | null = null;
266
+
267
+ const program = Effect.scoped(
268
+ runDevSession(orchestrator, (cleanup) => {
269
+ activeCleanup = cleanup;
270
+ }),
271
+ ).pipe(
272
+ Effect.catchAll((e) =>
273
+ Effect.sync(() => {
274
+ if (e instanceof Error) {
275
+ console.error("App server error:", e.message);
276
+ if (e.stack) console.error(e.stack);
277
+ } else {
278
+ console.error("App server error:", e);
279
+ }
280
+ }),
281
+ ),
282
+ );
283
+
284
+ const handleSignal = async () => {
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", () => {
306
+ signalCount++;
307
+ if (signalCount > 1) {
308
+ forceExit();
309
+ return;
310
+ }
311
+ const timeout = setTimeout(forceExit, 5000);
312
+ void handleSignal().finally(() => {
313
+ clearTimeout(timeout);
314
+ });
315
+ });
316
+
317
+ void Effect.runPromise(program);
318
+ };
package/src/fastkv.ts ADDED
@@ -0,0 +1,153 @@
1
+ export type NetworkId = "mainnet" | "testnet";
2
+
3
+ interface FastKvEntry {
4
+ value: unknown;
5
+ }
6
+
7
+ interface FastKvListResponse {
8
+ entries?: Array<FastKvEntry | null>;
9
+ }
10
+
11
+ const FASTKV_TIMEOUT_MS = 10_000;
12
+
13
+ function getNetworkIdForAccount(accountId: string): NetworkId {
14
+ return accountId.endsWith(".testnet") ? "testnet" : "mainnet";
15
+ }
16
+
17
+ export function getFastKvBaseUrlForNetwork(network: NetworkId): string {
18
+ return network === "testnet"
19
+ ? process.env.REGISTRY_FASTKV_TESTNET_URL || "https://kv.test.fastnear.com"
20
+ : process.env.REGISTRY_FASTKV_MAINNET_URL || "https://kv.main.fastnear.com";
21
+ }
22
+
23
+ function getFastKvBaseUrlForAccount(accountId: string): string {
24
+ return getNetworkIdForAccount(accountId) === "testnet"
25
+ ? getFastKvBaseUrlForNetwork("testnet")
26
+ : getFastKvBaseUrlForNetwork("mainnet");
27
+ }
28
+
29
+ export function buildRegistryConfigUrl(accountId: string, gatewayId: string): string {
30
+ const baseUrl = getFastKvBaseUrlForAccount(accountId);
31
+ const namespace = getRegistryNamespaceForAccount(accountId);
32
+ const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId));
33
+ return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`;
34
+ }
35
+
36
+ export function buildRegistryConfigUrlForNetwork(
37
+ network: NetworkId,
38
+ accountId: string,
39
+ gatewayId: string,
40
+ ): string {
41
+ const baseUrl = getFastKvBaseUrlForNetwork(network);
42
+ const namespace = getRegistryNamespaceForNetwork(network);
43
+ const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId));
44
+ return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`;
45
+ }
46
+
47
+ export function getRegistryNamespaceForAccount(accountId: string): string {
48
+ return accountId.endsWith(".testnet")
49
+ ? process.env.REGISTRY_FASTKV_TESTNET_NAMESPACE || "dev.everything.near"
50
+ : process.env.REGISTRY_FASTKV_MAINNET_NAMESPACE || accountId;
51
+ }
52
+
53
+ export function getRegistryNamespaceForNetwork(network: NetworkId): string {
54
+ return network === "testnet"
55
+ ? process.env.REGISTRY_FASTKV_TESTNET_NAMESPACE || "dev.everything.near"
56
+ : process.env.REGISTRY_FASTKV_MAINNET_NAMESPACE || "dev.everything.near";
57
+ }
58
+
59
+ function getRegistryConfigKey(
60
+ accountId: string,
61
+ gatewayId: string,
62
+ pathSegments: string[] = [],
63
+ ): string {
64
+ const suffix =
65
+ pathSegments.length > 0
66
+ ? `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`
67
+ : "";
68
+ return `apps/${accountId}/${gatewayId}${suffix}/bos.config.json`;
69
+ }
70
+
71
+ function parseBosUrl(bosUrl: string): {
72
+ accountId: string;
73
+ gatewayId: string;
74
+ pathSegments: string[];
75
+ } {
76
+ const match = bosUrl.match(/^bos:\/\/([^/]+)\/(.+)$/);
77
+ if (!match?.[1] || !match[2]) {
78
+ throw new Error(`Invalid BOS URL: ${bosUrl}`);
79
+ }
80
+
81
+ const pathSegments = match[2]
82
+ .split("/")
83
+ .filter(Boolean)
84
+ .map((segment) => decodeURIComponent(segment));
85
+ if (pathSegments.length === 0) {
86
+ throw new Error(`Invalid BOS URL: ${bosUrl}`);
87
+ }
88
+
89
+ const [gatewayId, ...pathSegmentsTail] = pathSegments;
90
+ if (!gatewayId) {
91
+ throw new Error(`Invalid BOS URL: ${bosUrl}`);
92
+ }
93
+
94
+ return {
95
+ accountId: match[1],
96
+ gatewayId,
97
+ pathSegments: pathSegmentsTail,
98
+ };
99
+ }
100
+
101
+ export async function fetchBosConfigFromFastKv<T>(bosUrl: string): Promise<T> {
102
+ const { accountId, gatewayId, pathSegments } = parseBosUrl(bosUrl);
103
+ const payload = await fetchJson<FastKvListResponse>(
104
+ `${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId))}/${encodeURIComponent(accountId)}`,
105
+ {
106
+ method: "POST",
107
+ body: JSON.stringify({
108
+ key: getRegistryConfigKey(accountId, gatewayId, pathSegments),
109
+ limit: 1,
110
+ }),
111
+ },
112
+ );
113
+ const value = payload?.entries?.find(Boolean)?.value;
114
+
115
+ if (!value) {
116
+ throw new Error(`No config found for ${bosUrl}`);
117
+ }
118
+
119
+ if (typeof value === "string") {
120
+ return JSON.parse(value) as T;
121
+ }
122
+
123
+ if (typeof value !== "object") {
124
+ throw new Error(`Invalid config value for ${bosUrl}`);
125
+ }
126
+
127
+ return value as T;
128
+ }
129
+
130
+ async function fetchJson<T>(url: string, init?: RequestInit): Promise<T | null> {
131
+ const controller = new AbortController();
132
+ const timeout = setTimeout(() => controller.abort(), FASTKV_TIMEOUT_MS);
133
+
134
+ try {
135
+ const response = await fetch(url, {
136
+ ...init,
137
+ headers: {
138
+ accept: "application/json",
139
+ "content-type": "application/json",
140
+ ...(init?.headers ?? {}),
141
+ },
142
+ signal: controller.signal,
143
+ });
144
+
145
+ if (!response.ok) {
146
+ return null;
147
+ }
148
+
149
+ return (await response.json()) as T;
150
+ } finally {
151
+ clearTimeout(timeout);
152
+ }
153
+ }
@@ -0,0 +1,43 @@
1
+ import { verifySriForUrl } from "./integrity";
2
+ import { ensureNodeRuntimePlugin, loadRemoteModule, registerRemote } from "./mf";
3
+ import type { RuntimeConfig } from "./types";
4
+ import type { RouterModule } from "./ui/types";
5
+
6
+ export type { RouterModule };
7
+
8
+ export async function loadRouterModule(config: RuntimeConfig): Promise<RouterModule> {
9
+ const isLocalDev = config.ui.source === "local";
10
+ const ssrUrl = config.ui.ssrUrl ?? (isLocalDev ? config.ui.url : undefined);
11
+
12
+ if (!ssrUrl) {
13
+ if (!isLocalDev) {
14
+ throw new Error(
15
+ "SSR URL not configured in production. Set app.ui.ssr in bos.config.json to enable SSR.",
16
+ );
17
+ }
18
+
19
+ throw new Error(
20
+ "SSR URL not configured. In local dev, set app.ui.ssr or use a UI package with SSR support.",
21
+ );
22
+ }
23
+
24
+ const ssrEntryUrl = `${ssrUrl.replace(/\/$/, "")}/remoteEntry.server.js`;
25
+
26
+ if (config.ui.ssrIntegrity) {
27
+ await verifySriForUrl(ssrEntryUrl, config.ui.ssrIntegrity);
28
+ }
29
+
30
+ await ensureNodeRuntimePlugin();
31
+ await registerRemote({
32
+ name: config.ui.name,
33
+ entry: ssrEntryUrl,
34
+ type: "script",
35
+ });
36
+
37
+ const loadedModule = await loadRemoteModule<any>(`${config.ui.name}/Router`, { from: "build" });
38
+ if (!loadedModule) {
39
+ throw new Error(`Module not found: ${config.ui.name}/Router`);
40
+ }
41
+
42
+ return loadedModule.default as RouterModule;
43
+ }