every-plugin
Version:
192 lines (190 loc) • 7.09 kB
JavaScript
//#region src/build/rspack/dev-server-middleware.ts
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
};
const applyCorsHeaders = (res) => {
Object.entries(corsHeaders).forEach(([key, value]) => {
res.setHeader(key, value);
});
};
const normalizePrefix = (prefix) => {
if (!prefix) return "";
const cleaned = prefix.replace(/^\/+|\/+$/g, "");
return cleaned ? `/${cleaned}` : "";
};
function setupPluginMiddleware(devServer, pluginInfo, devConfig, port) {
const rpcPrefix = normalizePrefix(devConfig?.prefix);
const handlers = {
rpc: null,
api: null
};
let cleanup = null;
const performCleanup = async () => {
if (cleanup) {
await cleanup();
cleanup = null;
}
};
(async () => {
await performCleanup();
try {
const { createPluginRuntime } = await Promise.resolve().then(() => require("../../index.cjs"));
const { RPCHandler } = await import("@orpc/server/fetch");
const { OpenAPIHandler } = await import("@orpc/openapi/fetch");
const { OpenAPIReferencePlugin } = await import("@orpc/openapi/plugins");
const { ZodToJsonSchemaConverter } = await import("@orpc/zod/zod4");
const { onError } = await Promise.resolve().then(() => require("../../orpc.cjs"));
const { formatORPCError } = await Promise.resolve().then(() => require("../../errors.cjs"));
const pluginId = devConfig?.pluginId || pluginInfo.normalizedName;
const runtime = createPluginRuntime({ registry: { [pluginId]: { remote: `http://localhost:${port}/remoteEntry.js` } } });
const loaded = await runtime.usePlugin(pluginId, devConfig?.config ?? {
variables: {},
secrets: {}
});
cleanup = async () => {
if (runtime) await runtime.shutdown();
handlers.rpc = null;
handlers.api = null;
if (devServer.app.locals.handlers) devServer.app.locals.handlers = null;
};
handlers.rpc = new RPCHandler(loaded.router, { interceptors: [onError((error) => {
formatORPCError(error);
})] });
handlers.api = new OpenAPIHandler(loaded.router, {
plugins: [new OpenAPIReferencePlugin({ schemaConverters: [new ZodToJsonSchemaConverter()] })],
interceptors: [onError((error) => {
formatORPCError(error);
})]
});
console.log(`╭─────────────────────────────────────────────`);
console.log(`│ ✅ Plugin dev server ready: `);
console.log(`├─────────────────────────────────────────────`);
console.log(`│ 📡 RPC: http://localhost:${port}/api/rpc${rpcPrefix}`);
console.log(`│ 📖 Docs: http://localhost:${port}/api`);
console.log(`│ 💚 Health: http://localhost:${port}/`);
console.log(`╰─────────────────────────────────────────────`);
devServer.app.locals.handlers = handlers;
if (devServer.server) devServer.server.once("close", async () => {
await performCleanup();
});
} catch (error) {
console.error("❌ Failed to load plugin:", error);
await performCleanup();
}
})().catch((err) => {
console.error("❌ Plugin dev server fatal error:", err);
});
process.once("SIGINT", async () => {
const timeout = setTimeout(() => process.exit(0), 3e3);
await performCleanup();
clearTimeout(timeout);
});
process.once("SIGTERM", async () => {
const timeout = setTimeout(() => process.exit(0), 3e3);
await performCleanup();
clearTimeout(timeout);
});
devServer.app.options("*", (_req, res) => {
applyCorsHeaders(res);
res.status(200).end();
});
devServer.app.get("/", (_req, res) => {
applyCorsHeaders(res);
res.json({
ok: true,
plugin: pluginInfo.normalizedName,
version: pluginInfo.version,
status: devServer.app.locals.handlers?.rpc ? "ready" : "loading",
endpoints: {
health: "/",
docs: "/api",
rpc: `/api/rpc${rpcPrefix}`
}
});
});
devServer.app.get("/health", (_req, res) => {
applyCorsHeaders(res);
res.status(200).send("OK");
});
const buildDevContext = (_req, webRequest) => {
const rawClone = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : webRequest.clone();
let cachedRawBody = null;
return {
reqHeaders: webRequest.headers,
getRawBody: async () => {
if (cachedRawBody !== null) return cachedRawBody;
if (!rawClone) {
cachedRawBody = "";
return cachedRawBody;
}
cachedRawBody = await rawClone.text();
return cachedRawBody;
}
};
};
const handleApiRequest = async (req, res) => {
applyCorsHeaders(res);
const apiHandler = devServer.app.locals.handlers?.api;
if (!apiHandler) return res.status(503).json({ error: "Plugin still loading..." });
try {
const url = `http://${req.headers.host}${req.url}`;
const webRequest = new Request(url, {
method: req.method,
headers: req.headers,
body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0,
duplex: req.method !== "GET" && req.method !== "HEAD" ? "half" : void 0
});
const result = await apiHandler.handle(webRequest, {
prefix: "/api",
context: buildDevContext(req, webRequest)
});
if (result.response) {
res.status(result.response.status);
result.response.headers.forEach((value, key) => {
res.setHeader(key, value);
});
const text = await result.response.text();
res.send(text);
} else res.status(404).send("Not Found");
} catch (error) {
console.error("OpenAPI error:", error);
res.status(500).json({ error: error.message });
}
};
devServer.app.all(`/api/rpc${rpcPrefix}/*`, async (req, res) => {
applyCorsHeaders(res);
const rpcHandler = devServer.app.locals.handlers?.rpc;
if (!rpcHandler) return res.status(503).json({ error: "Plugin still loading..." });
try {
const url = `http://${req.headers.host}${req.url}`;
const webRequest = new Request(url, {
method: req.method,
headers: req.headers,
body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0,
duplex: req.method !== "GET" && req.method !== "HEAD" ? "half" : void 0
});
const result = await rpcHandler.handle(webRequest, {
prefix: `/api/rpc${rpcPrefix}`,
context: buildDevContext(req, webRequest)
});
if (result.response) {
res.status(result.response.status);
result.response.headers.forEach((value, key) => {
res.setHeader(key, value);
});
const text = await result.response.text();
res.send(text);
} else res.status(404).send("Not Found");
} catch (error) {
console.error("RPC error:", error);
res.status(500).json({ error: error.message });
}
});
devServer.app.all("/api", handleApiRequest);
devServer.app.all("/api/*", handleApiRequest);
}
//#endregion
exports.setupPluginMiddleware = setupPluginMiddleware;
//# sourceMappingURL=dev-server-middleware.cjs.map