every-plugin 3.0.0-rc.0 → 3.0.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/build/rspack/chunk-completeness-plugin.cjs +19 -7
  2. package/dist/build/rspack/chunk-completeness-plugin.cjs.map +1 -1
  3. package/dist/build/rspack/chunk-completeness-plugin.mjs +19 -7
  4. package/dist/build/rspack/chunk-completeness-plugin.mjs.map +1 -1
  5. package/dist/build/ui/generated-config.cjs +4 -0
  6. package/dist/build/ui/generated-config.cjs.map +1 -1
  7. package/dist/build/ui/generated-config.d.cts.map +1 -1
  8. package/dist/build/ui/generated-config.d.mts.map +1 -1
  9. package/dist/build/ui/generated-config.mjs +4 -1
  10. package/dist/build/ui/generated-config.mjs.map +1 -1
  11. package/dist/build/ui/rsbuild-config.cjs +1 -0
  12. package/dist/build/ui/rsbuild-config.cjs.map +1 -1
  13. package/dist/build/ui/rsbuild-config.mjs +1 -0
  14. package/dist/build/ui/rsbuild-config.mjs.map +1 -1
  15. package/dist/client.cjs +69 -0
  16. package/dist/client.cjs.map +1 -0
  17. package/dist/client.d.cts +15 -0
  18. package/dist/client.d.cts.map +1 -0
  19. package/dist/client.d.mts +15 -0
  20. package/dist/client.d.mts.map +1 -0
  21. package/dist/client.mjs +68 -0
  22. package/dist/client.mjs.map +1 -0
  23. package/dist/dev/host.cjs +149 -0
  24. package/dist/dev/host.cjs.map +1 -0
  25. package/dist/dev/host.d.cts +32 -0
  26. package/dist/dev/host.d.cts.map +1 -0
  27. package/dist/dev/host.d.mts +32 -0
  28. package/dist/dev/host.d.mts.map +1 -0
  29. package/dist/dev/host.mjs +148 -0
  30. package/dist/dev/host.mjs.map +1 -0
  31. package/dist/dev/serve.cjs +16 -111
  32. package/dist/dev/serve.cjs.map +1 -1
  33. package/dist/dev/serve.d.cts.map +1 -1
  34. package/dist/dev/serve.d.mts.map +1 -1
  35. package/dist/dev/serve.mjs +17 -112
  36. package/dist/dev/serve.mjs.map +1 -1
  37. package/dist/errors.cjs +1 -0
  38. package/dist/errors.cjs.map +1 -1
  39. package/dist/errors.d.cts +2 -2
  40. package/dist/errors.d.mts +2 -2
  41. package/dist/errors.mjs +2 -2
  42. package/dist/errors.mjs.map +1 -1
  43. package/dist/index.cjs +3 -0
  44. package/dist/index.d.cts +5 -4
  45. package/dist/index.d.mts +5 -4
  46. package/dist/index.mjs +3 -2
  47. package/dist/plugin.cjs +1 -0
  48. package/dist/plugin.cjs.map +1 -1
  49. package/dist/plugin.d.cts +23 -3
  50. package/dist/plugin.d.cts.map +1 -1
  51. package/dist/plugin.d.mts +22 -2
  52. package/dist/plugin.d.mts.map +1 -1
  53. package/dist/plugin.mjs +1 -0
  54. package/dist/plugin.mjs.map +1 -1
  55. package/dist/remote-entry.cjs +0 -1
  56. package/dist/remote-entry.cjs.map +1 -1
  57. package/dist/remote-entry.d.cts +1 -2
  58. package/dist/remote-entry.d.cts.map +1 -1
  59. package/dist/remote-entry.d.mts +1 -2
  60. package/dist/remote-entry.d.mts.map +1 -1
  61. package/dist/remote-entry.mjs +1 -1
  62. package/dist/remote-entry.mjs.map +1 -1
  63. package/dist/runtime/errors.cjs +20 -0
  64. package/dist/runtime/errors.cjs.map +1 -1
  65. package/dist/runtime/errors.d.cts +23 -1
  66. package/dist/runtime/errors.d.cts.map +1 -1
  67. package/dist/runtime/errors.d.mts +23 -1
  68. package/dist/runtime/errors.d.mts.map +1 -1
  69. package/dist/runtime/errors.mjs +20 -1
  70. package/dist/runtime/errors.mjs.map +1 -1
  71. package/dist/runtime/mf-config.cjs +1 -1
  72. package/dist/runtime/mf-config.mjs +1 -1
  73. package/dist/runtime/services/module-federation.service.cjs +1 -7
  74. package/dist/runtime/services/module-federation.service.cjs.map +1 -1
  75. package/dist/runtime/services/module-federation.service.mjs +1 -7
  76. package/dist/runtime/services/module-federation.service.mjs.map +1 -1
  77. package/dist/runtime/services/plugin-loader.service.cjs +29 -1
  78. package/dist/runtime/services/plugin-loader.service.cjs.map +1 -1
  79. package/dist/runtime/services/plugin-loader.service.d.cts +2 -2
  80. package/dist/runtime/services/plugin-loader.service.mjs +29 -1
  81. package/dist/runtime/services/plugin-loader.service.mjs.map +1 -1
  82. package/dist/runtime/services/shared-identity.cjs +1 -1
  83. package/dist/runtime/services/shared-identity.cjs.map +1 -1
  84. package/dist/runtime/services/shared-identity.mjs +1 -1
  85. package/dist/runtime/services/shared-identity.mjs.map +1 -1
  86. package/dist/types.d.cts +27 -2
  87. package/dist/types.d.cts.map +1 -1
  88. package/dist/types.d.mts +26 -1
  89. package/dist/types.d.mts.map +1 -1
  90. package/dist/ui/manifest/construct.cjs +69 -18
  91. package/dist/ui/manifest/construct.cjs.map +1 -1
  92. package/dist/ui/manifest/construct.d.cts.map +1 -1
  93. package/dist/ui/manifest/construct.d.mts.map +1 -1
  94. package/dist/ui/manifest/construct.mjs +69 -18
  95. package/dist/ui/manifest/construct.mjs.map +1 -1
  96. package/dist/ui/manifest/generator.cjs +1 -1
  97. package/dist/ui/manifest/generator.mjs +1 -1
  98. package/dist/ui/manifest/index.cjs +1 -1
  99. package/dist/ui/manifest/index.mjs +1 -1
  100. package/dist/version-manifest.cjs +14 -2
  101. package/dist/version-manifest.cjs.map +1 -1
  102. package/dist/version-manifest.d.cts +11 -1
  103. package/dist/version-manifest.d.cts.map +1 -1
  104. package/dist/version-manifest.d.mts +11 -1
  105. package/dist/version-manifest.d.mts.map +1 -1
  106. package/dist/version-manifest.mjs +14 -2
  107. package/dist/version-manifest.mjs.map +1 -1
  108. package/package.json +20 -3
  109. package/skills/plugin-client/SKILL.md +10 -4
  110. package/skills/plugin-testing/SKILL.md +2 -1
  111. package/src/build/rspack/chunk-completeness-plugin.ts +15 -6
  112. package/src/build/ui/generated-config.ts +4 -0
  113. package/src/build/ui/rsbuild-config.ts +1 -0
  114. package/src/client.ts +111 -0
  115. package/src/dev/host.ts +235 -0
  116. package/src/dev/serve.ts +22 -140
  117. package/src/errors.ts +2 -1
  118. package/src/index.ts +9 -0
  119. package/src/plugin.ts +26 -1
  120. package/src/remote-entry.ts +1 -2
  121. package/src/runtime/errors.ts +44 -0
  122. package/src/runtime/services/module-federation.service.ts +4 -7
  123. package/src/runtime/services/plugin-loader.service.ts +46 -2
  124. package/src/runtime/services/shared-identity.ts +1 -1
  125. package/src/types.ts +40 -2
  126. package/src/ui/manifest/construct.ts +68 -15
  127. package/src/version-manifest.ts +19 -1
@@ -0,0 +1,149 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let node_http = require("node:http");
3
+
4
+ //#region src/dev/host.ts
5
+ const corsHeaders = {
6
+ "Access-Control-Allow-Origin": "*",
7
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
8
+ "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
9
+ };
10
+ const applyCorsHeaders = (res) => {
11
+ Object.entries(corsHeaders).forEach(([key, value]) => {
12
+ res.setHeader(key, value);
13
+ });
14
+ };
15
+ const sendJson = (res, status, body) => {
16
+ res.statusCode = status;
17
+ res.setHeader("content-type", "application/json; charset=utf-8");
18
+ res.end(JSON.stringify(body));
19
+ };
20
+ const sendText = (res, status, text) => {
21
+ res.statusCode = status;
22
+ res.setHeader("content-type", "text/plain; charset=utf-8");
23
+ res.end(text);
24
+ };
25
+ const pathnameOf = (req) => ((req.url ?? "/").split("?")[0] ?? "/").replace(/\/+$/, "") || "/";
26
+ const toWebRequest = (req, port) => {
27
+ const url = `http://localhost:${port}${req.url}`;
28
+ return new Request(url, {
29
+ method: req.method,
30
+ headers: req.headers,
31
+ body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0,
32
+ duplex: req.method !== "GET" && req.method !== "HEAD" ? "half" : void 0
33
+ });
34
+ };
35
+ const buildHostContext = (effectContext, webRequest, cachedRawBodyRef) => {
36
+ const rawClone = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : webRequest.clone();
37
+ return {
38
+ reqHeaders: webRequest.headers,
39
+ "effect/context": effectContext,
40
+ getRawBody: async () => {
41
+ if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;
42
+ if (!rawClone) {
43
+ cachedRawBodyRef.value = "";
44
+ return cachedRawBodyRef.value;
45
+ }
46
+ cachedRawBodyRef.value = await rawClone.text();
47
+ return cachedRawBodyRef.value;
48
+ }
49
+ };
50
+ };
51
+ const handleOrpc = async (req, res, handler, prefix, errorLabel, effectContext, port) => {
52
+ try {
53
+ const webRequest = toWebRequest(req, port);
54
+ const result = await handler.handle(webRequest, {
55
+ prefix,
56
+ context: buildHostContext(effectContext, webRequest, { value: null })
57
+ });
58
+ if (result.response) {
59
+ res.statusCode = result.response.status;
60
+ result.response.headers.forEach((value, key) => {
61
+ res.setHeader(key, value);
62
+ });
63
+ const text = await result.response.text();
64
+ if (!res.getHeader("content-length")) res.setHeader("content-length", Buffer.byteLength(text).toString());
65
+ res.end(text);
66
+ } else sendText(res, 404, "Not Found");
67
+ } catch (error) {
68
+ console.error(`${errorLabel} error:`, error);
69
+ sendJson(res, 500, { error: error.message });
70
+ }
71
+ };
72
+ async function startDirectModuleHost(options) {
73
+ const port = options.port ?? 0;
74
+ let resolvedPort = port;
75
+ const rpcPrefix = options.rpcPrefix ?? "/api/rpc";
76
+ const apiPrefix = options.apiPrefix ?? "/api";
77
+ const staticHandler = options.staticHandler ?? null;
78
+ const resolveEffectContext = typeof options.effectContext === "function" ? options.effectContext : () => options.effectContext;
79
+ const server = (0, node_http.createServer)((req, res) => {
80
+ applyCorsHeaders(res);
81
+ const pathname = pathnameOf(req);
82
+ if (req.method === "OPTIONS") {
83
+ res.statusCode = 200;
84
+ res.end();
85
+ return;
86
+ }
87
+ if (pathname === "/" && (req.method === "GET" || req.method === "HEAD")) {
88
+ sendJson(res, 200, {
89
+ ok: true,
90
+ module: options.name,
91
+ version: options.version,
92
+ status: options.handlers.rpc ? "ready" : "loading",
93
+ endpoints: {
94
+ health: "/health",
95
+ docs: apiPrefix,
96
+ rpc: rpcPrefix
97
+ }
98
+ });
99
+ return;
100
+ }
101
+ if (pathname === "/health" && (req.method === "GET" || req.method === "HEAD")) {
102
+ sendText(res, 200, "OK");
103
+ return;
104
+ }
105
+ if (pathname.startsWith(`${rpcPrefix}/`)) {
106
+ if (!options.handlers.rpc) {
107
+ sendJson(res, 503, { error: "Plugin still loading..." });
108
+ return;
109
+ }
110
+ handleOrpc(req, res, options.handlers.rpc, rpcPrefix, "RPC", resolveEffectContext(), resolvedPort).catch(() => {});
111
+ return;
112
+ }
113
+ if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {
114
+ if (!options.handlers.api) {
115
+ sendJson(res, 503, { error: "Plugin still loading..." });
116
+ return;
117
+ }
118
+ handleOrpc(req, res, options.handlers.api, apiPrefix, "OpenAPI", resolveEffectContext(), resolvedPort).catch(() => {});
119
+ return;
120
+ }
121
+ if (staticHandler) {
122
+ staticHandler(req, res, () => {
123
+ sendText(res, 404, "Not Found");
124
+ });
125
+ return;
126
+ }
127
+ sendText(res, 404, "Not Found");
128
+ });
129
+ await new Promise((resolve, reject) => {
130
+ server.once("error", reject);
131
+ server.listen(port, () => resolve());
132
+ });
133
+ const address = server.address();
134
+ if (!address || typeof address === "string") {
135
+ await new Promise((resolve) => server.close(() => resolve()));
136
+ throw new Error("Failed to resolve host port");
137
+ }
138
+ resolvedPort = address.port;
139
+ return {
140
+ port: resolvedPort,
141
+ close: async () => {
142
+ await new Promise((resolve) => server.close(() => resolve()));
143
+ }
144
+ };
145
+ }
146
+
147
+ //#endregion
148
+ exports.startDirectModuleHost = startDirectModuleHost;
149
+ //# sourceMappingURL=host.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.cjs","names":["createServer"],"sources":["../../src/dev/host.ts"],"sourcesContent":["import type http from \"node:http\";\nimport { createServer } from \"node:http\";\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: http.ServerResponse) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst sendJson = (res: http.ServerResponse, status: number, body: unknown) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify(body));\n};\n\nconst sendText = (res: http.ServerResponse, status: number, text: string) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(text);\n};\n\nconst pathnameOf = (req: http.IncomingMessage): string =>\n ((req.url ?? \"/\").split(\"?\")[0] ?? \"/\").replace(/\\/+$/, \"\") || \"/\";\n\nconst toWebRequest = (req: http.IncomingMessage, port: number): Request => {\n const url = `http://localhost:${port}${req.url}`;\n return 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\nconst buildHostContext = (\n effectContext: unknown,\n webRequest: Request,\n cachedRawBodyRef: { value: string | null },\n) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n\n return {\n reqHeaders: webRequest.headers,\n \"effect/context\": effectContext,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;\n if (!rawClone) {\n cachedRawBodyRef.value = \"\";\n return cachedRawBodyRef.value;\n }\n cachedRawBodyRef.value = await rawClone.text();\n return cachedRawBodyRef.value;\n },\n };\n};\n\nexport interface DirectModuleHostHandler {\n handle(\n request: Request,\n options: { prefix: string; context: unknown },\n ): Promise<{ response?: Response }>;\n}\n\nconst handleOrpc = async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n handler: DirectModuleHostHandler,\n prefix: string,\n errorLabel: string,\n effectContext: unknown,\n port: number,\n) => {\n try {\n const webRequest = toWebRequest(req, port);\n const cachedRawBodyRef = { value: null as string | null };\n const result = await handler.handle(webRequest, {\n prefix,\n context: buildHostContext(effectContext, webRequest, cachedRawBodyRef),\n });\n\n if (result.response) {\n res.statusCode = 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 if (!res.getHeader(\"content-length\")) {\n res.setHeader(\"content-length\", Buffer.byteLength(text).toString());\n }\n res.end(text);\n } else {\n sendText(res, 404, \"Not Found\");\n }\n } catch (error) {\n console.error(`${errorLabel} error:`, error);\n sendJson(res, 500, { error: (error as Error).message });\n }\n};\n\nexport interface DirectModuleHostHandlers {\n rpc: DirectModuleHostHandler | null;\n api: DirectModuleHostHandler | null;\n}\n\nexport interface DirectModuleHostOptions {\n name: string;\n handlers: DirectModuleHostHandlers;\n effectContext: unknown;\n port?: number;\n version?: string;\n rpcPrefix?: `/${string}`;\n apiPrefix?: `/${string}`;\n staticHandler?:\n | ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void)\n | null;\n}\n\nexport interface DirectModuleHostHandle {\n port: number;\n close: () => Promise<void>;\n}\n\nexport async function startDirectModuleHost(\n options: DirectModuleHostOptions,\n): Promise<DirectModuleHostHandle> {\n const port = options.port ?? 0;\n let resolvedPort = port;\n const rpcPrefix = options.rpcPrefix ?? \"/api/rpc\";\n const apiPrefix = options.apiPrefix ?? \"/api\";\n const staticHandler = options.staticHandler ?? null;\n const resolveEffectContext: () => unknown =\n typeof options.effectContext === \"function\"\n ? (options.effectContext as () => unknown)\n : () => options.effectContext;\n\n const server = createServer((req, res) => {\n applyCorsHeaders(res);\n const pathname = pathnameOf(req);\n\n if (req.method === \"OPTIONS\") {\n res.statusCode = 200;\n res.end();\n return;\n }\n\n if (pathname === \"/\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendJson(res, 200, {\n ok: true,\n module: options.name,\n version: options.version,\n status: options.handlers.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/health\",\n docs: apiPrefix,\n rpc: rpcPrefix,\n },\n });\n return;\n }\n\n if (pathname === \"/health\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendText(res, 200, \"OK\");\n return;\n }\n\n if (pathname.startsWith(`${rpcPrefix}/`)) {\n if (!options.handlers.rpc) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.rpc,\n rpcPrefix,\n \"RPC\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {\n if (!options.handlers.api) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.api,\n apiPrefix,\n \"OpenAPI\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (staticHandler) {\n staticHandler(req, res, () => {\n sendText(res, 404, \"Not Found\");\n });\n return;\n }\n\n sendText(res, 404, \"Not Found\");\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, () => resolve());\n });\n\n const address = server.address();\n if (!address || typeof address === \"string\") {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n throw new Error(\"Failed to resolve host port\");\n }\n resolvedPort = address.port;\n\n return {\n port: resolvedPort,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import http from "node:http";
2
+ //#region src/dev/host.d.ts
3
+ interface DirectModuleHostHandler {
4
+ handle(request: Request, options: {
5
+ prefix: string;
6
+ context: unknown;
7
+ }): Promise<{
8
+ response?: Response;
9
+ }>;
10
+ }
11
+ interface DirectModuleHostHandlers {
12
+ rpc: DirectModuleHostHandler | null;
13
+ api: DirectModuleHostHandler | null;
14
+ }
15
+ interface DirectModuleHostOptions {
16
+ name: string;
17
+ handlers: DirectModuleHostHandlers;
18
+ effectContext: unknown;
19
+ port?: number;
20
+ version?: string;
21
+ rpcPrefix?: `/${string}`;
22
+ apiPrefix?: `/${string}`;
23
+ staticHandler?: ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void) | null;
24
+ }
25
+ interface DirectModuleHostHandle {
26
+ port: number;
27
+ close: () => Promise<void>;
28
+ }
29
+ declare function startDirectModuleHost(options: DirectModuleHostOptions): Promise<DirectModuleHostHandle>;
30
+ //#endregion
31
+ export { DirectModuleHostHandle, DirectModuleHostHandler, DirectModuleHostHandlers, DirectModuleHostOptions, startDirectModuleHost };
32
+ //# sourceMappingURL=host.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.cts","names":[],"sources":["../../src/dev/host.ts"],"mappings":";;UA+DiB;EACf,OACE,SAAS,SACT;IAAW;IAAgB;MAC1B;IAAU,WAAW;;;UAuCT;EACf,KAAK;EACL,KAAK;;UAGU;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA,kBACM,KAAK,KAAK,iBAAiB,KAAK,KAAK,gBAAgB;;UAI5C;EACf;EACA,aAAa;;iBAGO,sBACpB,SAAS,0BACR,QAAQ"}
@@ -0,0 +1,32 @@
1
+ import http from "node:http";
2
+ //#region src/dev/host.d.ts
3
+ interface DirectModuleHostHandler {
4
+ handle(request: Request, options: {
5
+ prefix: string;
6
+ context: unknown;
7
+ }): Promise<{
8
+ response?: Response;
9
+ }>;
10
+ }
11
+ interface DirectModuleHostHandlers {
12
+ rpc: DirectModuleHostHandler | null;
13
+ api: DirectModuleHostHandler | null;
14
+ }
15
+ interface DirectModuleHostOptions {
16
+ name: string;
17
+ handlers: DirectModuleHostHandlers;
18
+ effectContext: unknown;
19
+ port?: number;
20
+ version?: string;
21
+ rpcPrefix?: `/${string}`;
22
+ apiPrefix?: `/${string}`;
23
+ staticHandler?: ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void) | null;
24
+ }
25
+ interface DirectModuleHostHandle {
26
+ port: number;
27
+ close: () => Promise<void>;
28
+ }
29
+ declare function startDirectModuleHost(options: DirectModuleHostOptions): Promise<DirectModuleHostHandle>;
30
+ //#endregion
31
+ export { DirectModuleHostHandle, DirectModuleHostHandler, DirectModuleHostHandlers, DirectModuleHostOptions, startDirectModuleHost };
32
+ //# sourceMappingURL=host.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.mts","names":[],"sources":["../../src/dev/host.ts"],"mappings":";;UA+DiB;EACf,OACE,SAAS,SACT;IAAW;IAAgB;MAC1B;IAAU,WAAW;;;UAuCT;EACf,KAAK;EACL,KAAK;;UAGU;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA,kBACM,KAAK,KAAK,iBAAiB,KAAK,KAAK,gBAAgB;;UAI5C;EACf;EACA,aAAa;;iBAGO,sBACpB,SAAS,0BACR,QAAQ"}
@@ -0,0 +1,148 @@
1
+ import { createServer } from "node:http";
2
+
3
+ //#region src/dev/host.ts
4
+ const corsHeaders = {
5
+ "Access-Control-Allow-Origin": "*",
6
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
7
+ "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
8
+ };
9
+ const applyCorsHeaders = (res) => {
10
+ Object.entries(corsHeaders).forEach(([key, value]) => {
11
+ res.setHeader(key, value);
12
+ });
13
+ };
14
+ const sendJson = (res, status, body) => {
15
+ res.statusCode = status;
16
+ res.setHeader("content-type", "application/json; charset=utf-8");
17
+ res.end(JSON.stringify(body));
18
+ };
19
+ const sendText = (res, status, text) => {
20
+ res.statusCode = status;
21
+ res.setHeader("content-type", "text/plain; charset=utf-8");
22
+ res.end(text);
23
+ };
24
+ const pathnameOf = (req) => ((req.url ?? "/").split("?")[0] ?? "/").replace(/\/+$/, "") || "/";
25
+ const toWebRequest = (req, port) => {
26
+ const url = `http://localhost:${port}${req.url}`;
27
+ return new Request(url, {
28
+ method: req.method,
29
+ headers: req.headers,
30
+ body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0,
31
+ duplex: req.method !== "GET" && req.method !== "HEAD" ? "half" : void 0
32
+ });
33
+ };
34
+ const buildHostContext = (effectContext, webRequest, cachedRawBodyRef) => {
35
+ const rawClone = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : webRequest.clone();
36
+ return {
37
+ reqHeaders: webRequest.headers,
38
+ "effect/context": effectContext,
39
+ getRawBody: async () => {
40
+ if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;
41
+ if (!rawClone) {
42
+ cachedRawBodyRef.value = "";
43
+ return cachedRawBodyRef.value;
44
+ }
45
+ cachedRawBodyRef.value = await rawClone.text();
46
+ return cachedRawBodyRef.value;
47
+ }
48
+ };
49
+ };
50
+ const handleOrpc = async (req, res, handler, prefix, errorLabel, effectContext, port) => {
51
+ try {
52
+ const webRequest = toWebRequest(req, port);
53
+ const result = await handler.handle(webRequest, {
54
+ prefix,
55
+ context: buildHostContext(effectContext, webRequest, { value: null })
56
+ });
57
+ if (result.response) {
58
+ res.statusCode = result.response.status;
59
+ result.response.headers.forEach((value, key) => {
60
+ res.setHeader(key, value);
61
+ });
62
+ const text = await result.response.text();
63
+ if (!res.getHeader("content-length")) res.setHeader("content-length", Buffer.byteLength(text).toString());
64
+ res.end(text);
65
+ } else sendText(res, 404, "Not Found");
66
+ } catch (error) {
67
+ console.error(`${errorLabel} error:`, error);
68
+ sendJson(res, 500, { error: error.message });
69
+ }
70
+ };
71
+ async function startDirectModuleHost(options) {
72
+ const port = options.port ?? 0;
73
+ let resolvedPort = port;
74
+ const rpcPrefix = options.rpcPrefix ?? "/api/rpc";
75
+ const apiPrefix = options.apiPrefix ?? "/api";
76
+ const staticHandler = options.staticHandler ?? null;
77
+ const resolveEffectContext = typeof options.effectContext === "function" ? options.effectContext : () => options.effectContext;
78
+ const server = createServer((req, res) => {
79
+ applyCorsHeaders(res);
80
+ const pathname = pathnameOf(req);
81
+ if (req.method === "OPTIONS") {
82
+ res.statusCode = 200;
83
+ res.end();
84
+ return;
85
+ }
86
+ if (pathname === "/" && (req.method === "GET" || req.method === "HEAD")) {
87
+ sendJson(res, 200, {
88
+ ok: true,
89
+ module: options.name,
90
+ version: options.version,
91
+ status: options.handlers.rpc ? "ready" : "loading",
92
+ endpoints: {
93
+ health: "/health",
94
+ docs: apiPrefix,
95
+ rpc: rpcPrefix
96
+ }
97
+ });
98
+ return;
99
+ }
100
+ if (pathname === "/health" && (req.method === "GET" || req.method === "HEAD")) {
101
+ sendText(res, 200, "OK");
102
+ return;
103
+ }
104
+ if (pathname.startsWith(`${rpcPrefix}/`)) {
105
+ if (!options.handlers.rpc) {
106
+ sendJson(res, 503, { error: "Plugin still loading..." });
107
+ return;
108
+ }
109
+ handleOrpc(req, res, options.handlers.rpc, rpcPrefix, "RPC", resolveEffectContext(), resolvedPort).catch(() => {});
110
+ return;
111
+ }
112
+ if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {
113
+ if (!options.handlers.api) {
114
+ sendJson(res, 503, { error: "Plugin still loading..." });
115
+ return;
116
+ }
117
+ handleOrpc(req, res, options.handlers.api, apiPrefix, "OpenAPI", resolveEffectContext(), resolvedPort).catch(() => {});
118
+ return;
119
+ }
120
+ if (staticHandler) {
121
+ staticHandler(req, res, () => {
122
+ sendText(res, 404, "Not Found");
123
+ });
124
+ return;
125
+ }
126
+ sendText(res, 404, "Not Found");
127
+ });
128
+ await new Promise((resolve, reject) => {
129
+ server.once("error", reject);
130
+ server.listen(port, () => resolve());
131
+ });
132
+ const address = server.address();
133
+ if (!address || typeof address === "string") {
134
+ await new Promise((resolve) => server.close(() => resolve()));
135
+ throw new Error("Failed to resolve host port");
136
+ }
137
+ resolvedPort = address.port;
138
+ return {
139
+ port: resolvedPort,
140
+ close: async () => {
141
+ await new Promise((resolve) => server.close(() => resolve()));
142
+ }
143
+ };
144
+ }
145
+
146
+ //#endregion
147
+ export { startDirectModuleHost };
148
+ //# sourceMappingURL=host.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.mjs","names":[],"sources":["../../src/dev/host.ts"],"sourcesContent":["import type http from \"node:http\";\nimport { createServer } from \"node:http\";\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: http.ServerResponse) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst sendJson = (res: http.ServerResponse, status: number, body: unknown) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify(body));\n};\n\nconst sendText = (res: http.ServerResponse, status: number, text: string) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(text);\n};\n\nconst pathnameOf = (req: http.IncomingMessage): string =>\n ((req.url ?? \"/\").split(\"?\")[0] ?? \"/\").replace(/\\/+$/, \"\") || \"/\";\n\nconst toWebRequest = (req: http.IncomingMessage, port: number): Request => {\n const url = `http://localhost:${port}${req.url}`;\n return 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\nconst buildHostContext = (\n effectContext: unknown,\n webRequest: Request,\n cachedRawBodyRef: { value: string | null },\n) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n\n return {\n reqHeaders: webRequest.headers,\n \"effect/context\": effectContext,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;\n if (!rawClone) {\n cachedRawBodyRef.value = \"\";\n return cachedRawBodyRef.value;\n }\n cachedRawBodyRef.value = await rawClone.text();\n return cachedRawBodyRef.value;\n },\n };\n};\n\nexport interface DirectModuleHostHandler {\n handle(\n request: Request,\n options: { prefix: string; context: unknown },\n ): Promise<{ response?: Response }>;\n}\n\nconst handleOrpc = async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n handler: DirectModuleHostHandler,\n prefix: string,\n errorLabel: string,\n effectContext: unknown,\n port: number,\n) => {\n try {\n const webRequest = toWebRequest(req, port);\n const cachedRawBodyRef = { value: null as string | null };\n const result = await handler.handle(webRequest, {\n prefix,\n context: buildHostContext(effectContext, webRequest, cachedRawBodyRef),\n });\n\n if (result.response) {\n res.statusCode = 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 if (!res.getHeader(\"content-length\")) {\n res.setHeader(\"content-length\", Buffer.byteLength(text).toString());\n }\n res.end(text);\n } else {\n sendText(res, 404, \"Not Found\");\n }\n } catch (error) {\n console.error(`${errorLabel} error:`, error);\n sendJson(res, 500, { error: (error as Error).message });\n }\n};\n\nexport interface DirectModuleHostHandlers {\n rpc: DirectModuleHostHandler | null;\n api: DirectModuleHostHandler | null;\n}\n\nexport interface DirectModuleHostOptions {\n name: string;\n handlers: DirectModuleHostHandlers;\n effectContext: unknown;\n port?: number;\n version?: string;\n rpcPrefix?: `/${string}`;\n apiPrefix?: `/${string}`;\n staticHandler?:\n | ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void)\n | null;\n}\n\nexport interface DirectModuleHostHandle {\n port: number;\n close: () => Promise<void>;\n}\n\nexport async function startDirectModuleHost(\n options: DirectModuleHostOptions,\n): Promise<DirectModuleHostHandle> {\n const port = options.port ?? 0;\n let resolvedPort = port;\n const rpcPrefix = options.rpcPrefix ?? \"/api/rpc\";\n const apiPrefix = options.apiPrefix ?? \"/api\";\n const staticHandler = options.staticHandler ?? null;\n const resolveEffectContext: () => unknown =\n typeof options.effectContext === \"function\"\n ? (options.effectContext as () => unknown)\n : () => options.effectContext;\n\n const server = createServer((req, res) => {\n applyCorsHeaders(res);\n const pathname = pathnameOf(req);\n\n if (req.method === \"OPTIONS\") {\n res.statusCode = 200;\n res.end();\n return;\n }\n\n if (pathname === \"/\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendJson(res, 200, {\n ok: true,\n module: options.name,\n version: options.version,\n status: options.handlers.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/health\",\n docs: apiPrefix,\n rpc: rpcPrefix,\n },\n });\n return;\n }\n\n if (pathname === \"/health\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendText(res, 200, \"OK\");\n return;\n }\n\n if (pathname.startsWith(`${rpcPrefix}/`)) {\n if (!options.handlers.rpc) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.rpc,\n rpcPrefix,\n \"RPC\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {\n if (!options.handlers.api) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.api,\n apiPrefix,\n \"OpenAPI\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (staticHandler) {\n staticHandler(req, res, () => {\n sendText(res, 404, \"Not Found\");\n });\n return;\n }\n\n sendText(res, 404, \"Not Found\");\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, () => resolve());\n });\n\n const address = server.address();\n if (!address || typeof address === \"string\") {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n throw new Error(\"Failed to resolve host port\");\n }\n resolvedPort = address.port;\n\n return {\n port: resolvedPort,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n },\n };\n}\n"],"mappings":"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"}
@@ -5,6 +5,7 @@ const require_build_artifact_names = require('../build/artifact-names.cjs');
5
5
  const require_remote_entry = require('../remote-entry.cjs');
6
6
  const require_utils = require('../build/rspack/utils.cjs');
7
7
  const require_generated_config = require('../build/ui/generated-config.cjs');
8
+ const require_dev_host = require('./host.cjs');
8
9
  const require_generated_config$1 = require('../build/rspack/generated-config.cjs');
9
10
  const require_watch_kill = require('./watch-kill.cjs');
10
11
  let effect = require("effect");
@@ -70,11 +71,6 @@ const loadPluginWithRetry = (runtime, pluginId, remoteUrl, options = {
70
71
  load: () => runtime.usePlugin(pluginId, options, pluginsMap),
71
72
  timeoutMs
72
73
  }));
73
- const sendJson = (res, status, body) => {
74
- res.statusCode = status;
75
- res.setHeader("content-type", "application/json; charset=utf-8");
76
- res.end(JSON.stringify(body));
77
- };
78
74
  const sendText = (res, status, text) => {
79
75
  res.statusCode = status;
80
76
  res.setHeader("content-type", "text/plain; charset=utf-8");
@@ -98,7 +94,6 @@ async function startPluginDevServer(options = {}) {
98
94
  const compositionKey = require_generated_config.pluginLayoutKey(cwd) ?? pluginId;
99
95
  const port = options.port ?? (Number(process.env.PORT) || devConfig?.port || 3999);
100
96
  const rpcPrefix = normalizePrefix(devConfig?.prefix);
101
- const rpcBase = `/api/rpc${rpcPrefix}`;
102
97
  const distDir = node_path.default.join(cwd, "dist");
103
98
  const serveStatic = (0, sirv.default)(distDir, { dev: true });
104
99
  const noWatch = options.watch === false || process.env.BOS_NO_WATCH === "1";
@@ -148,8 +143,8 @@ async function startPluginDevServer(options = {}) {
148
143
  "--config",
149
144
  uiConfig
150
145
  ]);
151
- const uiDistDir = node_path.default.basename(cwd) === "ui" ? node_path.default.join(cwd, "dist") : node_path.default.join(cwd, "ui", "dist");
152
- if (!node_fs.default.existsSync(node_path.default.join(uiDistDir, "remoteEntry.js"))) console.error(`❌ UI dist is missing at ${uiDistDir} — the ui static server would serve 404s for every asset (build output layout mismatch?)`);
146
+ const uiDistDir = require_generated_config.uiDistDirFor(cwd);
147
+ if (!node_fs.default.existsSync(node_path.default.join(uiDistDir, "remoteEntry.js"))) throw new Error(`❌ UI dist is missing at ${uiDistDir} after the rsbuild one-shot — build output layout mismatch?`);
153
148
  const serveUiStatic = (0, sirv.default)(uiDistDir, { dev: true });
154
149
  uiStaticServer = node_http.default.createServer((req, res) => {
155
150
  applyCorsHeaders(res);
@@ -186,108 +181,18 @@ async function startPluginDevServer(options = {}) {
186
181
  };
187
182
  const effectContextHolder = { context: null };
188
183
  let runtime = null;
189
- const pathnameOf = (req) => ((req.url ?? "/").split("?")[0] ?? "/").replace(/\/+$/, "") || "/";
190
- const buildDevContext = (_req, webRequest) => {
191
- const rawClone = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : webRequest.clone();
192
- let cachedRawBody = null;
193
- return {
194
- reqHeaders: webRequest.headers,
195
- "effect/context": effectContextHolder.context,
196
- getRawBody: async () => {
197
- if (cachedRawBody !== null) return cachedRawBody;
198
- if (!rawClone) {
199
- cachedRawBody = "";
200
- return cachedRawBody;
201
- }
202
- cachedRawBody = await rawClone.text();
203
- return cachedRawBody;
204
- }
205
- };
206
- };
207
- const toWebRequest = (req) => {
208
- const url = `http://localhost:${port}${req.url}`;
209
- return new Request(url, {
210
- method: req.method,
211
- headers: req.headers,
212
- body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0,
213
- duplex: req.method !== "GET" && req.method !== "HEAD" ? "half" : void 0
214
- });
215
- };
216
- const handleOrpc = async (req, res, handler, prefix, errorLabel) => {
217
- try {
218
- const webRequest = toWebRequest(req);
219
- const result = await handler.handle(webRequest, {
220
- prefix,
221
- context: buildDevContext(req, webRequest)
222
- });
223
- if (result.response) {
224
- res.statusCode = result.response.status;
225
- result.response.headers.forEach((value, key) => {
226
- res.setHeader(key, value);
227
- });
228
- const text = await result.response.text();
229
- if (!res.getHeader("content-length")) res.setHeader("content-length", Buffer.byteLength(text).toString());
230
- res.end(text);
231
- } else sendText(res, 404, "Not Found");
232
- } catch (error) {
233
- console.error(`${errorLabel} error:`, error);
234
- sendJson(res, 500, { error: error.message });
235
- }
236
- };
237
- const server = node_http.default.createServer((req, res) => {
238
- applyCorsHeaders(res);
239
- const pathname = pathnameOf(req);
240
- if (req.method === "OPTIONS") {
241
- res.statusCode = 200;
242
- res.end();
243
- return;
244
- }
245
- if (pathname === "/" && (req.method === "GET" || req.method === "HEAD")) {
246
- sendJson(res, 200, {
247
- ok: true,
248
- plugin: pluginInfo.normalizedName,
249
- version: pluginInfo.version,
250
- status: handlers.rpc ? "ready" : "loading",
251
- endpoints: {
252
- health: "/",
253
- docs: "/api",
254
- rpc: rpcBase
255
- }
256
- });
257
- return;
258
- }
259
- if (pathname === "/health" && (req.method === "GET" || req.method === "HEAD")) {
260
- sendText(res, 200, "OK");
261
- return;
262
- }
263
- if (pathname.startsWith(`${rpcBase}/`)) {
264
- if (!handlers.rpc) {
265
- sendJson(res, 503, { error: "Plugin still loading..." });
266
- return;
267
- }
268
- handleOrpc(req, res, handlers.rpc, rpcBase, "RPC").catch(() => {});
269
- return;
270
- }
271
- if (pathname === "/api" || pathname.startsWith("/api/")) {
272
- if (!handlers.api) {
273
- sendJson(res, 503, { error: "Plugin still loading..." });
274
- return;
275
- }
276
- handleOrpc(req, res, handlers.api, "/api", "OpenAPI").catch(() => {});
277
- return;
278
- }
279
- serveStatic(req, res, () => {
280
- sendText(res, 404, "Not Found");
281
- });
282
- });
283
- await new Promise((resolve, reject) => {
284
- server.once("error", reject);
285
- server.listen(port, () => resolve());
184
+ const host = await require_dev_host.startDirectModuleHost({
185
+ name: pluginInfo.normalizedName,
186
+ version: pluginInfo.version,
187
+ handlers,
188
+ effectContext: () => effectContextHolder.context,
189
+ port,
190
+ staticHandler: serveStatic
286
191
  });
287
- console.log(`🚀 ${pluginInfo.name} dev server listening on port ${port}`);
288
- console.log(`├─ 📡 RPC: http://localhost:${port}/api/rpc${rpcPrefix}`);
289
- console.log(`├─ 📖 Docs: http://localhost:${port}/api`);
290
- console.log(`└─ 💚 Health: http://localhost:${port}/`);
192
+ console.log(`🚀 ${pluginInfo.name} dev server listening on port ${host.port}`);
193
+ console.log(`├─ 📡 RPC: http://localhost:${host.port}/api/rpc${rpcPrefix}`);
194
+ console.log(`├─ 📖 Docs: http://localhost:${host.port}/api`);
195
+ console.log(`└─ 💚 Health: http://localhost:${host.port}/`);
291
196
  const load = async () => {
292
197
  try {
293
198
  const { createPluginRuntime } = await Promise.resolve().then(() => require("../runtime/index.cjs"));
@@ -302,7 +207,7 @@ async function startPluginDevServer(options = {}) {
302
207
  const { siblings, dependsOn } = collectSiblingRemotes(runtimeConfig, compositionKey);
303
208
  if (dependsOn.length > 0) console.log(`🔗 Loading sibling plugin(s): ${dependsOn.join(", ")}`);
304
209
  const registry = {
305
- [pluginId]: { remote: `http://localhost:${port}/${require_build_artifact_names.DEV_ENTRY_FILENAME}` },
210
+ [pluginId]: { remote: `http://localhost:${host.port}/${require_build_artifact_names.DEV_ENTRY_FILENAME}` },
306
211
  ...siblings
307
212
  };
308
213
  const mfRuntime = createPluginRuntime({ registry });
@@ -353,8 +258,8 @@ async function startPluginDevServer(options = {}) {
353
258
  effectContextHolder.context = null;
354
259
  for (const child of [watcher, uiWatcher]) if (child) await require_watch_kill.killChildEscalating(child);
355
260
  if (uiStaticServer) await new Promise((resolve) => uiStaticServer.close(() => resolve()));
261
+ await host.close();
356
262
  if (runtime) await runtime.shutdown().catch(() => {});
357
- await new Promise((resolve) => server.close(() => resolve()));
358
263
  };
359
264
  process.once("SIGINT", async () => {
360
265
  const timeout = setTimeout(() => process.exit(0), 3e3);