@module-federation/vite 1.18.2 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -221,6 +221,28 @@ You can specify the place the host initialization file is injected with the **ho
221
221
  The **moduleParseTimeout** option allows you to configure the maximum time to wait for module parsing during the build process.
222
222
  The **moduleParseIdleTimeout** option is an alternative that resets the timer on every parsed module. It only fires when there has been no module activity for the configured duration, making it suitable for large codebases where the total build time exceeds the fixed timeout.
223
223
 
224
+ ## Runtime capability optimization
225
+
226
+ Runtime features that a build never uses can be removed at build time:
227
+
228
+ ```ts
229
+ federation({
230
+ name: "remote",
231
+ exposes: {
232
+ "./remote-app": "./src/App.vue",
233
+ },
234
+ disableRemote: true,
235
+ disableShared: true,
236
+ disableSnapshot: true,
237
+ });
238
+ ```
239
+
240
+ - `disableRemote` removes remote-consumption support. Do not enable it when `remotes` are configured.
241
+ - `disableShared` removes shared-dependency support. Do not enable it when `shared` dependencies are configured.
242
+ - `disableSnapshot` removes snapshot support, including manifest-based remotes, preload, dynamic type hints, HMR, and devtools integration.
243
+
244
+ All three options default to `false`, except `disableSnapshot`, which defaults to `true` for Node/SSR builds. An explicitly configured Vite `define` value takes precedence over the corresponding option.
245
+
224
246
  ## Load the Remote App
225
247
 
226
248
  In your host app, you can now import and use the remote app with **defineAsyncComponent**
package/lib/index.d.ts CHANGED
@@ -96,6 +96,27 @@ type ModuleFederationOptions = {
96
96
  * @default 'web' (or 'node' if build.ssr is enabled)
97
97
  */
98
98
  target?: 'web' | 'node';
99
+ /**
100
+ * Removes remote-consumption support from the federation runtime.
101
+ * Only enable this for builds that never load remotes.
102
+ *
103
+ * @default false
104
+ */
105
+ disableRemote?: boolean;
106
+ /**
107
+ * Removes shared-dependency support from the federation runtime.
108
+ * Only enable this when the build has no shared dependencies.
109
+ *
110
+ * @default false
111
+ */
112
+ disableShared?: boolean;
113
+ /**
114
+ * Removes snapshot support, including manifest-based remotes, preload,
115
+ * dynamic type hints, HMR, and devtools integration.
116
+ *
117
+ * @default false (true for Node/SSR builds)
118
+ */
119
+ disableSnapshot?: boolean;
99
120
  /**
100
121
  * Additional packages to mark as external in the SSR remote entry build.
101
122
  * Shared packages and MF runtime packages are always external. Use this to
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as createModuleFederationError, a as getIsRolldown, b as rebaseImport, c as getPackageNameFromNodeModulePath, d as isNuxtProjectRoot, f as packageNameDecode, g as sharedCacheHelperCode, h as setPackageDetectionCwd, i as getInstalledPackageJson, l as getSharedCacheDescriptor, m as resolveImportPath, o as getPackageDetectionCwd, p as packageNameEncode, r as getInstalledPackageEntry, s as getPackageName, u as hasPackageDependency, v as mfWarn, y as normalizePathForImport } from "./pluginDts-CGDIZCsD.js";
1
+ import { _ as createModuleFederationError, a as getIsRolldown, b as rebaseImport, c as getPackageNameFromNodeModulePath, d as isNuxtProjectRoot, f as packageNameDecode, g as sharedCacheHelperCode, h as setPackageDetectionCwd, i as getInstalledPackageJson, l as getSharedCacheDescriptor, m as resolveImportPath, o as getPackageDetectionCwd, p as packageNameEncode, r as getInstalledPackageEntry, s as getPackageName, u as hasPackageDependency, v as mfWarn, y as normalizePathForImport } from "./pluginDts-Cd2opGcZ.js";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs$2 from "fs";
4
4
  import { existsSync, readFileSync, realpathSync, statSync, writeFileSync } from "fs";
@@ -237,6 +237,10 @@ const ASSET_LIKE_IMPORT_RE = new RegExp(`\\.(${[...[
237
237
  function isAssetLikeImport(source) {
238
238
  return ASSET_LIKE_IMPORT_RE.test(source);
239
239
  }
240
+ const VITE_OPTIMIZABLE_ENTRY_RE = /\.[cm]?[jt]s$/;
241
+ function isViteOptimizableEntry(resolvedPath) {
242
+ return VITE_OPTIMIZABLE_ENTRY_RE.test(resolvedPath);
243
+ }
240
244
  function removeTrailingSlash(value) {
241
245
  return value.endsWith("/") ? value.slice(0, -1) : value;
242
246
  }
@@ -544,7 +548,10 @@ function normalizeModuleFederationOptions(options) {
544
548
  moduleParseTimeout: options.moduleParseTimeout || 10,
545
549
  moduleParseIdleTimeout: options.moduleParseIdleTimeout,
546
550
  varFilename: options.varFilename,
547
- target: options.target
551
+ target: options.target,
552
+ disableRemote: options.disableRemote,
553
+ disableShared: options.disableShared,
554
+ disableSnapshot: options.disableSnapshot
548
555
  };
549
556
  explicitSharedKeysByOptions.set(normalized, new Set(explicitSharedKeys));
550
557
  return config = normalized;
@@ -4915,8 +4922,8 @@ function resolveReactRefreshRuntime(root) {
4915
4922
  * falls back to this remote's local runtime when the remote is opened directly.
4916
4923
  */
4917
4924
  const REACT_REFRESH_PROXY_MODULE = [
4918
- `const __remoteOrigin = new URL(import.meta.url).origin;`,
4919
- `const __target = window.location.origin === __remoteOrigin ? '${LOCAL_REACT_REFRESH_PATH}' : window.location.origin + '${REACT_REFRESH_PATH}';`,
4925
+ `const __remoteUrl = new URL(import.meta.url);`,
4926
+ `const __target = window.location.origin === __remoteUrl.origin ? new URL('.${LOCAL_REACT_REFRESH_PATH}', __remoteUrl).href : window.location.origin + '${REACT_REFRESH_PATH}';`,
4920
4927
  `const __rt = await import(__target);`,
4921
4928
  `export const injectIntoGlobalHook = __rt.injectIntoGlobalHook;`,
4922
4929
  `export const register = __rt.register;`,
@@ -4933,14 +4940,14 @@ const reactAdapter = {
4933
4940
  let reactRefreshRuntime;
4934
4941
  server.middlewares.use((req, res, next) => {
4935
4942
  const url = stripQuery(req.url);
4936
- if (url === LOCAL_REACT_REFRESH_PATH) {
4943
+ if (url?.endsWith(LOCAL_REACT_REFRESH_PATH)) {
4937
4944
  reactRefreshRuntime ??= resolveReactRefreshRuntime(server.config.root);
4938
4945
  res.setHeader("Content-Type", "application/javascript; charset=utf-8");
4939
4946
  res.setHeader("Access-Control-Allow-Origin", "*");
4940
4947
  res.end(reactRefreshRuntime);
4941
4948
  return;
4942
4949
  }
4943
- if (url !== REACT_REFRESH_PATH) return next();
4950
+ if (!url?.endsWith(REACT_REFRESH_PATH)) return next();
4944
4951
  res.setHeader("Content-Type", "application/javascript; charset=utf-8");
4945
4952
  res.setHeader("Access-Control-Allow-Origin", "*");
4946
4953
  res.end(REACT_REFRESH_PROXY_MODULE);
@@ -5630,6 +5637,35 @@ const chunkContainsCssModules = (modules) => {
5630
5637
  return false;
5631
5638
  };
5632
5639
  /**
5640
+ * Analyzes assets associated with a chunk without mutating the output map.
5641
+ * The static-import traversal is cycle-safe and ignores missing bundle entries.
5642
+ */
5643
+ const analyzeChunkAssets = (bundle, fileName, chunk) => {
5644
+ const dynamicAssets = [];
5645
+ const visited = /* @__PURE__ */ new Set();
5646
+ const queue = [fileName];
5647
+ for (let queueIndex = 0; queueIndex < queue.length; queueIndex++) {
5648
+ const currentFileName = queue[queueIndex];
5649
+ if (visited.has(currentFileName)) continue;
5650
+ visited.add(currentFileName);
5651
+ const currentChunk = bundle[currentFileName];
5652
+ if (!currentChunk || currentChunk.type !== "chunk") continue;
5653
+ for (const dynamicImport of currentChunk.dynamicImports ?? []) {
5654
+ if (!bundle[dynamicImport]) continue;
5655
+ dynamicAssets.push({
5656
+ fileName: dynamicImport,
5657
+ type: isCSSFile(dynamicImport) ? "css" : "js"
5658
+ });
5659
+ }
5660
+ for (const staticImport of currentChunk.imports ?? []) queue.push(staticImport);
5661
+ }
5662
+ return {
5663
+ importedCss: Array.from(chunk.viteMetadata?.importedCss ?? []),
5664
+ containsCssModules: chunkContainsCssModules(chunk.modules),
5665
+ dynamicAssets
5666
+ };
5667
+ };
5668
+ /**
5633
5669
  * Processes module assets and tracks them in the files map
5634
5670
  * @param bundle - The Rollup output bundle
5635
5671
  * @param filesMap - The preload map to populate
@@ -5637,6 +5673,7 @@ const chunkContainsCssModules = (modules) => {
5637
5673
  */
5638
5674
  const processModuleAssets = (bundle, filesMap, moduleMatcher, options = {}) => {
5639
5675
  const bundleCssAssets = collectCssAssets(bundle);
5676
+ const chunkAnalysisCache = /* @__PURE__ */ new Map();
5640
5677
  for (const [fileName, fileData] of Object.entries(bundle)) {
5641
5678
  if (fileData.type !== "chunk") continue;
5642
5679
  if (!fileData.modules) continue;
@@ -5649,27 +5686,19 @@ const processModuleAssets = (bundle, filesMap, moduleMatcher, options = {}) => {
5649
5686
  }
5650
5687
  const matchKey = comparableModulePaths.map(moduleMatcher).find(Boolean);
5651
5688
  if (!matchKey) continue;
5689
+ let analysis = chunkAnalysisCache.get(fileName);
5690
+ if (!analysis) {
5691
+ analysis = analyzeChunkAssets(bundle, fileName, fileData);
5692
+ chunkAnalysisCache.set(fileName, analysis);
5693
+ }
5652
5694
  trackAsset(filesMap, matchKey, fileName, false, "js");
5653
5695
  let foundCssViaMetadata = false;
5654
- if (fileData.viteMetadata?.importedCss?.size) for (const cssFile of Array.from(fileData.viteMetadata.importedCss)) {
5696
+ for (const cssFile of analysis.importedCss) {
5655
5697
  trackAsset(filesMap, matchKey, cssFile, false, "css");
5656
5698
  foundCssViaMetadata = true;
5657
5699
  }
5658
- if (!foundCssViaMetadata && chunkContainsCssModules(fileData.modules)) for (const cssAsset of Array.from(bundleCssAssets)) trackAsset(filesMap, matchKey, cssAsset, false, "css");
5659
- const visited = /* @__PURE__ */ new Set();
5660
- const queue = [fileName];
5661
- for (let queueIndex = 0; queueIndex < queue.length; queueIndex++) {
5662
- const cur = queue[queueIndex];
5663
- if (visited.has(cur)) continue;
5664
- visited.add(cur);
5665
- const chunk = bundle[cur];
5666
- if (!chunk || chunk.type !== "chunk") continue;
5667
- if (chunk.dynamicImports) for (const dynamicImport of chunk.dynamicImports) {
5668
- if (!bundle[dynamicImport]) continue;
5669
- trackAsset(filesMap, matchKey, dynamicImport, true, isCSSFile(dynamicImport) ? "css" : "js");
5670
- }
5671
- if (chunk.imports) for (const imp of chunk.imports) queue.push(imp);
5672
- }
5700
+ if (!foundCssViaMetadata && analysis.containsCssModules) for (const cssAsset of Array.from(bundleCssAssets)) trackAsset(filesMap, matchKey, cssAsset, false, "css");
5701
+ for (const asset of analysis.dynamicAssets) trackAsset(filesMap, matchKey, asset.fileName, true, asset.type);
5673
5702
  }
5674
5703
  }
5675
5704
  };
@@ -7759,6 +7788,44 @@ var normalizeOptimizeDeps_default = {
7759
7788
  }
7760
7789
  };
7761
7790
  //#endregion
7791
+ //#region src/utils/runtimeCapabilityOptimization.ts
7792
+ const RUNTIME_CAPABILITIES = [
7793
+ {
7794
+ option: "disableRemote",
7795
+ define: "FEDERATION_OPTIMIZE_NO_REMOTE"
7796
+ },
7797
+ {
7798
+ option: "disableShared",
7799
+ define: "FEDERATION_OPTIMIZE_NO_SHARED"
7800
+ },
7801
+ {
7802
+ option: "disableSnapshot",
7803
+ define: "FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN"
7804
+ }
7805
+ ];
7806
+ function isEquivalentBooleanDefine(value, expected) {
7807
+ return String(value) === JSON.stringify(expected);
7808
+ }
7809
+ function applyRuntimeCapabilityDefines(define, options, { defaultDisableSnapshot, onConflict } = {}) {
7810
+ for (const capability of RUNTIME_CAPABILITIES) {
7811
+ const explicitValue = options[capability.option];
7812
+ const desiredValue = capability.option === "disableSnapshot" ? explicitValue ?? defaultDisableSnapshot : explicitValue;
7813
+ if (desiredValue === void 0) continue;
7814
+ if (!(capability.define in define)) {
7815
+ define[capability.define] = JSON.stringify(desiredValue);
7816
+ continue;
7817
+ }
7818
+ if (explicitValue !== void 0 && !isEquivalentBooleanDefine(define[capability.define], explicitValue)) onConflict?.(`${capability.define} define (${define[capability.define]}) differs from ${capability.option} option (${explicitValue}). The existing define will not be overridden.`);
7819
+ }
7820
+ if (!("FEDERATION_HAS_EXPOSES" in define)) define["FEDERATION_HAS_EXPOSES"] = JSON.stringify(Object.keys(options.exposes).length > 0);
7821
+ }
7822
+ function getRuntimeCapabilityConfigurationWarnings(options) {
7823
+ const warnings = [];
7824
+ if (options.disableRemote && Object.keys(options.remotes).length > 0) warnings.push("disableRemote is true, but remotes are configured. Remote loading will be unavailable at runtime.");
7825
+ if (options.disableShared && Object.keys(options.shared).length > 0) warnings.push("disableShared is true, but shared dependencies are configured. Shared dependency loading will be unavailable at runtime.");
7826
+ return warnings;
7827
+ }
7828
+ //#endregion
7762
7829
  //#region src/index.ts
7763
7830
  const patchedManualChunks = /* @__PURE__ */ new WeakSet();
7764
7831
  const PRELOAD_HELPER_CHUNK = "vite-preload-helper";
@@ -7843,8 +7910,7 @@ function isFederationHtmlPreloadDependency(dep, includeSharedRuntime = false) {
7843
7910
  }
7844
7911
  function canResolveSharedSubpath(subpath, projectRoot) {
7845
7912
  try {
7846
- createRequire$1(pathToFileURL(path$1.join(projectRoot, "package.json"))).resolve(subpath);
7847
- return true;
7913
+ return isViteOptimizableEntry(createRequire$1(pathToFileURL(path$1.join(projectRoot, "package.json"))).resolve(subpath));
7848
7914
  } catch {
7849
7915
  return false;
7850
7916
  }
@@ -8029,16 +8095,21 @@ export default __mfShared.default ?? __mfShared;`
8029
8095
  const optimizeDeps = config.optimizeDeps ??= {};
8030
8096
  optimizeDeps.include ??= [];
8031
8097
  optimizeDeps.exclude ??= [];
8032
- const shouldBypassOptimizeDep = isLitShare(key);
8098
+ const shouldBypassOptimizeDep = isLitShare(key) || !canResolveSharedSubpath(key, root);
8033
8099
  if (optimizeDeps.include.includes(key)) optimizeDeps.exclude = optimizeDeps.exclude.filter((dep) => dep !== key);
8034
8100
  else if (shouldBypassOptimizeDep) optimizeDeps.exclude.push(key);
8035
8101
  else optimizeDeps.include.push(key);
8036
8102
  for (const subpath of getCommonSharedSubpaths(key)) {
8103
+ const canResolveSubpath = canResolveSharedSubpath(subpath, root);
8104
+ if (subpath === "react/compiler-runtime" && !canResolveSubpath) {
8105
+ optimizeDeps.exclude.push(subpath);
8106
+ continue;
8107
+ }
8037
8108
  getLoadShareModulePath(subpath, isRolldown, options);
8038
8109
  writeLoadShareModule(subpath, shareItem, _command, isRolldown, options);
8039
8110
  writePreBuildLibPath(subpath, shareItem, options);
8040
8111
  addUsedShares(subpath, options);
8041
- if (canResolveSharedSubpath(subpath, root)) optimizeDeps.include.push(subpath);
8112
+ if (canResolveSubpath) optimizeDeps.include.push(subpath);
8042
8113
  else optimizeDeps.exclude.push(subpath);
8043
8114
  }
8044
8115
  }
@@ -8086,9 +8157,18 @@ export default __mfShared.default ?? __mfShared;`
8086
8157
  };
8087
8158
  }
8088
8159
  const SSR_ONLY_PLUGINS = new Set(["@module-federation/vite/ssrEntryLoader"]);
8160
+ function applyBuildTimeRuntimeDefines(define, options, { target, isAstro, defaultDisableSnapshot }) {
8161
+ const envTargetDefineValue = !options.target && isAstro ? "undefined" : JSON.stringify(target);
8162
+ if (!("ENV_TARGET" in define)) define.ENV_TARGET = envTargetDefineValue;
8163
+ applyRuntimeCapabilityDefines(define, options, {
8164
+ defaultDisableSnapshot,
8165
+ onConflict: mfWarn
8166
+ });
8167
+ if (options.target && define.ENV_TARGET !== JSON.stringify(options.target)) mfWarn(`ENV_TARGET define (${define.ENV_TARGET}) differs from target option ("${options.target}"). ENV_TARGET will not be overridden.`);
8168
+ }
8089
8169
  function loadPluginDts(options) {
8090
8170
  if (options.dts === false) return [];
8091
- return [import("./pluginDts-CGDIZCsD.js").then((n) => n.n).then(({ default: pluginDts }) => pluginDts(options))];
8171
+ return [import("./pluginDts-Cd2opGcZ.js").then((n) => n.n).then(({ default: pluginDts }) => pluginDts(options))];
8092
8172
  }
8093
8173
  function federation(mfUserOptions) {
8094
8174
  if (isTestEnv()) return [];
@@ -8102,6 +8182,7 @@ function federation(mfUserOptions) {
8102
8182
  let command;
8103
8183
  let desiredRolldownOutput;
8104
8184
  let isSsrBuild = false;
8185
+ const emittedRuntimeCapabilityWarnings = /* @__PURE__ */ new Set();
8105
8186
  return [
8106
8187
  {
8107
8188
  name: "vite:module-federation-virtual-modules",
@@ -8444,11 +8525,17 @@ function federation(mfUserOptions) {
8444
8525
  }
8445
8526
  const isAstro = hasPackageDependency("astro");
8446
8527
  const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
8447
- const envTargetDefineValue = !options.target && isAstro ? "undefined" : JSON.stringify(resolvedTarget);
8448
8528
  if (!config.define) config.define = {};
8449
- if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = envTargetDefineValue;
8450
- if (resolvedTarget === "node" && !("FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN" in config.define)) config.define["FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN"] = "true";
8451
- if (options.target && "ENV_TARGET" in config.define && config.define["ENV_TARGET"] !== JSON.stringify(options.target)) mfWarn(`ENV_TARGET define (${config.define["ENV_TARGET"]}) differs from target option ("${options.target}"). ENV_TARGET will not be overridden.`);
8529
+ applyBuildTimeRuntimeDefines(config.define, options, {
8530
+ target: resolvedTarget,
8531
+ isAstro,
8532
+ defaultDisableSnapshot: resolvedTarget === "node" ? true : void 0
8533
+ });
8534
+ for (const warning of getRuntimeCapabilityConfigurationWarnings(options)) {
8535
+ if (emittedRuntimeCapabilityWarnings.has(warning)) continue;
8536
+ emittedRuntimeCapabilityWarnings.add(warning);
8537
+ mfWarn(warning);
8538
+ }
8452
8539
  },
8453
8540
  configResolved(config) {
8454
8541
  if (!hasPackageDependency("nitro")) return;
@@ -8458,11 +8545,12 @@ function federation(mfUserOptions) {
8458
8545
  configEnvironment(name, config) {
8459
8546
  if (!(config.consumer === "server" || name === "ssr" || name === "server" || config.build?.ssr === true)) return;
8460
8547
  const isAstro = hasPackageDependency("astro");
8461
- const envTargetDefineValue = !options.target && isAstro ? "undefined" : JSON.stringify(options.target ?? "node");
8462
8548
  config.define = { ...config.define ?? {} };
8463
- if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = envTargetDefineValue;
8464
- if (!("FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN" in config.define)) config.define["FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN"] = "true";
8465
- if (options.target && config.define["ENV_TARGET"] !== JSON.stringify(options.target)) mfWarn(`ENV_TARGET define (${config.define["ENV_TARGET"]}) differs from target option ("${options.target}"). ENV_TARGET will not be overridden.`);
8549
+ applyBuildTimeRuntimeDefines(config.define, options, {
8550
+ target: options.target ?? "node",
8551
+ isAstro,
8552
+ defaultDisableSnapshot: true
8553
+ });
8466
8554
  }
8467
8555
  },
8468
8556
  ...Manifest(options),
@@ -102,6 +102,7 @@ let packageDetectionCwd;
102
102
  function getDependencyCacheKey(cwd, dependencyName) {
103
103
  return `${cwd}:${dependencyName}`;
104
104
  }
105
+ const installedPackageJsonCache = /* @__PURE__ */ new Map();
105
106
  function setPackageDetectionCwd(cwd) {
106
107
  packageDetectionCwd = cwd;
107
108
  }
@@ -398,6 +399,13 @@ const sharedCacheHelperCode = `const __mfGetSharedCacheDescriptor = (pkg, single
398
399
  function getInstalledPackageJson(pkg, opts) {
399
400
  const cwd = opts?.cwd || getPackageDetectionCwd();
400
401
  const packageName = opts?.packageName || getPackageName(pkg);
402
+ const cacheKey = `${cwd}\0${pkg}\0${packageName}\0${opts?.fromResolvedEntry ?? ""}`;
403
+ if (installedPackageJsonCache.has(cacheKey)) return installedPackageJsonCache.get(cacheKey);
404
+ const result = resolveInstalledPackageJson(pkg, cwd, packageName, opts);
405
+ installedPackageJsonCache.set(cacheKey, result);
406
+ return result;
407
+ }
408
+ function resolveInstalledPackageJson(pkg, cwd, packageName, opts) {
401
409
  const tryReadPackageJson = (packageJsonPath) => {
402
410
  if (!existsSync(packageJsonPath)) return void 0;
403
411
  try {
@@ -1,5 +1,6 @@
1
1
  //#region src/utils/fetchWithTimeout.ts
2
2
  const DEFAULT_SSR_FETCH_TIMEOUT_MS = 1e4;
3
+ const DEFAULT_SSR_FETCH_MAX_BYTES = 10 * 1024 * 1024;
3
4
  function getFetchUrl(input) {
4
5
  const raw = typeof input === "string" || input instanceof URL ? String(input) : input.url;
5
6
  return new URL(raw);
@@ -10,6 +11,11 @@ function getSecureFetchUrl(input) {
10
11
  if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) throw new TypeError(`Refusing to fetch SSR resource over an insecure connection: ${url}`);
11
12
  return url;
12
13
  }
14
+ function isAbortLikeError(error) {
15
+ if (!error || typeof error !== "object") return false;
16
+ const name = error.name;
17
+ return name === "AbortError" || name === "TimeoutError";
18
+ }
13
19
  /** Fetch with a bounded wait. Set timeoutMs to 0 to disable the timeout. */
14
20
  async function fetchWithTimeout(input, init = {}, timeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS) {
15
21
  const inputUrl = getSecureFetchUrl(input);
@@ -29,11 +35,67 @@ async function fetchWithTimeout(input, init = {}, timeoutMs = DEFAULT_SSR_FETCH_
29
35
  try {
30
36
  return await request(inputUrl);
31
37
  } catch (error) {
32
- if (inputUrl.hostname !== "localhost") throw error;
38
+ if (inputUrl.hostname !== "localhost" || isAbortLikeError(error)) throw error;
33
39
  inputUrl.hostname = "[::1]";
34
40
  return request(inputUrl);
35
41
  }
36
42
  }
43
+ var SsrFetchBodyTooLargeError = class extends Error {
44
+ url;
45
+ maxBytes;
46
+ declaredBytes;
47
+ constructor(url, maxBytes, declaredBytes) {
48
+ super(declaredBytes != null ? `SSR response from ${url} declared ${declaredBytes} bytes which exceeds the ${maxBytes}-byte limit` : `SSR response from ${url} exceeded the ${maxBytes}-byte limit`);
49
+ this.name = "SsrFetchBodyTooLargeError";
50
+ this.url = url;
51
+ this.maxBytes = maxBytes;
52
+ this.declaredBytes = declaredBytes;
53
+ }
54
+ };
55
+ function isSsrFetchBodyTooLargeError(error) {
56
+ return error instanceof SsrFetchBodyTooLargeError;
57
+ }
58
+ /**
59
+ * Read a response body as text, rejecting when it exceeds `maxBytes`.
60
+ * Set `maxBytes` to 0 (or a non-finite value) to disable the limit.
61
+ */
62
+ async function readResponseTextBounded(res, maxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, url = res.url || "unknown") {
63
+ if (!Number.isFinite(maxBytes) || maxBytes <= 0) return res.text();
64
+ const contentLengthHeader = res.headers?.get?.("content-length") ?? null;
65
+ if (contentLengthHeader != null) {
66
+ const declaredBytes = Number(contentLengthHeader);
67
+ if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) {
68
+ try {
69
+ await res.body?.cancel();
70
+ } catch {}
71
+ throw new SsrFetchBodyTooLargeError(url, maxBytes, declaredBytes);
72
+ }
73
+ }
74
+ if (!res.body) return res.text();
75
+ const reader = res.body.getReader();
76
+ const chunks = [];
77
+ let total = 0;
78
+ while (true) {
79
+ const { done, value } = await reader.read();
80
+ if (done) break;
81
+ if (!value) continue;
82
+ total += value.byteLength;
83
+ if (total > maxBytes) {
84
+ try {
85
+ await reader.cancel();
86
+ } catch {}
87
+ throw new SsrFetchBodyTooLargeError(url, maxBytes);
88
+ }
89
+ chunks.push(value);
90
+ }
91
+ const merged = new Uint8Array(total);
92
+ let offset = 0;
93
+ for (const chunk of chunks) {
94
+ merged.set(chunk, offset);
95
+ offset += chunk.byteLength;
96
+ }
97
+ return new TextDecoder().decode(merged);
98
+ }
37
99
  //#endregion
38
100
  //#region src/utils/ssrEntryLoader.ts
39
101
  /**
@@ -93,8 +155,8 @@ async function getModuleRunnerModule() {
93
155
  * This is Vite 8+ only — older versions don't expose `vite/module-runner` or
94
156
  * the `/__mf_runner__` proxy endpoint.
95
157
  */
96
- async function getOrCreateRunner(remoteOrigin, fetchTimeoutMs) {
97
- const cacheKey = `${fetchTimeoutMs}::${remoteOrigin}`;
158
+ async function getOrCreateRunner(remoteOrigin, fetchTimeoutMs, fetchMaxBytes) {
159
+ const cacheKey = `${fetchTimeoutMs}::${fetchMaxBytes}::${remoteOrigin}`;
98
160
  if (runnerCache.has(cacheKey)) return runnerCache.get(cacheKey);
99
161
  const promise = (async () => {
100
162
  const viteRunner = await getModuleRunnerModule();
@@ -105,11 +167,12 @@ async function getOrCreateRunner(remoteOrigin, fetchTimeoutMs) {
105
167
  return new ModuleRunner({
106
168
  hmr: false,
107
169
  transport: { async invoke(payload) {
108
- return await (await fetchWithTimeout(runnerEndpoint, {
170
+ const text = await readResponseTextBounded(await fetchWithTimeout(runnerEndpoint, {
109
171
  method: "POST",
110
172
  headers: { "Content-Type": "application/json" },
111
173
  body: JSON.stringify(payload)
112
- }, fetchTimeoutMs)).json();
174
+ }, fetchTimeoutMs), fetchMaxBytes, runnerEndpoint);
175
+ return JSON.parse(text);
113
176
  } }
114
177
  }, new ESModulesEvaluator());
115
178
  } catch {
@@ -146,8 +209,8 @@ function computeManifestVersionKey(manifest) {
146
209
  }
147
210
  const ssrEntryCache = /* @__PURE__ */ new Map();
148
211
  const manifestFetchCache = /* @__PURE__ */ new Map();
149
- function makeUrlCacheKey(url, fetchTimeoutMs) {
150
- return `${fetchTimeoutMs}::${url}`;
212
+ function makeUrlCacheKey(url, fetchTimeoutMs, fetchMaxBytes) {
213
+ return `${fetchTimeoutMs}::${fetchMaxBytes}::${url}`;
151
214
  }
152
215
  var SsrEntryHttpError = class extends Error {
153
216
  constructor(url, status, statusText, bodyPreview) {
@@ -165,22 +228,26 @@ function getBodyPreview(body) {
165
228
  function isSsrEntryHttpError(error) {
166
229
  return error instanceof SsrEntryHttpError;
167
230
  }
168
- async function fetchManifest(manifestUrl, fetchTimeoutMs) {
231
+ async function fetchManifest(manifestUrl, fetchTimeoutMs, fetchMaxBytes) {
169
232
  try {
170
233
  const res = await fetchWithTimeout(manifestUrl, {}, fetchTimeoutMs);
171
234
  if (!res.ok) return null;
172
- return await res.json();
173
- } catch {
235
+ const text = await readResponseTextBounded(res, fetchMaxBytes, manifestUrl);
236
+ return JSON.parse(text);
237
+ } catch (error) {
238
+ if (isSsrFetchBodyTooLargeError(error)) throw error;
174
239
  return null;
175
240
  }
176
241
  }
177
- async function fetchManifestCached(manifestUrl, fetchTimeoutMs) {
178
- const cacheKey = makeUrlCacheKey(manifestUrl, fetchTimeoutMs);
242
+ async function fetchManifestCached(manifestUrl, fetchTimeoutMs, fetchMaxBytes) {
243
+ const cacheKey = makeUrlCacheKey(manifestUrl, fetchTimeoutMs, fetchMaxBytes);
179
244
  if (!manifestFetchCache.has(cacheKey)) {
180
- const promise = fetchManifest(manifestUrl, fetchTimeoutMs);
245
+ const promise = fetchManifest(manifestUrl, fetchTimeoutMs, fetchMaxBytes);
181
246
  manifestFetchCache.set(cacheKey, promise);
182
247
  promise.then((manifest) => {
183
248
  if (!manifest && manifestFetchCache.get(cacheKey) === promise) manifestFetchCache.delete(cacheKey);
249
+ }, () => {
250
+ if (manifestFetchCache.get(cacheKey) === promise) manifestFetchCache.delete(cacheKey);
184
251
  });
185
252
  }
186
253
  return manifestFetchCache.get(cacheKey);
@@ -239,9 +306,9 @@ function resolveAssetBaseUrl(entryUrl, manifest, manifestUrl) {
239
306
  if (!isManifestEntry(entryUrl)) return entryUrl;
240
307
  return new URL("remoteEntry.js", manifestUrl.replace(/\/[^/]+$/, "/")).href;
241
308
  }
242
- async function buildEntryContext(entryUrl, fetchTimeoutMs) {
309
+ async function buildEntryContext(entryUrl, fetchTimeoutMs, fetchMaxBytes) {
243
310
  const manifestUrl = getManifestUrl(entryUrl);
244
- const manifest = await fetchManifestCached(manifestUrl, fetchTimeoutMs);
311
+ const manifest = await fetchManifestCached(manifestUrl, fetchTimeoutMs, fetchMaxBytes);
245
312
  const assetBaseUrl = resolveAssetBaseUrl(entryUrl, manifest, manifestUrl);
246
313
  return {
247
314
  entryUrl,
@@ -279,7 +346,7 @@ async function resolveFirstReachableCandidate(candidates, fetchTimeoutMs) {
279
346
  }
280
347
  return null;
281
348
  }
282
- async function resolveSSREntryImpl(remoteEntryUrl, fetchTimeoutMs) {
349
+ async function resolveSSREntryImpl(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes) {
283
350
  if (isSsrEntry(remoteEntryUrl)) return {
284
351
  url: remoteEntryUrl,
285
352
  type: "module",
@@ -294,33 +361,35 @@ async function resolveSSREntryImpl(remoteEntryUrl, fetchTimeoutMs) {
294
361
  }, fetchTimeoutMs);
295
362
  if (fromServerBuild) return fromServerBuild;
296
363
  }
297
- const ctx = await buildEntryContext(remoteEntryUrl, fetchTimeoutMs);
364
+ const ctx = await buildEntryContext(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes);
298
365
  if (ctx.manifest) {
299
366
  const fromManifest = resolveSSREntryUrl(ctx.manifest, ctx.manifestUrl);
300
367
  if (fromManifest) return fromManifest;
301
368
  }
302
369
  return resolveFirstReachableCandidate(buildSsrEntryCandidates(ctx, { skipServerBuild: !isManifestEntry(remoteEntryUrl) }), fetchTimeoutMs);
303
370
  }
304
- function setSsrEntryCache(remoteEntryUrl, fetchTimeoutMs) {
305
- const cacheKey = makeUrlCacheKey(remoteEntryUrl, fetchTimeoutMs);
371
+ function setSsrEntryCache(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes) {
372
+ const cacheKey = makeUrlCacheKey(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes);
306
373
  const record = {
307
- promise: resolveSSREntryImpl(remoteEntryUrl, fetchTimeoutMs),
374
+ promise: resolveSSREntryImpl(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes),
308
375
  resolvedAt: Date.now()
309
376
  };
310
377
  ssrEntryCache.set(cacheKey, record);
311
378
  record.promise.then((entry) => {
312
379
  if (!entry && ssrEntryCache.get(cacheKey) === record) ssrEntryCache.delete(cacheKey);
380
+ }, () => {
381
+ if (ssrEntryCache.get(cacheKey) === record) ssrEntryCache.delete(cacheKey);
313
382
  });
314
383
  return record;
315
384
  }
316
- async function getSSREntry(remoteEntryUrl, maxAgeMs, fetchTimeoutMs) {
317
- const cacheKey = makeUrlCacheKey(remoteEntryUrl, fetchTimeoutMs);
385
+ async function getSSREntry(remoteEntryUrl, maxAgeMs, fetchTimeoutMs, fetchMaxBytes) {
386
+ const cacheKey = makeUrlCacheKey(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes);
318
387
  const cached = ssrEntryCache.get(cacheKey);
319
- if (!cached) return setSsrEntryCache(remoteEntryUrl, fetchTimeoutMs).promise;
388
+ if (!cached) return setSsrEntryCache(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes).promise;
320
389
  if (!(typeof maxAgeMs === "number" && maxAgeMs >= 0 && Date.now() - cached.resolvedAt >= maxAgeMs)) return cached.promise;
321
390
  const previous = await cached.promise.catch(() => null);
322
- manifestFetchCache.delete(makeUrlCacheKey(getManifestUrl(remoteEntryUrl), fetchTimeoutMs));
323
- const record = setSsrEntryCache(remoteEntryUrl, fetchTimeoutMs);
391
+ manifestFetchCache.delete(makeUrlCacheKey(getManifestUrl(remoteEntryUrl), fetchTimeoutMs, fetchMaxBytes));
392
+ const record = setSsrEntryCache(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes);
324
393
  const next = await record.promise.catch(() => null);
325
394
  if (previous && next && previous.versionKey !== next.versionKey) dropRemoteCaches(remoteEntryUrl);
326
395
  return record.promise;
@@ -337,7 +406,7 @@ function dropRemoteCaches(remoteEntryUrl) {
337
406
  } catch {
338
407
  return;
339
408
  }
340
- for (const [key] of tempFileCache) if (JSON.parse(key)[2].startsWith(origin)) {
409
+ for (const [key] of tempFileCache) if (JSON.parse(key).find((part) => typeof part === "string" && /^https?:\/\//.test(part))?.startsWith(origin)) {
341
410
  tempFileCache.delete(key);
342
411
  tempFilePathCache.delete(key);
343
412
  }
@@ -429,9 +498,10 @@ function isVitePreloadHelperSpecifier(specifier) {
429
498
  * a remote redeploy (new manifest → new key) produces new files and bypasses
430
499
  * Node's ESM module cache instead of serving the stale build.
431
500
  */
432
- async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default") {
501
+ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES) {
433
502
  const cacheKey = JSON.stringify([
434
503
  fetchTimeoutMs,
504
+ fetchMaxBytes,
435
505
  versionKey,
436
506
  url,
437
507
  contextKey
@@ -440,7 +510,8 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
440
510
  const cached = tempFileCache.get(cacheKey);
441
511
  if (cached) {
442
512
  pending.add(cached);
443
- const tmpFile = await tempFilePathCache.get(cacheKey);
513
+ const reserved = tempFilePathCache.get(cacheKey);
514
+ const tmpFile = reserved ? await reserved : await cached;
444
515
  visited.set(url, tmpFile);
445
516
  return tmpFile;
446
517
  }
@@ -454,7 +525,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
454
525
  const tmpFile = await tmpFilePromise;
455
526
  visited.set(url, tmpFile);
456
527
  const res = await fetchWithTimeout(url, {}, fetchTimeoutMs);
457
- let code = await res.text();
528
+ let code = await readResponseTextBounded(res, fetchMaxBytes, url);
458
529
  if (!res.ok) throw new SsrEntryHttpError(url, res.status, res.statusText, getBodyPreview(code));
459
530
  const base = url.replace(/\/[^/]*$/, "/");
460
531
  const relImports = [];
@@ -463,7 +534,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
463
534
  while ((m = relRegex.exec(code)) !== null) if ((m[1].startsWith("./") || m[1].startsWith("../")) && !isVitePreloadHelperSpecifier(m[1])) relImports.push(new URL(m[1], base).href);
464
535
  const subMap = /* @__PURE__ */ new Map();
465
536
  await Promise.all([...new Set(relImports)].filter((u) => u.startsWith("http://") || u.startsWith("https://")).map(async (u) => {
466
- const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey);
537
+ const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes);
467
538
  subMap.set(u, `file://${tmpPath}`);
468
539
  }));
469
540
  code = transformSsrCode(code, base, sharedPkgMap);
@@ -480,9 +551,9 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
480
551
  });
481
552
  return promise;
482
553
  }
483
- async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default") {
554
+ async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES) {
484
555
  const pending = /* @__PURE__ */ new Set();
485
- const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey);
556
+ const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes);
486
557
  await Promise.all(pending);
487
558
  return rootFile;
488
559
  }
@@ -494,7 +565,7 @@ async function importTempModule(filePath, versionKey) {
494
565
  }
495
566
  let warnedVmUnavailable = false;
496
567
  async function tryVmStrategy(ssrEntry, options) {
497
- const { loadViaVmStrategy, isVmStrategyAvailable } = await import("./ssrVmStrategy-ChF8MB7l.js");
568
+ const { loadViaVmStrategy, isVmStrategyAvailable } = await import("./ssrVmStrategy-Dpw20xiw.js");
498
569
  if (!await isVmStrategyAvailable()) {
499
570
  if (!warnedVmUnavailable) {
500
571
  warnedVmUnavailable = true;
@@ -507,6 +578,7 @@ async function tryVmStrategy(ssrEntry, options) {
507
578
  shareScopeName: options.shareScopeName,
508
579
  versionKey: ssrEntry.versionKey,
509
580
  fetchTimeoutMs: options.fetchTimeoutMs,
581
+ fetchMaxBytes: options.fetchMaxBytes,
510
582
  cacheContext: options.cacheContext,
511
583
  federationInstance: options.federationInstance
512
584
  });
@@ -525,14 +597,15 @@ async function loadSSRRemoteEntry(ssrEntry, options) {
525
597
  const urlObj = new URL(url);
526
598
  if (urlObj.pathname.includes("/__mf_ssr__/")) {
527
599
  const remoteOrigin = urlObj.origin;
528
- const runner = await getOrCreateRunner(remoteOrigin, options.fetchTimeoutMs);
600
+ const runner = await getOrCreateRunner(remoteOrigin, options.fetchTimeoutMs, options.fetchMaxBytes);
529
601
  if (!runner) {
530
602
  if (process.env.NODE_ENV !== "production") return null;
531
603
  } else try {
532
604
  const mod = await runner.import(urlObj.pathname);
533
605
  if (mod && typeof mod === "object" && "init" in mod) return mod;
534
606
  if (process.env.NODE_ENV !== "production") return null;
535
- } catch {
607
+ } catch (error) {
608
+ if (isSsrFetchBodyTooLargeError(error)) throw error;
536
609
  if (process.env.NODE_ENV !== "production") return null;
537
610
  }
538
611
  }
@@ -540,16 +613,16 @@ async function loadSSRRemoteEntry(ssrEntry, options) {
540
613
  const fromVm = await tryVmStrategy(ssrEntry, options);
541
614
  if (fromVm) return fromVm;
542
615
  } catch (error) {
543
- if (isSsrEntryHttpError(error)) throw error;
616
+ if (isSsrEntryHttpError(error) || isSsrFetchBodyTooLargeError(error)) throw error;
544
617
  }
545
618
  const { mkdirSync } = await _fs();
546
619
  const cacheDir = await getSSRCacheDir();
547
620
  mkdirSync(cacheDir, { recursive: true });
548
621
  const sharedPkgMap = new Map(Object.entries(resolvedShared));
549
622
  try {
550
- return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName)), versionKey);
623
+ return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes), versionKey);
551
624
  } catch (error) {
552
- if (isSsrEntryHttpError(error)) throw error;
625
+ if (isSsrEntryHttpError(error) || isSsrFetchBodyTooLargeError(error)) throw error;
553
626
  return null;
554
627
  }
555
628
  }
@@ -569,6 +642,7 @@ function ssrEntryLoaderPlugin(options = {}) {
569
642
  shareScopeName: options.shareScopeName ?? "default",
570
643
  maxAgeMs: options.maxAgeMs,
571
644
  fetchTimeoutMs: options.fetchTimeoutMs ?? 1e4,
645
+ fetchMaxBytes: options.fetchMaxBytes ?? 10485760,
572
646
  cacheContext: {}
573
647
  };
574
648
  return {
@@ -580,7 +654,7 @@ function ssrEntryLoaderPlugin(options = {}) {
580
654
  cacheContext: origin,
581
655
  federationInstance: origin
582
656
  } : resolved;
583
- const ssrEntry = await getSSREntry(remoteInfo.entry, loadOptions.maxAgeMs, loadOptions.fetchTimeoutMs);
657
+ const ssrEntry = await getSSREntry(remoteInfo.entry, loadOptions.maxAgeMs, loadOptions.fetchTimeoutMs, loadOptions.fetchMaxBytes);
584
658
  if (!ssrEntry) return;
585
659
  const mod = await loadSSRRemoteEntry(ssrEntry, loadOptions);
586
660
  if (!mod) return;
@@ -589,4 +663,4 @@ function ssrEntryLoaderPlugin(options = {}) {
589
663
  };
590
664
  }
591
665
  //#endregion
592
- export { DEFAULT_SSR_FETCH_TIMEOUT_MS as a, ssrEntryLoaderPlugin as i, neutralizeBrowserPreloadHelpers as n, fetchWithTimeout as o, revalidate as r, SsrEntryHttpError as t };
666
+ export { DEFAULT_SSR_FETCH_MAX_BYTES as a, readResponseTextBounded as c, ssrEntryLoaderPlugin as i, neutralizeBrowserPreloadHelpers as n, DEFAULT_SSR_FETCH_TIMEOUT_MS as o, revalidate as r, fetchWithTimeout as s, SsrEntryHttpError as t };
@@ -1,4 +1,4 @@
1
- import { n as neutralizeBrowserPreloadHelpers, o as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-DgsCQOqq.js";
1
+ import { c as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, s as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-CuZVKlRm.js";
2
2
  //#region src/utils/ssrVmStrategy.ts
3
3
  /**
4
4
  * vm.SourceTextModule strategy for loading remote SSR entries.
@@ -104,9 +104,9 @@ function getVmCacheContextKey(options) {
104
104
  function getBodyPreview(body) {
105
105
  return body.slice(0, 240).replace(/\s+/g, " ").trim();
106
106
  }
107
- async function fetchModuleSource(url, fetchTimeoutMs) {
107
+ async function fetchModuleSource(url, fetchTimeoutMs, fetchMaxBytes) {
108
108
  const res = await fetchWithTimeout(url, {}, fetchTimeoutMs);
109
- const text = await res.text();
109
+ const text = await readResponseTextBounded(res, fetchMaxBytes ?? 10485760, url);
110
110
  if (!res.ok) throw new SsrEntryHttpError(url, res.status, res.statusText, getBodyPreview(text));
111
111
  return neutralizeBrowserPreloadHelpers(text);
112
112
  }
@@ -123,11 +123,12 @@ function getHttpModule(vm, url, options) {
123
123
  const cacheKey = JSON.stringify([
124
124
  getVmCacheContextKey(options),
125
125
  options.fetchTimeoutMs ?? 1e4,
126
+ options.fetchMaxBytes ?? 10485760,
126
127
  options.versionKey,
127
128
  url
128
129
  ]);
129
130
  if (!httpModuleCache.has(cacheKey)) httpModuleCache.set(cacheKey, (async () => {
130
- const code = await fetchModuleSource(url, options.fetchTimeoutMs);
131
+ const code = await fetchModuleSource(url, options.fetchTimeoutMs, options.fetchMaxBytes);
131
132
  return new vm.SourceTextModule(code, {
132
133
  identifier: url,
133
134
  initializeImportMeta(meta) {
@@ -105,6 +105,11 @@ interface SsrEntryLoaderOptions {
105
105
  * 10 seconds. Set to `0` to disable the timeout.
106
106
  */
107
107
  fetchTimeoutMs?: number;
108
+ /**
109
+ * Maximum response body size in bytes for each SSR network request. Defaults
110
+ * to 10 MiB. Set to `0` to disable the limit.
111
+ */
112
+ fetchMaxBytes?: number;
108
113
  }
109
114
  declare function ssrEntryLoaderPlugin(options?: SsrEntryLoaderOptions): {
110
115
  name: string;
@@ -1,2 +1,2 @@
1
- import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-DgsCQOqq.js";
1
+ import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-CuZVKlRm.js";
2
2
  export { SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.18.2",
3
+ "version": "1.19.0",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -86,4 +86,4 @@
86
86
  "vite": "8.1.0",
87
87
  "vitest": "4.0.18"
88
88
  }
89
- }
89
+ }