every-plugin 2.10.0 → 2.10.1
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/dist/build/rspack/dev-server-middleware.cjs +57 -3
- package/dist/build/rspack/dev-server-middleware.cjs.map +1 -1
- package/dist/build/rspack/dev-server-middleware.mjs +57 -3
- package/dist/build/rspack/dev-server-middleware.mjs.map +1 -1
- package/dist/runtime/mf-config.cjs +1 -1
- package/dist/runtime/mf-config.mjs +1 -1
- package/package.json +2 -2
- package/src/build/rspack/dev-server-middleware.ts +81 -7
|
@@ -15,6 +15,48 @@ const normalizePrefix = (prefix) => {
|
|
|
15
15
|
const cleaned = prefix.replace(/^\/+|\/+$/g, "");
|
|
16
16
|
return cleaned ? `/${cleaned}` : "";
|
|
17
17
|
};
|
|
18
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
19
|
+
const readRuntimeConfigFromEnv = () => {
|
|
20
|
+
const raw = process.env.BOS_RUNTIME_CONFIG;
|
|
21
|
+
if (!raw) return null;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(raw);
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const collectSiblingRemotes = (runtimeConfig, pluginId) => {
|
|
29
|
+
const siblings = {};
|
|
30
|
+
const isApi = pluginId === "api" || runtimeConfig?.api?.name === pluginId;
|
|
31
|
+
const ownKey = runtimeConfig?.plugins?.[pluginId] ? pluginId : null;
|
|
32
|
+
let dependsOn = [];
|
|
33
|
+
if (ownKey) dependsOn = runtimeConfig.plugins[ownKey].dependsOn ?? [];
|
|
34
|
+
else if (isApi) dependsOn = runtimeConfig?.api?.dependsOn ?? [];
|
|
35
|
+
for (const depId of dependsOn) {
|
|
36
|
+
const dep = runtimeConfig?.plugins?.[depId];
|
|
37
|
+
if (!dep || dep.source !== "local" || !dep.url) continue;
|
|
38
|
+
if (depId === pluginId) continue;
|
|
39
|
+
siblings[depId] = { remote: `${dep.url.replace(/\/$/, "")}/remoteEntry.js` };
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
siblings,
|
|
43
|
+
dependsOn
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const loadPluginWithRetry = async (runtime, pluginId, timeoutMs = 9e4) => {
|
|
47
|
+
const deadline = Date.now() + timeoutMs;
|
|
48
|
+
let lastError;
|
|
49
|
+
while (Date.now() < deadline) try {
|
|
50
|
+
return await runtime.usePlugin(pluginId, {
|
|
51
|
+
variables: {},
|
|
52
|
+
secrets: {}
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
lastError = error;
|
|
56
|
+
await sleep(500);
|
|
57
|
+
}
|
|
58
|
+
throw lastError;
|
|
59
|
+
};
|
|
18
60
|
function setupPluginMiddleware(devServer, pluginInfo, devConfig, port) {
|
|
19
61
|
const rpcPrefix = normalizePrefix(devConfig?.prefix);
|
|
20
62
|
const handlers = {
|
|
@@ -39,11 +81,23 @@ function setupPluginMiddleware(devServer, pluginInfo, devConfig, port) {
|
|
|
39
81
|
const { onError } = await Promise.resolve().then(() => require("../../orpc.cjs"));
|
|
40
82
|
const { formatORPCError } = await Promise.resolve().then(() => require("../../errors.cjs"));
|
|
41
83
|
const pluginId = devConfig?.pluginId || pluginInfo.normalizedName;
|
|
42
|
-
const
|
|
43
|
-
const
|
|
84
|
+
const runtimeConfig = readRuntimeConfigFromEnv();
|
|
85
|
+
const { siblings, dependsOn } = collectSiblingRemotes(runtimeConfig, pluginId);
|
|
86
|
+
if (dependsOn.length > 0) console.log(`│ 🔗 Loading sibling plugin(s) for ${pluginId}: ${dependsOn.join(", ")}`);
|
|
87
|
+
const runtime = createPluginRuntime({ registry: {
|
|
88
|
+
[pluginId]: { remote: `http://localhost:${port}/remoteEntry.js` },
|
|
89
|
+
...siblings
|
|
90
|
+
} });
|
|
91
|
+
const defaultConfig = {
|
|
44
92
|
variables: {},
|
|
45
93
|
secrets: {}
|
|
46
|
-
}
|
|
94
|
+
};
|
|
95
|
+
const pluginsMap = {};
|
|
96
|
+
for (const depId of Object.keys(siblings)) {
|
|
97
|
+
pluginsMap[depId] = (await loadPluginWithRetry(runtime, depId)).createClient;
|
|
98
|
+
console.log(`│ ✅ Loaded dependency plugin: ${depId}`);
|
|
99
|
+
}
|
|
100
|
+
const loaded = await runtime.usePlugin(pluginId, devConfig?.config ?? defaultConfig, Object.keys(pluginsMap).length > 0 ? pluginsMap : void 0);
|
|
47
101
|
cleanup = async () => {
|
|
48
102
|
handlers.rpc = null;
|
|
49
103
|
handlers.api = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server-middleware.cjs","names":[],"sources":["../../../src/build/rspack/dev-server-middleware.ts"],"sourcesContent":["import type { PluginInfo } from \"./utils\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: any) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst normalizePrefix = (prefix?: string): string => {\n if (!prefix) return \"\";\n const cleaned = prefix.replace(/^\\/+|\\/+$/g, \"\");\n return cleaned ? `/${cleaned}` : \"\";\n};\n\nexport function setupPluginMiddleware(\n devServer: any,\n pluginInfo: PluginInfo,\n devConfig: any,\n port: number,\n) {\n const rpcPrefix = normalizePrefix(devConfig?.prefix);\n const handlers: { rpc: any; api: any } = { rpc: null, api: null };\n let cleanup: (() => Promise<void>) | null = null;\n\n const performCleanup = async () => {\n if (cleanup) {\n await cleanup();\n cleanup = null;\n }\n };\n\n (async () => {\n await performCleanup();\n\n try {\n const { createPluginRuntime } = await import(\"every-plugin\");\n const { RPCHandler } = await import(\"@orpc/server/fetch\");\n const { OpenAPIHandler } = await import(\"@orpc/openapi/fetch\");\n const { OpenAPIReferencePlugin } = await import(\"@orpc/openapi/plugins\");\n const { ZodToJsonSchemaConverter } = await import(\"@orpc/zod/zod4\");\n const { onError } = await import(\"every-plugin/orpc\");\n const { formatORPCError } = await import(\"every-plugin/errors\");\n\n const pluginId = devConfig?.pluginId || pluginInfo.normalizedName;\n\n const runtime = createPluginRuntime({\n registry: {\n [pluginId]: {\n remote: `http://localhost:${port}/remoteEntry.js`,\n },\n },\n });\n\n const defaultConfig = { variables: {}, secrets: {} };\n\n // @ts-expect-error we don't know the plugin id\n const loaded = await runtime.usePlugin(pluginId, (devConfig?.config ?? defaultConfig) as any);\n\n cleanup = async () => {\n handlers.rpc = null;\n handlers.api = null;\n if (devServer.app.locals.handlers) {\n devServer.app.locals.handlers = null;\n }\n if (runtime) await runtime.shutdown();\n };\n\n handlers.rpc = new RPCHandler(loaded.router, {\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n handlers.api = new OpenAPIHandler(loaded.router, {\n plugins: [\n new OpenAPIReferencePlugin({\n schemaConverters: [new ZodToJsonSchemaConverter()],\n }),\n ],\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n console.log(`╭─────────────────────────────────────────────`);\n console.log(`│ ✅ Plugin dev server ready: `);\n console.log(`├─────────────────────────────────────────────`);\n console.log(`│ 📡 RPC: http://localhost:${port}/api/rpc${rpcPrefix}`);\n console.log(`│ 📖 Docs: http://localhost:${port}/api`);\n console.log(`│ 💚 Health: http://localhost:${port}/`);\n console.log(`╰─────────────────────────────────────────────`);\n\n devServer.app.locals.handlers = handlers;\n\n if (devServer.server) {\n devServer.server.once(\"close\", async () => {\n await performCleanup();\n });\n }\n } catch (error) {\n console.error(\"❌ Failed to load plugin:\", error);\n await performCleanup();\n }\n })().catch((err) => {\n console.error(\"❌ Plugin dev server fatal error:\", err);\n });\n\n process.once(\"SIGINT\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await performCleanup();\n clearTimeout(timeout);\n });\n process.once(\"SIGTERM\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await performCleanup();\n clearTimeout(timeout);\n });\n\n devServer.app.options(\"*\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.status(200).end();\n });\n\n devServer.app.get(\"/\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.json({\n ok: true,\n plugin: pluginInfo.normalizedName,\n version: pluginInfo.version,\n status: devServer.app.locals.handlers?.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/\",\n docs: \"/api\",\n rpc: `/api/rpc${rpcPrefix}`,\n },\n });\n });\n\n devServer.app.get(\"/health\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.status(200).send(\"OK\");\n });\n\n const buildDevContext = (_req: any, webRequest: Request) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n let cachedRawBody: string | null = null;\n return {\n reqHeaders: webRequest.headers,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBody !== null) return cachedRawBody;\n if (!rawClone) {\n cachedRawBody = \"\";\n return cachedRawBody;\n }\n cachedRawBody = await rawClone.text();\n return cachedRawBody;\n },\n };\n };\n\n const handleApiRequest = async (req: any, res: any) => {\n applyCorsHeaders(res);\n const apiHandler = devServer.app.locals.handlers?.api;\n if (!apiHandler) {\n return res.status(503).json({ error: \"Plugin still loading...\" });\n }\n\n try {\n const url = `http://${req.headers.host}${req.url}`;\n const webRequest = new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n\n const result = await apiHandler.handle(webRequest, {\n prefix: \"/api\",\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.status(result.response.status);\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n res.send(text);\n } else {\n res.status(404).send(\"Not Found\");\n }\n } catch (error) {\n console.error(\"OpenAPI error:\", error);\n res.status(500).json({ error: (error as Error).message });\n }\n };\n\n devServer.app.all(`/api/rpc${rpcPrefix}/*`, async (req: any, res: any) => {\n applyCorsHeaders(res);\n const rpcHandler = devServer.app.locals.handlers?.rpc;\n if (!rpcHandler) {\n return res.status(503).json({ error: \"Plugin still loading...\" });\n }\n\n try {\n const url = `http://${req.headers.host}${req.url}`;\n const webRequest = new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n\n const result = await rpcHandler.handle(webRequest, {\n prefix: `/api/rpc${rpcPrefix}`,\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.status(result.response.status);\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n res.send(text);\n } else {\n res.status(404).send(\"Not Found\");\n }\n } catch (error) {\n console.error(\"RPC error:\", error);\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n devServer.app.all(\"/api\", handleApiRequest);\n devServer.app.all(\"/api/*\", handleApiRequest);\n}\n"],"mappings":";;AAEA,MAAM,cAAc;CAClB,+BAA+B;CAC/B,gCAAgC;CAChC,gCAAgC;AAClC;AAEA,MAAM,oBAAoB,QAAa;CACrC,OAAO,QAAQ,WAAW,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EACpD,IAAI,UAAU,KAAK,KAAK;CAC1B,CAAC;AACH;AAEA,MAAM,mBAAmB,WAA4B;CACnD,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,UAAU,OAAO,QAAQ,cAAc,EAAE;CAC/C,OAAO,UAAU,IAAI,YAAY;AACnC;AAEA,SAAgB,sBACd,WACA,YACA,WACA,MACA;CACA,MAAM,YAAY,gBAAgB,WAAW,MAAM;CACnD,MAAM,WAAmC;EAAE,KAAK;EAAM,KAAK;CAAK;CAChE,IAAI,UAAwC;CAE5C,MAAM,iBAAiB,YAAY;EACjC,IAAI,SAAS;GACX,MAAM,QAAQ;GACd,UAAU;EACZ;CACF;CAEA,CAAC,YAAY;EACX,MAAM,eAAe;EAErB,IAAI;GACF,MAAM,EAAE,wBAAwB,2CAAM;GACtC,MAAM,EAAE,eAAe,MAAM,OAAO;GACpC,MAAM,EAAE,mBAAmB,MAAM,OAAO;GACxC,MAAM,EAAE,2BAA2B,MAAM,OAAO;GAChD,MAAM,EAAE,6BAA6B,MAAM,OAAO;GAClD,MAAM,EAAE,YAAY,2CAAM;GAC1B,MAAM,EAAE,oBAAoB,2CAAM;GAElC,MAAM,WAAW,WAAW,YAAY,WAAW;GAEnD,MAAM,UAAU,oBAAoB,EAClC,UAAU,GACP,WAAW,EACV,QAAQ,oBAAoB,KAAK,iBACnC,EACF,EACF,CAAC;GAKD,MAAM,SAAS,MAAM,QAAQ,UAAU,UAAW,WAAW,UAAU;IAH/C,WAAW,CAAC;IAAG,SAAS,CAAC;GAGkC,CAAS;GAE5F,UAAU,YAAY;IACpB,SAAS,MAAM;IACf,SAAS,MAAM;IACf,IAAI,UAAU,IAAI,OAAO,UACvB,UAAU,IAAI,OAAO,WAAW;IAElC,IAAI,SAAS,MAAM,QAAQ,SAAS;GACtC;GAEA,SAAS,MAAM,IAAI,WAAW,OAAO,QAAQ,EAC3C,cAAc,CACZ,SAAS,UAAe;IACtB,MAAM,YAAY,gBAAgB,KAAK;IACvC,IAAI,WAAW,QAAQ,MAAM,SAAS;GACxC,CAAC,CACH,EACF,CAAC;GAED,SAAS,MAAM,IAAI,eAAe,OAAO,QAAQ;IAC/C,SAAS,CACP,IAAI,uBAAuB,EACzB,kBAAkB,CAAC,IAAI,yBAAyB,CAAC,EACnD,CAAC,CACH;IACA,cAAc,CACZ,SAAS,UAAe;KACtB,MAAM,YAAY,gBAAgB,KAAK;KACvC,IAAI,WAAW,QAAQ,MAAM,SAAS;IACxC,CAAC,CACH;GACF,CAAC;GAED,QAAQ,IAAI,gDAAgD;GAC5D,QAAQ,IAAI,gCAAgC;GAC5C,QAAQ,IAAI,gDAAgD;GAC5D,QAAQ,IAAI,kCAAkC,KAAK,UAAU,WAAW;GACxE,QAAQ,IAAI,kCAAkC,KAAK,KAAK;GACxD,QAAQ,IAAI,kCAAkC,KAAK,EAAE;GACrD,QAAQ,IAAI,gDAAgD;GAE5D,UAAU,IAAI,OAAO,WAAW;GAEhC,IAAI,UAAU,QACZ,UAAU,OAAO,KAAK,SAAS,YAAY;IACzC,MAAM,eAAe;GACvB,CAAC;EAEL,SAAS,OAAO;GACd,QAAQ,MAAM,4BAA4B,KAAK;GAC/C,MAAM,eAAe;EACvB;CACF,EAAC,CAAE,CAAC,CAAC,OAAO,QAAQ;EAClB,QAAQ,MAAM,oCAAoC,GAAG;CACvD,CAAC;CAED,QAAQ,KAAK,UAAU,YAAY;EACjC,MAAM,UAAU,iBAAiB,QAAQ,KAAK,CAAC,GAAG,GAAI;EACtD,MAAM,eAAe;EACrB,aAAa,OAAO;CACtB,CAAC;CACD,QAAQ,KAAK,WAAW,YAAY;EAClC,MAAM,UAAU,iBAAiB,QAAQ,KAAK,CAAC,GAAG,GAAI;EACtD,MAAM,eAAe;EACrB,aAAa,OAAO;CACtB,CAAC;CAED,UAAU,IAAI,QAAQ,MAAM,MAAW,QAAa;EAClD,iBAAiB,GAAG;EACpB,IAAI,OAAO,GAAG,CAAC,CAAC,IAAI;CACtB,CAAC;CAED,UAAU,IAAI,IAAI,MAAM,MAAW,QAAa;EAC9C,iBAAiB,GAAG;EACpB,IAAI,KAAK;GACP,IAAI;GACJ,QAAQ,WAAW;GACnB,SAAS,WAAW;GACpB,QAAQ,UAAU,IAAI,OAAO,UAAU,MAAM,UAAU;GACvD,WAAW;IACT,QAAQ;IACR,MAAM;IACN,KAAK,WAAW;GAClB;EACF,CAAC;CACH,CAAC;CAED,UAAU,IAAI,IAAI,YAAY,MAAW,QAAa;EACpD,iBAAiB,GAAG;EACpB,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI;CAC3B,CAAC;CAED,MAAM,mBAAmB,MAAW,eAAwB;EAC1D,MAAM,WACJ,WAAW,WAAW,SAAS,WAAW,WAAW,SAAS,OAAO,WAAW,MAAM;EACxF,IAAI,gBAA+B;EACnC,OAAO;GACL,YAAY,WAAW;GACvB,YAAY,YAA6B;IACvC,IAAI,kBAAkB,MAAM,OAAO;IACnC,IAAI,CAAC,UAAU;KACb,gBAAgB;KAChB,OAAO;IACT;IACA,gBAAgB,MAAM,SAAS,KAAK;IACpC,OAAO;GACT;EACF;CACF;CAEA,MAAM,mBAAmB,OAAO,KAAU,QAAa;EACrD,iBAAiB,GAAG;EACpB,MAAM,aAAa,UAAU,IAAI,OAAO,UAAU;EAClD,IAAI,CAAC,YACH,OAAO,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,0BAA0B,CAAC;EAGlE,IAAI;GACF,MAAM,MAAM,UAAU,IAAI,QAAQ,OAAO,IAAI;GAC7C,MAAM,aAAa,IAAI,QAAQ,KAAK;IAClC,QAAQ,IAAI;IACZ,SAAS,IAAI;IACb,MAAM,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,MAAM;IAC5D,QAAQ,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,SAAS;GACnE,CAAgB;GAEhB,MAAM,SAAS,MAAM,WAAW,OAAO,YAAY;IACjD,QAAQ;IACR,SAAS,gBAAgB,KAAK,UAAU;GAC1C,CAAC;GAED,IAAI,OAAO,UAAU;IACnB,IAAI,OAAO,OAAO,SAAS,MAAM;IACjC,OAAO,SAAS,QAAQ,SAAS,OAAe,QAAgB;KAC9D,IAAI,UAAU,KAAK,KAAK;IAC1B,CAAC;IACD,MAAM,OAAO,MAAM,OAAO,SAAS,KAAK;IACxC,IAAI,KAAK,IAAI;GACf,OACE,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,WAAW;EAEpC,SAAS,OAAO;GACd,QAAQ,MAAM,kBAAkB,KAAK;GACrC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;EAC1D;CACF;CAEA,UAAU,IAAI,IAAI,WAAW,UAAU,KAAK,OAAO,KAAU,QAAa;EACxE,iBAAiB,GAAG;EACpB,MAAM,aAAa,UAAU,IAAI,OAAO,UAAU;EAClD,IAAI,CAAC,YACH,OAAO,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,0BAA0B,CAAC;EAGlE,IAAI;GACF,MAAM,MAAM,UAAU,IAAI,QAAQ,OAAO,IAAI;GAC7C,MAAM,aAAa,IAAI,QAAQ,KAAK;IAClC,QAAQ,IAAI;IACZ,SAAS,IAAI;IACb,MAAM,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,MAAM;IAC5D,QAAQ,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,SAAS;GACnE,CAAgB;GAEhB,MAAM,SAAS,MAAM,WAAW,OAAO,YAAY;IACjD,QAAQ,WAAW;IACnB,SAAS,gBAAgB,KAAK,UAAU;GAC1C,CAAC;GAED,IAAI,OAAO,UAAU;IACnB,IAAI,OAAO,OAAO,SAAS,MAAM;IACjC,OAAO,SAAS,QAAQ,SAAS,OAAe,QAAgB;KAC9D,IAAI,UAAU,KAAK,KAAK;IAC1B,CAAC;IACD,MAAM,OAAO,MAAM,OAAO,SAAS,KAAK;IACxC,IAAI,KAAK,IAAI;GACf,OACE,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,WAAW;EAEpC,SAAS,OAAO;GACd,QAAQ,MAAM,cAAc,KAAK;GACjC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;EAC1D;CACF,CAAC;CAED,UAAU,IAAI,IAAI,QAAQ,gBAAgB;CAC1C,UAAU,IAAI,IAAI,UAAU,gBAAgB;AAC9C"}
|
|
1
|
+
{"version":3,"file":"dev-server-middleware.cjs","names":[],"sources":["../../../src/build/rspack/dev-server-middleware.ts"],"sourcesContent":["import type { PluginInfo } from \"./utils\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: any) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst normalizePrefix = (prefix?: string): string => {\n if (!prefix) return \"\";\n const cleaned = prefix.replace(/^\\/+|\\/+$/g, \"\");\n return cleaned ? `/${cleaned}` : \"\";\n};\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst readRuntimeConfigFromEnv = (): any => {\n const raw = process.env.BOS_RUNTIME_CONFIG;\n if (!raw) return null;\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n};\n\nconst collectSiblingRemotes = (runtimeConfig: any, pluginId: string) => {\n const siblings: Record<string, { remote: string }> = {};\n\n const isApi = pluginId === \"api\" || runtimeConfig?.api?.name === pluginId;\n\n const ownKey = runtimeConfig?.plugins?.[pluginId] ? pluginId : null;\n let dependsOn: string[] = [];\n if (ownKey) {\n dependsOn = runtimeConfig.plugins[ownKey].dependsOn ?? [];\n } else if (isApi) {\n dependsOn = runtimeConfig?.api?.dependsOn ?? [];\n }\n\n for (const depId of dependsOn) {\n const dep = runtimeConfig?.plugins?.[depId];\n if (!dep || dep.source !== \"local\" || !dep.url) continue;\n if (depId === pluginId) continue;\n const base = dep.url.replace(/\\/$/, \"\");\n siblings[depId] = { remote: `${base}/remoteEntry.js` };\n }\n\n return { siblings, dependsOn };\n};\n\nconst loadPluginWithRetry = async (\n runtime: any,\n pluginId: string,\n timeoutMs = 90000,\n): Promise<any> => {\n const deadline = Date.now() + timeoutMs;\n let lastError: unknown;\n while (Date.now() < deadline) {\n try {\n return await runtime.usePlugin(pluginId, { variables: {}, secrets: {} } as any);\n } catch (error) {\n lastError = error;\n await sleep(500);\n }\n }\n throw lastError;\n};\n\nexport function setupPluginMiddleware(\n devServer: any,\n pluginInfo: PluginInfo,\n devConfig: any,\n port: number,\n) {\n const rpcPrefix = normalizePrefix(devConfig?.prefix);\n const handlers: { rpc: any; api: any } = { rpc: null, api: null };\n let cleanup: (() => Promise<void>) | null = null;\n\n const performCleanup = async () => {\n if (cleanup) {\n await cleanup();\n cleanup = null;\n }\n };\n\n (async () => {\n await performCleanup();\n\n try {\n const { createPluginRuntime } = await import(\"every-plugin\");\n const { RPCHandler } = await import(\"@orpc/server/fetch\");\n const { OpenAPIHandler } = await import(\"@orpc/openapi/fetch\");\n const { OpenAPIReferencePlugin } = await import(\"@orpc/openapi/plugins\");\n const { ZodToJsonSchemaConverter } = await import(\"@orpc/zod/zod4\");\n const { onError } = await import(\"every-plugin/orpc\");\n const { formatORPCError } = await import(\"every-plugin/errors\");\n\n const pluginId = devConfig?.pluginId || pluginInfo.normalizedName;\n\n const runtimeConfig = readRuntimeConfigFromEnv();\n const { siblings, dependsOn } = collectSiblingRemotes(runtimeConfig, pluginId);\n\n if (dependsOn.length > 0) {\n console.log(`│ 🔗 Loading sibling plugin(s) for ${pluginId}: ${dependsOn.join(\", \")}`);\n }\n\n const registry: Record<string, { remote: string }> = {\n [pluginId]: {\n remote: `http://localhost:${port}/remoteEntry.js`,\n },\n ...siblings,\n };\n\n const runtime = createPluginRuntime({\n registry,\n });\n\n const defaultConfig = { variables: {}, secrets: {} };\n\n const pluginsMap: Record<string, unknown> = {};\n for (const depId of Object.keys(siblings)) {\n const dep = await loadPluginWithRetry(runtime, depId);\n pluginsMap[depId] = dep.createClient;\n console.log(`│ ✅ Loaded dependency plugin: ${depId}`);\n }\n\n const loaded: any = await runtime.usePlugin<typeof pluginId>(\n pluginId,\n (devConfig?.config ?? defaultConfig) as any,\n Object.keys(pluginsMap).length > 0 ? pluginsMap : undefined,\n );\n\n cleanup = async () => {\n handlers.rpc = null;\n handlers.api = null;\n if (devServer.app.locals.handlers) {\n devServer.app.locals.handlers = null;\n }\n if (runtime) await runtime.shutdown();\n };\n\n handlers.rpc = new RPCHandler(loaded.router, {\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n handlers.api = new OpenAPIHandler(loaded.router, {\n plugins: [\n new OpenAPIReferencePlugin({\n schemaConverters: [new ZodToJsonSchemaConverter()],\n }),\n ],\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n console.log(`╭─────────────────────────────────────────────`);\n console.log(`│ ✅ Plugin dev server ready: `);\n console.log(`├─────────────────────────────────────────────`);\n console.log(`│ 📡 RPC: http://localhost:${port}/api/rpc${rpcPrefix}`);\n console.log(`│ 📖 Docs: http://localhost:${port}/api`);\n console.log(`│ 💚 Health: http://localhost:${port}/`);\n console.log(`╰─────────────────────────────────────────────`);\n\n devServer.app.locals.handlers = handlers;\n\n if (devServer.server) {\n devServer.server.once(\"close\", async () => {\n await performCleanup();\n });\n }\n } catch (error) {\n console.error(\"❌ Failed to load plugin:\", error);\n await performCleanup();\n }\n })().catch((err) => {\n console.error(\"❌ Plugin dev server fatal error:\", err);\n });\n\n process.once(\"SIGINT\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await performCleanup();\n clearTimeout(timeout);\n });\n process.once(\"SIGTERM\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await performCleanup();\n clearTimeout(timeout);\n });\n\n devServer.app.options(\"*\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.status(200).end();\n });\n\n devServer.app.get(\"/\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.json({\n ok: true,\n plugin: pluginInfo.normalizedName,\n version: pluginInfo.version,\n status: devServer.app.locals.handlers?.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/\",\n docs: \"/api\",\n rpc: `/api/rpc${rpcPrefix}`,\n },\n });\n });\n\n devServer.app.get(\"/health\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.status(200).send(\"OK\");\n });\n\n const buildDevContext = (_req: any, webRequest: Request) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n let cachedRawBody: string | null = null;\n return {\n reqHeaders: webRequest.headers,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBody !== null) return cachedRawBody;\n if (!rawClone) {\n cachedRawBody = \"\";\n return cachedRawBody;\n }\n cachedRawBody = await rawClone.text();\n return cachedRawBody;\n },\n };\n };\n\n const handleApiRequest = async (req: any, res: any) => {\n applyCorsHeaders(res);\n const apiHandler = devServer.app.locals.handlers?.api;\n if (!apiHandler) {\n return res.status(503).json({ error: \"Plugin still loading...\" });\n }\n\n try {\n const url = `http://${req.headers.host}${req.url}`;\n const webRequest = new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n\n const result = await apiHandler.handle(webRequest, {\n prefix: \"/api\",\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.status(result.response.status);\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n res.send(text);\n } else {\n res.status(404).send(\"Not Found\");\n }\n } catch (error) {\n console.error(\"OpenAPI error:\", error);\n res.status(500).json({ error: (error as Error).message });\n }\n };\n\n devServer.app.all(`/api/rpc${rpcPrefix}/*`, async (req: any, res: any) => {\n applyCorsHeaders(res);\n const rpcHandler = devServer.app.locals.handlers?.rpc;\n if (!rpcHandler) {\n return res.status(503).json({ error: \"Plugin still loading...\" });\n }\n\n try {\n const url = `http://${req.headers.host}${req.url}`;\n const webRequest = new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n\n const result = await rpcHandler.handle(webRequest, {\n prefix: `/api/rpc${rpcPrefix}`,\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.status(result.response.status);\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n res.send(text);\n } else {\n res.status(404).send(\"Not Found\");\n }\n } catch (error) {\n console.error(\"RPC error:\", error);\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n devServer.app.all(\"/api\", handleApiRequest);\n devServer.app.all(\"/api/*\", handleApiRequest);\n}\n"],"mappings":";;AAEA,MAAM,cAAc;CAClB,+BAA+B;CAC/B,gCAAgC;CAChC,gCAAgC;AAClC;AAEA,MAAM,oBAAoB,QAAa;CACrC,OAAO,QAAQ,WAAW,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EACpD,IAAI,UAAU,KAAK,KAAK;CAC1B,CAAC;AACH;AAEA,MAAM,mBAAmB,WAA4B;CACnD,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,UAAU,OAAO,QAAQ,cAAc,EAAE;CAC/C,OAAO,UAAU,IAAI,YAAY;AACnC;AAEA,MAAM,SAAS,OAAe,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AAE9E,MAAM,iCAAsC;CAC1C,MAAM,MAAM,QAAQ,IAAI;CACxB,IAAI,CAAC,KAAK,OAAO;CACjB,IAAI;EACF,OAAO,KAAK,MAAM,GAAG;CACvB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,yBAAyB,eAAoB,aAAqB;CACtE,MAAM,WAA+C,CAAC;CAEtD,MAAM,QAAQ,aAAa,SAAS,eAAe,KAAK,SAAS;CAEjE,MAAM,SAAS,eAAe,UAAU,YAAY,WAAW;CAC/D,IAAI,YAAsB,CAAC;CAC3B,IAAI,QACF,YAAY,cAAc,QAAQ,OAAO,CAAC,aAAa,CAAC;MACnD,IAAI,OACT,YAAY,eAAe,KAAK,aAAa,CAAC;CAGhD,KAAK,MAAM,SAAS,WAAW;EAC7B,MAAM,MAAM,eAAe,UAAU;EACrC,IAAI,CAAC,OAAO,IAAI,WAAW,WAAW,CAAC,IAAI,KAAK;EAChD,IAAI,UAAU,UAAU;EAExB,SAAS,SAAS,EAAE,QAAQ,GADf,IAAI,IAAI,QAAQ,OAAO,EACF,EAAE,iBAAiB;CACvD;CAEA,OAAO;EAAE;EAAU;CAAU;AAC/B;AAEA,MAAM,sBAAsB,OAC1B,SACA,UACA,YAAY,QACK;CACjB,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,IAAI;CACJ,OAAO,KAAK,IAAI,IAAI,UAClB,IAAI;EACF,OAAO,MAAM,QAAQ,UAAU,UAAU;GAAE,WAAW,CAAC;GAAG,SAAS,CAAC;EAAE,CAAQ;CAChF,SAAS,OAAO;EACd,YAAY;EACZ,MAAM,MAAM,GAAG;CACjB;CAEF,MAAM;AACR;AAEA,SAAgB,sBACd,WACA,YACA,WACA,MACA;CACA,MAAM,YAAY,gBAAgB,WAAW,MAAM;CACnD,MAAM,WAAmC;EAAE,KAAK;EAAM,KAAK;CAAK;CAChE,IAAI,UAAwC;CAE5C,MAAM,iBAAiB,YAAY;EACjC,IAAI,SAAS;GACX,MAAM,QAAQ;GACd,UAAU;EACZ;CACF;CAEA,CAAC,YAAY;EACX,MAAM,eAAe;EAErB,IAAI;GACF,MAAM,EAAE,wBAAwB,2CAAM;GACtC,MAAM,EAAE,eAAe,MAAM,OAAO;GACpC,MAAM,EAAE,mBAAmB,MAAM,OAAO;GACxC,MAAM,EAAE,2BAA2B,MAAM,OAAO;GAChD,MAAM,EAAE,6BAA6B,MAAM,OAAO;GAClD,MAAM,EAAE,YAAY,2CAAM;GAC1B,MAAM,EAAE,oBAAoB,2CAAM;GAElC,MAAM,WAAW,WAAW,YAAY,WAAW;GAEnD,MAAM,gBAAgB,yBAAyB;GAC/C,MAAM,EAAE,UAAU,cAAc,sBAAsB,eAAe,QAAQ;GAE7E,IAAI,UAAU,SAAS,GACrB,QAAQ,IAAI,uCAAuC,SAAS,IAAI,UAAU,KAAK,IAAI,GAAG;GAUxF,MAAM,UAAU,oBAAoB,EAClC;KAPC,WAAW,EACV,QAAQ,oBAAoB,KAAK,iBACnC;IACA,GAAG;GAII,EACT,CAAC;GAED,MAAM,gBAAgB;IAAE,WAAW,CAAC;IAAG,SAAS,CAAC;GAAE;GAEnD,MAAM,aAAsC,CAAC;GAC7C,KAAK,MAAM,SAAS,OAAO,KAAK,QAAQ,GAAG;IAEzC,WAAW,UAAS,MADF,oBAAoB,SAAS,KAAK,EAC7B,CAAC;IACxB,QAAQ,IAAI,kCAAkC,OAAO;GACvD;GAEA,MAAM,SAAc,MAAM,QAAQ,UAChC,UACC,WAAW,UAAU,eACtB,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,IAAI,aAAa,MACpD;GAEA,UAAU,YAAY;IACpB,SAAS,MAAM;IACf,SAAS,MAAM;IACf,IAAI,UAAU,IAAI,OAAO,UACvB,UAAU,IAAI,OAAO,WAAW;IAElC,IAAI,SAAS,MAAM,QAAQ,SAAS;GACtC;GAEA,SAAS,MAAM,IAAI,WAAW,OAAO,QAAQ,EAC3C,cAAc,CACZ,SAAS,UAAe;IACtB,MAAM,YAAY,gBAAgB,KAAK;IACvC,IAAI,WAAW,QAAQ,MAAM,SAAS;GACxC,CAAC,CACH,EACF,CAAC;GAED,SAAS,MAAM,IAAI,eAAe,OAAO,QAAQ;IAC/C,SAAS,CACP,IAAI,uBAAuB,EACzB,kBAAkB,CAAC,IAAI,yBAAyB,CAAC,EACnD,CAAC,CACH;IACA,cAAc,CACZ,SAAS,UAAe;KACtB,MAAM,YAAY,gBAAgB,KAAK;KACvC,IAAI,WAAW,QAAQ,MAAM,SAAS;IACxC,CAAC,CACH;GACF,CAAC;GAED,QAAQ,IAAI,gDAAgD;GAC5D,QAAQ,IAAI,gCAAgC;GAC5C,QAAQ,IAAI,gDAAgD;GAC5D,QAAQ,IAAI,kCAAkC,KAAK,UAAU,WAAW;GACxE,QAAQ,IAAI,kCAAkC,KAAK,KAAK;GACxD,QAAQ,IAAI,kCAAkC,KAAK,EAAE;GACrD,QAAQ,IAAI,gDAAgD;GAE5D,UAAU,IAAI,OAAO,WAAW;GAEhC,IAAI,UAAU,QACZ,UAAU,OAAO,KAAK,SAAS,YAAY;IACzC,MAAM,eAAe;GACvB,CAAC;EAEL,SAAS,OAAO;GACd,QAAQ,MAAM,4BAA4B,KAAK;GAC/C,MAAM,eAAe;EACvB;CACF,EAAC,CAAE,CAAC,CAAC,OAAO,QAAQ;EAClB,QAAQ,MAAM,oCAAoC,GAAG;CACvD,CAAC;CAED,QAAQ,KAAK,UAAU,YAAY;EACjC,MAAM,UAAU,iBAAiB,QAAQ,KAAK,CAAC,GAAG,GAAI;EACtD,MAAM,eAAe;EACrB,aAAa,OAAO;CACtB,CAAC;CACD,QAAQ,KAAK,WAAW,YAAY;EAClC,MAAM,UAAU,iBAAiB,QAAQ,KAAK,CAAC,GAAG,GAAI;EACtD,MAAM,eAAe;EACrB,aAAa,OAAO;CACtB,CAAC;CAED,UAAU,IAAI,QAAQ,MAAM,MAAW,QAAa;EAClD,iBAAiB,GAAG;EACpB,IAAI,OAAO,GAAG,CAAC,CAAC,IAAI;CACtB,CAAC;CAED,UAAU,IAAI,IAAI,MAAM,MAAW,QAAa;EAC9C,iBAAiB,GAAG;EACpB,IAAI,KAAK;GACP,IAAI;GACJ,QAAQ,WAAW;GACnB,SAAS,WAAW;GACpB,QAAQ,UAAU,IAAI,OAAO,UAAU,MAAM,UAAU;GACvD,WAAW;IACT,QAAQ;IACR,MAAM;IACN,KAAK,WAAW;GAClB;EACF,CAAC;CACH,CAAC;CAED,UAAU,IAAI,IAAI,YAAY,MAAW,QAAa;EACpD,iBAAiB,GAAG;EACpB,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI;CAC3B,CAAC;CAED,MAAM,mBAAmB,MAAW,eAAwB;EAC1D,MAAM,WACJ,WAAW,WAAW,SAAS,WAAW,WAAW,SAAS,OAAO,WAAW,MAAM;EACxF,IAAI,gBAA+B;EACnC,OAAO;GACL,YAAY,WAAW;GACvB,YAAY,YAA6B;IACvC,IAAI,kBAAkB,MAAM,OAAO;IACnC,IAAI,CAAC,UAAU;KACb,gBAAgB;KAChB,OAAO;IACT;IACA,gBAAgB,MAAM,SAAS,KAAK;IACpC,OAAO;GACT;EACF;CACF;CAEA,MAAM,mBAAmB,OAAO,KAAU,QAAa;EACrD,iBAAiB,GAAG;EACpB,MAAM,aAAa,UAAU,IAAI,OAAO,UAAU;EAClD,IAAI,CAAC,YACH,OAAO,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,0BAA0B,CAAC;EAGlE,IAAI;GACF,MAAM,MAAM,UAAU,IAAI,QAAQ,OAAO,IAAI;GAC7C,MAAM,aAAa,IAAI,QAAQ,KAAK;IAClC,QAAQ,IAAI;IACZ,SAAS,IAAI;IACb,MAAM,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,MAAM;IAC5D,QAAQ,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,SAAS;GACnE,CAAgB;GAEhB,MAAM,SAAS,MAAM,WAAW,OAAO,YAAY;IACjD,QAAQ;IACR,SAAS,gBAAgB,KAAK,UAAU;GAC1C,CAAC;GAED,IAAI,OAAO,UAAU;IACnB,IAAI,OAAO,OAAO,SAAS,MAAM;IACjC,OAAO,SAAS,QAAQ,SAAS,OAAe,QAAgB;KAC9D,IAAI,UAAU,KAAK,KAAK;IAC1B,CAAC;IACD,MAAM,OAAO,MAAM,OAAO,SAAS,KAAK;IACxC,IAAI,KAAK,IAAI;GACf,OACE,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,WAAW;EAEpC,SAAS,OAAO;GACd,QAAQ,MAAM,kBAAkB,KAAK;GACrC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;EAC1D;CACF;CAEA,UAAU,IAAI,IAAI,WAAW,UAAU,KAAK,OAAO,KAAU,QAAa;EACxE,iBAAiB,GAAG;EACpB,MAAM,aAAa,UAAU,IAAI,OAAO,UAAU;EAClD,IAAI,CAAC,YACH,OAAO,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,0BAA0B,CAAC;EAGlE,IAAI;GACF,MAAM,MAAM,UAAU,IAAI,QAAQ,OAAO,IAAI;GAC7C,MAAM,aAAa,IAAI,QAAQ,KAAK;IAClC,QAAQ,IAAI;IACZ,SAAS,IAAI;IACb,MAAM,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,MAAM;IAC5D,QAAQ,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,SAAS;GACnE,CAAgB;GAEhB,MAAM,SAAS,MAAM,WAAW,OAAO,YAAY;IACjD,QAAQ,WAAW;IACnB,SAAS,gBAAgB,KAAK,UAAU;GAC1C,CAAC;GAED,IAAI,OAAO,UAAU;IACnB,IAAI,OAAO,OAAO,SAAS,MAAM;IACjC,OAAO,SAAS,QAAQ,SAAS,OAAe,QAAgB;KAC9D,IAAI,UAAU,KAAK,KAAK;IAC1B,CAAC;IACD,MAAM,OAAO,MAAM,OAAO,SAAS,KAAK;IACxC,IAAI,KAAK,IAAI;GACf,OACE,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,WAAW;EAEpC,SAAS,OAAO;GACd,QAAQ,MAAM,cAAc,KAAK;GACjC,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;EAC1D;CACF,CAAC;CAED,UAAU,IAAI,IAAI,QAAQ,gBAAgB;CAC1C,UAAU,IAAI,IAAI,UAAU,gBAAgB;AAC9C"}
|
|
@@ -14,6 +14,48 @@ const normalizePrefix = (prefix) => {
|
|
|
14
14
|
const cleaned = prefix.replace(/^\/+|\/+$/g, "");
|
|
15
15
|
return cleaned ? `/${cleaned}` : "";
|
|
16
16
|
};
|
|
17
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
|
+
const readRuntimeConfigFromEnv = () => {
|
|
19
|
+
const raw = process.env.BOS_RUNTIME_CONFIG;
|
|
20
|
+
if (!raw) return null;
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const collectSiblingRemotes = (runtimeConfig, pluginId) => {
|
|
28
|
+
const siblings = {};
|
|
29
|
+
const isApi = pluginId === "api" || runtimeConfig?.api?.name === pluginId;
|
|
30
|
+
const ownKey = runtimeConfig?.plugins?.[pluginId] ? pluginId : null;
|
|
31
|
+
let dependsOn = [];
|
|
32
|
+
if (ownKey) dependsOn = runtimeConfig.plugins[ownKey].dependsOn ?? [];
|
|
33
|
+
else if (isApi) dependsOn = runtimeConfig?.api?.dependsOn ?? [];
|
|
34
|
+
for (const depId of dependsOn) {
|
|
35
|
+
const dep = runtimeConfig?.plugins?.[depId];
|
|
36
|
+
if (!dep || dep.source !== "local" || !dep.url) continue;
|
|
37
|
+
if (depId === pluginId) continue;
|
|
38
|
+
siblings[depId] = { remote: `${dep.url.replace(/\/$/, "")}/remoteEntry.js` };
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
siblings,
|
|
42
|
+
dependsOn
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
const loadPluginWithRetry = async (runtime, pluginId, timeoutMs = 9e4) => {
|
|
46
|
+
const deadline = Date.now() + timeoutMs;
|
|
47
|
+
let lastError;
|
|
48
|
+
while (Date.now() < deadline) try {
|
|
49
|
+
return await runtime.usePlugin(pluginId, {
|
|
50
|
+
variables: {},
|
|
51
|
+
secrets: {}
|
|
52
|
+
});
|
|
53
|
+
} catch (error) {
|
|
54
|
+
lastError = error;
|
|
55
|
+
await sleep(500);
|
|
56
|
+
}
|
|
57
|
+
throw lastError;
|
|
58
|
+
};
|
|
17
59
|
function setupPluginMiddleware(devServer, pluginInfo, devConfig, port) {
|
|
18
60
|
const rpcPrefix = normalizePrefix(devConfig?.prefix);
|
|
19
61
|
const handlers = {
|
|
@@ -38,11 +80,23 @@ function setupPluginMiddleware(devServer, pluginInfo, devConfig, port) {
|
|
|
38
80
|
const { onError } = await import("../../orpc.mjs");
|
|
39
81
|
const { formatORPCError } = await import("../../errors.mjs");
|
|
40
82
|
const pluginId = devConfig?.pluginId || pluginInfo.normalizedName;
|
|
41
|
-
const
|
|
42
|
-
const
|
|
83
|
+
const runtimeConfig = readRuntimeConfigFromEnv();
|
|
84
|
+
const { siblings, dependsOn } = collectSiblingRemotes(runtimeConfig, pluginId);
|
|
85
|
+
if (dependsOn.length > 0) console.log(`│ 🔗 Loading sibling plugin(s) for ${pluginId}: ${dependsOn.join(", ")}`);
|
|
86
|
+
const runtime = createPluginRuntime({ registry: {
|
|
87
|
+
[pluginId]: { remote: `http://localhost:${port}/remoteEntry.js` },
|
|
88
|
+
...siblings
|
|
89
|
+
} });
|
|
90
|
+
const defaultConfig = {
|
|
43
91
|
variables: {},
|
|
44
92
|
secrets: {}
|
|
45
|
-
}
|
|
93
|
+
};
|
|
94
|
+
const pluginsMap = {};
|
|
95
|
+
for (const depId of Object.keys(siblings)) {
|
|
96
|
+
pluginsMap[depId] = (await loadPluginWithRetry(runtime, depId)).createClient;
|
|
97
|
+
console.log(`│ ✅ Loaded dependency plugin: ${depId}`);
|
|
98
|
+
}
|
|
99
|
+
const loaded = await runtime.usePlugin(pluginId, devConfig?.config ?? defaultConfig, Object.keys(pluginsMap).length > 0 ? pluginsMap : void 0);
|
|
46
100
|
cleanup = async () => {
|
|
47
101
|
handlers.rpc = null;
|
|
48
102
|
handlers.api = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server-middleware.mjs","names":[],"sources":["../../../src/build/rspack/dev-server-middleware.ts"],"sourcesContent":["import type { PluginInfo } from \"./utils\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: any) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst normalizePrefix = (prefix?: string): string => {\n if (!prefix) return \"\";\n const cleaned = prefix.replace(/^\\/+|\\/+$/g, \"\");\n return cleaned ? `/${cleaned}` : \"\";\n};\n\nexport function setupPluginMiddleware(\n devServer: any,\n pluginInfo: PluginInfo,\n devConfig: any,\n port: number,\n) {\n const rpcPrefix = normalizePrefix(devConfig?.prefix);\n const handlers: { rpc: any; api: any } = { rpc: null, api: null };\n let cleanup: (() => Promise<void>) | null = null;\n\n const performCleanup = async () => {\n if (cleanup) {\n await cleanup();\n cleanup = null;\n }\n };\n\n (async () => {\n await performCleanup();\n\n try {\n const { createPluginRuntime } = await import(\"every-plugin\");\n const { RPCHandler } = await import(\"@orpc/server/fetch\");\n const { OpenAPIHandler } = await import(\"@orpc/openapi/fetch\");\n const { OpenAPIReferencePlugin } = await import(\"@orpc/openapi/plugins\");\n const { ZodToJsonSchemaConverter } = await import(\"@orpc/zod/zod4\");\n const { onError } = await import(\"every-plugin/orpc\");\n const { formatORPCError } = await import(\"every-plugin/errors\");\n\n const pluginId = devConfig?.pluginId || pluginInfo.normalizedName;\n\n const runtime = createPluginRuntime({\n registry: {\n [pluginId]: {\n remote: `http://localhost:${port}/remoteEntry.js`,\n },\n },\n });\n\n const defaultConfig = { variables: {}, secrets: {} };\n\n // @ts-expect-error we don't know the plugin id\n const loaded = await runtime.usePlugin(pluginId, (devConfig?.config ?? defaultConfig) as any);\n\n cleanup = async () => {\n handlers.rpc = null;\n handlers.api = null;\n if (devServer.app.locals.handlers) {\n devServer.app.locals.handlers = null;\n }\n if (runtime) await runtime.shutdown();\n };\n\n handlers.rpc = new RPCHandler(loaded.router, {\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n handlers.api = new OpenAPIHandler(loaded.router, {\n plugins: [\n new OpenAPIReferencePlugin({\n schemaConverters: [new ZodToJsonSchemaConverter()],\n }),\n ],\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n console.log(`╭─────────────────────────────────────────────`);\n console.log(`│ ✅ Plugin dev server ready: `);\n console.log(`├─────────────────────────────────────────────`);\n console.log(`│ 📡 RPC: http://localhost:${port}/api/rpc${rpcPrefix}`);\n console.log(`│ 📖 Docs: http://localhost:${port}/api`);\n console.log(`│ 💚 Health: http://localhost:${port}/`);\n console.log(`╰─────────────────────────────────────────────`);\n\n devServer.app.locals.handlers = handlers;\n\n if (devServer.server) {\n devServer.server.once(\"close\", async () => {\n await performCleanup();\n });\n }\n } catch (error) {\n console.error(\"❌ Failed to load plugin:\", error);\n await performCleanup();\n }\n })().catch((err) => {\n console.error(\"❌ Plugin dev server fatal error:\", err);\n });\n\n process.once(\"SIGINT\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await performCleanup();\n clearTimeout(timeout);\n });\n process.once(\"SIGTERM\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await performCleanup();\n clearTimeout(timeout);\n });\n\n devServer.app.options(\"*\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.status(200).end();\n });\n\n devServer.app.get(\"/\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.json({\n ok: true,\n plugin: pluginInfo.normalizedName,\n version: pluginInfo.version,\n status: devServer.app.locals.handlers?.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/\",\n docs: \"/api\",\n rpc: `/api/rpc${rpcPrefix}`,\n },\n });\n });\n\n devServer.app.get(\"/health\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.status(200).send(\"OK\");\n });\n\n const buildDevContext = (_req: any, webRequest: Request) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n let cachedRawBody: string | null = null;\n return {\n reqHeaders: webRequest.headers,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBody !== null) return cachedRawBody;\n if (!rawClone) {\n cachedRawBody = \"\";\n return cachedRawBody;\n }\n cachedRawBody = await rawClone.text();\n return cachedRawBody;\n },\n };\n };\n\n const handleApiRequest = async (req: any, res: any) => {\n applyCorsHeaders(res);\n const apiHandler = devServer.app.locals.handlers?.api;\n if (!apiHandler) {\n return res.status(503).json({ error: \"Plugin still loading...\" });\n }\n\n try {\n const url = `http://${req.headers.host}${req.url}`;\n const webRequest = new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n\n const result = await apiHandler.handle(webRequest, {\n prefix: \"/api\",\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.status(result.response.status);\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n res.send(text);\n } else {\n res.status(404).send(\"Not Found\");\n }\n } catch (error) {\n console.error(\"OpenAPI error:\", error);\n res.status(500).json({ error: (error as Error).message });\n }\n };\n\n devServer.app.all(`/api/rpc${rpcPrefix}/*`, async (req: any, res: any) => {\n applyCorsHeaders(res);\n const rpcHandler = devServer.app.locals.handlers?.rpc;\n if (!rpcHandler) {\n return res.status(503).json({ error: \"Plugin still loading...\" });\n }\n\n try {\n const url = `http://${req.headers.host}${req.url}`;\n const webRequest = new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n\n const result = await rpcHandler.handle(webRequest, {\n prefix: `/api/rpc${rpcPrefix}`,\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.status(result.response.status);\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n res.send(text);\n } else {\n res.status(404).send(\"Not Found\");\n }\n } catch (error) {\n console.error(\"RPC error:\", error);\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n devServer.app.all(\"/api\", handleApiRequest);\n devServer.app.all(\"/api/*\", handleApiRequest);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dev-server-middleware.mjs","names":[],"sources":["../../../src/build/rspack/dev-server-middleware.ts"],"sourcesContent":["import type { PluginInfo } from \"./utils\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: any) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst normalizePrefix = (prefix?: string): string => {\n if (!prefix) return \"\";\n const cleaned = prefix.replace(/^\\/+|\\/+$/g, \"\");\n return cleaned ? `/${cleaned}` : \"\";\n};\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst readRuntimeConfigFromEnv = (): any => {\n const raw = process.env.BOS_RUNTIME_CONFIG;\n if (!raw) return null;\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n};\n\nconst collectSiblingRemotes = (runtimeConfig: any, pluginId: string) => {\n const siblings: Record<string, { remote: string }> = {};\n\n const isApi = pluginId === \"api\" || runtimeConfig?.api?.name === pluginId;\n\n const ownKey = runtimeConfig?.plugins?.[pluginId] ? pluginId : null;\n let dependsOn: string[] = [];\n if (ownKey) {\n dependsOn = runtimeConfig.plugins[ownKey].dependsOn ?? [];\n } else if (isApi) {\n dependsOn = runtimeConfig?.api?.dependsOn ?? [];\n }\n\n for (const depId of dependsOn) {\n const dep = runtimeConfig?.plugins?.[depId];\n if (!dep || dep.source !== \"local\" || !dep.url) continue;\n if (depId === pluginId) continue;\n const base = dep.url.replace(/\\/$/, \"\");\n siblings[depId] = { remote: `${base}/remoteEntry.js` };\n }\n\n return { siblings, dependsOn };\n};\n\nconst loadPluginWithRetry = async (\n runtime: any,\n pluginId: string,\n timeoutMs = 90000,\n): Promise<any> => {\n const deadline = Date.now() + timeoutMs;\n let lastError: unknown;\n while (Date.now() < deadline) {\n try {\n return await runtime.usePlugin(pluginId, { variables: {}, secrets: {} } as any);\n } catch (error) {\n lastError = error;\n await sleep(500);\n }\n }\n throw lastError;\n};\n\nexport function setupPluginMiddleware(\n devServer: any,\n pluginInfo: PluginInfo,\n devConfig: any,\n port: number,\n) {\n const rpcPrefix = normalizePrefix(devConfig?.prefix);\n const handlers: { rpc: any; api: any } = { rpc: null, api: null };\n let cleanup: (() => Promise<void>) | null = null;\n\n const performCleanup = async () => {\n if (cleanup) {\n await cleanup();\n cleanup = null;\n }\n };\n\n (async () => {\n await performCleanup();\n\n try {\n const { createPluginRuntime } = await import(\"every-plugin\");\n const { RPCHandler } = await import(\"@orpc/server/fetch\");\n const { OpenAPIHandler } = await import(\"@orpc/openapi/fetch\");\n const { OpenAPIReferencePlugin } = await import(\"@orpc/openapi/plugins\");\n const { ZodToJsonSchemaConverter } = await import(\"@orpc/zod/zod4\");\n const { onError } = await import(\"every-plugin/orpc\");\n const { formatORPCError } = await import(\"every-plugin/errors\");\n\n const pluginId = devConfig?.pluginId || pluginInfo.normalizedName;\n\n const runtimeConfig = readRuntimeConfigFromEnv();\n const { siblings, dependsOn } = collectSiblingRemotes(runtimeConfig, pluginId);\n\n if (dependsOn.length > 0) {\n console.log(`│ 🔗 Loading sibling plugin(s) for ${pluginId}: ${dependsOn.join(\", \")}`);\n }\n\n const registry: Record<string, { remote: string }> = {\n [pluginId]: {\n remote: `http://localhost:${port}/remoteEntry.js`,\n },\n ...siblings,\n };\n\n const runtime = createPluginRuntime({\n registry,\n });\n\n const defaultConfig = { variables: {}, secrets: {} };\n\n const pluginsMap: Record<string, unknown> = {};\n for (const depId of Object.keys(siblings)) {\n const dep = await loadPluginWithRetry(runtime, depId);\n pluginsMap[depId] = dep.createClient;\n console.log(`│ ✅ Loaded dependency plugin: ${depId}`);\n }\n\n const loaded: any = await runtime.usePlugin<typeof pluginId>(\n pluginId,\n (devConfig?.config ?? defaultConfig) as any,\n Object.keys(pluginsMap).length > 0 ? pluginsMap : undefined,\n );\n\n cleanup = async () => {\n handlers.rpc = null;\n handlers.api = null;\n if (devServer.app.locals.handlers) {\n devServer.app.locals.handlers = null;\n }\n if (runtime) await runtime.shutdown();\n };\n\n handlers.rpc = new RPCHandler(loaded.router, {\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n handlers.api = new OpenAPIHandler(loaded.router, {\n plugins: [\n new OpenAPIReferencePlugin({\n schemaConverters: [new ZodToJsonSchemaConverter()],\n }),\n ],\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n console.log(`╭─────────────────────────────────────────────`);\n console.log(`│ ✅ Plugin dev server ready: `);\n console.log(`├─────────────────────────────────────────────`);\n console.log(`│ 📡 RPC: http://localhost:${port}/api/rpc${rpcPrefix}`);\n console.log(`│ 📖 Docs: http://localhost:${port}/api`);\n console.log(`│ 💚 Health: http://localhost:${port}/`);\n console.log(`╰─────────────────────────────────────────────`);\n\n devServer.app.locals.handlers = handlers;\n\n if (devServer.server) {\n devServer.server.once(\"close\", async () => {\n await performCleanup();\n });\n }\n } catch (error) {\n console.error(\"❌ Failed to load plugin:\", error);\n await performCleanup();\n }\n })().catch((err) => {\n console.error(\"❌ Plugin dev server fatal error:\", err);\n });\n\n process.once(\"SIGINT\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await performCleanup();\n clearTimeout(timeout);\n });\n process.once(\"SIGTERM\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await performCleanup();\n clearTimeout(timeout);\n });\n\n devServer.app.options(\"*\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.status(200).end();\n });\n\n devServer.app.get(\"/\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.json({\n ok: true,\n plugin: pluginInfo.normalizedName,\n version: pluginInfo.version,\n status: devServer.app.locals.handlers?.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/\",\n docs: \"/api\",\n rpc: `/api/rpc${rpcPrefix}`,\n },\n });\n });\n\n devServer.app.get(\"/health\", (_req: any, res: any) => {\n applyCorsHeaders(res);\n res.status(200).send(\"OK\");\n });\n\n const buildDevContext = (_req: any, webRequest: Request) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n let cachedRawBody: string | null = null;\n return {\n reqHeaders: webRequest.headers,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBody !== null) return cachedRawBody;\n if (!rawClone) {\n cachedRawBody = \"\";\n return cachedRawBody;\n }\n cachedRawBody = await rawClone.text();\n return cachedRawBody;\n },\n };\n };\n\n const handleApiRequest = async (req: any, res: any) => {\n applyCorsHeaders(res);\n const apiHandler = devServer.app.locals.handlers?.api;\n if (!apiHandler) {\n return res.status(503).json({ error: \"Plugin still loading...\" });\n }\n\n try {\n const url = `http://${req.headers.host}${req.url}`;\n const webRequest = new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n\n const result = await apiHandler.handle(webRequest, {\n prefix: \"/api\",\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.status(result.response.status);\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n res.send(text);\n } else {\n res.status(404).send(\"Not Found\");\n }\n } catch (error) {\n console.error(\"OpenAPI error:\", error);\n res.status(500).json({ error: (error as Error).message });\n }\n };\n\n devServer.app.all(`/api/rpc${rpcPrefix}/*`, async (req: any, res: any) => {\n applyCorsHeaders(res);\n const rpcHandler = devServer.app.locals.handlers?.rpc;\n if (!rpcHandler) {\n return res.status(503).json({ error: \"Plugin still loading...\" });\n }\n\n try {\n const url = `http://${req.headers.host}${req.url}`;\n const webRequest = new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n\n const result = await rpcHandler.handle(webRequest, {\n prefix: `/api/rpc${rpcPrefix}`,\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.status(result.response.status);\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n res.send(text);\n } else {\n res.status(404).send(\"Not Found\");\n }\n } catch (error) {\n console.error(\"RPC error:\", error);\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n devServer.app.all(\"/api\", handleApiRequest);\n devServer.app.all(\"/api/*\", handleApiRequest);\n}\n"],"mappings":"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"}
|
|
@@ -20,7 +20,7 @@ function getInstalledPackageVersion(packageName, fallbackRange) {
|
|
|
20
20
|
return match ? match[0] : fallbackRange.replace(/^[\^~>=<\s]+/, "");
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
const PLUGIN_VERSION = "2.10.
|
|
23
|
+
const PLUGIN_VERSION = "2.10.1";
|
|
24
24
|
const SHARE_CONFIG = {
|
|
25
25
|
singleton: true,
|
|
26
26
|
requiredVersion: false,
|
|
@@ -19,7 +19,7 @@ function getInstalledPackageVersion(packageName, fallbackRange) {
|
|
|
19
19
|
return match ? match[0] : fallbackRange.replace(/^[\^~>=<\s]+/, "");
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
const PLUGIN_VERSION = "2.10.
|
|
22
|
+
const PLUGIN_VERSION = "2.10.1";
|
|
23
23
|
const SHARE_CONFIG = {
|
|
24
24
|
singleton: true,
|
|
25
25
|
requiredVersion: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "every-plugin",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@rspack/core": "1.7.11",
|
|
95
95
|
"typescript": "^5.9.3",
|
|
96
96
|
"effect": "3.21.2",
|
|
97
|
-
"zod": "4.3
|
|
97
|
+
"zod": "4.4.3",
|
|
98
98
|
"@orpc/contract": "1.14.3",
|
|
99
99
|
"@orpc/server": "1.14.3",
|
|
100
100
|
"@orpc/client": "1.14.3",
|
|
@@ -18,6 +18,60 @@ const normalizePrefix = (prefix?: string): string => {
|
|
|
18
18
|
return cleaned ? `/${cleaned}` : "";
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
|
+
|
|
23
|
+
const readRuntimeConfigFromEnv = (): any => {
|
|
24
|
+
const raw = process.env.BOS_RUNTIME_CONFIG;
|
|
25
|
+
if (!raw) return null;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(raw);
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const collectSiblingRemotes = (runtimeConfig: any, pluginId: string) => {
|
|
34
|
+
const siblings: Record<string, { remote: string }> = {};
|
|
35
|
+
|
|
36
|
+
const isApi = pluginId === "api" || runtimeConfig?.api?.name === pluginId;
|
|
37
|
+
|
|
38
|
+
const ownKey = runtimeConfig?.plugins?.[pluginId] ? pluginId : null;
|
|
39
|
+
let dependsOn: string[] = [];
|
|
40
|
+
if (ownKey) {
|
|
41
|
+
dependsOn = runtimeConfig.plugins[ownKey].dependsOn ?? [];
|
|
42
|
+
} else if (isApi) {
|
|
43
|
+
dependsOn = runtimeConfig?.api?.dependsOn ?? [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const depId of dependsOn) {
|
|
47
|
+
const dep = runtimeConfig?.plugins?.[depId];
|
|
48
|
+
if (!dep || dep.source !== "local" || !dep.url) continue;
|
|
49
|
+
if (depId === pluginId) continue;
|
|
50
|
+
const base = dep.url.replace(/\/$/, "");
|
|
51
|
+
siblings[depId] = { remote: `${base}/remoteEntry.js` };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { siblings, dependsOn };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const loadPluginWithRetry = async (
|
|
58
|
+
runtime: any,
|
|
59
|
+
pluginId: string,
|
|
60
|
+
timeoutMs = 90000,
|
|
61
|
+
): Promise<any> => {
|
|
62
|
+
const deadline = Date.now() + timeoutMs;
|
|
63
|
+
let lastError: unknown;
|
|
64
|
+
while (Date.now() < deadline) {
|
|
65
|
+
try {
|
|
66
|
+
return await runtime.usePlugin(pluginId, { variables: {}, secrets: {} } as any);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
lastError = error;
|
|
69
|
+
await sleep(500);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
throw lastError;
|
|
73
|
+
};
|
|
74
|
+
|
|
21
75
|
export function setupPluginMiddleware(
|
|
22
76
|
devServer: any,
|
|
23
77
|
pluginInfo: PluginInfo,
|
|
@@ -49,18 +103,38 @@ export function setupPluginMiddleware(
|
|
|
49
103
|
|
|
50
104
|
const pluginId = devConfig?.pluginId || pluginInfo.normalizedName;
|
|
51
105
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
106
|
+
const runtimeConfig = readRuntimeConfigFromEnv();
|
|
107
|
+
const { siblings, dependsOn } = collectSiblingRemotes(runtimeConfig, pluginId);
|
|
108
|
+
|
|
109
|
+
if (dependsOn.length > 0) {
|
|
110
|
+
console.log(`│ 🔗 Loading sibling plugin(s) for ${pluginId}: ${dependsOn.join(", ")}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const registry: Record<string, { remote: string }> = {
|
|
114
|
+
[pluginId]: {
|
|
115
|
+
remote: `http://localhost:${port}/remoteEntry.js`,
|
|
57
116
|
},
|
|
117
|
+
...siblings,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const runtime = createPluginRuntime({
|
|
121
|
+
registry,
|
|
58
122
|
});
|
|
59
123
|
|
|
60
124
|
const defaultConfig = { variables: {}, secrets: {} };
|
|
61
125
|
|
|
62
|
-
|
|
63
|
-
const
|
|
126
|
+
const pluginsMap: Record<string, unknown> = {};
|
|
127
|
+
for (const depId of Object.keys(siblings)) {
|
|
128
|
+
const dep = await loadPluginWithRetry(runtime, depId);
|
|
129
|
+
pluginsMap[depId] = dep.createClient;
|
|
130
|
+
console.log(`│ ✅ Loaded dependency plugin: ${depId}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const loaded: any = await runtime.usePlugin<typeof pluginId>(
|
|
134
|
+
pluginId,
|
|
135
|
+
(devConfig?.config ?? defaultConfig) as any,
|
|
136
|
+
Object.keys(pluginsMap).length > 0 ? pluginsMap : undefined,
|
|
137
|
+
);
|
|
64
138
|
|
|
65
139
|
cleanup = async () => {
|
|
66
140
|
handlers.rpc = null;
|