@bleedingdev/modern-js-server-core 3.2.0-ultramodern.99 → 3.4.0-ultramodern.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.
Files changed (113) hide show
  1. package/dist/cjs/adapters/node/helper/index.js +9 -5
  2. package/dist/cjs/adapters/node/helper/loadCache.js +9 -5
  3. package/dist/cjs/adapters/node/helper/loadConfig.js +9 -5
  4. package/dist/cjs/adapters/node/helper/loadEnv.js +9 -5
  5. package/dist/cjs/adapters/node/helper/loadPlugin.js +9 -5
  6. package/dist/cjs/adapters/node/helper/utils.js +12 -8
  7. package/dist/cjs/adapters/node/hono.js +9 -5
  8. package/dist/cjs/adapters/node/index.js +9 -5
  9. package/dist/cjs/adapters/node/node.js +9 -5
  10. package/dist/cjs/adapters/node/plugins/index.js +9 -5
  11. package/dist/cjs/adapters/node/plugins/nodeServer.js +12 -8
  12. package/dist/cjs/adapters/node/plugins/resource.js +10 -11
  13. package/dist/cjs/adapters/node/plugins/static.js +20 -190
  14. package/dist/cjs/adapters/node/plugins/staticModuleFederation.js +165 -0
  15. package/dist/cjs/adapters/node/plugins/staticPrecompressed.js +135 -0
  16. package/dist/cjs/constants.js +18 -13
  17. package/dist/cjs/context.js +9 -5
  18. package/dist/cjs/helper.js +12 -8
  19. package/dist/cjs/hono.js +9 -5
  20. package/dist/cjs/index.js +60 -32
  21. package/dist/cjs/plugins/compat/hooks.js +14 -10
  22. package/dist/cjs/plugins/compat/index.js +9 -5
  23. package/dist/cjs/plugins/default.js +9 -7
  24. package/dist/cjs/plugins/favicon.js +12 -8
  25. package/dist/cjs/plugins/index.js +11 -88
  26. package/dist/cjs/plugins/log.js +9 -5
  27. package/dist/cjs/plugins/middlewares.js +12 -8
  28. package/dist/cjs/plugins/monitors.js +9 -5
  29. package/dist/cjs/plugins/processedBy.js +12 -8
  30. package/dist/cjs/plugins/render/csrRscRender.js +9 -5
  31. package/dist/cjs/plugins/render/dataHandler.js +9 -5
  32. package/dist/cjs/plugins/render/index.js +12 -8
  33. package/dist/cjs/plugins/render/inject.js +12 -7
  34. package/dist/cjs/plugins/render/render.js +14 -6
  35. package/dist/cjs/plugins/render/renderRscHandler.js +9 -5
  36. package/dist/cjs/plugins/render/serverActionHandler.js +9 -5
  37. package/dist/cjs/plugins/render/ssrCache.js +9 -5
  38. package/dist/cjs/plugins/render/ssrRender.js +9 -5
  39. package/dist/cjs/plugins/render/utils.js +12 -8
  40. package/dist/cjs/plugins/route.js +9 -5
  41. package/dist/cjs/serverBase.js +9 -5
  42. package/dist/cjs/types/config/bffRuntime.js +18 -0
  43. package/dist/cjs/types/config/index.js +9 -5
  44. package/dist/cjs/types/config/serverTelemetry.js +18 -0
  45. package/dist/cjs/types/index.js +9 -5
  46. package/dist/cjs/types/plugins/index.js +9 -5
  47. package/dist/cjs/utils/entry.js +13 -9
  48. package/dist/cjs/utils/env.js +13 -9
  49. package/dist/cjs/utils/error.js +71 -5
  50. package/dist/cjs/utils/index.js +9 -5
  51. package/dist/cjs/utils/middlewareCollector.js +13 -9
  52. package/dist/cjs/utils/publicDir.js +9 -5
  53. package/dist/cjs/utils/request.js +16 -12
  54. package/dist/cjs/utils/serverConfig.js +9 -5
  55. package/dist/cjs/utils/storage.js +9 -5
  56. package/dist/cjs/utils/transformStream.js +13 -9
  57. package/dist/cjs/utils/warmup.js +12 -8
  58. package/dist/esm/adapters/node/plugins/resource.mjs +1 -6
  59. package/dist/esm/adapters/node/plugins/static.mjs +7 -181
  60. package/dist/esm/adapters/node/plugins/staticModuleFederation.mjs +96 -0
  61. package/dist/esm/adapters/node/plugins/staticPrecompressed.mjs +91 -0
  62. package/dist/esm/index.mjs +1 -1
  63. package/dist/esm/plugins/default.mjs +0 -2
  64. package/dist/esm/plugins/index.mjs +0 -3
  65. package/dist/esm/plugins/render/inject.mjs +3 -2
  66. package/dist/esm/plugins/render/render.mjs +7 -3
  67. package/dist/esm/types/config/bffRuntime.mjs +0 -0
  68. package/dist/esm/types/config/serverTelemetry.mjs +0 -0
  69. package/dist/esm/utils/error.mjs +54 -1
  70. package/dist/esm-node/adapters/node/plugins/resource.mjs +1 -6
  71. package/dist/esm-node/adapters/node/plugins/static.mjs +7 -181
  72. package/dist/esm-node/adapters/node/plugins/staticModuleFederation.mjs +97 -0
  73. package/dist/esm-node/adapters/node/plugins/staticPrecompressed.mjs +92 -0
  74. package/dist/esm-node/index.mjs +1 -1
  75. package/dist/esm-node/plugins/default.mjs +0 -2
  76. package/dist/esm-node/plugins/index.mjs +0 -3
  77. package/dist/esm-node/plugins/render/inject.mjs +3 -2
  78. package/dist/esm-node/plugins/render/render.mjs +5 -1
  79. package/dist/esm-node/types/config/bffRuntime.mjs +1 -0
  80. package/dist/esm-node/types/config/serverTelemetry.mjs +1 -0
  81. package/dist/esm-node/utils/error.mjs +54 -1
  82. package/dist/types/adapters/node/plugins/staticModuleFederation.d.ts +13 -0
  83. package/dist/types/adapters/node/plugins/staticPrecompressed.d.ts +13 -0
  84. package/dist/types/index.d.ts +2 -1
  85. package/dist/types/plugins/index.d.ts +0 -3
  86. package/dist/types/types/config/bff.d.ts +2 -97
  87. package/dist/types/types/config/bffRuntime.d.ts +105 -0
  88. package/dist/types/types/config/server.d.ts +3 -337
  89. package/dist/types/types/config/serverTelemetry.d.ts +319 -0
  90. package/dist/types/types/plugins/base.d.ts +7 -2
  91. package/dist/types/types/plugins/index.d.ts +1 -1
  92. package/dist/types/utils/error.d.ts +16 -0
  93. package/package.json +12 -12
  94. package/dist/cjs/adapters/node/plugins/moduleFederationCss.js +0 -172
  95. package/dist/cjs/plugins/contractGateAutopilot.js +0 -158
  96. package/dist/cjs/plugins/contractGateSnapshotStore.js +0 -239
  97. package/dist/cjs/plugins/mfCache.js +0 -78
  98. package/dist/cjs/plugins/telemetry.js +0 -1283
  99. package/dist/esm/adapters/node/plugins/moduleFederationCss.mjs +0 -125
  100. package/dist/esm/plugins/contractGateAutopilot.mjs +0 -124
  101. package/dist/esm/plugins/contractGateSnapshotStore.mjs +0 -180
  102. package/dist/esm/plugins/mfCache.mjs +0 -35
  103. package/dist/esm/plugins/telemetry.mjs +0 -1195
  104. package/dist/esm-node/adapters/node/plugins/moduleFederationCss.mjs +0 -126
  105. package/dist/esm-node/plugins/contractGateAutopilot.mjs +0 -125
  106. package/dist/esm-node/plugins/contractGateSnapshotStore.mjs +0 -182
  107. package/dist/esm-node/plugins/mfCache.mjs +0 -36
  108. package/dist/esm-node/plugins/telemetry.mjs +0 -1196
  109. package/dist/types/adapters/node/plugins/moduleFederationCss.d.ts +0 -33
  110. package/dist/types/plugins/contractGateAutopilot.d.ts +0 -35
  111. package/dist/types/plugins/contractGateSnapshotStore.d.ts +0 -57
  112. package/dist/types/plugins/mfCache.d.ts +0 -12
  113. package/dist/types/plugins/telemetry.d.ts +0 -309
@@ -3,7 +3,6 @@ import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
3
  import { LOADABLE_STATS_FILE, MAIN_ENTRY_NAME, NESTED_ROUTE_SPEC_FILE, ROUTE_MANIFEST_FILE, SERVER_BUNDLE_DIRECTORY, compatibleRequire, fs, isProd } from "@modern-js/utils";
4
4
  import path from "path";
5
5
  import { uniqueKeyByRoute } from "../../../utils/index.mjs";
6
- import { collectDirectRemoteModuleFederationCss } from "./moduleFederationCss.mjs";
7
6
  async function getHtmlTemplates(pwd, routes) {
8
7
  const htmlRoutes = routes.filter((route)=>route.entryName);
9
8
  const htmls = await Promise.all(htmlRoutes.map(async (route)=>{
@@ -58,16 +57,12 @@ async function getServerManifest(pwd, routes, monitors) {
58
57
  const routeManifest = await compatibleRequire(routesManifestUri).catch((_)=>({}));
59
58
  const nestedRoutesJsonPath = path.join(pwd, NESTED_ROUTE_SPEC_FILE);
60
59
  const nestedRoutesJson = await compatibleRequire(nestedRoutesJsonPath).catch((_)=>({}));
61
- const moduleFederationCssAssets = await collectDirectRemoteModuleFederationCss(pwd, {
62
- monitors
63
- });
64
60
  return {
65
61
  loaderBundles,
66
62
  renderBundles,
67
63
  loadableStats,
68
64
  routeManifest,
69
- nestedRoutesJson,
70
- moduleFederationCssAssets
65
+ nestedRoutesJson
71
66
  };
72
67
  }
73
68
  function injectServerManifest(pwd, routes, manifestPromise) {
@@ -5,6 +5,8 @@ import { getMimeType } from "hono/utils/mime";
5
5
  import path from "path";
6
6
  import { sortRoutes } from "../../../utils/index.mjs";
7
7
  import { getPublicDirPatterns } from "../../../utils/publicDir.mjs";
8
+ import { applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath } from "./staticModuleFederation.mjs";
9
+ import { applyPreCompressedAssetHeaders, resolvePreCompressedAsset } from "./staticPrecompressed.mjs";
8
10
  const serverStaticPlugin = ()=>({
9
11
  name: '@modern-js/plugin-server-static',
10
12
  setup (api) {
@@ -25,91 +27,6 @@ const serverStaticPlugin = ()=>({
25
27
  });
26
28
  }
27
29
  });
28
- const PRE_COMPRESSED_ASSET_EXTENSIONS = {
29
- br: '.br',
30
- gzip: '.gz'
31
- };
32
- const PRE_COMPRESSED_SUPPORTED_ENCODINGS = [
33
- 'br',
34
- 'gzip'
35
- ];
36
- const parseAcceptEncoding = (value)=>value.split(',').map((item)=>item.trim()).filter(Boolean).map((item)=>{
37
- const [rawName, ...params] = item.split(';');
38
- const name = rawName.trim().toLowerCase();
39
- let q = 1;
40
- for (const param of params){
41
- const [key, rawValue] = param.split('=').map((v)=>v.trim());
42
- if ('q' !== key.toLowerCase() || null == rawValue) continue;
43
- const parsedQ = Number(rawValue);
44
- if (!Number.isNaN(parsedQ)) q = Math.max(0, Math.min(parsedQ, 1));
45
- }
46
- return {
47
- name,
48
- q
49
- };
50
- });
51
- const getAcceptedEncodings = (value)=>{
52
- if (!value) return [];
53
- const parsed = parseAcceptEncoding(value);
54
- const qualityByEncoding = new Map();
55
- let wildcardQuality;
56
- for (const { name, q } of parsed){
57
- if ('*' === name) {
58
- wildcardQuality = q;
59
- continue;
60
- }
61
- qualityByEncoding.set(name, q);
62
- }
63
- const getQuality = (encoding)=>{
64
- const explicit = qualityByEncoding.get(encoding);
65
- if (void 0 !== explicit) return explicit;
66
- return wildcardQuality ?? 0;
67
- };
68
- return PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
69
- encoding,
70
- quality: getQuality(encoding)
71
- })).filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
72
- };
73
- const appendVaryHeader = (c, value)=>{
74
- const current = c.res.headers.get('Vary');
75
- if (!current) return void c.header('Vary', value);
76
- const values = current.split(',').map((item)=>item.trim().toLowerCase()).filter(Boolean);
77
- if (!values.includes(value.toLowerCase())) c.header('Vary', `${current}, ${value}`);
78
- };
79
- const resolvePreCompressedAsset = async (c, filepath)=>{
80
- const brPath = `${filepath}${PRE_COMPRESSED_ASSET_EXTENSIONS.br}`;
81
- const gzipPath = `${filepath}${PRE_COMPRESSED_ASSET_EXTENSIONS.gzip}`;
82
- const [hasBr, hasGzip] = await Promise.all([
83
- fs.pathExists(brPath),
84
- fs.pathExists(gzipPath)
85
- ]);
86
- const hasVariant = hasBr || hasGzip;
87
- if (!hasVariant) return {
88
- selected: null,
89
- hasVariant: false
90
- };
91
- const acceptedEncodings = getAcceptedEncodings(c.req.header('accept-encoding'));
92
- for (const encoding of acceptedEncodings){
93
- if ('br' === encoding && hasBr) return {
94
- selected: {
95
- filepath: brPath,
96
- encoding
97
- },
98
- hasVariant: true
99
- };
100
- if ('gzip' === encoding && hasGzip) return {
101
- selected: {
102
- filepath: gzipPath,
103
- encoding
104
- },
105
- hasVariant: true
106
- };
107
- }
108
- return {
109
- selected: null,
110
- hasVariant: true
111
- };
112
- };
113
30
  function createPublicMiddleware({ pwd, routes }) {
114
31
  return async (c, next)=>{
115
32
  const route = matchPublicRoute(c.req, routes);
@@ -126,8 +43,7 @@ function createPublicMiddleware({ pwd, routes }) {
126
43
  Object.entries(route.responseHeaders || {}).forEach(([k, v])=>{
127
44
  c.header(k, v);
128
45
  });
129
- if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
130
- if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
46
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
131
47
  c.header('Content-Length', String(data.byteLength));
132
48
  return c.body(body, 200);
133
49
  }
@@ -147,91 +63,6 @@ const extractPathname = (url)=>{
147
63
  return url;
148
64
  }
149
65
  };
150
- const MODULE_FEDERATION_MANIFEST_FILE = 'mf-manifest.json';
151
- const MODULE_FEDERATION_OPTIONAL_FILES = [
152
- 'mf-stats.json'
153
- ];
154
- const trimLeadingSlash = (value)=>value.replace(/^\/+/, '');
155
- const joinModuleFederationAssetPath = (assetPath, assetName)=>{
156
- if (!assetName) return '';
157
- return trimLeadingSlash(path.posix.join(assetPath || '', assetName));
158
- };
159
- const appendModuleFederationAsset = (set, assetPath)=>{
160
- if (!assetPath) return;
161
- set.add(trimLeadingSlash(assetPath));
162
- };
163
- const appendModuleFederationAssets = (set, assets)=>{
164
- assets?.js?.sync?.forEach((asset)=>appendModuleFederationAsset(set, asset));
165
- assets?.js?.async?.forEach((asset)=>appendModuleFederationAsset(set, asset));
166
- assets?.css?.sync?.forEach((asset)=>appendModuleFederationAsset(set, asset));
167
- assets?.css?.async?.forEach((asset)=>appendModuleFederationAsset(set, asset));
168
- };
169
- const hasAbsoluteProtocol = (value)=>/^https?:\/\//i.test(value) || value.startsWith('//');
170
- const ensureLeadingSlash = (value)=>{
171
- if ('' === value) return '/';
172
- return value.startsWith('/') ? value : `/${value}`;
173
- };
174
- const ensureTrailingSlash = (value)=>value.endsWith('/') ? value : `${value}/`;
175
- const patchModuleFederationManifestPublicPath = (c, manifestBuffer, pathPrefix)=>{
176
- try {
177
- const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
178
- const publicPath = manifest.metaData?.publicPath;
179
- if (!publicPath || hasAbsoluteProtocol(publicPath)) return manifestBuffer;
180
- const requestURL = new URL(c.req.url);
181
- const prefixPath = ensureTrailingSlash(ensureLeadingSlash(pathPrefix || '/'));
182
- manifest.metaData = {
183
- ...manifest.metaData,
184
- publicPath: `${requestURL.origin}${prefixPath}`
185
- };
186
- return Buffer.from(JSON.stringify(manifest), 'utf-8');
187
- } catch {
188
- return manifestBuffer;
189
- }
190
- };
191
- const patchModuleFederationRemoteEntryPublicPath = (c, remoteEntryBuffer, pathPrefix)=>{
192
- const requestURL = new URL(c.req.url);
193
- const prefixPath = ensureTrailingSlash(ensureLeadingSlash(pathPrefix || '/'));
194
- const publicPath = `${requestURL.origin}${prefixPath}`;
195
- const source = remoteEntryBuffer.toString('utf-8');
196
- const patched = source.replace(/__webpack_require__\.p\s*=\s*(['"`])[^'"`]*\1;/, `__webpack_require__.p = ${JSON.stringify(publicPath)};`).replace(/__rspack_require__\.p\s*=\s*(['"`])[^'"`]*\1;/, `__rspack_require__.p = ${JSON.stringify(publicPath)};`);
197
- if (patched === source) return remoteEntryBuffer;
198
- return Buffer.from(patched, 'utf-8');
199
- };
200
- const getModuleFederationAssetList = async (pwd)=>{
201
- const assets = new Set();
202
- const manifestPath = path.join(pwd, MODULE_FEDERATION_MANIFEST_FILE);
203
- if (!await fs.pathExists(manifestPath)) return {
204
- assets,
205
- remoteEntry: null
206
- };
207
- assets.add(MODULE_FEDERATION_MANIFEST_FILE);
208
- const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
209
- if (null === manifestBuffer) return {
210
- assets,
211
- remoteEntry: null
212
- };
213
- for (const filename of MODULE_FEDERATION_OPTIONAL_FILES)if (await fs.pathExists(path.join(pwd, filename))) assets.add(filename);
214
- let remoteEntryFile = null;
215
- try {
216
- const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
217
- const remoteEntry = joinModuleFederationAssetPath(manifest.metaData?.remoteEntry?.path, manifest.metaData?.remoteEntry?.name);
218
- const dtsZip = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.zip);
219
- const dtsApi = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.api);
220
- if (remoteEntry) {
221
- assets.add(remoteEntry);
222
- remoteEntryFile = remoteEntry;
223
- }
224
- if (dtsZip) assets.add(dtsZip);
225
- if (dtsApi) assets.add(dtsApi);
226
- manifest.shared?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
227
- manifest.remotes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
228
- manifest.exposes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
229
- } catch {}
230
- return {
231
- assets,
232
- remoteEntry: remoteEntryFile
233
- };
234
- };
235
66
  function createStaticMiddleware(options) {
236
67
  const { pwd, routes } = options;
237
68
  const prefix = options.output.assetPrefix || '/';
@@ -272,14 +103,10 @@ function createStaticMiddleware(options) {
272
103
  return moduleFederationAssetsPromise;
273
104
  };
274
105
  const serveFile = async (c, filepath, moduleFederationAsset = false, moduleFederationRemoteEntry = false, requestPath = '')=>{
275
- if (moduleFederationAsset) {
276
- c.header('Access-Control-Allow-Origin', '*');
277
- c.header('Access-Control-Allow-Headers', '*');
278
- c.header('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS');
279
- }
106
+ if (moduleFederationAsset) applyModuleFederationAssetHeaders(c);
280
107
  const mimeType = getMimeType(filepath);
281
108
  if (mimeType) c.header('Content-Type', mimeType);
282
- const shouldPatchManifest = moduleFederationAsset && requestPath === MODULE_FEDERATION_MANIFEST_FILE;
109
+ const shouldPatchManifest = moduleFederationAsset && isModuleFederationManifestRequest(requestPath);
283
110
  const shouldPatchRemoteEntry = moduleFederationRemoteEntry;
284
111
  const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
285
112
  const preCompressedAsset = canUsePreCompressed ? await resolvePreCompressedAsset(c, filepath) : {
@@ -290,8 +117,7 @@ function createStaticMiddleware(options) {
290
117
  const chunk = await fileReader.readFileFromSystem(targetFilepath, 'buffer');
291
118
  if (null === chunk) return null;
292
119
  const responseChunk = shouldPatchManifest ? patchModuleFederationManifestPublicPath(c, chunk, pathPrefix) : shouldPatchRemoteEntry ? patchModuleFederationRemoteEntryPublicPath(c, chunk, pathPrefix) : chunk;
293
- if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
294
- if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
120
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
295
121
  c.header('Content-Length', String(responseChunk.byteLength));
296
122
  const body = new Uint8Array(responseChunk.buffer, responseChunk.byteOffset, responseChunk.byteLength);
297
123
  return c.body(body, 200);
@@ -301,7 +127,7 @@ function createStaticMiddleware(options) {
301
127
  const pathname = c.req.path;
302
128
  if (pageRoute && '' === path.extname(pathname)) return next();
303
129
  const hit = staticPathRegExp.test(pathname);
304
- const requestPath = trimLeadingSlash(pathname.replace(pathPrefix, ()=>''));
130
+ const requestPath = getModuleFederationRequestPath(pathname, pathPrefix);
305
131
  if (requestPath.includes('..')) return next();
306
132
  const moduleFederationAssetMeta = await getModuleFederationAssets();
307
133
  const isModuleFederationAsset = moduleFederationAssetMeta.assets.has(requestPath);
@@ -0,0 +1,97 @@
1
+ import "node:module";
2
+ import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
+ import { fs } from "@modern-js/utils";
4
+ import path from "path";
5
+ const MODULE_FEDERATION_MANIFEST_FILE = 'mf-manifest.json';
6
+ const MODULE_FEDERATION_OPTIONAL_FILES = [
7
+ 'mf-stats.json'
8
+ ];
9
+ const trimLeadingSlash = (value)=>value.replace(/^\/+/, '');
10
+ const getModuleFederationRequestPath = (pathname, pathPrefix)=>trimLeadingSlash(pathname.replace(pathPrefix, ()=>''));
11
+ const isModuleFederationManifestRequest = (requestPath)=>requestPath === MODULE_FEDERATION_MANIFEST_FILE;
12
+ const applyModuleFederationAssetHeaders = (c)=>{
13
+ c.header('Access-Control-Allow-Origin', '*');
14
+ c.header('Access-Control-Allow-Headers', '*');
15
+ c.header('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS');
16
+ };
17
+ const joinModuleFederationAssetPath = (assetPath, assetName)=>{
18
+ if (!assetName) return '';
19
+ return trimLeadingSlash(path.posix.join(assetPath || '', assetName));
20
+ };
21
+ const appendModuleFederationAsset = (set, assetPath)=>{
22
+ if (!assetPath) return;
23
+ set.add(trimLeadingSlash(assetPath));
24
+ };
25
+ const appendModuleFederationAssets = (set, assets)=>{
26
+ assets?.js?.sync?.forEach((asset)=>appendModuleFederationAsset(set, asset));
27
+ assets?.js?.async?.forEach((asset)=>appendModuleFederationAsset(set, asset));
28
+ assets?.css?.sync?.forEach((asset)=>appendModuleFederationAsset(set, asset));
29
+ assets?.css?.async?.forEach((asset)=>appendModuleFederationAsset(set, asset));
30
+ };
31
+ const hasAbsoluteProtocol = (value)=>/^https?:\/\//i.test(value) || value.startsWith('//');
32
+ const ensureLeadingSlash = (value)=>{
33
+ if ('' === value) return '/';
34
+ return value.startsWith('/') ? value : `/${value}`;
35
+ };
36
+ const ensureTrailingSlash = (value)=>value.endsWith('/') ? value : `${value}/`;
37
+ const patchModuleFederationManifestPublicPath = (c, manifestBuffer, pathPrefix)=>{
38
+ try {
39
+ const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
40
+ const publicPath = manifest.metaData?.publicPath;
41
+ if (!publicPath || hasAbsoluteProtocol(publicPath)) return manifestBuffer;
42
+ const requestURL = new URL(c.req.url);
43
+ const prefixPath = ensureTrailingSlash(ensureLeadingSlash(pathPrefix || '/'));
44
+ manifest.metaData = {
45
+ ...manifest.metaData,
46
+ publicPath: `${requestURL.origin}${prefixPath}`
47
+ };
48
+ return Buffer.from(JSON.stringify(manifest), 'utf-8');
49
+ } catch {
50
+ return manifestBuffer;
51
+ }
52
+ };
53
+ const patchModuleFederationRemoteEntryPublicPath = (c, remoteEntryBuffer, pathPrefix)=>{
54
+ const requestURL = new URL(c.req.url);
55
+ const prefixPath = ensureTrailingSlash(ensureLeadingSlash(pathPrefix || '/'));
56
+ const publicPath = `${requestURL.origin}${prefixPath}`;
57
+ const source = remoteEntryBuffer.toString('utf-8');
58
+ const patched = source.replace(/__webpack_require__\.p\s*=\s*(['"`])[^'"`]*\1;/, `__webpack_require__.p = ${JSON.stringify(publicPath)};`).replace(/__rspack_require__\.p\s*=\s*(['"`])[^'"`]*\1;/, `__rspack_require__.p = ${JSON.stringify(publicPath)};`);
59
+ if (patched === source) return remoteEntryBuffer;
60
+ return Buffer.from(patched, 'utf-8');
61
+ };
62
+ const getModuleFederationAssetList = async (pwd)=>{
63
+ const assets = new Set();
64
+ const manifestPath = path.join(pwd, MODULE_FEDERATION_MANIFEST_FILE);
65
+ if (!await fs.pathExists(manifestPath)) return {
66
+ assets,
67
+ remoteEntry: null
68
+ };
69
+ assets.add(MODULE_FEDERATION_MANIFEST_FILE);
70
+ const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
71
+ if (null === manifestBuffer) return {
72
+ assets,
73
+ remoteEntry: null
74
+ };
75
+ for (const filename of MODULE_FEDERATION_OPTIONAL_FILES)if (await fs.pathExists(path.join(pwd, filename))) assets.add(filename);
76
+ let remoteEntryFile = null;
77
+ try {
78
+ const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
79
+ const remoteEntry = joinModuleFederationAssetPath(manifest.metaData?.remoteEntry?.path, manifest.metaData?.remoteEntry?.name);
80
+ const dtsZip = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.zip);
81
+ const dtsApi = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.api);
82
+ if (remoteEntry) {
83
+ assets.add(remoteEntry);
84
+ remoteEntryFile = remoteEntry;
85
+ }
86
+ if (dtsZip) assets.add(dtsZip);
87
+ if (dtsApi) assets.add(dtsApi);
88
+ manifest.shared?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
89
+ manifest.remotes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
90
+ manifest.exposes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
91
+ } catch {}
92
+ return {
93
+ assets,
94
+ remoteEntry: remoteEntryFile
95
+ };
96
+ };
97
+ export { MODULE_FEDERATION_MANIFEST_FILE, applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath, trimLeadingSlash };
@@ -0,0 +1,92 @@
1
+ import "node:module";
2
+ import { fs } from "@modern-js/utils";
3
+ const PRE_COMPRESSED_ASSET_EXTENSIONS = {
4
+ br: '.br',
5
+ gzip: '.gz'
6
+ };
7
+ const PRE_COMPRESSED_SUPPORTED_ENCODINGS = [
8
+ 'br',
9
+ 'gzip'
10
+ ];
11
+ const parseAcceptEncoding = (value)=>value.split(',').map((item)=>item.trim()).filter(Boolean).map((item)=>{
12
+ const [rawName, ...params] = item.split(';');
13
+ const name = rawName.trim().toLowerCase();
14
+ let q = 1;
15
+ for (const param of params){
16
+ const [key, rawValue] = param.split('=').map((v)=>v.trim());
17
+ if ('q' !== key.toLowerCase() || null == rawValue) continue;
18
+ const parsedQ = Number(rawValue);
19
+ if (!Number.isNaN(parsedQ)) q = Math.max(0, Math.min(parsedQ, 1));
20
+ }
21
+ return {
22
+ name,
23
+ q
24
+ };
25
+ });
26
+ const getAcceptedEncodings = (value)=>{
27
+ if (!value) return [];
28
+ const parsed = parseAcceptEncoding(value);
29
+ const qualityByEncoding = new Map();
30
+ let wildcardQuality;
31
+ for (const { name, q } of parsed){
32
+ if ('*' === name) {
33
+ wildcardQuality = q;
34
+ continue;
35
+ }
36
+ qualityByEncoding.set(name, q);
37
+ }
38
+ const getQuality = (encoding)=>{
39
+ const explicit = qualityByEncoding.get(encoding);
40
+ if (void 0 !== explicit) return explicit;
41
+ return wildcardQuality ?? 0;
42
+ };
43
+ return PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
44
+ encoding,
45
+ quality: getQuality(encoding)
46
+ })).filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
47
+ };
48
+ const appendVaryHeader = (c, value)=>{
49
+ const current = c.res.headers.get('Vary');
50
+ if (!current) return void c.header('Vary', value);
51
+ const values = current.split(',').map((item)=>item.trim().toLowerCase()).filter(Boolean);
52
+ if (!values.includes(value.toLowerCase())) c.header('Vary', `${current}, ${value}`);
53
+ };
54
+ const resolvePreCompressedAsset = async (c, filepath)=>{
55
+ const brPath = `${filepath}${PRE_COMPRESSED_ASSET_EXTENSIONS.br}`;
56
+ const gzipPath = `${filepath}${PRE_COMPRESSED_ASSET_EXTENSIONS.gzip}`;
57
+ const [hasBr, hasGzip] = await Promise.all([
58
+ fs.pathExists(brPath),
59
+ fs.pathExists(gzipPath)
60
+ ]);
61
+ const hasVariant = hasBr || hasGzip;
62
+ if (!hasVariant) return {
63
+ selected: null,
64
+ hasVariant: false
65
+ };
66
+ const acceptedEncodings = getAcceptedEncodings(c.req.header('accept-encoding'));
67
+ for (const encoding of acceptedEncodings){
68
+ if ('br' === encoding && hasBr) return {
69
+ selected: {
70
+ filepath: brPath,
71
+ encoding
72
+ },
73
+ hasVariant: true
74
+ };
75
+ if ('gzip' === encoding && hasGzip) return {
76
+ selected: {
77
+ filepath: gzipPath,
78
+ encoding
79
+ },
80
+ hasVariant: true
81
+ };
82
+ }
83
+ return {
84
+ selected: null,
85
+ hasVariant: true
86
+ };
87
+ };
88
+ const applyPreCompressedAssetHeaders = (c, preCompressedAsset)=>{
89
+ if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
90
+ if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
91
+ };
92
+ export { appendVaryHeader, applyPreCompressedAssetHeaders, resolvePreCompressedAsset };
@@ -9,5 +9,5 @@ export { AGGRED_DIR } from "./constants.mjs";
9
9
  export { run, useHonoContext } from "./context.mjs";
10
10
  export { getLoaderCtx } from "./helper.mjs";
11
11
  export { createServerBase } from "./serverBase.mjs";
12
- export { ErrorDigest, createErrorHtml, onError } from "./utils/index.mjs";
12
+ export { ErrorDigest, createErrorHtml, createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus, onError } from "./utils/index.mjs";
13
13
  export { getPublicDirConfig, getPublicDirPatterns, getPublicDirRoutePrefixes, normalizePublicDir, normalizePublicDirPath, resolvePublicDirPaths } from "./utils/publicDir.mjs";
@@ -5,7 +5,6 @@ import { initMonitorsPlugin, injectServerTiming, injectloggerPlugin } from "./mo
5
5
  import { processedByPlugin } from "./processedBy.mjs";
6
6
  import { injectRenderHandlerPlugin } from "./render/index.mjs";
7
7
  import { injectRoutePlugin } from "./route.mjs";
8
- import { injectTelemetryPlugin } from "./telemetry.mjs";
9
8
  function createSilenceLogger() {
10
9
  return new Proxy({}, {
11
10
  get: ()=>()=>{}
@@ -16,7 +15,6 @@ function createDefaultPlugins(options = {}) {
16
15
  compatPlugin(),
17
16
  logPlugin(),
18
17
  initMonitorsPlugin(),
19
- injectTelemetryPlugin(),
20
18
  injectRenderHandlerPlugin(options),
21
19
  injectloggerPlugin(options.logger ? options.logger : createSilenceLogger()),
22
20
  injectServerTiming(),
@@ -1,7 +1,5 @@
1
1
  import "node:module";
2
2
  export { compatPlugin, handleSetupResult } from "./compat/index.mjs";
3
- export { ContractGateAutopilot } from "./contractGateAutopilot.mjs";
4
- export { CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION, DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH, createFileContractGateSnapshotStore, createHttpContractGateSnapshotStore, resolveContractGateSnapshotPath, resolveContractGateSnapshotStore } from "./contractGateSnapshotStore.mjs";
5
3
  export { createDefaultPlugins } from "./default.mjs";
6
4
  export { faviconPlugin } from "./favicon.mjs";
7
5
  export { logPlugin } from "./log.mjs";
@@ -9,4 +7,3 @@ export { injectConfigMiddlewarePlugin } from "./middlewares.mjs";
9
7
  export { injectServerTiming, injectloggerPlugin } from "./monitors.mjs";
10
8
  export { processedByPlugin } from "./processedBy.mjs";
11
9
  export { getRenderHandler, injectRenderHandlerPlugin, renderPlugin } from "./render/index.mjs";
12
- export { DEFAULT_RUNTIME_FALLBACK_SIGNAL_ENDPOINT, DEFAULT_RUNTIME_STATUS_ENDPOINT, TelemetryCanaryOrchestrator, TelemetryRegistry, TelemetryStartupHealthError, createOtlpTelemetryExporter, createRuntimeFallbackSignalRuntimeState, createRuntimeSignalError, createTelemetryAwareMetrics, createVictoriaMetricsTelemetryExporter, enforceRuntimeFallbackSignalAuthToken, enforceRuntimeFallbackSignalTrustPolicy, getRuntimeSignalErrorStatusCode, hasEnabledTelemetryExporters, injectTelemetryPlugin, normalizeRuntimeFallbackSignalAuthConfig, normalizeRuntimeFallbackTrustPolicy, parseRuntimeFallbackSignalPayloadFromRawBody, resolveRuntimeFallbackSignalEndpoint } from "./telemetry.mjs";
@@ -8,10 +8,11 @@ const injectRenderHandlerPlugin = ({ staticGenerate, cacheConfig })=>({
8
8
  const config = api.getServerConfig();
9
9
  const hooks = api.getHooks();
10
10
  if (!routes) return;
11
- const onFallback = async (reason, error)=>{
11
+ const onFallback = async (reason, error, context)=>{
12
12
  await hooks.fallback.call({
13
13
  reason,
14
- error
14
+ error,
15
+ context
15
16
  });
16
17
  };
17
18
  const getRenderHandlerOptions = {
@@ -76,9 +76,13 @@ async function createRender({ routes, pwd, metaName, staticGenerate, cacheConfig
76
76
  const framework = cutNameByHyphen(metaName || 'modern-js');
77
77
  const fallbackHeader = `x-${framework}-ssr-fallback`;
78
78
  let fallbackReason = null;
79
+ const fallbackContext = {
80
+ request: req,
81
+ monitors
82
+ };
79
83
  const fallbackWrapper = async (reason, error)=>{
80
84
  fallbackReason = reason;
81
- return onFallback?.(reason, error);
85
+ return onFallback?.(reason, error, fallbackContext);
82
86
  };
83
87
  if (!routeInfo) return new Response(createErrorHtml(404), {
84
88
  status: 404,
@@ -0,0 +1 @@
1
+ import "node:module";
@@ -0,0 +1 @@
1
+ import "node:module";
@@ -4,6 +4,59 @@ const ERROR_PAGE_TEXT = {
4
4
  404: 'This page could not be found.',
5
5
  500: 'Internal Server Error.'
6
6
  };
7
+ const SAFE_FAILURE_MESSAGES = {
8
+ 500: 'Internal Server Error',
9
+ 503: 'Service Unavailable'
10
+ };
11
+ const SAFE_FAILURE_CODES = {
12
+ 500: 'INTERNAL_SERVER_ERROR',
13
+ 503: 'SERVICE_UNAVAILABLE'
14
+ };
15
+ const readErrorProperty = (error, key)=>{
16
+ if ('object' != typeof error || null === error || !(key in error)) return;
17
+ return error[key];
18
+ };
19
+ const normalizeFailureStatus = (value)=>{
20
+ if ('number' != typeof value || !Number.isInteger(value)) return;
21
+ return value >= 400 && value <= 599 ? value : void 0;
22
+ };
23
+ const normalizeRetryAfter = (value)=>{
24
+ if ('number' == typeof value && Number.isFinite(value) && value >= 0) return String(Math.ceil(value));
25
+ if ('string' == typeof value) {
26
+ const trimmed = value.trim();
27
+ return trimmed.length > 0 ? trimmed : void 0;
28
+ }
29
+ if (value instanceof Date) return value.toUTCString();
30
+ };
31
+ const getSafeFailureStatus = (error)=>normalizeFailureStatus(readErrorProperty(error, 'status')) ?? normalizeFailureStatus(readErrorProperty(error, 'statusCode')) ?? 500;
32
+ const createSafeFailureHttpResult = (error)=>{
33
+ const status = getSafeFailureStatus(error);
34
+ const retryAfter = 503 === status ? normalizeRetryAfter(readErrorProperty(error, 'retryAfter')) ?? normalizeRetryAfter(readErrorProperty(error, 'retryAfterSeconds')) ?? normalizeRetryAfter('number' == typeof readErrorProperty(error, 'retryAfterMs') ? readErrorProperty(error, 'retryAfterMs') / 1000 : void 0) : void 0;
35
+ return {
36
+ status,
37
+ body: {
38
+ success: false,
39
+ error: {
40
+ code: SAFE_FAILURE_CODES[status] ?? 'REQUEST_FAILED',
41
+ message: SAFE_FAILURE_MESSAGES[status] ?? 'Request failed',
42
+ status
43
+ }
44
+ },
45
+ headers: {
46
+ 'content-type': 'application/json; charset=utf-8',
47
+ ...retryAfter ? {
48
+ 'Retry-After': retryAfter
49
+ } : {}
50
+ }
51
+ };
52
+ };
53
+ const createSafeJsonFailureResponse = (error)=>{
54
+ const result = createSafeFailureHttpResult(error);
55
+ return new Response(JSON.stringify(result.body), {
56
+ status: result.status,
57
+ headers: result.headers
58
+ });
59
+ };
7
60
  const createErrorHtml = (status)=>{
8
61
  const text = ERROR_PAGE_TEXT[status] || '';
9
62
  const title = `${status}: ${text}`;
@@ -51,4 +104,4 @@ function onError(digest, error, monitors, req) {
51
104
  else if (req) console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error}, req.url = ${req.url}, req.headers = ${JSON.stringify(headerData)}`);
52
105
  else console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error} `);
53
106
  }
54
- export { createErrorHtml, error_ErrorDigest as ErrorDigest, onError };
107
+ export { createErrorHtml, createSafeFailureHttpResult, createSafeJsonFailureResponse, error_ErrorDigest as ErrorDigest, getSafeFailureStatus, onError };
@@ -0,0 +1,13 @@
1
+ import type { Middleware } from '../../../types';
2
+ export declare const MODULE_FEDERATION_MANIFEST_FILE = "mf-manifest.json";
3
+ export type ModuleFederationServeAssets = {
4
+ assets: Set<string>;
5
+ remoteEntry: string | null;
6
+ };
7
+ export declare const trimLeadingSlash: (value: string) => string;
8
+ export declare const getModuleFederationRequestPath: (pathname: string, pathPrefix: string) => string;
9
+ export declare const isModuleFederationManifestRequest: (requestPath: string) => requestPath is "mf-manifest.json";
10
+ export declare const applyModuleFederationAssetHeaders: (c: Parameters<Middleware>[0]) => void;
11
+ export declare const patchModuleFederationManifestPublicPath: (c: Parameters<Middleware>[0], manifestBuffer: Buffer, pathPrefix: string) => Buffer<ArrayBufferLike>;
12
+ export declare const patchModuleFederationRemoteEntryPublicPath: (c: Parameters<Middleware>[0], remoteEntryBuffer: Buffer, pathPrefix: string) => Buffer<ArrayBufferLike>;
13
+ export declare const getModuleFederationAssetList: (pwd: string) => Promise<ModuleFederationServeAssets>;
@@ -0,0 +1,13 @@
1
+ import type { Middleware } from '../../../types';
2
+ type SupportedEncoding = 'br' | 'gzip';
3
+ export type ResolvePreCompressedAssetResult = {
4
+ selected: {
5
+ filepath: string;
6
+ encoding: SupportedEncoding;
7
+ } | null;
8
+ hasVariant: boolean;
9
+ };
10
+ export declare const appendVaryHeader: (c: Parameters<Middleware>[0], value: string) => void;
11
+ export declare const resolvePreCompressedAsset: (c: Parameters<Middleware>[0], filepath: string) => Promise<ResolvePreCompressedAssetResult>;
12
+ export declare const applyPreCompressedAssetHeaders: (c: Parameters<Middleware>[0], preCompressedAsset: ResolvePreCompressedAssetResult) => void;
13
+ export {};
@@ -10,5 +10,6 @@ export * from './types/config';
10
10
  export * from './types/plugins';
11
11
  export * from './types/render';
12
12
  export * from './types/requestHandler';
13
- export { createErrorHtml, ErrorDigest, onError } from './utils';
13
+ export type { SafeFailureEnvelope, SafeFailureHttpResult } from './utils';
14
+ export { createErrorHtml, createSafeFailureHttpResult, createSafeJsonFailureResponse, ErrorDigest, getSafeFailureStatus, onError, } from './utils';
14
15
  export { getPublicDirConfig, getPublicDirPatterns, getPublicDirRoutePrefixes, normalizePublicDir, normalizePublicDirPath, resolvePublicDirPaths, } from './utils/publicDir';
@@ -1,6 +1,4 @@
1
1
  export { compatPlugin, handleSetupResult } from './compat';
2
- export { ContractGateAutopilot, type ContractGateAutopilotOptions, } from './contractGateAutopilot';
3
- export { CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION, type ContractGateSnapshotHttpStoreOptions, type ContractGateSnapshotStore, type ContractGateSnapshotStoreFactory, type ContractGateSnapshotStoreFactoryContext, type ContractGateSnapshotStoreModule, type ContractGateSnapshotStoreUserConfig, createFileContractGateSnapshotStore, createHttpContractGateSnapshotStore, DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH, type GateSnapshot, type GateSnapshotGateValue, resolveContractGateSnapshotPath, resolveContractGateSnapshotStore, } from './contractGateSnapshotStore';
4
2
  export { type CreateDefaultPluginsOptions, createDefaultPlugins, } from './default';
5
3
  export { faviconPlugin } from './favicon';
6
4
  export { logPlugin } from './log';
@@ -8,4 +6,3 @@ export { injectConfigMiddlewarePlugin } from './middlewares';
8
6
  export { injectloggerPlugin, injectServerTiming } from './monitors';
9
7
  export { processedByPlugin } from './processedBy';
10
8
  export { getRenderHandler, type InjectRenderHandlerOptions, injectRenderHandlerPlugin, renderPlugin, } from './render';
11
- export { createOtlpTelemetryExporter, createRuntimeFallbackSignalRuntimeState, createRuntimeSignalError, createTelemetryAwareMetrics, createVictoriaMetricsTelemetryExporter, DEFAULT_RUNTIME_FALLBACK_SIGNAL_ENDPOINT, DEFAULT_RUNTIME_STATUS_ENDPOINT, enforceRuntimeFallbackSignalAuthToken, enforceRuntimeFallbackSignalTrustPolicy, getRuntimeSignalErrorStatusCode, hasEnabledTelemetryExporters, injectTelemetryPlugin, normalizeRuntimeFallbackSignalAuthConfig, normalizeRuntimeFallbackTrustPolicy, type OtlpExporterOptions, parseRuntimeFallbackSignalPayloadFromRawBody, type RuntimeFallbackSignalAuthConfig, type RuntimeFallbackSignalRuntimeState, type RuntimeFallbackSignalTrustContext, type RuntimeFallbackSignalTrustPolicy, type RuntimeSignalError, type RuntimeSignalErrorCode, resolveRuntimeFallbackSignalEndpoint, type TelemetryCanaryDecision, TelemetryCanaryOrchestrator, type TelemetryCanaryStatusSnapshot, type TelemetryEnvelope, type TelemetryExporter, type TelemetryQueueStats, TelemetryRegistry, type TelemetryRegistryOptions, type TelemetrySignalType, type TelemetrySloAlert, TelemetryStartupHealthError, type VictoriaMetricsExporterOptions, } from './telemetry';