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,309 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { dirname, join, relative } from "node:path";
4
+ import type { RuntimeConfig, RuntimePluginConfig } from "./types";
5
+
6
+ export interface ApiPluginManifest {
7
+ schemaVersion: 1;
8
+ kind: "every-plugin/manifest";
9
+ plugin: {
10
+ name: string;
11
+ version: string;
12
+ };
13
+ runtime: {
14
+ remoteEntry: string;
15
+ };
16
+ contract?: {
17
+ kind: "orpc";
18
+ types: {
19
+ path: string;
20
+ exportName: string;
21
+ typeName: string;
22
+ sha256?: string;
23
+ };
24
+ };
25
+ }
26
+
27
+ interface ContractSource {
28
+ key: string;
29
+ importName: string;
30
+ sourceFilePath: string;
31
+ generatedPath?: string;
32
+ }
33
+
34
+ function sha256(input: string): string {
35
+ return createHash("sha256").update(input).digest("hex");
36
+ }
37
+
38
+ function trimTrailingSlash(input: string): string {
39
+ return input.replace(/\/$/, "");
40
+ }
41
+
42
+ function sanitizeIdentifier(input: string): string {
43
+ return input.replace(/[^A-Za-z0-9_]/g, "_").replace(/^[^A-Za-z_]+/, "_");
44
+ }
45
+
46
+ function toImportPath(fromFile: string, targetFile: string): string {
47
+ const rel = relative(dirname(fromFile), targetFile).replace(/\\/g, "/");
48
+ return rel.startsWith(".") ? rel : `./${rel}`;
49
+ }
50
+
51
+ function writeFileIfChanged(filePath: string, content: string) {
52
+ try {
53
+ if (readFileSync(filePath, "utf8") === content) return false;
54
+ } catch {
55
+ // file does not exist yet
56
+ }
57
+
58
+ writeFileSync(filePath, content);
59
+ return true;
60
+ }
61
+
62
+ function getApiPluginManifestUrl(apiBaseUrl: string): string {
63
+ return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;
64
+ }
65
+
66
+ async function fetchApiPluginManifest(apiBaseUrl: string): Promise<ApiPluginManifest> {
67
+ const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));
68
+ if (!response.ok) {
69
+ throw new Error(
70
+ `Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`,
71
+ );
72
+ }
73
+
74
+ const manifest = (await response.json()) as ApiPluginManifest;
75
+ if (manifest.schemaVersion !== 1 || manifest.kind !== "every-plugin/manifest") {
76
+ throw new Error("Unsupported API plugin manifest format");
77
+ }
78
+
79
+ return manifest;
80
+ }
81
+
82
+ function localApiContractSource(configDir: string): ContractSource {
83
+ const sourcePath = join(configDir, "api", "src", "contract.ts");
84
+ return {
85
+ key: "api",
86
+ importName: "BaseApiContract",
87
+ sourceFilePath: sourcePath,
88
+ };
89
+ }
90
+
91
+ async function remoteContractSource(opts: {
92
+ configDir: string;
93
+ runtimeDir: string;
94
+ name: string;
95
+ baseUrl: string;
96
+ generatedSubdir: string;
97
+ }): Promise<ContractSource> {
98
+ const manifest = await fetchApiPluginManifest(opts.baseUrl);
99
+ if (!manifest.contract) {
100
+ throw new Error(
101
+ `Plugin manifest for ${manifest.plugin.name} does not advertise contract types`,
102
+ );
103
+ }
104
+
105
+ const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\.\//, "")}`;
106
+ const contractResponse = await fetch(contractUrl);
107
+ if (!contractResponse.ok) {
108
+ throw new Error(
109
+ `Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`,
110
+ );
111
+ }
112
+
113
+ const contractTypes = await contractResponse.text();
114
+ if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) {
115
+ throw new Error("Fetched contract types failed checksum verification");
116
+ }
117
+
118
+ const generatedPath = join(opts.runtimeDir, opts.generatedSubdir, "contract.d.ts");
119
+ mkdirSync(dirname(generatedPath), { recursive: true });
120
+ writeFileIfChanged(generatedPath, contractTypes);
121
+
122
+ return {
123
+ key: opts.name,
124
+ importName: `${sanitizeIdentifier(opts.name)}Contract`,
125
+ sourceFilePath: generatedPath,
126
+ generatedPath,
127
+ };
128
+ }
129
+
130
+ async function resolveContractSource(opts: {
131
+ configDir: string;
132
+ runtimeDir: string;
133
+ key: string;
134
+ source: RuntimePluginConfig | { url: string; localPath?: string; name: string } | null;
135
+ baseUrl: string;
136
+ generatedSubdir: string;
137
+ }): Promise<ContractSource> {
138
+ if (
139
+ opts.key === "api" &&
140
+ (!opts.source || !("localPath" in opts.source) || opts.source.localPath)
141
+ ) {
142
+ const localPath = opts.source && "localPath" in opts.source ? opts.source.localPath : undefined;
143
+ if (localPath) {
144
+ return {
145
+ key: opts.key,
146
+ importName: "BaseApiContract",
147
+ sourceFilePath: join(localPath, "src", "contract.ts"),
148
+ };
149
+ }
150
+
151
+ if (!opts.baseUrl) {
152
+ return localApiContractSource(opts.configDir);
153
+ }
154
+ }
155
+
156
+ if (opts.source && "localPath" in opts.source && opts.source.localPath) {
157
+ return {
158
+ key: opts.key,
159
+ importName: `${sanitizeIdentifier(opts.key)}Contract`,
160
+ sourceFilePath: join(opts.source.localPath, "src", "contract.ts"),
161
+ };
162
+ }
163
+
164
+ return remoteContractSource({
165
+ configDir: opts.configDir,
166
+ runtimeDir: opts.runtimeDir,
167
+ name: opts.key,
168
+ baseUrl: opts.baseUrl,
169
+ generatedSubdir: opts.generatedSubdir,
170
+ });
171
+ }
172
+
173
+ function writeAggregateContractFile(opts: {
174
+ configDir: string;
175
+ sources: ContractSource[];
176
+ pluginKeys: string[];
177
+ }) {
178
+ const baseSource = opts.sources.find((source) => source.key === "api");
179
+ const pluginSources = opts.pluginKeys
180
+ .map((key) => opts.sources.find((entry) => entry.key === key))
181
+ .filter((source): source is ContractSource => Boolean(source));
182
+
183
+ if (!baseSource) {
184
+ throw new Error("API contract source is required to generate the aggregate contract");
185
+ }
186
+
187
+ // --- Generate ui/src/api-contract.gen.ts ---
188
+ const uiContractPath = join(opts.configDir, "ui", "src", "api-contract.gen.ts");
189
+ const uiLines: string[] = [];
190
+
191
+ for (const source of opts.sources) {
192
+ const importPath = toImportPath(uiContractPath, source.sourceFilePath);
193
+ uiLines.push(`import type { ContractType as ${source.importName} } from "${importPath}";`);
194
+ }
195
+
196
+ uiLines.push("");
197
+ if (pluginSources.length === 0) {
198
+ uiLines.push(`export type ApiContract = ${baseSource.importName};`);
199
+ } else {
200
+ uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);
201
+ for (const source of pluginSources) {
202
+ const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)
203
+ ? source.key
204
+ : JSON.stringify(source.key);
205
+ uiLines.push(` ${key}: ${source.importName};`);
206
+ }
207
+ uiLines.push("};");
208
+ }
209
+ mkdirSync(dirname(uiContractPath), { recursive: true });
210
+ writeFileIfChanged(uiContractPath, `${uiLines.join("\n")}\n`);
211
+
212
+ // --- Generate api/src/plugins-client.gen.ts ---
213
+ const pluginsClientPath = join(opts.configDir, "api", "src", "plugins-client.gen.ts");
214
+ const pluginsClientLines: string[] = [];
215
+
216
+ for (const source of pluginSources) {
217
+ const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);
218
+ pluginsClientLines.push(
219
+ `import type { ContractType as ${source.importName} } from "${importPath}";`,
220
+ );
221
+ }
222
+
223
+ pluginsClientLines.push(
224
+ 'import type { ContractRouterClient, AnyContractRouter } from "@orpc/contract";',
225
+ );
226
+ pluginsClientLines.push(
227
+ "type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;",
228
+ );
229
+ pluginsClientLines.push("");
230
+
231
+ if (pluginSources.length === 0) {
232
+ pluginsClientLines.push("export type PluginsClient = Record<string, never>;");
233
+ } else {
234
+ pluginsClientLines.push("export type PluginsClient = {");
235
+ for (const source of pluginSources) {
236
+ const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)
237
+ ? source.key
238
+ : JSON.stringify(source.key);
239
+ pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);
240
+ }
241
+ pluginsClientLines.push("};");
242
+ }
243
+
244
+ mkdirSync(dirname(pluginsClientPath), { recursive: true });
245
+ writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join("\n")}\n`);
246
+
247
+ return uiContractPath;
248
+ }
249
+
250
+ export async function syncApiContractBridge(opts: {
251
+ configDir: string;
252
+ runtimeConfig: RuntimeConfig;
253
+ apiBaseUrl: string;
254
+ }): Promise<{
255
+ bridgePath: string;
256
+ generatedPath: string | null;
257
+ manifest: ApiPluginManifest | null;
258
+ source: "local" | "remote";
259
+ }> {
260
+ const runtimeDir = join(opts.configDir, ".bos", "generated");
261
+ const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) =>
262
+ a.localeCompare(b),
263
+ );
264
+ const sources: ContractSource[] = [];
265
+ let manifest: ApiPluginManifest | null = null;
266
+ let generatedPath: string | null = null;
267
+
268
+ const baseSource = await resolveContractSource({
269
+ configDir: opts.configDir,
270
+ runtimeDir,
271
+ key: "api",
272
+ source: opts.runtimeConfig.api,
273
+ baseUrl: opts.apiBaseUrl,
274
+ generatedSubdir: "api",
275
+ });
276
+ sources.push(baseSource);
277
+
278
+ for (const [key, plugin] of pluginEntries) {
279
+ const source = await resolveContractSource({
280
+ configDir: opts.configDir,
281
+ runtimeDir,
282
+ key,
283
+ source: plugin,
284
+ baseUrl: plugin.url,
285
+ generatedSubdir: `plugins/${key}`,
286
+ });
287
+ sources.push(source);
288
+ if (source.generatedPath) {
289
+ generatedPath = source.generatedPath;
290
+ }
291
+ }
292
+
293
+ writeAggregateContractFile({
294
+ configDir: opts.configDir,
295
+ sources,
296
+ pluginKeys: pluginEntries.map(([key]) => key),
297
+ });
298
+
299
+ if (opts.runtimeConfig.api.source !== "local") {
300
+ manifest = await fetchApiPluginManifest(opts.apiBaseUrl);
301
+ }
302
+
303
+ return {
304
+ bridgePath: join(opts.configDir, "ui", "src", "api-contract.gen.ts"),
305
+ generatedPath,
306
+ manifest,
307
+ source: opts.runtimeConfig.api.source,
308
+ };
309
+ }
package/src/api.ts ADDED
@@ -0,0 +1,181 @@
1
+ import { verifySriForUrl } from "./integrity";
2
+ import { ensureNodeRuntimePlugin, registerRemote } from "./mf";
3
+ import { createPluginRuntime } from "./sdk";
4
+ import type { RuntimeConfig, RuntimePluginConfig } from "./types";
5
+
6
+ export interface LoadedPluginResult {
7
+ key: string;
8
+ name: string;
9
+ router: any;
10
+ createClient: (context?: unknown) => any;
11
+ metadata: {
12
+ remoteUrl: string;
13
+ version?: string;
14
+ };
15
+ }
16
+
17
+ export interface LoadedPluginsResult {
18
+ base: LoadedPluginResult | null;
19
+ plugins: LoadedPluginResult[];
20
+ errors: Array<{ key: string; error: string }>;
21
+ }
22
+
23
+ export async function loadApiPlugin(opts: {
24
+ key: string;
25
+ runtimeId: string;
26
+ name: string;
27
+ entry: string;
28
+ variables?: Record<string, string>;
29
+ secrets?: Record<string, string>;
30
+ integrity?: string;
31
+ plugins?: Record<string, unknown>;
32
+ }): Promise<LoadedPluginResult> {
33
+ const remoteEntryUrl = (() => {
34
+ if (opts.entry.endsWith("/remoteEntry.js")) return opts.entry;
35
+ if (opts.entry.endsWith("/mf-manifest.json")) {
36
+ return `${opts.entry.replace(/\/mf-manifest\.json$/, "")}/remoteEntry.js`;
37
+ }
38
+ if (opts.entry.endsWith(".js")) return opts.entry;
39
+ return `${opts.entry.replace(/\/$/, "")}/remoteEntry.js`;
40
+ })();
41
+
42
+ if (opts.integrity) {
43
+ await verifySriForUrl(remoteEntryUrl, opts.integrity);
44
+ }
45
+
46
+ await ensureNodeRuntimePlugin();
47
+ await registerRemote({ name: opts.runtimeId, entry: remoteEntryUrl });
48
+
49
+ const runtime: any = createPluginRuntime({
50
+ registry: {
51
+ [opts.runtimeId]: { remote: remoteEntryUrl },
52
+ },
53
+ secrets: opts.secrets ?? {},
54
+ });
55
+
56
+ // biome-ignore lint/correctness/useHookAtTopLevel: usePlugin is not a React hook
57
+ const plugin = await runtime.usePlugin(
58
+ opts.runtimeId,
59
+ {
60
+ variables: opts.variables ?? {},
61
+ secrets: opts.secrets ?? {},
62
+ },
63
+ opts.plugins,
64
+ );
65
+
66
+ return {
67
+ key: opts.key,
68
+ name: opts.name,
69
+ router: plugin.router,
70
+ createClient: plugin.createClient as (context?: unknown) => any,
71
+ metadata: {
72
+ remoteUrl: remoteEntryUrl,
73
+ version: plugin.metadata.version,
74
+ },
75
+ };
76
+ }
77
+
78
+ function collectSecrets(config: RuntimePluginConfig, envSecrets?: Record<string, string>) {
79
+ const secrets: Record<string, string> = {};
80
+ for (const key of config.secrets ?? []) {
81
+ const value = envSecrets?.[key] ?? process.env[key];
82
+ if (value) {
83
+ secrets[key] = value;
84
+ }
85
+ }
86
+ return secrets;
87
+ }
88
+
89
+ export async function loadApiPluginsFromRuntimeConfig(
90
+ runtimeConfig: RuntimeConfig,
91
+ envSecrets?: Record<string, string>,
92
+ ): Promise<LoadedPluginsResult> {
93
+ const entries: Array<[string, RuntimePluginConfig]> = [];
94
+
95
+ if (runtimeConfig.api?.url) {
96
+ entries.push(["api", runtimeConfig.api]);
97
+ }
98
+
99
+ for (const [key, plugin] of Object.entries(runtimeConfig.plugins ?? {})) {
100
+ if (plugin.url) {
101
+ entries.push([key, plugin]);
102
+ }
103
+ }
104
+
105
+ if (entries.length === 0) {
106
+ console.log("[API] No plugins configured");
107
+ return { base: null, plugins: [], errors: [] };
108
+ }
109
+
110
+ // Phase 1: Load non-API plugins first
111
+ const pluginEntries = entries.filter(([key]) => key !== "api");
112
+ const apiEntry = entries.find(([key]) => key === "api");
113
+
114
+ const pluginResults = await Promise.allSettled(
115
+ pluginEntries.map(async ([key, pluginConfig]) => {
116
+ console.log(`[API] Loading plugin: ${pluginConfig.name} from ${pluginConfig.entry}`);
117
+ return loadApiPlugin({
118
+ key,
119
+ runtimeId: pluginConfig.name,
120
+ name: pluginConfig.name,
121
+ entry: pluginConfig.entry,
122
+ variables: pluginConfig.variables,
123
+ secrets: collectSecrets(pluginConfig, envSecrets),
124
+ integrity: pluginConfig.integrity,
125
+ });
126
+ }),
127
+ );
128
+
129
+ const plugins: LoadedPluginResult[] = [];
130
+ const errors: Array<{ key: string; error: string }> = [];
131
+
132
+ const pluginsClient: Record<string, unknown> = {};
133
+
134
+ pluginResults.forEach((result, index) => {
135
+ const [key] = pluginEntries[index] ?? ["unknown"];
136
+ if (result.status === "fulfilled") {
137
+ plugins.push(result.value);
138
+ pluginsClient[key] = result.value.createClient;
139
+ } else {
140
+ errors.push({
141
+ key,
142
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
143
+ });
144
+ }
145
+ });
146
+
147
+ // Phase 2: Load API plugin with injected plugins client
148
+ let base: LoadedPluginResult | null = null;
149
+
150
+ if (apiEntry) {
151
+ const [key, apiConfig] = apiEntry;
152
+ try {
153
+ console.log(`[API] Loading API plugin: ${apiConfig.name} from ${apiConfig.entry}`);
154
+ base = await loadApiPlugin({
155
+ key,
156
+ runtimeId: apiConfig.name,
157
+ name: apiConfig.name,
158
+ entry: apiConfig.entry,
159
+ variables: apiConfig.variables,
160
+ secrets: collectSecrets(apiConfig, envSecrets),
161
+ integrity: apiConfig.integrity,
162
+ plugins: pluginsClient,
163
+ });
164
+ } catch (error) {
165
+ errors.push({
166
+ key,
167
+ error: error instanceof Error ? error.message : String(error),
168
+ });
169
+ }
170
+ }
171
+
172
+ return { base, plugins, errors };
173
+ }
174
+
175
+ export function createStitchedRouter(baseRouter: any, plugins: LoadedPluginResult[] | null): any {
176
+ if (!plugins || plugins.length === 0) {
177
+ return baseRouter;
178
+ }
179
+
180
+ return plugins.reduce((router, plugin) => Object.assign(router, plugin.router), baseRouter);
181
+ }