everything-dev 0.3.2 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/cli.js +10 -0
- package/dist/_virtual/_rolldown/runtime.cjs +29 -0
- package/dist/api-contract.cjs +172 -0
- package/dist/api-contract.cjs.map +1 -0
- package/dist/api-contract.mjs +171 -0
- package/dist/api-contract.mjs.map +1 -0
- package/dist/api.cjs +124 -0
- package/dist/api.cjs.map +1 -0
- package/dist/api.d.cts +36 -0
- package/dist/api.d.cts.map +1 -0
- package/dist/api.d.mts +36 -0
- package/dist/api.d.mts.map +1 -0
- package/dist/api.mjs +119 -0
- package/dist/api.mjs.map +1 -0
- package/dist/app.cjs +156 -0
- package/dist/app.cjs.map +1 -0
- package/dist/app.mjs +153 -0
- package/dist/app.mjs.map +1 -0
- package/dist/cli/catalog.cjs +30 -0
- package/dist/cli/catalog.cjs.map +1 -0
- package/dist/cli/catalog.mjs +29 -0
- package/dist/cli/catalog.mjs.map +1 -0
- package/dist/cli/help.cjs +16 -0
- package/dist/cli/help.cjs.map +1 -0
- package/dist/cli/help.mjs +16 -0
- package/dist/cli/help.mjs.map +1 -0
- package/dist/cli/init.cjs +317 -0
- package/dist/cli/init.cjs.map +1 -0
- package/dist/cli/init.d.cts +36 -0
- package/dist/cli/init.d.cts.map +1 -0
- package/dist/cli/init.d.mts +36 -0
- package/dist/cli/init.d.mts.map +1 -0
- package/dist/cli/init.mjs +309 -0
- package/dist/cli/init.mjs.map +1 -0
- package/dist/cli/parse.cjs +96 -0
- package/dist/cli/parse.cjs.map +1 -0
- package/dist/cli/parse.mjs +95 -0
- package/dist/cli/parse.mjs.map +1 -0
- package/dist/cli/prompts.cjs +42 -0
- package/dist/cli/prompts.cjs.map +1 -0
- package/dist/cli/prompts.mjs +41 -0
- package/dist/cli/prompts.mjs.map +1 -0
- package/dist/cli.cjs +167 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +166 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/components/dev-view.cjs +307 -0
- package/dist/components/dev-view.cjs.map +1 -0
- package/dist/components/dev-view.mjs +306 -0
- package/dist/components/dev-view.mjs.map +1 -0
- package/dist/components/streaming-view.cjs +146 -0
- package/dist/components/streaming-view.cjs.map +1 -0
- package/dist/components/streaming-view.mjs +144 -0
- package/dist/components/streaming-view.mjs.map +1 -0
- package/dist/config.cjs +280 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +35 -0
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +35 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +266 -0
- package/dist/config.mjs.map +1 -0
- package/dist/contract.cjs +209 -0
- package/dist/contract.cjs.map +1 -0
- package/dist/contract.d.cts +490 -0
- package/dist/contract.d.cts.map +1 -0
- package/dist/contract.d.mts +490 -0
- package/dist/contract.d.mts.map +1 -0
- package/dist/contract.meta.cjs +104 -0
- package/dist/contract.meta.cjs.map +1 -0
- package/dist/contract.meta.d.cts +141 -0
- package/dist/contract.meta.d.cts.map +1 -0
- package/dist/contract.meta.d.mts +141 -0
- package/dist/contract.meta.d.mts.map +1 -0
- package/dist/contract.meta.mjs +102 -0
- package/dist/contract.meta.mjs.map +1 -0
- package/dist/contract.mjs +186 -0
- package/dist/contract.mjs.map +1 -0
- package/dist/dev-logs.cjs +53 -0
- package/dist/dev-logs.cjs.map +1 -0
- package/dist/dev-logs.mjs +51 -0
- package/dist/dev-logs.mjs.map +1 -0
- package/dist/dev-session.cjs +195 -0
- package/dist/dev-session.cjs.map +1 -0
- package/dist/dev-session.mjs +194 -0
- package/dist/dev-session.mjs.map +1 -0
- package/dist/fastkv.cjs +89 -0
- package/dist/fastkv.cjs.map +1 -0
- package/dist/fastkv.d.cts +11 -0
- package/dist/fastkv.d.cts.map +1 -0
- package/dist/fastkv.d.mts +11 -0
- package/dist/fastkv.d.mts.map +1 -0
- package/dist/fastkv.mjs +82 -0
- package/dist/fastkv.mjs.map +1 -0
- package/dist/federation.server.cjs +27 -0
- package/dist/federation.server.cjs.map +1 -0
- package/dist/federation.server.mjs +27 -0
- package/dist/federation.server.mjs.map +1 -0
- package/dist/host.cjs +367 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.d.cts +22 -0
- package/dist/host.d.cts.map +1 -0
- package/dist/host.d.mts +22 -0
- package/dist/host.d.mts.map +1 -0
- package/dist/host.mjs +364 -0
- package/dist/host.mjs.map +1 -0
- package/dist/index.cjs +122 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +9 -0
- package/dist/integrity.cjs +39 -0
- package/dist/integrity.cjs.map +1 -0
- package/dist/integrity.d.cts +7 -0
- package/dist/integrity.d.cts.map +1 -0
- package/dist/integrity.d.mts +7 -0
- package/dist/integrity.d.mts.map +1 -0
- package/dist/integrity.mjs +35 -0
- package/dist/integrity.mjs.map +1 -0
- package/dist/mf.cjs +77 -0
- package/dist/mf.cjs.map +1 -0
- package/dist/mf.d.cts +19 -0
- package/dist/mf.d.cts.map +1 -0
- package/dist/mf.d.mts +19 -0
- package/dist/mf.d.mts.map +1 -0
- package/dist/mf.mjs +71 -0
- package/dist/mf.mjs.map +1 -0
- package/dist/near-cli.cjs +196 -0
- package/dist/near-cli.cjs.map +1 -0
- package/dist/near-cli.mjs +193 -0
- package/dist/near-cli.mjs.map +1 -0
- package/dist/network.cjs +9 -0
- package/dist/network.cjs.map +1 -0
- package/dist/network.mjs +8 -0
- package/dist/network.mjs.map +1 -0
- package/dist/orchestrator.cjs +441 -0
- package/dist/orchestrator.cjs.map +1 -0
- package/dist/orchestrator.d.cts +40 -0
- package/dist/orchestrator.d.cts.map +1 -0
- package/dist/orchestrator.d.mts +40 -0
- package/dist/orchestrator.d.mts.map +1 -0
- package/dist/orchestrator.mjs +436 -0
- package/dist/orchestrator.mjs.map +1 -0
- package/dist/plugin.cjs +825 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +347 -0
- package/dist/plugin.d.cts.map +1 -0
- package/dist/plugin.d.mts +348 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +822 -0
- package/dist/plugin.mjs.map +1 -0
- package/dist/process-registry.cjs +120 -0
- package/dist/process-registry.cjs.map +1 -0
- package/dist/process-registry.d.cts +25 -0
- package/dist/process-registry.d.cts.map +1 -0
- package/dist/process-registry.d.mts +25 -0
- package/dist/process-registry.d.mts.map +1 -0
- package/dist/process-registry.mjs +119 -0
- package/dist/process-registry.mjs.map +1 -0
- package/dist/sdk.cjs +61 -0
- package/dist/sdk.d.cts +5 -0
- package/dist/sdk.d.mts +5 -0
- package/dist/sdk.mjs +6 -0
- package/dist/shared.cjs +143 -0
- package/dist/shared.cjs.map +1 -0
- package/dist/shared.d.cts +33 -0
- package/dist/shared.d.cts.map +1 -0
- package/dist/shared.d.mts +33 -0
- package/dist/shared.d.mts.map +1 -0
- package/dist/shared.mjs +140 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/types.cjs +160 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +269 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +269 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +144 -0
- package/dist/types.mjs.map +1 -0
- package/dist/ui/head.cjs +67 -0
- package/dist/ui/head.cjs.map +1 -0
- package/dist/ui/head.d.cts +19 -0
- package/dist/ui/head.d.cts.map +1 -0
- package/dist/ui/head.d.mts +19 -0
- package/dist/ui/head.d.mts.map +1 -0
- package/dist/ui/head.mjs +61 -0
- package/dist/ui/head.mjs.map +1 -0
- package/dist/ui/index.cjs +32 -0
- package/dist/ui/index.d.cts +7 -0
- package/dist/ui/index.d.mts +7 -0
- package/dist/ui/index.mjs +6 -0
- package/dist/ui/metadata.cjs +106 -0
- package/dist/ui/metadata.cjs.map +1 -0
- package/dist/ui/metadata.d.cts +35 -0
- package/dist/ui/metadata.d.cts.map +1 -0
- package/dist/ui/metadata.d.mts +35 -0
- package/dist/ui/metadata.d.mts.map +1 -0
- package/dist/ui/metadata.mjs +100 -0
- package/dist/ui/metadata.mjs.map +1 -0
- package/dist/ui/router.cjs +56 -0
- package/dist/ui/router.cjs.map +1 -0
- package/dist/ui/router.d.cts +11 -0
- package/dist/ui/router.d.cts.map +1 -0
- package/dist/ui/router.d.mts +11 -0
- package/dist/ui/router.d.mts.map +1 -0
- package/dist/ui/router.mjs +51 -0
- package/dist/ui/router.mjs.map +1 -0
- package/dist/ui/runtime.cjs +65 -0
- package/dist/ui/runtime.cjs.map +1 -0
- package/dist/ui/runtime.d.cts +29 -0
- package/dist/ui/runtime.d.cts.map +1 -0
- package/dist/ui/runtime.d.mts +29 -0
- package/dist/ui/runtime.d.mts.map +1 -0
- package/dist/ui/runtime.mjs +53 -0
- package/dist/ui/runtime.mjs.map +1 -0
- package/dist/ui/types.cjs +0 -0
- package/dist/ui/types.d.cts +52 -0
- package/dist/ui/types.d.cts.map +1 -0
- package/dist/ui/types.d.mts +52 -0
- package/dist/ui/types.d.mts.map +1 -0
- package/dist/ui/types.mjs +1 -0
- package/dist/utils/banner.cjs +24 -0
- package/dist/utils/banner.cjs.map +1 -0
- package/dist/utils/banner.mjs +23 -0
- package/dist/utils/banner.mjs.map +1 -0
- package/dist/utils/linkify.cjs +15 -0
- package/dist/utils/linkify.cjs.map +1 -0
- package/dist/utils/linkify.mjs +14 -0
- package/dist/utils/linkify.mjs.map +1 -0
- package/dist/utils/run.cjs +40 -0
- package/dist/utils/run.cjs.map +1 -0
- package/dist/utils/run.mjs +39 -0
- package/dist/utils/run.mjs.map +1 -0
- package/dist/utils/theme.cjs +44 -0
- package/dist/utils/theme.cjs.map +1 -0
- package/dist/utils/theme.mjs +37 -0
- package/dist/utils/theme.mjs.map +1 -0
- package/package.json +269 -80
- package/src/api-contract.ts +309 -0
- package/src/api.ts +181 -0
- package/src/app.ts +346 -0
- package/src/cli/catalog.ts +49 -0
- package/src/cli/help.ts +13 -0
- package/src/cli/init.ts +415 -0
- package/src/cli/parse.ts +130 -0
- package/src/cli/prompts.ts +64 -0
- package/src/cli.ts +203 -1507
- package/src/components/dev-view.tsx +104 -41
- package/src/components/streaming-view.ts +89 -22
- package/src/config.ts +462 -532
- package/src/contract.meta.ts +96 -0
- package/src/contract.ts +164 -561
- package/src/dev-logs.ts +85 -0
- package/src/dev-session.ts +318 -0
- package/src/fastkv.ts +153 -0
- package/src/federation.server.ts +43 -0
- package/src/host.ts +526 -0
- package/src/index.ts +6 -3
- package/src/integrity.ts +54 -0
- package/src/mf.ts +105 -0
- package/src/near-cli.ts +284 -0
- package/src/network.ts +3 -0
- package/src/orchestrator.ts +648 -0
- package/src/plugin.ts +1116 -2303
- package/src/process-registry.ts +154 -0
- package/src/scripts/sync-api-contract.ts +24 -0
- package/src/sdk.ts +14 -0
- package/src/shared.ts +206 -0
- package/src/types.ts +152 -206
- package/src/ui/head.ts +34 -27
- package/src/ui/index.ts +3 -3
- package/src/ui/metadata.ts +95 -0
- package/src/ui/router.ts +22 -6
- package/src/ui/runtime.ts +55 -6
- package/src/ui/types.ts +24 -11
- package/src/utils/banner.ts +10 -6
- package/src/utils/run.ts +26 -27
- package/src/utils/theme.ts +3 -66
- package/src/components/monitor-view.tsx +0 -475
- package/src/components/status-view.tsx +0 -173
- package/src/lib/env.ts +0 -109
- package/src/lib/near-cli.ts +0 -289
- package/src/lib/nova.ts +0 -266
- package/src/lib/orchestrator.ts +0 -276
- package/src/lib/process-registry.ts +0 -166
- package/src/lib/process.ts +0 -549
- package/src/lib/resource-monitor/assertions.ts +0 -234
- package/src/lib/resource-monitor/command.ts +0 -283
- package/src/lib/resource-monitor/diff.ts +0 -157
- package/src/lib/resource-monitor/errors.ts +0 -127
- package/src/lib/resource-monitor/index.ts +0 -305
- package/src/lib/resource-monitor/platform/darwin.ts +0 -306
- package/src/lib/resource-monitor/platform/index.ts +0 -35
- package/src/lib/resource-monitor/platform/linux.ts +0 -332
- package/src/lib/resource-monitor/platform/windows.ts +0 -298
- package/src/lib/resource-monitor/snapshot.ts +0 -217
- package/src/lib/resource-monitor/types.ts +0 -74
- package/src/lib/session-recorder/errors.ts +0 -102
- package/src/lib/session-recorder/flows/login.ts +0 -210
- package/src/lib/session-recorder/index.ts +0 -361
- package/src/lib/session-recorder/playwright.ts +0 -257
- package/src/lib/session-recorder/report.ts +0 -353
- package/src/lib/session-recorder/server.ts +0 -267
- package/src/lib/session-recorder/types.ts +0 -115
- package/src/lib/sync.ts +0 -1
- package/src/ui/files.ts +0 -134
package/src/host.ts
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import { serve } from "@hono/node-server";
|
|
2
|
+
import { OpenAPIHandler } from "@orpc/openapi/fetch";
|
|
3
|
+
import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
|
|
4
|
+
import { RPCHandler } from "@orpc/server/fetch";
|
|
5
|
+
import { BatchHandlerPlugin } from "@orpc/server/plugins";
|
|
6
|
+
import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
|
|
7
|
+
import { Hono } from "hono";
|
|
8
|
+
import { cors } from "hono/cors";
|
|
9
|
+
import { secureHeaders } from "hono/secure-headers";
|
|
10
|
+
import {
|
|
11
|
+
createStitchedRouter,
|
|
12
|
+
type LoadedPluginResult,
|
|
13
|
+
type LoadedPluginsResult,
|
|
14
|
+
loadApiPluginsFromRuntimeConfig,
|
|
15
|
+
} from "./api";
|
|
16
|
+
import { loadRouterModule, type RouterModule } from "./federation.server";
|
|
17
|
+
import type { ClientRuntimeConfig, RuntimeConfig } from "./types";
|
|
18
|
+
|
|
19
|
+
export interface HostServerConfig {
|
|
20
|
+
runtimeConfig: RuntimeConfig;
|
|
21
|
+
configDir: string;
|
|
22
|
+
port?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface HostServerHandle {
|
|
26
|
+
ready: Promise<void>;
|
|
27
|
+
shutdown: () => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildClientRuntimeConfig(runtimeConfig: RuntimeConfig): ClientRuntimeConfig {
|
|
31
|
+
return {
|
|
32
|
+
env: runtimeConfig.env,
|
|
33
|
+
account: runtimeConfig.account,
|
|
34
|
+
networkId: runtimeConfig.networkId,
|
|
35
|
+
hostUrl: runtimeConfig.hostUrl,
|
|
36
|
+
assetsUrl: runtimeConfig.ui.url,
|
|
37
|
+
apiBase: "/api",
|
|
38
|
+
rpcBase: "/api/rpc",
|
|
39
|
+
ui: runtimeConfig.ui
|
|
40
|
+
? {
|
|
41
|
+
name: runtimeConfig.ui.name,
|
|
42
|
+
url: runtimeConfig.ui.url,
|
|
43
|
+
entry: runtimeConfig.ui.entry,
|
|
44
|
+
integrity: runtimeConfig.ui.integrity,
|
|
45
|
+
}
|
|
46
|
+
: undefined,
|
|
47
|
+
api: runtimeConfig.api
|
|
48
|
+
? {
|
|
49
|
+
name: runtimeConfig.api.name,
|
|
50
|
+
url: runtimeConfig.api.url,
|
|
51
|
+
entry: runtimeConfig.api.entry,
|
|
52
|
+
integrity: runtimeConfig.api.integrity,
|
|
53
|
+
}
|
|
54
|
+
: undefined,
|
|
55
|
+
plugins: runtimeConfig.plugins
|
|
56
|
+
? Object.fromEntries(
|
|
57
|
+
Object.entries(runtimeConfig.plugins).map(([key, plugin]) => [
|
|
58
|
+
key,
|
|
59
|
+
{
|
|
60
|
+
name: plugin.name,
|
|
61
|
+
url: plugin.url,
|
|
62
|
+
entry: plugin.entry,
|
|
63
|
+
integrity: plugin.integrity,
|
|
64
|
+
},
|
|
65
|
+
]),
|
|
66
|
+
)
|
|
67
|
+
: undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function renderLoadingShell(runtimeConfig: ClientRuntimeConfig, error?: string | null) {
|
|
72
|
+
const bootstrap = `window.__RUNTIME_CONFIG__ = ${JSON.stringify(runtimeConfig)};window.addEventListener('load', function handleEverythingDevHydrate() { window.__hydrate?.(); }, { once: true });`;
|
|
73
|
+
const errorMarkup = error
|
|
74
|
+
? `<p style="color: #fca5a5;">Error loading UI: ${escapeHtml(error)}</p>`
|
|
75
|
+
: "<p>Loading UI...</p>";
|
|
76
|
+
|
|
77
|
+
const uiIntegrity = runtimeConfig.ui?.integrity;
|
|
78
|
+
const sriAttr = uiIntegrity ? ` integrity="${uiIntegrity}" crossorigin="anonymous"` : "";
|
|
79
|
+
|
|
80
|
+
return `
|
|
81
|
+
<!DOCTYPE html>
|
|
82
|
+
<html lang="en">
|
|
83
|
+
<head>
|
|
84
|
+
<meta charset="utf-8" />
|
|
85
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
86
|
+
<title>Loading...</title>
|
|
87
|
+
<style>
|
|
88
|
+
body { background: #171717; color: #fafafa; display: flex; align-items: center; justify-content: center; height: 100vh; font-family: system-ui; }
|
|
89
|
+
.fade { animation: fadeIn 0.3s ease-in; text-align: center; padding: 2rem; }
|
|
90
|
+
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
91
|
+
</style>
|
|
92
|
+
${runtimeConfig.assetsUrl ? `<script src="${runtimeConfig.assetsUrl}/remoteEntry.js"${sriAttr}></script>` : ""}
|
|
93
|
+
<script>${bootstrap}</script>
|
|
94
|
+
</head>
|
|
95
|
+
<body>
|
|
96
|
+
<div id="root" class="fade">${errorMarkup}</div>
|
|
97
|
+
</body>
|
|
98
|
+
</html>
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function escapeHtml(value: string) {
|
|
103
|
+
return value
|
|
104
|
+
.replaceAll("&", "&")
|
|
105
|
+
.replaceAll("<", "<")
|
|
106
|
+
.replaceAll(">", ">")
|
|
107
|
+
.replaceAll('"', """)
|
|
108
|
+
.replaceAll("'", "'");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function createHostServer(config: HostServerConfig): HostServerHandle {
|
|
112
|
+
const port = config.port ?? 3000;
|
|
113
|
+
const { runtimeConfig } = config;
|
|
114
|
+
|
|
115
|
+
let shutdownImpl: (() => Promise<void>) | null = null;
|
|
116
|
+
|
|
117
|
+
const ready = (async () => {
|
|
118
|
+
const started = await runHostServer({ runtimeConfig, port });
|
|
119
|
+
shutdownImpl = started.shutdown;
|
|
120
|
+
})();
|
|
121
|
+
|
|
122
|
+
const shutdown = async () => {
|
|
123
|
+
console.log("[Host] Shutting down...");
|
|
124
|
+
const timeout = setTimeout(() => {
|
|
125
|
+
console.log("[Host] Force exit");
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}, 5000);
|
|
128
|
+
await ready.catch(() => {});
|
|
129
|
+
if (shutdownImpl) {
|
|
130
|
+
await shutdownImpl().catch(() => {});
|
|
131
|
+
}
|
|
132
|
+
clearTimeout(timeout);
|
|
133
|
+
console.log("[Host] Shutdown complete");
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return { ready, shutdown };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function runHostServer(opts: {
|
|
140
|
+
runtimeConfig: RuntimeConfig;
|
|
141
|
+
port: number;
|
|
142
|
+
}): Promise<{ shutdown: () => Promise<void> }> {
|
|
143
|
+
const { runtimeConfig, port } = opts;
|
|
144
|
+
|
|
145
|
+
let apiPlugins: LoadedPluginResult[] = [];
|
|
146
|
+
let baseApiPlugin: LoadedPluginResult | null = null;
|
|
147
|
+
let apiPluginError: string | null = null;
|
|
148
|
+
let apiPluginLoading: Promise<LoadedPluginsResult | null> | null = null;
|
|
149
|
+
let ssrRouterModule: RouterModule | null = null;
|
|
150
|
+
let ssrRouterError: string | null = null;
|
|
151
|
+
let ssrRouterLoading: Promise<RouterModule | null> | null = null;
|
|
152
|
+
let rpcHandler: RPCHandler<any> | null = null;
|
|
153
|
+
let openApiHandler: OpenAPIHandler<any> | null = null;
|
|
154
|
+
|
|
155
|
+
const clientRuntimeConfig = buildClientRuntimeConfig(runtimeConfig);
|
|
156
|
+
|
|
157
|
+
const initApiHandlers = () => {
|
|
158
|
+
const baseRouter = baseApiPlugin?.router ?? {};
|
|
159
|
+
const stitchedRouter = createStitchedRouter(
|
|
160
|
+
baseRouter,
|
|
161
|
+
apiPlugins.filter((plugin) => plugin.key !== "api"),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (!baseApiPlugin) {
|
|
165
|
+
rpcHandler = null;
|
|
166
|
+
openApiHandler = null;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
rpcHandler = new RPCHandler(stitchedRouter as any, {
|
|
171
|
+
plugins: [new BatchHandlerPlugin()],
|
|
172
|
+
});
|
|
173
|
+
openApiHandler = new OpenAPIHandler(stitchedRouter as any, {
|
|
174
|
+
plugins: [
|
|
175
|
+
new OpenAPIReferencePlugin({
|
|
176
|
+
schemaConverters: [new ZodToJsonSchemaConverter()],
|
|
177
|
+
}),
|
|
178
|
+
],
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const ensureApiPluginLoaded = async (): Promise<LoadedPluginsResult | null> => {
|
|
183
|
+
if (apiPlugins.length > 0) return { base: baseApiPlugin, plugins: apiPlugins, errors: [] };
|
|
184
|
+
if (!runtimeConfig.api) return null;
|
|
185
|
+
if (apiPluginLoading) return apiPluginLoading;
|
|
186
|
+
|
|
187
|
+
apiPluginLoading = loadApiPluginsFromRuntimeConfig(runtimeConfig, process.env as any)
|
|
188
|
+
.then((loaded) => {
|
|
189
|
+
if (loaded) {
|
|
190
|
+
apiPlugins = loaded.plugins;
|
|
191
|
+
baseApiPlugin = loaded.base;
|
|
192
|
+
apiPluginError =
|
|
193
|
+
loaded.errors.length > 0 ? loaded.errors.map((item) => item.error).join("; ") : null;
|
|
194
|
+
initApiHandlers();
|
|
195
|
+
}
|
|
196
|
+
return loaded;
|
|
197
|
+
})
|
|
198
|
+
.catch((e) => {
|
|
199
|
+
apiPluginError = e instanceof Error ? e.message : String(e);
|
|
200
|
+
return null;
|
|
201
|
+
})
|
|
202
|
+
.finally(() => {
|
|
203
|
+
apiPluginLoading = null;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return apiPluginLoading;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const ensureRouterModuleLoaded = async (): Promise<RouterModule | null> => {
|
|
210
|
+
if (ssrRouterModule) {
|
|
211
|
+
return ssrRouterModule;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (ssrRouterLoading) {
|
|
215
|
+
return ssrRouterLoading;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
ssrRouterLoading = loadRouterModule(runtimeConfig)
|
|
219
|
+
.then((routerModule) => {
|
|
220
|
+
ssrRouterModule = routerModule;
|
|
221
|
+
ssrRouterError = null;
|
|
222
|
+
return routerModule;
|
|
223
|
+
})
|
|
224
|
+
.catch((error) => {
|
|
225
|
+
ssrRouterError = error instanceof Error ? error.message : String(error);
|
|
226
|
+
return null;
|
|
227
|
+
})
|
|
228
|
+
.finally(() => {
|
|
229
|
+
ssrRouterLoading = null;
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return ssrRouterLoading;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Kick off API plugin load in the background; host should still start even if
|
|
236
|
+
// the remote isn't ready yet.
|
|
237
|
+
void ensureApiPluginLoaded();
|
|
238
|
+
void ensureRouterModuleLoaded();
|
|
239
|
+
|
|
240
|
+
const app = new Hono();
|
|
241
|
+
|
|
242
|
+
const corsOrigins = process.env.CORS_ORIGIN?.split(",").map((o) => o.trim()) ?? [
|
|
243
|
+
runtimeConfig.hostUrl,
|
|
244
|
+
...(runtimeConfig.ui?.url ? [runtimeConfig.ui.url] : []),
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
if (!process.env.CORS_ORIGIN && runtimeConfig.env === "production") {
|
|
248
|
+
console.warn(
|
|
249
|
+
"[Security] CORS_ORIGIN is not set in production. Using host and UI URLs as allowed origins.",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
app.use(
|
|
254
|
+
"/*",
|
|
255
|
+
cors({
|
|
256
|
+
origin: corsOrigins,
|
|
257
|
+
credentials: true,
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
app.use("*", secureHeaders());
|
|
262
|
+
|
|
263
|
+
app.get("/health", (c) => c.text("OK"));
|
|
264
|
+
app.get("/ready", async (c) => {
|
|
265
|
+
type Check = {
|
|
266
|
+
name: string;
|
|
267
|
+
url: string;
|
|
268
|
+
required: boolean;
|
|
269
|
+
ok: boolean;
|
|
270
|
+
status?: number;
|
|
271
|
+
latencyMs?: number;
|
|
272
|
+
error?: string;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const probe = async (url: string, timeoutMs = 400): Promise<Check> => {
|
|
276
|
+
const started = Date.now();
|
|
277
|
+
const controller = new AbortController();
|
|
278
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
279
|
+
try {
|
|
280
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
281
|
+
return {
|
|
282
|
+
name: "",
|
|
283
|
+
url,
|
|
284
|
+
required: true,
|
|
285
|
+
ok: res.ok,
|
|
286
|
+
status: res.status,
|
|
287
|
+
latencyMs: Date.now() - started,
|
|
288
|
+
};
|
|
289
|
+
} catch (e) {
|
|
290
|
+
return {
|
|
291
|
+
name: "",
|
|
292
|
+
url,
|
|
293
|
+
required: true,
|
|
294
|
+
ok: false,
|
|
295
|
+
latencyMs: Date.now() - started,
|
|
296
|
+
error: e instanceof Error ? e.message : String(e),
|
|
297
|
+
};
|
|
298
|
+
} finally {
|
|
299
|
+
clearTimeout(timer);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const checks: Check[] = [];
|
|
304
|
+
|
|
305
|
+
if (runtimeConfig.ui?.url) {
|
|
306
|
+
const base = runtimeConfig.ui.url.replace(/\/$/, "");
|
|
307
|
+
const manifest = await probe(`${base}/mf-manifest.json`);
|
|
308
|
+
manifest.name = "ui";
|
|
309
|
+
// mf-manifest.json is preferred but not always present; fall back to
|
|
310
|
+
// remoteEntry.js for readiness.
|
|
311
|
+
manifest.required = false;
|
|
312
|
+
checks.push(manifest);
|
|
313
|
+
if (!manifest.ok) {
|
|
314
|
+
const remoteEntry = await probe(`${base}/remoteEntry.js`);
|
|
315
|
+
remoteEntry.name = "ui";
|
|
316
|
+
remoteEntry.required = true;
|
|
317
|
+
checks.push(remoteEntry);
|
|
318
|
+
} else {
|
|
319
|
+
manifest.required = true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (runtimeConfig.ui?.ssrUrl) {
|
|
324
|
+
const base = runtimeConfig.ui.ssrUrl.replace(/\/$/, "");
|
|
325
|
+
const ssr = await probe(`${base}/`);
|
|
326
|
+
ssr.name = "ui-ssr";
|
|
327
|
+
ssr.required = false;
|
|
328
|
+
checks.push(ssr);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (runtimeConfig.api?.url) {
|
|
332
|
+
const base = runtimeConfig.api.url.replace(/\/$/, "");
|
|
333
|
+
const api = await probe(`${base}/`);
|
|
334
|
+
api.name = "api";
|
|
335
|
+
api.required = true;
|
|
336
|
+
checks.push(api);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (runtimeConfig.api) {
|
|
340
|
+
checks.push({
|
|
341
|
+
name: "api-plugin",
|
|
342
|
+
url: runtimeConfig.api.entry,
|
|
343
|
+
required: true,
|
|
344
|
+
ok: baseApiPlugin !== null,
|
|
345
|
+
status: baseApiPlugin !== null ? 200 : 503,
|
|
346
|
+
error:
|
|
347
|
+
baseApiPlugin !== null
|
|
348
|
+
? undefined
|
|
349
|
+
: apiPluginLoading
|
|
350
|
+
? "loading"
|
|
351
|
+
: (apiPluginError ?? "not loaded"),
|
|
352
|
+
});
|
|
353
|
+
if (!baseApiPlugin && !apiPluginLoading) {
|
|
354
|
+
void ensureApiPluginLoaded();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
for (const [key, plugin] of Object.entries(runtimeConfig.plugins ?? {})) {
|
|
359
|
+
const loaded = apiPlugins.find((item) => item.key === key);
|
|
360
|
+
checks.push({
|
|
361
|
+
name: key,
|
|
362
|
+
url: plugin.entry,
|
|
363
|
+
required: true,
|
|
364
|
+
ok: Boolean(loaded),
|
|
365
|
+
status: loaded ? 200 : 503,
|
|
366
|
+
error: loaded ? undefined : (apiPluginError ?? "not loaded"),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const allRequiredOk = checks.filter((x) => x.required).every((x) => x.ok);
|
|
371
|
+
|
|
372
|
+
return c.json(
|
|
373
|
+
{
|
|
374
|
+
status: allRequiredOk ? "ready" : "not_ready",
|
|
375
|
+
host: {
|
|
376
|
+
url: runtimeConfig.hostUrl,
|
|
377
|
+
env: runtimeConfig.env,
|
|
378
|
+
},
|
|
379
|
+
checks,
|
|
380
|
+
timestamp: new Date().toISOString(),
|
|
381
|
+
},
|
|
382
|
+
allRequiredOk ? 200 : 503,
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
app.get("/api/_health", (c) =>
|
|
386
|
+
c.json({
|
|
387
|
+
status: "ready",
|
|
388
|
+
mode: runtimeConfig.env,
|
|
389
|
+
ui: runtimeConfig.ui?.url ?? null,
|
|
390
|
+
uiSsr: runtimeConfig.ui?.ssrUrl ?? null,
|
|
391
|
+
ssrRouterLoaded: ssrRouterModule !== null,
|
|
392
|
+
ssrRouterLoading: ssrRouterLoading !== null,
|
|
393
|
+
ssrRouterError,
|
|
394
|
+
api: runtimeConfig.api?.url ?? null,
|
|
395
|
+
apiPluginLoaded: baseApiPlugin !== null,
|
|
396
|
+
apiPluginLoading: apiPluginLoading !== null,
|
|
397
|
+
apiPluginError,
|
|
398
|
+
}),
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
app.all("/api/rpc/*", async (c) => {
|
|
402
|
+
if (!rpcHandler) {
|
|
403
|
+
await ensureApiPluginLoaded();
|
|
404
|
+
}
|
|
405
|
+
if (!rpcHandler) {
|
|
406
|
+
return c.json({ error: "API plugin not loaded", detail: apiPluginError }, 503);
|
|
407
|
+
}
|
|
408
|
+
const result = await rpcHandler.handle(c.req.raw, {
|
|
409
|
+
prefix: "/api/rpc",
|
|
410
|
+
context: {},
|
|
411
|
+
});
|
|
412
|
+
return result.response
|
|
413
|
+
? c.newResponse(result.response.body, result.response)
|
|
414
|
+
: c.text("Not Found", 404);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
app.all("/api/*", async (c) => {
|
|
418
|
+
if (!openApiHandler) {
|
|
419
|
+
await ensureApiPluginLoaded();
|
|
420
|
+
}
|
|
421
|
+
if (!openApiHandler) {
|
|
422
|
+
return c.json({ error: "API plugin not loaded", detail: apiPluginError }, 503);
|
|
423
|
+
}
|
|
424
|
+
const result = await openApiHandler.handle(c.req.raw, {
|
|
425
|
+
prefix: "/api",
|
|
426
|
+
context: {},
|
|
427
|
+
});
|
|
428
|
+
return result.response
|
|
429
|
+
? c.newResponse(result.response.body, result.response)
|
|
430
|
+
: c.text("Not Found", 404);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
if (runtimeConfig.ui) {
|
|
434
|
+
app.all("/__mf/ui/*", async (c) => {
|
|
435
|
+
const targetUrl = `${runtimeConfig.ui!.url}${c.req.path.replace("/__mf/ui", "")}`;
|
|
436
|
+
const response = await fetch(targetUrl, {
|
|
437
|
+
method: c.req.method,
|
|
438
|
+
headers: c.req.header(),
|
|
439
|
+
});
|
|
440
|
+
return response;
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
if (runtimeConfig.ui.ssrUrl) {
|
|
444
|
+
app.all("/__mf/ui/ssr/*", async (c) => {
|
|
445
|
+
const targetUrl = `${runtimeConfig.ui!.ssrUrl}${c.req.path.replace("/__mf/ui/ssr", "")}`;
|
|
446
|
+
const response = await fetch(targetUrl, {
|
|
447
|
+
method: c.req.method,
|
|
448
|
+
headers: c.req.header(),
|
|
449
|
+
});
|
|
450
|
+
return response;
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
app.get("*", async (c) => {
|
|
456
|
+
const routerModule = await ensureRouterModuleLoaded();
|
|
457
|
+
|
|
458
|
+
if (!routerModule) {
|
|
459
|
+
return c.html(renderLoadingShell(clientRuntimeConfig, ssrRouterError), 503);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
try {
|
|
463
|
+
const apiClient = baseApiPlugin?.createClient();
|
|
464
|
+
|
|
465
|
+
const result = await routerModule.renderToStream(c.req.raw, {
|
|
466
|
+
assetsUrl: runtimeConfig.ui.url,
|
|
467
|
+
runtimeConfig: clientRuntimeConfig,
|
|
468
|
+
apiClient,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
return new Response(result.stream, {
|
|
472
|
+
status: result.statusCode,
|
|
473
|
+
headers: result.headers,
|
|
474
|
+
});
|
|
475
|
+
} catch (error) {
|
|
476
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
477
|
+
return c.html(renderLoadingShell(clientRuntimeConfig, message), 500);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const hostname = process.env.HOST ?? "0.0.0.0";
|
|
482
|
+
let resolveReady: (() => void) | null = null;
|
|
483
|
+
const ready = new Promise<void>((resolve) => {
|
|
484
|
+
resolveReady = resolve;
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
const proxiedFetch = (req: Request): Response | Promise<Response> => {
|
|
488
|
+
const url = new URL(req.url);
|
|
489
|
+
const forwardedProto = req.headers.get("x-forwarded-proto");
|
|
490
|
+
const forwardedHost = req.headers.get("x-forwarded-host");
|
|
491
|
+
|
|
492
|
+
if (forwardedProto) {
|
|
493
|
+
url.protocol = forwardedProto;
|
|
494
|
+
}
|
|
495
|
+
if (forwardedHost) {
|
|
496
|
+
url.host = forwardedHost;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (forwardedProto || forwardedHost) {
|
|
500
|
+
req = new Request(url, req);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return app.fetch(req);
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
const server = serve({ fetch: proxiedFetch, port, hostname }, (info) => {
|
|
507
|
+
console.log(`[Host] Server running at http://${hostname}:${info.port}`);
|
|
508
|
+
console.log(`[Host] API: http://${hostname}:${info.port}/api/rpc`);
|
|
509
|
+
resolveReady?.();
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
await ready;
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
shutdown: () =>
|
|
516
|
+
new Promise<void>((resolve) => {
|
|
517
|
+
try {
|
|
518
|
+
server.close(() => resolve());
|
|
519
|
+
} catch {
|
|
520
|
+
resolve();
|
|
521
|
+
}
|
|
522
|
+
}),
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export { runHostServer };
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
export * from "./config";
|
|
2
|
+
export * from "./contract";
|
|
3
|
+
export * from "./contract.meta";
|
|
4
|
+
export * from "./fastkv";
|
|
5
|
+
export * from "./plugin";
|
|
6
|
+
export * from "./sdk";
|
|
4
7
|
export * from "./types";
|
package/src/integrity.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export function computeSriHash(content: string | Buffer): string {
|
|
4
|
+
return `sha384-${createHash("sha384").update(content).digest("base64")}`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function computeSriHashForUrl(url: string): Promise<string | null> {
|
|
8
|
+
try {
|
|
9
|
+
const entryUrl = url.endsWith("/remoteEntry.js")
|
|
10
|
+
? url
|
|
11
|
+
: url.endsWith("/mf-manifest.json")
|
|
12
|
+
? `${url.replace(/\/mf-manifest\.json$/, "")}/remoteEntry.js`
|
|
13
|
+
: `${url.replace(/\/$/, "")}/remoteEntry.js`;
|
|
14
|
+
|
|
15
|
+
const response = await fetch(entryUrl);
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
21
|
+
return computeSriHash(buffer);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.warn(
|
|
24
|
+
`[SRI] Error computing integrity for ${url}:`,
|
|
25
|
+
error instanceof Error ? error.message : error,
|
|
26
|
+
);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function verifySriForUrl(url: string, expectedIntegrity: string): Promise<void> {
|
|
32
|
+
const entryUrl = url.endsWith("/remoteEntry.js")
|
|
33
|
+
? url
|
|
34
|
+
: url.endsWith("/mf-manifest.json")
|
|
35
|
+
? `${url.replace(/\/mf-manifest\.json$/, "")}/remoteEntry.js`
|
|
36
|
+
: `${url.replace(/\/$/, "")}/remoteEntry.js`;
|
|
37
|
+
|
|
38
|
+
const response = await fetch(entryUrl);
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
45
|
+
const computed = computeSriHash(buffer);
|
|
46
|
+
|
|
47
|
+
if (computed !== expectedIntegrity) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`[SRI] Integrity check failed for ${entryUrl}\n Expected: ${expectedIntegrity}\n Computed: ${computed}`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(`[SRI] Integrity verified for ${entryUrl}`);
|
|
54
|
+
}
|
package/src/mf.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createInstance, getInstance } from "@module-federation/enhanced/runtime";
|
|
2
|
+
import { setGlobalFederationInstance } from "@module-federation/runtime-core";
|
|
3
|
+
|
|
4
|
+
type FederationInstance = ReturnType<typeof createInstance>;
|
|
5
|
+
|
|
6
|
+
let mfInstance: FederationInstance | null = null;
|
|
7
|
+
|
|
8
|
+
export function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void {
|
|
9
|
+
if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) return;
|
|
10
|
+
if ((mf as any).__everythingDevPatchedManifestFetch === true) return;
|
|
11
|
+
(mf as any).__everythingDevPatchedManifestFetch = true;
|
|
12
|
+
|
|
13
|
+
(mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {
|
|
14
|
+
if (typeof url !== "string" || !url.endsWith("/mf-manifest.json")) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
return fetch(url, init as any)
|
|
18
|
+
.then((res) => res.json())
|
|
19
|
+
.then((json: any) => {
|
|
20
|
+
json.metaData = json.metaData ?? {};
|
|
21
|
+
json.metaData.ssrPublicPath =
|
|
22
|
+
json.metaData.ssrPublicPath ?? url.replace(/\/mf-manifest\.json$/, "/");
|
|
23
|
+
return new Response(JSON.stringify(json), {
|
|
24
|
+
headers: { "content-type": "application/json" },
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getFederationInstance(): FederationInstance {
|
|
31
|
+
if (mfInstance) return mfInstance;
|
|
32
|
+
|
|
33
|
+
const existing = getInstance();
|
|
34
|
+
if (existing) {
|
|
35
|
+
mfInstance = existing as FederationInstance;
|
|
36
|
+
setGlobalFederationInstance(mfInstance as any);
|
|
37
|
+
patchManifestFetchForSsrPublicPath(mfInstance);
|
|
38
|
+
return mfInstance;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
mfInstance = createInstance({
|
|
42
|
+
name: "host",
|
|
43
|
+
remotes: [],
|
|
44
|
+
}) as FederationInstance;
|
|
45
|
+
setGlobalFederationInstance(mfInstance as any);
|
|
46
|
+
patchManifestFetchForSsrPublicPath(mfInstance);
|
|
47
|
+
return mfInstance;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function registerRemote(opts: {
|
|
51
|
+
name: string;
|
|
52
|
+
entry: string;
|
|
53
|
+
type?: "manifest" | "script";
|
|
54
|
+
}): Promise<void> {
|
|
55
|
+
const instance = getFederationInstance();
|
|
56
|
+
|
|
57
|
+
const inferType = (): "manifest" | "script" => {
|
|
58
|
+
if (opts.type) return opts.type;
|
|
59
|
+
if (opts.entry.endsWith("/mf-manifest.json")) return "manifest";
|
|
60
|
+
if (opts.entry.endsWith("/remoteEntry.js")) return "script";
|
|
61
|
+
return typeof window === "undefined" ? "script" : "manifest";
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const remoteType = inferType();
|
|
65
|
+
|
|
66
|
+
instance.registerRemotes([
|
|
67
|
+
{
|
|
68
|
+
name: opts.name,
|
|
69
|
+
entry: opts.entry,
|
|
70
|
+
type: remoteType,
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function loadRemoteModule<T>(
|
|
76
|
+
specifier: string,
|
|
77
|
+
options?: { loadFactory?: boolean; from?: "build" | "runtime" },
|
|
78
|
+
): Promise<T> {
|
|
79
|
+
const instance = getFederationInstance();
|
|
80
|
+
|
|
81
|
+
const isServer = typeof window === "undefined";
|
|
82
|
+
if (isServer) {
|
|
83
|
+
await (instance as any).initializeSharing?.("default");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const mod = await instance.loadRemote<T>(specifier, options as any);
|
|
87
|
+
if (!mod) {
|
|
88
|
+
throw new Error(`Failed to load remote module: ${specifier}`);
|
|
89
|
+
}
|
|
90
|
+
return mod;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function ensureNodeRuntimePlugin(): Promise<void> {
|
|
94
|
+
const instance = getFederationInstance();
|
|
95
|
+
if (typeof window !== "undefined") return;
|
|
96
|
+
if ((instance as any).__nodeRuntimePluginLoaded) return;
|
|
97
|
+
|
|
98
|
+
const mod: any = await import("@module-federation/node/runtimePlugin");
|
|
99
|
+
const factory = mod?.default ?? mod;
|
|
100
|
+
const plugin = typeof factory === "function" ? factory() : null;
|
|
101
|
+
if (plugin) {
|
|
102
|
+
instance.registerPlugins([plugin]);
|
|
103
|
+
}
|
|
104
|
+
(instance as any).__nodeRuntimePluginLoaded = true;
|
|
105
|
+
}
|