everything-dev 0.3.3 → 1.3.3

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 (313) 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 +287 -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 +279 -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/internal/manifest-normalizer.cjs +140 -0
  123. package/dist/internal/manifest-normalizer.cjs.map +1 -0
  124. package/dist/internal/manifest-normalizer.mjs +138 -0
  125. package/dist/internal/manifest-normalizer.mjs.map +1 -0
  126. package/dist/mf.cjs +77 -0
  127. package/dist/mf.cjs.map +1 -0
  128. package/dist/mf.d.cts +19 -0
  129. package/dist/mf.d.cts.map +1 -0
  130. package/dist/mf.d.mts +19 -0
  131. package/dist/mf.d.mts.map +1 -0
  132. package/dist/mf.mjs +71 -0
  133. package/dist/mf.mjs.map +1 -0
  134. package/dist/near-cli.cjs +196 -0
  135. package/dist/near-cli.cjs.map +1 -0
  136. package/dist/near-cli.mjs +193 -0
  137. package/dist/near-cli.mjs.map +1 -0
  138. package/dist/network.cjs +9 -0
  139. package/dist/network.cjs.map +1 -0
  140. package/dist/network.mjs +8 -0
  141. package/dist/network.mjs.map +1 -0
  142. package/dist/orchestrator.cjs +441 -0
  143. package/dist/orchestrator.cjs.map +1 -0
  144. package/dist/orchestrator.d.cts +40 -0
  145. package/dist/orchestrator.d.cts.map +1 -0
  146. package/dist/orchestrator.d.mts +40 -0
  147. package/dist/orchestrator.d.mts.map +1 -0
  148. package/dist/orchestrator.mjs +436 -0
  149. package/dist/orchestrator.mjs.map +1 -0
  150. package/dist/plugin.cjs +830 -0
  151. package/dist/plugin.cjs.map +1 -0
  152. package/dist/plugin.d.cts +347 -0
  153. package/dist/plugin.d.cts.map +1 -0
  154. package/dist/plugin.d.mts +348 -0
  155. package/dist/plugin.d.mts.map +1 -0
  156. package/dist/plugin.mjs +827 -0
  157. package/dist/plugin.mjs.map +1 -0
  158. package/dist/process-registry.cjs +120 -0
  159. package/dist/process-registry.cjs.map +1 -0
  160. package/dist/process-registry.d.cts +25 -0
  161. package/dist/process-registry.d.cts.map +1 -0
  162. package/dist/process-registry.d.mts +25 -0
  163. package/dist/process-registry.d.mts.map +1 -0
  164. package/dist/process-registry.mjs +119 -0
  165. package/dist/process-registry.mjs.map +1 -0
  166. package/dist/sdk.cjs +61 -0
  167. package/dist/sdk.d.cts +5 -0
  168. package/dist/sdk.d.mts +5 -0
  169. package/dist/sdk.mjs +6 -0
  170. package/dist/shared.cjs +143 -0
  171. package/dist/shared.cjs.map +1 -0
  172. package/dist/shared.d.cts +33 -0
  173. package/dist/shared.d.cts.map +1 -0
  174. package/dist/shared.d.mts +33 -0
  175. package/dist/shared.d.mts.map +1 -0
  176. package/dist/shared.mjs +140 -0
  177. package/dist/shared.mjs.map +1 -0
  178. package/dist/types.cjs +160 -0
  179. package/dist/types.cjs.map +1 -0
  180. package/dist/types.d.cts +269 -0
  181. package/dist/types.d.cts.map +1 -0
  182. package/dist/types.d.mts +269 -0
  183. package/dist/types.d.mts.map +1 -0
  184. package/dist/types.mjs +144 -0
  185. package/dist/types.mjs.map +1 -0
  186. package/dist/ui/head.cjs +67 -0
  187. package/dist/ui/head.cjs.map +1 -0
  188. package/dist/ui/head.d.cts +19 -0
  189. package/dist/ui/head.d.cts.map +1 -0
  190. package/dist/ui/head.d.mts +19 -0
  191. package/dist/ui/head.d.mts.map +1 -0
  192. package/dist/ui/head.mjs +61 -0
  193. package/dist/ui/head.mjs.map +1 -0
  194. package/dist/ui/index.cjs +32 -0
  195. package/dist/ui/index.d.cts +7 -0
  196. package/dist/ui/index.d.mts +7 -0
  197. package/dist/ui/index.mjs +6 -0
  198. package/dist/ui/metadata.cjs +106 -0
  199. package/dist/ui/metadata.cjs.map +1 -0
  200. package/dist/ui/metadata.d.cts +35 -0
  201. package/dist/ui/metadata.d.cts.map +1 -0
  202. package/dist/ui/metadata.d.mts +35 -0
  203. package/dist/ui/metadata.d.mts.map +1 -0
  204. package/dist/ui/metadata.mjs +100 -0
  205. package/dist/ui/metadata.mjs.map +1 -0
  206. package/dist/ui/router.cjs +56 -0
  207. package/dist/ui/router.cjs.map +1 -0
  208. package/dist/ui/router.d.cts +11 -0
  209. package/dist/ui/router.d.cts.map +1 -0
  210. package/dist/ui/router.d.mts +11 -0
  211. package/dist/ui/router.d.mts.map +1 -0
  212. package/dist/ui/router.mjs +51 -0
  213. package/dist/ui/router.mjs.map +1 -0
  214. package/dist/ui/runtime.cjs +65 -0
  215. package/dist/ui/runtime.cjs.map +1 -0
  216. package/dist/ui/runtime.d.cts +29 -0
  217. package/dist/ui/runtime.d.cts.map +1 -0
  218. package/dist/ui/runtime.d.mts +29 -0
  219. package/dist/ui/runtime.d.mts.map +1 -0
  220. package/dist/ui/runtime.mjs +53 -0
  221. package/dist/ui/runtime.mjs.map +1 -0
  222. package/dist/ui/types.cjs +0 -0
  223. package/dist/ui/types.d.cts +52 -0
  224. package/dist/ui/types.d.cts.map +1 -0
  225. package/dist/ui/types.d.mts +52 -0
  226. package/dist/ui/types.d.mts.map +1 -0
  227. package/dist/ui/types.mjs +1 -0
  228. package/dist/utils/banner.cjs +24 -0
  229. package/dist/utils/banner.cjs.map +1 -0
  230. package/dist/utils/banner.mjs +23 -0
  231. package/dist/utils/banner.mjs.map +1 -0
  232. package/dist/utils/linkify.cjs +15 -0
  233. package/dist/utils/linkify.cjs.map +1 -0
  234. package/dist/utils/linkify.mjs +14 -0
  235. package/dist/utils/linkify.mjs.map +1 -0
  236. package/dist/utils/run.cjs +40 -0
  237. package/dist/utils/run.cjs.map +1 -0
  238. package/dist/utils/run.mjs +39 -0
  239. package/dist/utils/run.mjs.map +1 -0
  240. package/dist/utils/theme.cjs +44 -0
  241. package/dist/utils/theme.cjs.map +1 -0
  242. package/dist/utils/theme.mjs +37 -0
  243. package/dist/utils/theme.mjs.map +1 -0
  244. package/package.json +269 -80
  245. package/src/api-contract.ts +309 -0
  246. package/src/api.ts +181 -0
  247. package/src/app.ts +346 -0
  248. package/src/cli/catalog.ts +49 -0
  249. package/src/cli/help.ts +13 -0
  250. package/src/cli/init.ts +386 -0
  251. package/src/cli/parse.ts +130 -0
  252. package/src/cli/prompts.ts +64 -0
  253. package/src/cli.ts +203 -1507
  254. package/src/components/dev-view.tsx +307 -255
  255. package/src/components/streaming-view.ts +164 -128
  256. package/src/config.ts +462 -532
  257. package/src/contract.meta.ts +96 -0
  258. package/src/contract.ts +164 -561
  259. package/src/dev-logs.ts +85 -0
  260. package/src/dev-session.ts +318 -0
  261. package/src/fastkv.ts +153 -0
  262. package/src/federation.server.ts +43 -0
  263. package/src/host.ts +526 -0
  264. package/src/index.ts +6 -3
  265. package/src/integrity.ts +54 -0
  266. package/src/internal/manifest-normalizer.ts +251 -0
  267. package/src/mf.ts +105 -0
  268. package/src/near-cli.ts +284 -0
  269. package/src/network.ts +3 -0
  270. package/src/orchestrator.ts +648 -0
  271. package/src/plugin.ts +1130 -2311
  272. package/src/process-registry.ts +154 -0
  273. package/src/scripts/sync-api-contract.ts +24 -0
  274. package/src/sdk.ts +14 -0
  275. package/src/shared.ts +206 -0
  276. package/src/types.ts +152 -206
  277. package/src/ui/head.ts +34 -27
  278. package/src/ui/index.ts +3 -3
  279. package/src/ui/metadata.ts +95 -0
  280. package/src/ui/router.ts +22 -6
  281. package/src/ui/runtime.ts +55 -6
  282. package/src/ui/types.ts +24 -11
  283. package/src/utils/banner.ts +10 -6
  284. package/src/utils/run.ts +26 -27
  285. package/src/utils/theme.ts +3 -66
  286. package/src/components/monitor-view.tsx +0 -475
  287. package/src/components/status-view.tsx +0 -173
  288. package/src/lib/env.ts +0 -109
  289. package/src/lib/near-cli.ts +0 -289
  290. package/src/lib/nova.ts +0 -266
  291. package/src/lib/orchestrator.ts +0 -276
  292. package/src/lib/process-registry.ts +0 -166
  293. package/src/lib/process.ts +0 -550
  294. package/src/lib/resource-monitor/assertions.ts +0 -234
  295. package/src/lib/resource-monitor/command.ts +0 -283
  296. package/src/lib/resource-monitor/diff.ts +0 -157
  297. package/src/lib/resource-monitor/errors.ts +0 -127
  298. package/src/lib/resource-monitor/index.ts +0 -305
  299. package/src/lib/resource-monitor/platform/darwin.ts +0 -306
  300. package/src/lib/resource-monitor/platform/index.ts +0 -35
  301. package/src/lib/resource-monitor/platform/linux.ts +0 -332
  302. package/src/lib/resource-monitor/platform/windows.ts +0 -298
  303. package/src/lib/resource-monitor/snapshot.ts +0 -217
  304. package/src/lib/resource-monitor/types.ts +0 -74
  305. package/src/lib/session-recorder/errors.ts +0 -102
  306. package/src/lib/session-recorder/flows/login.ts +0 -210
  307. package/src/lib/session-recorder/index.ts +0 -361
  308. package/src/lib/session-recorder/playwright.ts +0 -257
  309. package/src/lib/session-recorder/report.ts +0 -353
  310. package/src/lib/session-recorder/server.ts +0 -268
  311. package/src/lib/session-recorder/types.ts +0 -115
  312. package/src/lib/sync.ts +0 -1
  313. 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
+ }