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
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # everything-dev
2
+
3
+ A consolidated product package for building Module Federation apps with oRPC APIs.
4
+
5
+ ## Subpath Exports
6
+
7
+ - `everything-dev/types` - Zod schemas and TypeScript types
8
+ - `everything-dev/config` - Config loading and resolution
9
+ - `everything-dev/shared` - Shared dependency policy generation
10
+ - `everything-dev/mf` - Module Federation runtime utilities
11
+ - `everything-dev/plugin` - Plugin authoring (re-exports from every-plugin)
12
+ - `everything-dev/api` - API plugin loading and router stitching
13
+ - `everything-dev/host` - Host server orchestration
14
+ - `everything-dev/cli` - CLI entry point and plugin workspace commands
15
+
16
+ ### Plugin commands
17
+
18
+ - `bos plugin add <source>` - attach local or remote plugins
19
+ - `bos plugin remove <key>` - detach a plugin
20
+ - `bos plugin list` - list configured plugins
21
+ - `bos plugin publish <key>` - publish one plugin package
22
+
23
+ ## Demo
24
+
25
+ See `demo-next/docs-book/` for a minimal example that demonstrates:
26
+ - Host server loading UI and API remotes via MF manifest
27
+ - API plugin with oRPC contract
28
+ - UI with TanStack Router
29
+ - E2E tests with Playwright
30
+
31
+ ## Quick Start
32
+
33
+ ```bash
34
+ # Build the package
35
+ cd packages/everything-dev
36
+ npm run build
37
+
38
+ # Run the demo
39
+ cd ../../demo-next/docs-book/host
40
+ npm run dev
41
+ ```
42
+
43
+ ## Architecture
44
+
45
+ ```
46
+ bos.config.json
47
+
48
+ everything-dev/config (load + resolve)
49
+
50
+ everything-dev/shared (generate shared deps policy)
51
+
52
+ everything-dev/host (orchestrate)
53
+ ├── everything-dev/api (load API plugins via MF)
54
+ └── everything-dev/mf (load UI remote via MF)
55
+ ```
56
+
57
+ ## MF Manifest Standard
58
+
59
+ All remotes (UI + API) use `mf-manifest.json` as the standard entry point:
60
+
61
+ - `ui.entry = <uiBaseUrl>/mf-manifest.json`
62
+ - `api.entry = <apiBaseUrl>/mf-manifest.json`
63
+
64
+ This ensures consistent remote loading across Node SSR and browser environments.
package/cli.js ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bun
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const distEntry = join(__dirname, "dist", "cli.js");
8
+ const srcEntry = join(__dirname, "src", "cli.ts");
9
+
10
+ await import(existsSync(distEntry) ? distEntry : srcEntry);
@@ -0,0 +1,29 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+
29
+ exports.__toESM = __toESM;
@@ -0,0 +1,172 @@
1
+ const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
+ let node_fs = require("node:fs");
3
+ let node_path = require("node:path");
4
+ let node_crypto = require("node:crypto");
5
+
6
+ //#region src/api-contract.ts
7
+ function sha256(input) {
8
+ return (0, node_crypto.createHash)("sha256").update(input).digest("hex");
9
+ }
10
+ function trimTrailingSlash(input) {
11
+ return input.replace(/\/$/, "");
12
+ }
13
+ function sanitizeIdentifier(input) {
14
+ return input.replace(/[^A-Za-z0-9_]/g, "_").replace(/^[^A-Za-z_]+/, "_");
15
+ }
16
+ function toImportPath(fromFile, targetFile) {
17
+ const rel = (0, node_path.relative)((0, node_path.dirname)(fromFile), targetFile).replace(/\\/g, "/");
18
+ return rel.startsWith(".") ? rel : `./${rel}`;
19
+ }
20
+ function writeFileIfChanged(filePath, content) {
21
+ try {
22
+ if ((0, node_fs.readFileSync)(filePath, "utf8") === content) return false;
23
+ } catch {}
24
+ (0, node_fs.writeFileSync)(filePath, content);
25
+ return true;
26
+ }
27
+ function getApiPluginManifestUrl(apiBaseUrl) {
28
+ return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;
29
+ }
30
+ async function fetchApiPluginManifest(apiBaseUrl) {
31
+ const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));
32
+ if (!response.ok) throw new Error(`Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`);
33
+ const manifest = await response.json();
34
+ if (manifest.schemaVersion !== 1 || manifest.kind !== "every-plugin/manifest") throw new Error("Unsupported API plugin manifest format");
35
+ return manifest;
36
+ }
37
+ function localApiContractSource(configDir) {
38
+ return {
39
+ key: "api",
40
+ importName: "BaseApiContract",
41
+ sourceFilePath: (0, node_path.join)(configDir, "api", "src", "contract.ts")
42
+ };
43
+ }
44
+ async function remoteContractSource(opts) {
45
+ const manifest = await fetchApiPluginManifest(opts.baseUrl);
46
+ if (!manifest.contract) throw new Error(`Plugin manifest for ${manifest.plugin.name} does not advertise contract types`);
47
+ const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\.\//, "")}`;
48
+ const contractResponse = await fetch(contractUrl);
49
+ if (!contractResponse.ok) throw new Error(`Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`);
50
+ const contractTypes = await contractResponse.text();
51
+ if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) throw new Error("Fetched contract types failed checksum verification");
52
+ const generatedPath = (0, node_path.join)(opts.runtimeDir, opts.generatedSubdir, "contract.d.ts");
53
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(generatedPath), { recursive: true });
54
+ writeFileIfChanged(generatedPath, contractTypes);
55
+ return {
56
+ key: opts.name,
57
+ importName: `${sanitizeIdentifier(opts.name)}Contract`,
58
+ sourceFilePath: generatedPath,
59
+ generatedPath
60
+ };
61
+ }
62
+ async function resolveContractSource(opts) {
63
+ if (opts.key === "api" && (!opts.source || !("localPath" in opts.source) || opts.source.localPath)) {
64
+ const localPath = opts.source && "localPath" in opts.source ? opts.source.localPath : void 0;
65
+ if (localPath) return {
66
+ key: opts.key,
67
+ importName: "BaseApiContract",
68
+ sourceFilePath: (0, node_path.join)(localPath, "src", "contract.ts")
69
+ };
70
+ if (!opts.baseUrl) return localApiContractSource(opts.configDir);
71
+ }
72
+ if (opts.source && "localPath" in opts.source && opts.source.localPath) return {
73
+ key: opts.key,
74
+ importName: `${sanitizeIdentifier(opts.key)}Contract`,
75
+ sourceFilePath: (0, node_path.join)(opts.source.localPath, "src", "contract.ts")
76
+ };
77
+ return remoteContractSource({
78
+ configDir: opts.configDir,
79
+ runtimeDir: opts.runtimeDir,
80
+ name: opts.key,
81
+ baseUrl: opts.baseUrl,
82
+ generatedSubdir: opts.generatedSubdir
83
+ });
84
+ }
85
+ function writeAggregateContractFile(opts) {
86
+ const baseSource = opts.sources.find((source) => source.key === "api");
87
+ const pluginSources = opts.pluginKeys.map((key) => opts.sources.find((entry) => entry.key === key)).filter((source) => Boolean(source));
88
+ if (!baseSource) throw new Error("API contract source is required to generate the aggregate contract");
89
+ const uiContractPath = (0, node_path.join)(opts.configDir, "ui", "src", "api-contract.gen.ts");
90
+ const uiLines = [];
91
+ for (const source of opts.sources) {
92
+ const importPath = toImportPath(uiContractPath, source.sourceFilePath);
93
+ uiLines.push(`import type { ContractType as ${source.importName} } from "${importPath}";`);
94
+ }
95
+ uiLines.push("");
96
+ if (pluginSources.length === 0) uiLines.push(`export type ApiContract = ${baseSource.importName};`);
97
+ else {
98
+ uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);
99
+ for (const source of pluginSources) {
100
+ const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key) ? source.key : JSON.stringify(source.key);
101
+ uiLines.push(` ${key}: ${source.importName};`);
102
+ }
103
+ uiLines.push("};");
104
+ }
105
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(uiContractPath), { recursive: true });
106
+ writeFileIfChanged(uiContractPath, `${uiLines.join("\n")}\n`);
107
+ const pluginsClientPath = (0, node_path.join)(opts.configDir, "api", "src", "plugins-client.gen.ts");
108
+ const pluginsClientLines = [];
109
+ for (const source of pluginSources) {
110
+ const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);
111
+ pluginsClientLines.push(`import type { ContractType as ${source.importName} } from "${importPath}";`);
112
+ }
113
+ pluginsClientLines.push("import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";");
114
+ pluginsClientLines.push("type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;");
115
+ pluginsClientLines.push("");
116
+ if (pluginSources.length === 0) pluginsClientLines.push("export type PluginsClient = Record<string, never>;");
117
+ else {
118
+ pluginsClientLines.push("export type PluginsClient = {");
119
+ for (const source of pluginSources) {
120
+ const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key) ? source.key : JSON.stringify(source.key);
121
+ pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);
122
+ }
123
+ pluginsClientLines.push("};");
124
+ }
125
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(pluginsClientPath), { recursive: true });
126
+ writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join("\n")}\n`);
127
+ return uiContractPath;
128
+ }
129
+ async function syncApiContractBridge(opts) {
130
+ const runtimeDir = (0, node_path.join)(opts.configDir, ".bos", "generated");
131
+ const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) => a.localeCompare(b));
132
+ const sources = [];
133
+ let manifest = null;
134
+ let generatedPath = null;
135
+ const baseSource = await resolveContractSource({
136
+ configDir: opts.configDir,
137
+ runtimeDir,
138
+ key: "api",
139
+ source: opts.runtimeConfig.api,
140
+ baseUrl: opts.apiBaseUrl,
141
+ generatedSubdir: "api"
142
+ });
143
+ sources.push(baseSource);
144
+ for (const [key, plugin] of pluginEntries) {
145
+ const source = await resolveContractSource({
146
+ configDir: opts.configDir,
147
+ runtimeDir,
148
+ key,
149
+ source: plugin,
150
+ baseUrl: plugin.url,
151
+ generatedSubdir: `plugins/${key}`
152
+ });
153
+ sources.push(source);
154
+ if (source.generatedPath) generatedPath = source.generatedPath;
155
+ }
156
+ writeAggregateContractFile({
157
+ configDir: opts.configDir,
158
+ sources,
159
+ pluginKeys: pluginEntries.map(([key]) => key)
160
+ });
161
+ if (opts.runtimeConfig.api.source !== "local") manifest = await fetchApiPluginManifest(opts.apiBaseUrl);
162
+ return {
163
+ bridgePath: (0, node_path.join)(opts.configDir, "ui", "src", "api-contract.gen.ts"),
164
+ generatedPath,
165
+ manifest,
166
+ source: opts.runtimeConfig.api.source
167
+ };
168
+ }
169
+
170
+ //#endregion
171
+ exports.syncApiContractBridge = syncApiContractBridge;
172
+ //# sourceMappingURL=api-contract.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-contract.cjs","names":[],"sources":["../src/api-contract.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport type { RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface ApiPluginManifest {\n schemaVersion: 1;\n kind: \"every-plugin/manifest\";\n plugin: {\n name: string;\n version: string;\n };\n runtime: {\n remoteEntry: string;\n };\n contract?: {\n kind: \"orpc\";\n types: {\n path: string;\n exportName: string;\n typeName: string;\n sha256?: string;\n };\n };\n}\n\ninterface ContractSource {\n key: string;\n importName: string;\n sourceFilePath: string;\n generatedPath?: string;\n}\n\nfunction sha256(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction trimTrailingSlash(input: string): string {\n return input.replace(/\\/$/, \"\");\n}\n\nfunction sanitizeIdentifier(input: string): string {\n return input.replace(/[^A-Za-z0-9_]/g, \"_\").replace(/^[^A-Za-z_]+/, \"_\");\n}\n\nfunction toImportPath(fromFile: string, targetFile: string): string {\n const rel = relative(dirname(fromFile), targetFile).replace(/\\\\/g, \"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction writeFileIfChanged(filePath: string, content: string) {\n try {\n if (readFileSync(filePath, \"utf8\") === content) return false;\n } catch {\n // file does not exist yet\n }\n\n writeFileSync(filePath, content);\n return true;\n}\n\nfunction getApiPluginManifestUrl(apiBaseUrl: string): string {\n return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;\n}\n\nasync function fetchApiPluginManifest(apiBaseUrl: string): Promise<ApiPluginManifest> {\n const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));\n if (!response.ok) {\n throw new Error(\n `Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`,\n );\n }\n\n const manifest = (await response.json()) as ApiPluginManifest;\n if (manifest.schemaVersion !== 1 || manifest.kind !== \"every-plugin/manifest\") {\n throw new Error(\"Unsupported API plugin manifest format\");\n }\n\n return manifest;\n}\n\nfunction localApiContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"api\", \"src\", \"contract.ts\");\n return {\n key: \"api\",\n importName: \"BaseApiContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nasync function remoteContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n name: string;\n baseUrl: string;\n generatedSubdir: string;\n}): Promise<ContractSource> {\n const manifest = await fetchApiPluginManifest(opts.baseUrl);\n if (!manifest.contract) {\n throw new Error(\n `Plugin manifest for ${manifest.plugin.name} does not advertise contract types`,\n );\n }\n\n const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\\.\\//, \"\")}`;\n const contractResponse = await fetch(contractUrl);\n if (!contractResponse.ok) {\n throw new Error(\n `Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`,\n );\n }\n\n const contractTypes = await contractResponse.text();\n if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) {\n throw new Error(\"Fetched contract types failed checksum verification\");\n }\n\n const generatedPath = join(opts.runtimeDir, opts.generatedSubdir, \"contract.d.ts\");\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, contractTypes);\n\n return {\n key: opts.name,\n importName: `${sanitizeIdentifier(opts.name)}Contract`,\n sourceFilePath: generatedPath,\n generatedPath,\n };\n}\n\nasync function resolveContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n key: string;\n source: RuntimePluginConfig | { url: string; localPath?: string; name: string } | null;\n baseUrl: string;\n generatedSubdir: string;\n}): Promise<ContractSource> {\n if (\n opts.key === \"api\" &&\n (!opts.source || !(\"localPath\" in opts.source) || opts.source.localPath)\n ) {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath) {\n return {\n key: opts.key,\n importName: \"BaseApiContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return localApiContractSource(opts.configDir);\n }\n }\n\n if (opts.source && \"localPath\" in opts.source && opts.source.localPath) {\n return {\n key: opts.key,\n importName: `${sanitizeIdentifier(opts.key)}Contract`,\n sourceFilePath: join(opts.source.localPath, \"src\", \"contract.ts\"),\n };\n }\n\n return remoteContractSource({\n configDir: opts.configDir,\n runtimeDir: opts.runtimeDir,\n name: opts.key,\n baseUrl: opts.baseUrl,\n generatedSubdir: opts.generatedSubdir,\n });\n}\n\nfunction writeAggregateContractFile(opts: {\n configDir: string;\n sources: ContractSource[];\n pluginKeys: string[];\n}) {\n const baseSource = opts.sources.find((source) => source.key === \"api\");\n const pluginSources = opts.pluginKeys\n .map((key) => opts.sources.find((entry) => entry.key === key))\n .filter((source): source is ContractSource => Boolean(source));\n\n if (!baseSource) {\n throw new Error(\"API contract source is required to generate the aggregate contract\");\n }\n\n // --- Generate ui/src/api-contract.gen.ts ---\n const uiContractPath = join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\");\n const uiLines: string[] = [];\n\n for (const source of opts.sources) {\n const importPath = toImportPath(uiContractPath, source.sourceFilePath);\n uiLines.push(`import type { ContractType as ${source.importName} } from \"${importPath}\";`);\n }\n\n uiLines.push(\"\");\n if (pluginSources.length === 0) {\n uiLines.push(`export type ApiContract = ${baseSource.importName};`);\n } else {\n uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n uiLines.push(` ${key}: ${source.importName};`);\n }\n uiLines.push(\"};\");\n }\n mkdirSync(dirname(uiContractPath), { recursive: true });\n writeFileIfChanged(uiContractPath, `${uiLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/plugins-client.gen.ts ---\n const pluginsClientPath = join(opts.configDir, \"api\", \"src\", \"plugins-client.gen.ts\");\n const pluginsClientLines: string[] = [];\n\n for (const source of pluginSources) {\n const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);\n pluginsClientLines.push(\n `import type { ContractType as ${source.importName} } from \"${importPath}\";`,\n );\n }\n\n pluginsClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n pluginsClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n pluginsClientLines.push(\"\");\n\n if (pluginSources.length === 0) {\n pluginsClientLines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n pluginsClientLines.push(\"export type PluginsClient = {\");\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);\n }\n pluginsClientLines.push(\"};\");\n }\n\n mkdirSync(dirname(pluginsClientPath), { recursive: true });\n writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join(\"\\n\")}\\n`);\n\n return uiContractPath;\n}\n\nexport async function syncApiContractBridge(opts: {\n configDir: string;\n runtimeConfig: RuntimeConfig;\n apiBaseUrl: string;\n}): Promise<{\n bridgePath: string;\n generatedPath: string | null;\n manifest: ApiPluginManifest | null;\n source: \"local\" | \"remote\";\n}> {\n const runtimeDir = join(opts.configDir, \".bos\", \"generated\");\n const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n );\n const sources: ContractSource[] = [];\n let manifest: ApiPluginManifest | null = null;\n let generatedPath: string | null = null;\n\n const baseSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"api\",\n source: opts.runtimeConfig.api,\n baseUrl: opts.apiBaseUrl,\n generatedSubdir: \"api\",\n });\n sources.push(baseSource);\n\n for (const [key, plugin] of pluginEntries) {\n const source = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key,\n source: plugin,\n baseUrl: plugin.url,\n generatedSubdir: `plugins/${key}`,\n });\n sources.push(source);\n if (source.generatedPath) {\n generatedPath = source.generatedPath;\n }\n }\n\n writeAggregateContractFile({\n configDir: opts.configDir,\n sources,\n pluginKeys: pluginEntries.map(([key]) => key),\n });\n\n if (opts.runtimeConfig.api.source !== \"local\") {\n manifest = await fetchApiPluginManifest(opts.apiBaseUrl);\n }\n\n return {\n bridgePath: join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\"),\n generatedPath,\n manifest,\n source: opts.runtimeConfig.api.source,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,171 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, relative } from "node:path";
3
+ import { createHash } from "node:crypto";
4
+
5
+ //#region src/api-contract.ts
6
+ function sha256(input) {
7
+ return createHash("sha256").update(input).digest("hex");
8
+ }
9
+ function trimTrailingSlash(input) {
10
+ return input.replace(/\/$/, "");
11
+ }
12
+ function sanitizeIdentifier(input) {
13
+ return input.replace(/[^A-Za-z0-9_]/g, "_").replace(/^[^A-Za-z_]+/, "_");
14
+ }
15
+ function toImportPath(fromFile, targetFile) {
16
+ const rel = relative(dirname(fromFile), targetFile).replace(/\\/g, "/");
17
+ return rel.startsWith(".") ? rel : `./${rel}`;
18
+ }
19
+ function writeFileIfChanged(filePath, content) {
20
+ try {
21
+ if (readFileSync(filePath, "utf8") === content) return false;
22
+ } catch {}
23
+ writeFileSync(filePath, content);
24
+ return true;
25
+ }
26
+ function getApiPluginManifestUrl(apiBaseUrl) {
27
+ return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;
28
+ }
29
+ async function fetchApiPluginManifest(apiBaseUrl) {
30
+ const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));
31
+ if (!response.ok) throw new Error(`Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`);
32
+ const manifest = await response.json();
33
+ if (manifest.schemaVersion !== 1 || manifest.kind !== "every-plugin/manifest") throw new Error("Unsupported API plugin manifest format");
34
+ return manifest;
35
+ }
36
+ function localApiContractSource(configDir) {
37
+ return {
38
+ key: "api",
39
+ importName: "BaseApiContract",
40
+ sourceFilePath: join(configDir, "api", "src", "contract.ts")
41
+ };
42
+ }
43
+ async function remoteContractSource(opts) {
44
+ const manifest = await fetchApiPluginManifest(opts.baseUrl);
45
+ if (!manifest.contract) throw new Error(`Plugin manifest for ${manifest.plugin.name} does not advertise contract types`);
46
+ const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\.\//, "")}`;
47
+ const contractResponse = await fetch(contractUrl);
48
+ if (!contractResponse.ok) throw new Error(`Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`);
49
+ const contractTypes = await contractResponse.text();
50
+ if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) throw new Error("Fetched contract types failed checksum verification");
51
+ const generatedPath = join(opts.runtimeDir, opts.generatedSubdir, "contract.d.ts");
52
+ mkdirSync(dirname(generatedPath), { recursive: true });
53
+ writeFileIfChanged(generatedPath, contractTypes);
54
+ return {
55
+ key: opts.name,
56
+ importName: `${sanitizeIdentifier(opts.name)}Contract`,
57
+ sourceFilePath: generatedPath,
58
+ generatedPath
59
+ };
60
+ }
61
+ async function resolveContractSource(opts) {
62
+ if (opts.key === "api" && (!opts.source || !("localPath" in opts.source) || opts.source.localPath)) {
63
+ const localPath = opts.source && "localPath" in opts.source ? opts.source.localPath : void 0;
64
+ if (localPath) return {
65
+ key: opts.key,
66
+ importName: "BaseApiContract",
67
+ sourceFilePath: join(localPath, "src", "contract.ts")
68
+ };
69
+ if (!opts.baseUrl) return localApiContractSource(opts.configDir);
70
+ }
71
+ if (opts.source && "localPath" in opts.source && opts.source.localPath) return {
72
+ key: opts.key,
73
+ importName: `${sanitizeIdentifier(opts.key)}Contract`,
74
+ sourceFilePath: join(opts.source.localPath, "src", "contract.ts")
75
+ };
76
+ return remoteContractSource({
77
+ configDir: opts.configDir,
78
+ runtimeDir: opts.runtimeDir,
79
+ name: opts.key,
80
+ baseUrl: opts.baseUrl,
81
+ generatedSubdir: opts.generatedSubdir
82
+ });
83
+ }
84
+ function writeAggregateContractFile(opts) {
85
+ const baseSource = opts.sources.find((source) => source.key === "api");
86
+ const pluginSources = opts.pluginKeys.map((key) => opts.sources.find((entry) => entry.key === key)).filter((source) => Boolean(source));
87
+ if (!baseSource) throw new Error("API contract source is required to generate the aggregate contract");
88
+ const uiContractPath = join(opts.configDir, "ui", "src", "api-contract.gen.ts");
89
+ const uiLines = [];
90
+ for (const source of opts.sources) {
91
+ const importPath = toImportPath(uiContractPath, source.sourceFilePath);
92
+ uiLines.push(`import type { ContractType as ${source.importName} } from "${importPath}";`);
93
+ }
94
+ uiLines.push("");
95
+ if (pluginSources.length === 0) uiLines.push(`export type ApiContract = ${baseSource.importName};`);
96
+ else {
97
+ uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);
98
+ for (const source of pluginSources) {
99
+ const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key) ? source.key : JSON.stringify(source.key);
100
+ uiLines.push(` ${key}: ${source.importName};`);
101
+ }
102
+ uiLines.push("};");
103
+ }
104
+ mkdirSync(dirname(uiContractPath), { recursive: true });
105
+ writeFileIfChanged(uiContractPath, `${uiLines.join("\n")}\n`);
106
+ const pluginsClientPath = join(opts.configDir, "api", "src", "plugins-client.gen.ts");
107
+ const pluginsClientLines = [];
108
+ for (const source of pluginSources) {
109
+ const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);
110
+ pluginsClientLines.push(`import type { ContractType as ${source.importName} } from "${importPath}";`);
111
+ }
112
+ pluginsClientLines.push("import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";");
113
+ pluginsClientLines.push("type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;");
114
+ pluginsClientLines.push("");
115
+ if (pluginSources.length === 0) pluginsClientLines.push("export type PluginsClient = Record<string, never>;");
116
+ else {
117
+ pluginsClientLines.push("export type PluginsClient = {");
118
+ for (const source of pluginSources) {
119
+ const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key) ? source.key : JSON.stringify(source.key);
120
+ pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);
121
+ }
122
+ pluginsClientLines.push("};");
123
+ }
124
+ mkdirSync(dirname(pluginsClientPath), { recursive: true });
125
+ writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join("\n")}\n`);
126
+ return uiContractPath;
127
+ }
128
+ async function syncApiContractBridge(opts) {
129
+ const runtimeDir = join(opts.configDir, ".bos", "generated");
130
+ const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) => a.localeCompare(b));
131
+ const sources = [];
132
+ let manifest = null;
133
+ let generatedPath = null;
134
+ const baseSource = await resolveContractSource({
135
+ configDir: opts.configDir,
136
+ runtimeDir,
137
+ key: "api",
138
+ source: opts.runtimeConfig.api,
139
+ baseUrl: opts.apiBaseUrl,
140
+ generatedSubdir: "api"
141
+ });
142
+ sources.push(baseSource);
143
+ for (const [key, plugin] of pluginEntries) {
144
+ const source = await resolveContractSource({
145
+ configDir: opts.configDir,
146
+ runtimeDir,
147
+ key,
148
+ source: plugin,
149
+ baseUrl: plugin.url,
150
+ generatedSubdir: `plugins/${key}`
151
+ });
152
+ sources.push(source);
153
+ if (source.generatedPath) generatedPath = source.generatedPath;
154
+ }
155
+ writeAggregateContractFile({
156
+ configDir: opts.configDir,
157
+ sources,
158
+ pluginKeys: pluginEntries.map(([key]) => key)
159
+ });
160
+ if (opts.runtimeConfig.api.source !== "local") manifest = await fetchApiPluginManifest(opts.apiBaseUrl);
161
+ return {
162
+ bridgePath: join(opts.configDir, "ui", "src", "api-contract.gen.ts"),
163
+ generatedPath,
164
+ manifest,
165
+ source: opts.runtimeConfig.api.source
166
+ };
167
+ }
168
+
169
+ //#endregion
170
+ export { syncApiContractBridge };
171
+ //# sourceMappingURL=api-contract.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-contract.mjs","names":[],"sources":["../src/api-contract.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport type { RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface ApiPluginManifest {\n schemaVersion: 1;\n kind: \"every-plugin/manifest\";\n plugin: {\n name: string;\n version: string;\n };\n runtime: {\n remoteEntry: string;\n };\n contract?: {\n kind: \"orpc\";\n types: {\n path: string;\n exportName: string;\n typeName: string;\n sha256?: string;\n };\n };\n}\n\ninterface ContractSource {\n key: string;\n importName: string;\n sourceFilePath: string;\n generatedPath?: string;\n}\n\nfunction sha256(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction trimTrailingSlash(input: string): string {\n return input.replace(/\\/$/, \"\");\n}\n\nfunction sanitizeIdentifier(input: string): string {\n return input.replace(/[^A-Za-z0-9_]/g, \"_\").replace(/^[^A-Za-z_]+/, \"_\");\n}\n\nfunction toImportPath(fromFile: string, targetFile: string): string {\n const rel = relative(dirname(fromFile), targetFile).replace(/\\\\/g, \"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction writeFileIfChanged(filePath: string, content: string) {\n try {\n if (readFileSync(filePath, \"utf8\") === content) return false;\n } catch {\n // file does not exist yet\n }\n\n writeFileSync(filePath, content);\n return true;\n}\n\nfunction getApiPluginManifestUrl(apiBaseUrl: string): string {\n return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;\n}\n\nasync function fetchApiPluginManifest(apiBaseUrl: string): Promise<ApiPluginManifest> {\n const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));\n if (!response.ok) {\n throw new Error(\n `Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`,\n );\n }\n\n const manifest = (await response.json()) as ApiPluginManifest;\n if (manifest.schemaVersion !== 1 || manifest.kind !== \"every-plugin/manifest\") {\n throw new Error(\"Unsupported API plugin manifest format\");\n }\n\n return manifest;\n}\n\nfunction localApiContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"api\", \"src\", \"contract.ts\");\n return {\n key: \"api\",\n importName: \"BaseApiContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nasync function remoteContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n name: string;\n baseUrl: string;\n generatedSubdir: string;\n}): Promise<ContractSource> {\n const manifest = await fetchApiPluginManifest(opts.baseUrl);\n if (!manifest.contract) {\n throw new Error(\n `Plugin manifest for ${manifest.plugin.name} does not advertise contract types`,\n );\n }\n\n const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\\.\\//, \"\")}`;\n const contractResponse = await fetch(contractUrl);\n if (!contractResponse.ok) {\n throw new Error(\n `Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`,\n );\n }\n\n const contractTypes = await contractResponse.text();\n if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) {\n throw new Error(\"Fetched contract types failed checksum verification\");\n }\n\n const generatedPath = join(opts.runtimeDir, opts.generatedSubdir, \"contract.d.ts\");\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, contractTypes);\n\n return {\n key: opts.name,\n importName: `${sanitizeIdentifier(opts.name)}Contract`,\n sourceFilePath: generatedPath,\n generatedPath,\n };\n}\n\nasync function resolveContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n key: string;\n source: RuntimePluginConfig | { url: string; localPath?: string; name: string } | null;\n baseUrl: string;\n generatedSubdir: string;\n}): Promise<ContractSource> {\n if (\n opts.key === \"api\" &&\n (!opts.source || !(\"localPath\" in opts.source) || opts.source.localPath)\n ) {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath) {\n return {\n key: opts.key,\n importName: \"BaseApiContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return localApiContractSource(opts.configDir);\n }\n }\n\n if (opts.source && \"localPath\" in opts.source && opts.source.localPath) {\n return {\n key: opts.key,\n importName: `${sanitizeIdentifier(opts.key)}Contract`,\n sourceFilePath: join(opts.source.localPath, \"src\", \"contract.ts\"),\n };\n }\n\n return remoteContractSource({\n configDir: opts.configDir,\n runtimeDir: opts.runtimeDir,\n name: opts.key,\n baseUrl: opts.baseUrl,\n generatedSubdir: opts.generatedSubdir,\n });\n}\n\nfunction writeAggregateContractFile(opts: {\n configDir: string;\n sources: ContractSource[];\n pluginKeys: string[];\n}) {\n const baseSource = opts.sources.find((source) => source.key === \"api\");\n const pluginSources = opts.pluginKeys\n .map((key) => opts.sources.find((entry) => entry.key === key))\n .filter((source): source is ContractSource => Boolean(source));\n\n if (!baseSource) {\n throw new Error(\"API contract source is required to generate the aggregate contract\");\n }\n\n // --- Generate ui/src/api-contract.gen.ts ---\n const uiContractPath = join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\");\n const uiLines: string[] = [];\n\n for (const source of opts.sources) {\n const importPath = toImportPath(uiContractPath, source.sourceFilePath);\n uiLines.push(`import type { ContractType as ${source.importName} } from \"${importPath}\";`);\n }\n\n uiLines.push(\"\");\n if (pluginSources.length === 0) {\n uiLines.push(`export type ApiContract = ${baseSource.importName};`);\n } else {\n uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n uiLines.push(` ${key}: ${source.importName};`);\n }\n uiLines.push(\"};\");\n }\n mkdirSync(dirname(uiContractPath), { recursive: true });\n writeFileIfChanged(uiContractPath, `${uiLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/plugins-client.gen.ts ---\n const pluginsClientPath = join(opts.configDir, \"api\", \"src\", \"plugins-client.gen.ts\");\n const pluginsClientLines: string[] = [];\n\n for (const source of pluginSources) {\n const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);\n pluginsClientLines.push(\n `import type { ContractType as ${source.importName} } from \"${importPath}\";`,\n );\n }\n\n pluginsClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n pluginsClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n pluginsClientLines.push(\"\");\n\n if (pluginSources.length === 0) {\n pluginsClientLines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n pluginsClientLines.push(\"export type PluginsClient = {\");\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);\n }\n pluginsClientLines.push(\"};\");\n }\n\n mkdirSync(dirname(pluginsClientPath), { recursive: true });\n writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join(\"\\n\")}\\n`);\n\n return uiContractPath;\n}\n\nexport async function syncApiContractBridge(opts: {\n configDir: string;\n runtimeConfig: RuntimeConfig;\n apiBaseUrl: string;\n}): Promise<{\n bridgePath: string;\n generatedPath: string | null;\n manifest: ApiPluginManifest | null;\n source: \"local\" | \"remote\";\n}> {\n const runtimeDir = join(opts.configDir, \".bos\", \"generated\");\n const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n );\n const sources: ContractSource[] = [];\n let manifest: ApiPluginManifest | null = null;\n let generatedPath: string | null = null;\n\n const baseSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"api\",\n source: opts.runtimeConfig.api,\n baseUrl: opts.apiBaseUrl,\n generatedSubdir: \"api\",\n });\n sources.push(baseSource);\n\n for (const [key, plugin] of pluginEntries) {\n const source = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key,\n source: plugin,\n baseUrl: plugin.url,\n generatedSubdir: `plugins/${key}`,\n });\n sources.push(source);\n if (source.generatedPath) {\n generatedPath = source.generatedPath;\n }\n }\n\n writeAggregateContractFile({\n configDir: opts.configDir,\n sources,\n pluginKeys: pluginEntries.map(([key]) => key),\n });\n\n if (opts.runtimeConfig.api.source !== \"local\") {\n manifest = await fetchApiPluginManifest(opts.apiBaseUrl);\n }\n\n return {\n bridgePath: join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\"),\n generatedPath,\n manifest,\n source: opts.runtimeConfig.api.source,\n };\n}\n"],"mappings":"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"}
package/dist/api.cjs ADDED
@@ -0,0 +1,124 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
3
+ require('./sdk.cjs');
4
+ const require_mf = require('./mf.cjs');
5
+ const require_integrity = require('./integrity.cjs');
6
+ let every_plugin = require("every-plugin");
7
+
8
+ //#region src/api.ts
9
+ async function loadApiPlugin(opts) {
10
+ const remoteEntryUrl = (() => {
11
+ if (opts.entry.endsWith("/remoteEntry.js")) return opts.entry;
12
+ if (opts.entry.endsWith("/mf-manifest.json")) return `${opts.entry.replace(/\/mf-manifest\.json$/, "")}/remoteEntry.js`;
13
+ if (opts.entry.endsWith(".js")) return opts.entry;
14
+ return `${opts.entry.replace(/\/$/, "")}/remoteEntry.js`;
15
+ })();
16
+ if (opts.integrity) await require_integrity.verifySriForUrl(remoteEntryUrl, opts.integrity);
17
+ await require_mf.ensureNodeRuntimePlugin();
18
+ await require_mf.registerRemote({
19
+ name: opts.runtimeId,
20
+ entry: remoteEntryUrl
21
+ });
22
+ const plugin = await (0, every_plugin.createPluginRuntime)({
23
+ registry: { [opts.runtimeId]: { remote: remoteEntryUrl } },
24
+ secrets: opts.secrets ?? {}
25
+ }).usePlugin(opts.runtimeId, {
26
+ variables: opts.variables ?? {},
27
+ secrets: opts.secrets ?? {}
28
+ }, opts.plugins);
29
+ return {
30
+ key: opts.key,
31
+ name: opts.name,
32
+ router: plugin.router,
33
+ createClient: plugin.createClient,
34
+ metadata: {
35
+ remoteUrl: remoteEntryUrl,
36
+ version: plugin.metadata.version
37
+ }
38
+ };
39
+ }
40
+ function collectSecrets(config, envSecrets) {
41
+ const secrets = {};
42
+ for (const key of config.secrets ?? []) {
43
+ const value = envSecrets?.[key] ?? process.env[key];
44
+ if (value) secrets[key] = value;
45
+ }
46
+ return secrets;
47
+ }
48
+ async function loadApiPluginsFromRuntimeConfig(runtimeConfig, envSecrets) {
49
+ const entries = [];
50
+ if (runtimeConfig.api?.url) entries.push(["api", runtimeConfig.api]);
51
+ for (const [key, plugin] of Object.entries(runtimeConfig.plugins ?? {})) if (plugin.url) entries.push([key, plugin]);
52
+ if (entries.length === 0) {
53
+ console.log("[API] No plugins configured");
54
+ return {
55
+ base: null,
56
+ plugins: [],
57
+ errors: []
58
+ };
59
+ }
60
+ const pluginEntries = entries.filter(([key]) => key !== "api");
61
+ const apiEntry = entries.find(([key]) => key === "api");
62
+ const pluginResults = await Promise.allSettled(pluginEntries.map(async ([key, pluginConfig]) => {
63
+ console.log(`[API] Loading plugin: ${pluginConfig.name} from ${pluginConfig.entry}`);
64
+ return loadApiPlugin({
65
+ key,
66
+ runtimeId: pluginConfig.name,
67
+ name: pluginConfig.name,
68
+ entry: pluginConfig.entry,
69
+ variables: pluginConfig.variables,
70
+ secrets: collectSecrets(pluginConfig, envSecrets),
71
+ integrity: pluginConfig.integrity
72
+ });
73
+ }));
74
+ const plugins = [];
75
+ const errors = [];
76
+ const pluginsClient = {};
77
+ pluginResults.forEach((result, index) => {
78
+ const [key] = pluginEntries[index] ?? ["unknown"];
79
+ if (result.status === "fulfilled") {
80
+ plugins.push(result.value);
81
+ pluginsClient[key] = result.value.createClient;
82
+ } else errors.push({
83
+ key,
84
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
85
+ });
86
+ });
87
+ let base = null;
88
+ if (apiEntry) {
89
+ const [key, apiConfig] = apiEntry;
90
+ try {
91
+ console.log(`[API] Loading API plugin: ${apiConfig.name} from ${apiConfig.entry}`);
92
+ base = await loadApiPlugin({
93
+ key,
94
+ runtimeId: apiConfig.name,
95
+ name: apiConfig.name,
96
+ entry: apiConfig.entry,
97
+ variables: apiConfig.variables,
98
+ secrets: collectSecrets(apiConfig, envSecrets),
99
+ integrity: apiConfig.integrity,
100
+ plugins: pluginsClient
101
+ });
102
+ } catch (error) {
103
+ errors.push({
104
+ key,
105
+ error: error instanceof Error ? error.message : String(error)
106
+ });
107
+ }
108
+ }
109
+ return {
110
+ base,
111
+ plugins,
112
+ errors
113
+ };
114
+ }
115
+ function createStitchedRouter(baseRouter, plugins) {
116
+ if (!plugins || plugins.length === 0) return baseRouter;
117
+ return plugins.reduce((router, plugin) => Object.assign(router, plugin.router), baseRouter);
118
+ }
119
+
120
+ //#endregion
121
+ exports.createStitchedRouter = createStitchedRouter;
122
+ exports.loadApiPlugin = loadApiPlugin;
123
+ exports.loadApiPluginsFromRuntimeConfig = loadApiPluginsFromRuntimeConfig;
124
+ //# sourceMappingURL=api.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.cjs","names":["verifySriForUrl","ensureNodeRuntimePlugin","registerRemote"],"sources":["../src/api.ts"],"sourcesContent":["import { verifySriForUrl } from \"./integrity\";\nimport { ensureNodeRuntimePlugin, registerRemote } from \"./mf\";\nimport { createPluginRuntime } from \"./sdk\";\nimport type { RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface LoadedPluginResult {\n key: string;\n name: string;\n router: any;\n createClient: (context?: unknown) => any;\n metadata: {\n remoteUrl: string;\n version?: string;\n };\n}\n\nexport interface LoadedPluginsResult {\n base: LoadedPluginResult | null;\n plugins: LoadedPluginResult[];\n errors: Array<{ key: string; error: string }>;\n}\n\nexport async function loadApiPlugin(opts: {\n key: string;\n runtimeId: string;\n name: string;\n entry: string;\n variables?: Record<string, string>;\n secrets?: Record<string, string>;\n integrity?: string;\n plugins?: Record<string, unknown>;\n}): Promise<LoadedPluginResult> {\n const remoteEntryUrl = (() => {\n if (opts.entry.endsWith(\"/remoteEntry.js\")) return opts.entry;\n if (opts.entry.endsWith(\"/mf-manifest.json\")) {\n return `${opts.entry.replace(/\\/mf-manifest\\.json$/, \"\")}/remoteEntry.js`;\n }\n if (opts.entry.endsWith(\".js\")) return opts.entry;\n return `${opts.entry.replace(/\\/$/, \"\")}/remoteEntry.js`;\n })();\n\n if (opts.integrity) {\n await verifySriForUrl(remoteEntryUrl, opts.integrity);\n }\n\n await ensureNodeRuntimePlugin();\n await registerRemote({ name: opts.runtimeId, entry: remoteEntryUrl });\n\n const runtime: any = createPluginRuntime({\n registry: {\n [opts.runtimeId]: { remote: remoteEntryUrl },\n },\n secrets: opts.secrets ?? {},\n });\n\n // biome-ignore lint/correctness/useHookAtTopLevel: usePlugin is not a React hook\n const plugin = await runtime.usePlugin(\n opts.runtimeId,\n {\n variables: opts.variables ?? {},\n secrets: opts.secrets ?? {},\n },\n opts.plugins,\n );\n\n return {\n key: opts.key,\n name: opts.name,\n router: plugin.router,\n createClient: plugin.createClient as (context?: unknown) => any,\n metadata: {\n remoteUrl: remoteEntryUrl,\n version: plugin.metadata.version,\n },\n };\n}\n\nfunction collectSecrets(config: RuntimePluginConfig, envSecrets?: Record<string, string>) {\n const secrets: Record<string, string> = {};\n for (const key of config.secrets ?? []) {\n const value = envSecrets?.[key] ?? process.env[key];\n if (value) {\n secrets[key] = value;\n }\n }\n return secrets;\n}\n\nexport async function loadApiPluginsFromRuntimeConfig(\n runtimeConfig: RuntimeConfig,\n envSecrets?: Record<string, string>,\n): Promise<LoadedPluginsResult> {\n const entries: Array<[string, RuntimePluginConfig]> = [];\n\n if (runtimeConfig.api?.url) {\n entries.push([\"api\", runtimeConfig.api]);\n }\n\n for (const [key, plugin] of Object.entries(runtimeConfig.plugins ?? {})) {\n if (plugin.url) {\n entries.push([key, plugin]);\n }\n }\n\n if (entries.length === 0) {\n console.log(\"[API] No plugins configured\");\n return { base: null, plugins: [], errors: [] };\n }\n\n // Phase 1: Load non-API plugins first\n const pluginEntries = entries.filter(([key]) => key !== \"api\");\n const apiEntry = entries.find(([key]) => key === \"api\");\n\n const pluginResults = await Promise.allSettled(\n pluginEntries.map(async ([key, pluginConfig]) => {\n console.log(`[API] Loading plugin: ${pluginConfig.name} from ${pluginConfig.entry}`);\n return loadApiPlugin({\n key,\n runtimeId: pluginConfig.name,\n name: pluginConfig.name,\n entry: pluginConfig.entry,\n variables: pluginConfig.variables,\n secrets: collectSecrets(pluginConfig, envSecrets),\n integrity: pluginConfig.integrity,\n });\n }),\n );\n\n const plugins: LoadedPluginResult[] = [];\n const errors: Array<{ key: string; error: string }> = [];\n\n const pluginsClient: Record<string, unknown> = {};\n\n pluginResults.forEach((result, index) => {\n const [key] = pluginEntries[index] ?? [\"unknown\"];\n if (result.status === \"fulfilled\") {\n plugins.push(result.value);\n pluginsClient[key] = result.value.createClient;\n } else {\n errors.push({\n key,\n error: result.reason instanceof Error ? result.reason.message : String(result.reason),\n });\n }\n });\n\n // Phase 2: Load API plugin with injected plugins client\n let base: LoadedPluginResult | null = null;\n\n if (apiEntry) {\n const [key, apiConfig] = apiEntry;\n try {\n console.log(`[API] Loading API plugin: ${apiConfig.name} from ${apiConfig.entry}`);\n base = await loadApiPlugin({\n key,\n runtimeId: apiConfig.name,\n name: apiConfig.name,\n entry: apiConfig.entry,\n variables: apiConfig.variables,\n secrets: collectSecrets(apiConfig, envSecrets),\n integrity: apiConfig.integrity,\n plugins: pluginsClient,\n });\n } catch (error) {\n errors.push({\n key,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return { base, plugins, errors };\n}\n\nexport function createStitchedRouter(baseRouter: any, plugins: LoadedPluginResult[] | null): any {\n if (!plugins || plugins.length === 0) {\n return baseRouter;\n }\n\n return plugins.reduce((router, plugin) => Object.assign(router, plugin.router), baseRouter);\n}\n"],"mappings":"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"}