@4399ywkf/core 5.1.5 → 5.3.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/dist/index.js CHANGED
@@ -876,7 +876,7 @@ function createMockMiddleware(mockPath, options) {
876
876
  scanMockFiles(mockPath);
877
877
  return async (_req, _res, next) => {
878
878
  if (delay > 0) {
879
- await new Promise((resolve5) => setTimeout(resolve5, delay));
879
+ await new Promise((resolve6) => setTimeout(resolve6, delay));
880
880
  }
881
881
  next();
882
882
  };
@@ -1830,11 +1830,11 @@ var zustandPlugin = createPlugin((options = {}) => ({
1830
1830
  const hooks = {
1831
1831
  modifyRspackConfig(rspackConfig) {
1832
1832
  logger.info("Zustand \u5DF2\u542F\u7528");
1833
- const resolve5 = rspackConfig.resolve || {};
1834
- const alias = resolve5.alias || {};
1833
+ const resolve6 = rspackConfig.resolve || {};
1834
+ const alias = resolve6.alias || {};
1835
1835
  alias["@store"] = storePath;
1836
- resolve5.alias = alias;
1837
- rspackConfig.resolve = resolve5;
1836
+ resolve6.alias = alias;
1837
+ rspackConfig.resolve = resolve6;
1838
1838
  return rspackConfig;
1839
1839
  },
1840
1840
  generateFiles(_ctx) {
@@ -2175,7 +2175,9 @@ var defaultConfig = {
2175
2175
  tools: {},
2176
2176
  env: {
2177
2177
  publicEnvFile: "config/env/.env.public",
2178
- envDir: "config/env"
2178
+ envDir: "config/env",
2179
+ expose: [],
2180
+ exposePrefixes: ["YWKF_PUBLIC_", "NEXT_PUBLIC_"]
2179
2181
  },
2180
2182
  alias: {},
2181
2183
  plugins: []
@@ -2743,12 +2745,13 @@ function LazyRoute({
2743
2745
 
2744
2746
  import React, { lazy, Suspense } from "react";
2745
2747
  import { createBrowserRouter, type RouteObject } from "react-router";
2748
+ import { RouteErrorBoundary } from "@4399ywkf/core/runtime";
2749
+ ${errorImports.length > 0 ? `
2750
+ // \u540C\u6B65\u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6
2751
+ ${errorImports.join("\n")}` : ""}
2746
2752
 
2747
2753
  // \u61D2\u52A0\u8F7D\u9875\u9762\u7EC4\u4EF6
2748
2754
  ${lazyImports.join("\n")}
2749
- ${errorImports.length > 0 ? `
2750
- // \u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6
2751
- ${errorImports.join("\n")}` : ""}
2752
2755
  ${loadingImports.length > 0 ? `
2753
2756
  // \u52A0\u8F7D\u72B6\u6001\u7EC4\u4EF6
2754
2757
  ${loadingImports.join("\n")}` : ""}
@@ -2783,7 +2786,7 @@ export default routes;
2783
2786
  }
2784
2787
  if (route.errorFile) {
2785
2788
  errorImports.push(
2786
- `const ${name}Error = lazy(() => import("@/pages/${toImportPath(route.errorFile)}"));`
2789
+ `import ${name}Error from "@/pages/${toImportPath(route.errorFile)}";`
2787
2790
  );
2788
2791
  }
2789
2792
  if (route.loadingFile) {
@@ -2798,13 +2801,13 @@ export default routes;
2798
2801
  }
2799
2802
  }
2800
2803
  }
2801
- emitRouteArray(routes, parentPath) {
2802
- const items = routes.map((r) => this.emitRouteObject(r, parentPath));
2804
+ emitRouteArray(routes, parentPath, depth = 0) {
2805
+ const items = routes.map((r) => this.emitRouteObject(r, parentPath, depth));
2803
2806
  return `[
2804
2807
  ${items.join(",\n ")}
2805
2808
  ]`;
2806
2809
  }
2807
- emitRouteObject(route, parentPath) {
2810
+ emitRouteObject(route, parentPath, depth) {
2808
2811
  const parts = [];
2809
2812
  const name = route.name;
2810
2813
  if (route.index) {
@@ -2825,11 +2828,13 @@ export default routes;
2825
2828
  parts.push(`element: <LazyRoute Component={${name}Page} />`);
2826
2829
  }
2827
2830
  if (route.errorFile) {
2828
- parts.push(`errorElement: <LazyRoute Component={${name}Error} />`);
2831
+ parts.push(`errorElement: <RouteErrorBoundary fallback={<${name}Error />} />`);
2832
+ } else if (depth === 0) {
2833
+ parts.push(`errorElement: <RouteErrorBoundary />`);
2829
2834
  }
2830
2835
  if (route.children && route.children.length > 0) {
2831
2836
  const childParent = route.pathless ? parentPath : route.path;
2832
- parts.push(`children: ${this.emitRouteArray(route.children, childParent)}`);
2837
+ parts.push(`children: ${this.emitRouteArray(route.children, childParent, depth + 1)}`);
2833
2838
  }
2834
2839
  return `{
2835
2840
  ${parts.join(",\n ")}
@@ -3437,14 +3442,93 @@ var YwkfGeneratorPlugin = class {
3437
3442
  }
3438
3443
  };
3439
3444
 
3445
+ // src/rspack/client-env.ts
3446
+ import { existsSync as existsSync12, readFileSync as readFileSync4 } from "fs";
3447
+ import { resolve as resolve3 } from "path";
3448
+ import dotenv from "dotenv";
3449
+ var FRAMEWORK_ENV_KEYS = [
3450
+ "APP_CNAME",
3451
+ "APP_HOST",
3452
+ "APP_MODE",
3453
+ "APP_NAME",
3454
+ "APP_PORT",
3455
+ "DEBUG",
3456
+ "NODE_ENV",
3457
+ "OUTPUT_PATH",
3458
+ "PUBLIC_PATH",
3459
+ "YWKF_BUILD_ID",
3460
+ "YWKF_PREFIX_CLS"
3461
+ ];
3462
+ var DEFAULT_EXPOSE_PREFIXES = ["YWKF_PUBLIC_", "NEXT_PUBLIC_"];
3463
+ var SECRET_KEY_PATTERN = /TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE|AUTH|ACCESS_KEY|API_KEY|SIGNING_KEY/i;
3464
+ var SECRET_VALUE_PATTERN = /^(sntry[su]_|sk-|ghp_|gho_|ghu_|ghs_|github_pat_|glpat-|xox[abprs]-|AKIA|ya29\.)/;
3465
+ function readEnvFileKeys(filePath) {
3466
+ if (!existsSync12(filePath)) return [];
3467
+ try {
3468
+ return Object.keys(dotenv.parse(readFileSync4(filePath, "utf-8")));
3469
+ } catch {
3470
+ return [];
3471
+ }
3472
+ }
3473
+ function collectClientEnvKeys(options) {
3474
+ const { cwd, env = {}, mode = process.env.APP_MODE || "development" } = options;
3475
+ const keys = new Set(FRAMEWORK_ENV_KEYS);
3476
+ const files = [
3477
+ env.publicEnvFile ?? "config/env/.env.public",
3478
+ ".env.public",
3479
+ ".env",
3480
+ `${env.envDir ?? "config/env"}/.env.${mode}`,
3481
+ `.env.${mode}`
3482
+ ];
3483
+ for (const file of files) {
3484
+ for (const key of readEnvFileKeys(resolve3(cwd, file))) keys.add(key);
3485
+ }
3486
+ for (const key of env.expose ?? []) keys.add(key);
3487
+ return keys;
3488
+ }
3489
+ function buildClientEnv(options) {
3490
+ const { cwd, env = {}, mode, processEnv = process.env } = options;
3491
+ const prefixes = env.exposePrefixes ?? DEFAULT_EXPOSE_PREFIXES;
3492
+ const allowed = collectClientEnvKeys({ cwd, env, mode });
3493
+ const result = {};
3494
+ const dropped = [];
3495
+ for (const [key, value] of Object.entries(processEnv)) {
3496
+ if (value === void 0) continue;
3497
+ const isAllowed = allowed.has(key) || prefixes.some((prefix) => key.startsWith(prefix));
3498
+ if (!isAllowed) continue;
3499
+ if (SECRET_KEY_PATTERN.test(key) || SECRET_VALUE_PATTERN.test(value)) {
3500
+ dropped.push(key);
3501
+ continue;
3502
+ }
3503
+ result[key] = value;
3504
+ }
3505
+ return { env: result, dropped };
3506
+ }
3507
+
3440
3508
  // src/rspack/base.ts
3441
3509
  var require2 = createRequire(import.meta.url);
3442
3510
  var __dirname = dirname(fileURLToPath(import.meta.url));
3443
3511
  var coreNodeModules = join12(__dirname, "../../node_modules");
3512
+ function resolveYwkfBuildId(appName) {
3513
+ const releaseId = [
3514
+ process.env.YWKF_BUILD_ID,
3515
+ process.env.CI_COMMIT_SHA,
3516
+ process.env.GITHUB_SHA,
3517
+ process.env.GIT_COMMIT
3518
+ ].find((value) => value?.trim())?.trim();
3519
+ return `${appName}:${releaseId || Date.now().toString(36)}`;
3520
+ }
3444
3521
  function createBaseConfig(config, cwd, options = {}) {
3445
3522
  const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
3446
3523
  const { resolveApp } = createPathResolver(cwd);
3447
3524
  const { appName, appCName, output, html, alias: userAlias, microFrontend, router, reactCompiler } = config;
3525
+ const buildId = resolveYwkfBuildId(appName);
3526
+ const { env: clientEnv, dropped: droppedEnvKeys } = buildClientEnv({ cwd, env: config.env });
3527
+ if (droppedEnvKeys.length > 0) {
3528
+ console.warn(
3529
+ `[ywkf] \u4EE5\u4E0B\u73AF\u5883\u53D8\u91CF\u7591\u4F3C\u5BC6\u94A5,\u5DF2\u4ECE\u5BA2\u6237\u7AEF\u4EA7\u7269\u4E2D\u5254\u9664:${droppedEnvKeys.join(", ")}\u3002\u5982\u786E\u4E3A\u516C\u5F00\u914D\u7F6E,\u8BF7\u91CD\u547D\u540D\u6216\u8C03\u6574 env.expose\u3002`
3530
+ );
3531
+ }
3448
3532
  const ywkfOutputDir = resolveApp(".ywkf");
3449
3533
  const defaultAlias = {
3450
3534
  "@": resolveApp("src"),
@@ -3568,7 +3652,11 @@ function createBaseConfig(config, cwd, options = {}) {
3568
3652
  Buffer: ["buffer", "Buffer"]
3569
3653
  }),
3570
3654
  new rspack2.DefinePlugin({
3571
- "process.env": JSON.stringify(process.env)
3655
+ "globalThis.__YWKF_BUILD_ID__": JSON.stringify(buildId),
3656
+ // 只内联白名单内的公开配置。整包 process.env 会把构建机环境(CI 凭据、内网地址)
3657
+ // 写进公网可下载的产物,见 client-env.ts。外层括号不可省:该值以 `{` 开头,
3658
+ // `() => process.env.X` 会被替换成 `() => {…}` 而当成函数块体,导致整条 chunk 语法失败。
3659
+ "process.env": `(${JSON.stringify(clientEnv)})`
3572
3660
  }),
3573
3661
  new rspack2.HtmlRspackPlugin({
3574
3662
  template: resolveApp(html.template ?? "public/index.html"),
@@ -4086,28 +4174,28 @@ function createRspackConfig(config, cwd, options = {}) {
4086
4174
  }
4087
4175
 
4088
4176
  // src/cli/env.ts
4089
- import { existsSync as existsSync12, readFileSync as readFileSync4 } from "fs";
4090
- import { resolve as resolve3 } from "path";
4091
- import dotenv from "dotenv";
4177
+ import { existsSync as existsSync13, readFileSync as readFileSync5 } from "fs";
4178
+ import { resolve as resolve4 } from "path";
4179
+ import dotenv2 from "dotenv";
4092
4180
  function preloadEnv(cwd, mode, nodeEnv) {
4093
4181
  process.env.NODE_ENV = nodeEnv;
4094
4182
  process.env.APP_MODE = mode;
4095
4183
  const defaultPaths = [
4096
- resolve3(cwd, "config/env/.env.public"),
4097
- resolve3(cwd, ".env.public"),
4098
- resolve3(cwd, ".env")
4184
+ resolve4(cwd, "config/env/.env.public"),
4185
+ resolve4(cwd, ".env.public"),
4186
+ resolve4(cwd, ".env")
4099
4187
  ];
4100
4188
  for (const envPath of defaultPaths) {
4101
- if (existsSync12(envPath)) {
4102
- dotenv.config({ path: envPath });
4189
+ if (existsSync13(envPath)) {
4190
+ dotenv2.config({ path: envPath });
4103
4191
  break;
4104
4192
  }
4105
4193
  }
4106
- const modeEnvPaths = [resolve3(cwd, `config/env/.env.${mode}`), resolve3(cwd, `.env.${mode}`)];
4194
+ const modeEnvPaths = [resolve4(cwd, `config/env/.env.${mode}`), resolve4(cwd, `.env.${mode}`)];
4107
4195
  for (const envPath of modeEnvPaths) {
4108
- if (existsSync12(envPath)) {
4109
- const envContent = readFileSync4(envPath, "utf-8");
4110
- const parsed = dotenv.parse(envContent);
4196
+ if (existsSync13(envPath)) {
4197
+ const envContent = readFileSync5(envPath, "utf-8");
4198
+ const parsed = dotenv2.parse(envContent);
4111
4199
  for (const key in parsed) {
4112
4200
  process.env[key] = parsed[key];
4113
4201
  }
@@ -4119,14 +4207,14 @@ function preloadEnv(cwd, mode, nodeEnv) {
4119
4207
  }
4120
4208
  function loadEnv(config, cwd, mode, nodeEnv) {
4121
4209
  const { env } = config;
4122
- const publicEnvPath = resolve3(cwd, env.publicEnvFile ?? "config/env/.env.public");
4123
- if (existsSync12(publicEnvPath)) {
4124
- dotenv.config({ path: publicEnvPath });
4125
- }
4126
- const modeEnvPath = resolve3(cwd, env.envDir ?? "config/env", `.env.${mode}`);
4127
- if (existsSync12(modeEnvPath)) {
4128
- const envContent = readFileSync4(modeEnvPath, "utf-8");
4129
- const parsed = dotenv.parse(envContent);
4210
+ const publicEnvPath = resolve4(cwd, env.publicEnvFile ?? "config/env/.env.public");
4211
+ if (existsSync13(publicEnvPath)) {
4212
+ dotenv2.config({ path: publicEnvPath });
4213
+ }
4214
+ const modeEnvPath = resolve4(cwd, env.envDir ?? "config/env", `.env.${mode}`);
4215
+ if (existsSync13(modeEnvPath)) {
4216
+ const envContent = readFileSync5(modeEnvPath, "utf-8");
4217
+ const parsed = dotenv2.parse(envContent);
4130
4218
  for (const key in parsed) {
4131
4219
  process.env[key] = parsed[key];
4132
4220
  }
@@ -4404,7 +4492,7 @@ async function build(options = {}) {
4404
4492
  rspackConfig.stats = "none";
4405
4493
  rspackConfig.infrastructureLogging = { level: "none" };
4406
4494
  const compiler = rspack4(rspackConfig);
4407
- const stats = await new Promise((resolve5, reject) => {
4495
+ const stats = await new Promise((resolve6, reject) => {
4408
4496
  compiler.run((err, stats2) => {
4409
4497
  if (err) {
4410
4498
  reject(err);
@@ -4414,13 +4502,13 @@ async function build(options = {}) {
4414
4502
  reject(new Error("Build failed: no stats available"));
4415
4503
  return;
4416
4504
  }
4417
- resolve5(stats2);
4505
+ resolve6(stats2);
4418
4506
  });
4419
4507
  });
4420
- await new Promise((resolve5, reject) => {
4508
+ await new Promise((resolve6, reject) => {
4421
4509
  compiler.close((err) => {
4422
4510
  if (err) reject(err);
4423
- else resolve5();
4511
+ else resolve6();
4424
4512
  });
4425
4513
  });
4426
4514
  const hasErrors = stats.hasErrors();
@@ -4462,7 +4550,7 @@ var PortInUseError = class extends Error {
4462
4550
  }
4463
4551
  };
4464
4552
  function ensurePortAvailable(port, host) {
4465
- return new Promise((resolve5, reject) => {
4553
+ return new Promise((resolve6, reject) => {
4466
4554
  const server = net.createServer();
4467
4555
  server.once("error", (err) => {
4468
4556
  if (err.code === "EADDRINUSE") {
@@ -4472,7 +4560,7 @@ function ensurePortAvailable(port, host) {
4472
4560
  }
4473
4561
  });
4474
4562
  server.once("listening", () => {
4475
- server.close(() => resolve5());
4563
+ server.close(() => resolve6());
4476
4564
  });
4477
4565
  server.listen(port, host);
4478
4566
  });
@@ -4520,8 +4608,8 @@ function parseWindowsNetstatPid(stdout, port) {
4520
4608
  }
4521
4609
 
4522
4610
  // src/cli/restart-watcher.ts
4523
- import { existsSync as existsSync13, watch as watch2 } from "fs";
4524
- import { resolve as resolve4 } from "path";
4611
+ import { existsSync as existsSync14, watch as watch2 } from "fs";
4612
+ import { resolve as resolve5 } from "path";
4525
4613
  function isTriggerFile(filename, mode) {
4526
4614
  if (!filename) return false;
4527
4615
  if (CONFIG_FILES.includes(filename)) return true;
@@ -4551,8 +4639,8 @@ function watchForRestart(opts) {
4551
4639
  }, debounceMs);
4552
4640
  };
4553
4641
  watchers.push(watch2(cwd, { recursive: false }, (_event, filename) => trigger(filename)));
4554
- const envDir = resolve4(cwd, "config/env");
4555
- if (existsSync13(envDir)) {
4642
+ const envDir = resolve5(cwd, "config/env");
4643
+ if (existsSync14(envDir)) {
4556
4644
  watchers.push(watch2(envDir, { recursive: false }, (_event, filename) => trigger(filename)));
4557
4645
  }
4558
4646
  return {
@@ -4659,10 +4747,10 @@ async function startDevServer(cwd, config) {
4659
4747
  if (closed) return;
4660
4748
  closed = true;
4661
4749
  await server.stop();
4662
- await new Promise((resolve5, reject) => {
4750
+ await new Promise((resolve6, reject) => {
4663
4751
  compiler.close((err) => {
4664
4752
  if (err) reject(err);
4665
- else resolve5();
4753
+ else resolve6();
4666
4754
  });
4667
4755
  });
4668
4756
  };
@@ -4757,7 +4845,7 @@ async function dev(options = {}) {
4757
4845
  }
4758
4846
 
4759
4847
  // src/router/plugin.ts
4760
- import { existsSync as existsSync14, watch as watch3 } from "fs";
4848
+ import { existsSync as existsSync15, watch as watch3 } from "fs";
4761
4849
  import { join as join14 } from "path";
4762
4850
  var ConventionalRoutePlugin = class {
4763
4851
  options;
@@ -4813,7 +4901,7 @@ var ConventionalRoutePlugin = class {
4813
4901
  * 监听页面目录变化
4814
4902
  */
4815
4903
  watchPages(compiler) {
4816
- if (!existsSync14(this.options.pagesDir)) {
4904
+ if (!existsSync15(this.options.pagesDir)) {
4817
4905
  return;
4818
4906
  }
4819
4907
  this.isWatching = true;
@@ -4840,6 +4928,370 @@ var ConventionalRoutePlugin = class {
4840
4928
  }
4841
4929
  };
4842
4930
 
4931
+ // src/runtime/chunk-recovery.ts
4932
+ var DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS = 3e4;
4933
+ var CHUNK_RECOVERY_STORAGE_KEY = "@4399ywkf/core:chunk-recovery-attempt";
4934
+ var CHUNK_RECOVERY_CACHE_BUST_PARAM = "__ywkf_chunk_retry";
4935
+ var CHUNK_RECOVERY_ACTIVE_SYMBOL = /* @__PURE__ */ Symbol.for(
4936
+ "@4399ywkf/core/chunk-recovery-active"
4937
+ );
4938
+ var CHUNK_RECOVERY_REGISTRY_SYMBOL = /* @__PURE__ */ Symbol.for(
4939
+ "@4399ywkf/core/chunk-recovery-registry"
4940
+ );
4941
+ var CHUNK_LOAD_ERROR_PATTERNS = [
4942
+ /\bChunkLoadError\b/i,
4943
+ /\bLoading (?:CSS )?chunk\s+\S+\s+failed\b/i,
4944
+ /\bFailed to fetch dynamically imported module\b/i,
4945
+ /\bImporting a module script failed\b/i,
4946
+ /\berror loading dynamically imported module\b/i,
4947
+ /\bUnable to preload CSS for\b/i,
4948
+ /\bFailed to load module script\b/i
4949
+ ];
4950
+ function getErrorLikeValue(error, key) {
4951
+ if ((typeof error !== "object" || error === null) && typeof error !== "function") {
4952
+ return void 0;
4953
+ }
4954
+ try {
4955
+ return error[key];
4956
+ } catch {
4957
+ return void 0;
4958
+ }
4959
+ }
4960
+ function matchesChunkLoadError(error, depth) {
4961
+ if (depth > 3) {
4962
+ return false;
4963
+ }
4964
+ if (typeof error === "string") {
4965
+ return CHUNK_LOAD_ERROR_PATTERNS.some((pattern) => pattern.test(error));
4966
+ }
4967
+ const name = getErrorLikeValue(error, "name");
4968
+ const message = getErrorLikeValue(error, "message");
4969
+ const code = getErrorLikeValue(error, "code");
4970
+ const signature = [name, message].filter((value) => typeof value === "string").join(": ");
4971
+ if (code === "CSS_CHUNK_LOAD_FAILED" || CHUNK_LOAD_ERROR_PATTERNS.some((pattern) => pattern.test(signature))) {
4972
+ return true;
4973
+ }
4974
+ const cause = getErrorLikeValue(error, "cause");
4975
+ return cause !== void 0 && cause !== error && matchesChunkLoadError(cause, depth + 1);
4976
+ }
4977
+ function isChunkLoadError(error) {
4978
+ return matchesChunkLoadError(error, 0);
4979
+ }
4980
+ function hashRecoveryIdentity(value) {
4981
+ let hash = 2166136261;
4982
+ for (let index = 0; index < value.length; index += 1) {
4983
+ hash ^= value.charCodeAt(index);
4984
+ hash = Math.imul(hash, 16777619);
4985
+ }
4986
+ return (hash >>> 0).toString(36);
4987
+ }
4988
+ function getFrameworkBuildId() {
4989
+ const buildId = globalThis.__YWKF_BUILD_ID__;
4990
+ return typeof buildId === "string" ? buildId.trim() : "";
4991
+ }
4992
+ function resolveRecoveryId(error, config) {
4993
+ const configuredBuildId = typeof config === "object" && typeof config.buildId === "string" ? config.buildId.trim() : "";
4994
+ const buildId = configuredBuildId || getFrameworkBuildId();
4995
+ if (buildId) {
4996
+ return `build-${hashRecoveryIdentity(buildId)}`;
4997
+ }
4998
+ const request = getErrorLikeValue(error, "request");
4999
+ const name = getErrorLikeValue(error, "name");
5000
+ const message = getErrorLikeValue(error, "message");
5001
+ const signature = [request, name, message].filter((value) => typeof value === "string").join(": ");
5002
+ return `error-${hashRecoveryIdentity(signature || String(error))}`;
5003
+ }
5004
+ function toError(error, fallbackMessage = "Chunk loading failed") {
5005
+ if (error instanceof Error) {
5006
+ return error;
5007
+ }
5008
+ if (typeof error === "string") {
5009
+ return new Error(error);
5010
+ }
5011
+ const message = getErrorLikeValue(error, "message");
5012
+ const name = getErrorLikeValue(error, "name");
5013
+ const normalizedError = new Error(typeof message === "string" ? message : fallbackMessage);
5014
+ if (typeof name === "string") {
5015
+ normalizedError.name = name;
5016
+ }
5017
+ return normalizedError;
5018
+ }
5019
+ function normalizeCooldownMs(value) {
5020
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 1e3) {
5021
+ return DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS;
5022
+ }
5023
+ return value;
5024
+ }
5025
+ function resolveChunkRecoveryPolicy(config, context) {
5026
+ const options = typeof config === "object" ? config : void 0;
5027
+ const configuredEnabled = typeof config === "boolean" ? config : options?.enabled;
5028
+ const enabledForEnvironment = configuredEnabled ?? context.isProduction;
5029
+ if (!enabledForEnvironment) {
5030
+ return {
5031
+ enabled: false,
5032
+ disabledReason: configuredEnabled === false ? "configuration" : "environment",
5033
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
5034
+ globalHandlers: options?.globalHandlers === true
5035
+ };
5036
+ }
5037
+ if (context.isMicroApp && options?.allowInMicroApp !== true) {
5038
+ return {
5039
+ enabled: false,
5040
+ disabledReason: "micro-app",
5041
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
5042
+ globalHandlers: options?.globalHandlers === true
5043
+ };
5044
+ }
5045
+ return {
5046
+ enabled: true,
5047
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
5048
+ globalHandlers: options?.globalHandlers === true
5049
+ };
5050
+ }
5051
+ function parseStoredChunkRecoveryState(value) {
5052
+ if (value === null) {
5053
+ return { attempts: [] };
5054
+ }
5055
+ const parsed = JSON.parse(value);
5056
+ if (!Array.isArray(parsed.attempts)) {
5057
+ throw new Error("Invalid chunk recovery state");
5058
+ }
5059
+ const attempts = parsed.attempts.map((attempt) => {
5060
+ if (typeof attempt !== "object" || attempt === null || typeof attempt.recoveryId !== "string" || typeof attempt.attemptedAt !== "number" || !Number.isFinite(attempt.attemptedAt)) {
5061
+ throw new Error("Invalid chunk recovery attempt");
5062
+ }
5063
+ return attempt;
5064
+ });
5065
+ if (parsed.lastAttemptAt !== void 0 && (typeof parsed.lastAttemptAt !== "number" || !Number.isFinite(parsed.lastAttemptAt))) {
5066
+ throw new Error("Invalid chunk recovery timestamp");
5067
+ }
5068
+ return { attempts, lastAttemptAt: parsed.lastAttemptAt };
5069
+ }
5070
+ function claimChunkRecoveryAttempt(storage, options) {
5071
+ const {
5072
+ cooldownMs = DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS,
5073
+ now,
5074
+ recoveryId,
5075
+ storageKey = CHUNK_RECOVERY_STORAGE_KEY
5076
+ } = options;
5077
+ let state;
5078
+ try {
5079
+ state = parseStoredChunkRecoveryState(storage.getItem(storageKey));
5080
+ } catch {
5081
+ return { claimed: false, reason: "storage-unavailable" };
5082
+ }
5083
+ const existingAttempt = state.attempts.find((attempt) => attempt.recoveryId === recoveryId);
5084
+ if (existingAttempt) {
5085
+ return {
5086
+ claimed: false,
5087
+ reason: "already-attempted",
5088
+ attemptedAt: existingAttempt.attemptedAt
5089
+ };
5090
+ }
5091
+ const inCooldown = state.lastAttemptAt !== void 0 && (state.lastAttemptAt > now || now - state.lastAttemptAt < cooldownMs);
5092
+ if (inCooldown) {
5093
+ return { claimed: false, reason: "cooldown", attemptedAt: state.lastAttemptAt };
5094
+ }
5095
+ try {
5096
+ storage.setItem(
5097
+ storageKey,
5098
+ JSON.stringify({
5099
+ attempts: [...state.attempts, { attemptedAt: now, recoveryId }],
5100
+ lastAttemptAt: now
5101
+ })
5102
+ );
5103
+ } catch {
5104
+ return { claimed: false, reason: "storage-unavailable" };
5105
+ }
5106
+ return { claimed: true, attemptedAt: now };
5107
+ }
5108
+ function isMicroAppRuntime() {
5109
+ if (typeof window === "undefined") {
5110
+ return false;
5111
+ }
5112
+ const runtimeWindow = window;
5113
+ return Boolean(runtimeWindow.__POWERED_BY_QIANKUN__ || runtimeWindow.__GARFISH__);
5114
+ }
5115
+ function getSessionStorage() {
5116
+ if (typeof window === "undefined") {
5117
+ return null;
5118
+ }
5119
+ try {
5120
+ return window.sessionStorage;
5121
+ } catch {
5122
+ return null;
5123
+ }
5124
+ }
5125
+ function invokeSafely(callback, label) {
5126
+ if (!callback) {
5127
+ return;
5128
+ }
5129
+ try {
5130
+ callback();
5131
+ } catch (error) {
5132
+ console.error(`[ywkf] ${label} \u6267\u884C\u5931\u8D25:`, error);
5133
+ }
5134
+ }
5135
+ function schedulePageReplace(timestamp) {
5136
+ setTimeout(() => {
5137
+ try {
5138
+ const reloadUrl = new URL(window.location.href);
5139
+ reloadUrl.searchParams.set(CHUNK_RECOVERY_CACHE_BUST_PARAM, String(timestamp));
5140
+ window.location.replace(reloadUrl.toString());
5141
+ } catch (error) {
5142
+ console.error("[ywkf] chunk \u6062\u590D\u5237\u65B0\u5931\u8D25:", error);
5143
+ }
5144
+ }, 0);
5145
+ }
5146
+ function clearChunkRecoveryCacheBustParam() {
5147
+ if (typeof window === "undefined") {
5148
+ return;
5149
+ }
5150
+ try {
5151
+ const currentUrl = new URL(window.location.href);
5152
+ if (!currentUrl.searchParams.has(CHUNK_RECOVERY_CACHE_BUST_PARAM)) {
5153
+ return;
5154
+ }
5155
+ currentUrl.searchParams.delete(CHUNK_RECOVERY_CACHE_BUST_PARAM);
5156
+ window.history.replaceState(window.history.state, "", currentUrl.toString());
5157
+ } catch (error) {
5158
+ console.error("[ywkf] \u6E05\u7406 chunk \u6062\u590D\u53C2\u6570\u5931\u8D25:", error);
5159
+ }
5160
+ }
5161
+ function handleChunkLoadErrorInternal(reason, options, trustedChunkEvent) {
5162
+ if (!trustedChunkEvent && !isChunkLoadError(reason)) {
5163
+ return null;
5164
+ }
5165
+ const error = toError(reason);
5166
+ const timestamp = Date.now();
5167
+ const recoveryId = resolveRecoveryId(reason, options.config);
5168
+ const isMicroApp = options.isMicroApp ?? isMicroAppRuntime();
5169
+ const policy = resolveChunkRecoveryPolicy(options.config, {
5170
+ isProduction: process.env.NODE_ENV === "production",
5171
+ isMicroApp
5172
+ });
5173
+ let outcome;
5174
+ if (!policy.enabled) {
5175
+ outcome = policy.disabledReason === "micro-app" ? "reload-skipped-micro-app" : "reload-skipped-disabled";
5176
+ } else {
5177
+ const storage = getSessionStorage();
5178
+ const claim = storage ? claimChunkRecoveryAttempt(storage, {
5179
+ cooldownMs: policy.cooldownMs,
5180
+ now: timestamp,
5181
+ recoveryId
5182
+ }) : { claimed: false, reason: "storage-unavailable" };
5183
+ if (claim.claimed) {
5184
+ outcome = "reload-scheduled";
5185
+ } else if (claim.reason === "already-attempted") {
5186
+ outcome = "reload-skipped-already-attempted";
5187
+ } else if (claim.reason === "cooldown") {
5188
+ outcome = "reload-skipped-cooldown";
5189
+ } else {
5190
+ outcome = "reload-skipped-storage-unavailable";
5191
+ }
5192
+ }
5193
+ const event = {
5194
+ error,
5195
+ source: options.source,
5196
+ outcome,
5197
+ timestamp,
5198
+ cooldownMs: policy.cooldownMs,
5199
+ recoveryId
5200
+ };
5201
+ invokeSafely(() => options.onError?.(error), "onError");
5202
+ invokeSafely(() => options.onRecoveryEvent?.(event), "onRecoveryEvent");
5203
+ if (outcome === "reload-scheduled") {
5204
+ schedulePageReplace(timestamp);
5205
+ }
5206
+ return event;
5207
+ }
5208
+ function handleChunkLoadError(reason, options) {
5209
+ return handleChunkLoadErrorInternal(reason, options, false);
5210
+ }
5211
+ function getLatestSubscriber(registry) {
5212
+ let latest;
5213
+ for (const subscriber of registry.subscribers.values()) {
5214
+ latest = subscriber;
5215
+ }
5216
+ return latest;
5217
+ }
5218
+ function createGlobalChunkRecoveryRegistry() {
5219
+ const registry = {
5220
+ subscribers: /* @__PURE__ */ new Map()
5221
+ };
5222
+ registry.handleUnhandledRejection = (event) => {
5223
+ const subscriber = getLatestSubscriber(registry);
5224
+ if (!subscriber) return;
5225
+ const recoveryEvent = handleChunkLoadError(event.reason, {
5226
+ ...subscriber,
5227
+ source: "unhandled-rejection"
5228
+ });
5229
+ if (recoveryEvent?.outcome === "reload-scheduled") {
5230
+ event.preventDefault();
5231
+ }
5232
+ };
5233
+ registry.handleVitePreloadError = (event) => {
5234
+ const subscriber = getLatestSubscriber(registry);
5235
+ if (!subscriber) return;
5236
+ const payload = event.payload;
5237
+ const recoveryEvent = handleChunkLoadErrorInternal(
5238
+ payload,
5239
+ {
5240
+ ...subscriber,
5241
+ source: "vite-preload-error"
5242
+ },
5243
+ true
5244
+ );
5245
+ if (recoveryEvent?.outcome === "reload-scheduled") {
5246
+ event.preventDefault();
5247
+ }
5248
+ };
5249
+ return registry;
5250
+ }
5251
+ function installGlobalChunkRecoveryHandlers(options) {
5252
+ if (typeof window === "undefined") {
5253
+ return () => {
5254
+ };
5255
+ }
5256
+ const isMicroApp = options.isMicroApp ?? isMicroAppRuntime();
5257
+ const policy = resolveChunkRecoveryPolicy(options.config, {
5258
+ isProduction: process.env.NODE_ENV === "production",
5259
+ isMicroApp
5260
+ });
5261
+ if (!policy.enabled || !policy.globalHandlers) {
5262
+ return () => {
5263
+ };
5264
+ }
5265
+ const runtimeWindow = window;
5266
+ let registry = runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL];
5267
+ if (!registry && runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL] === true) {
5268
+ return () => {
5269
+ };
5270
+ }
5271
+ if (!registry) {
5272
+ registry = createGlobalChunkRecoveryRegistry();
5273
+ runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL] = registry;
5274
+ runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL] = true;
5275
+ window.addEventListener("unhandledrejection", registry.handleUnhandledRejection);
5276
+ window.addEventListener("vite:preloadError", registry.handleVitePreloadError);
5277
+ }
5278
+ const subscriberId = /* @__PURE__ */ Symbol("chunk-recovery-subscriber");
5279
+ registry.subscribers.set(subscriberId, { ...options, isMicroApp });
5280
+ let disposed = false;
5281
+ return () => {
5282
+ if (disposed) return;
5283
+ disposed = true;
5284
+ registry.subscribers.delete(subscriberId);
5285
+ if (registry.subscribers.size > 0) return;
5286
+ window.removeEventListener("unhandledrejection", registry.handleUnhandledRejection);
5287
+ window.removeEventListener("vite:preloadError", registry.handleVitePreloadError);
5288
+ if (runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL] === registry) {
5289
+ delete runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL];
5290
+ delete runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL];
5291
+ }
5292
+ };
5293
+ }
5294
+
4843
5295
  // src/runtime/bootstrap.tsx
4844
5296
  import { createRoot } from "react-dom/client";
4845
5297
 
@@ -4891,8 +5343,23 @@ function useIsDev() {
4891
5343
  }
4892
5344
 
4893
5345
  // src/runtime/error-boundary.tsx
4894
- import { Component } from "react";
5346
+ import {
5347
+ Component,
5348
+ createContext as createContext2,
5349
+ useContext as useContext2,
5350
+ useEffect,
5351
+ useMemo,
5352
+ useRef
5353
+ } from "react";
5354
+ import { isRouteErrorResponse, useRouteError } from "react-router";
4895
5355
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
5356
+ var ChunkRecoveryRuntimeContext = createContext2(null);
5357
+ function ChunkRecoveryRuntimeProvider({
5358
+ children,
5359
+ ...value
5360
+ }) {
5361
+ return /* @__PURE__ */ jsx2(ChunkRecoveryRuntimeContext.Provider, { value, children });
5362
+ }
4896
5363
  var ErrorBoundary = class extends Component {
4897
5364
  constructor(props) {
4898
5365
  super(props);
@@ -4902,15 +5369,31 @@ var ErrorBoundary = class extends Component {
4902
5369
  return { hasError: true, error };
4903
5370
  }
4904
5371
  componentDidCatch(error, errorInfo) {
4905
- console.error("[ErrorBoundary] Caught error:", error, errorInfo);
4906
- this.props.onError?.(error, errorInfo);
5372
+ const reportError = () => {
5373
+ console.error("[ErrorBoundary] Caught error:", error, errorInfo);
5374
+ this.props.onError?.(error, errorInfo);
5375
+ };
5376
+ if (this.props.chunkRecovery === void 0) {
5377
+ reportError();
5378
+ return;
5379
+ }
5380
+ const recoveryEvent = handleChunkLoadError(error, {
5381
+ source: "error-boundary",
5382
+ config: this.props.chunkRecovery,
5383
+ isMicroApp: this.props.isMicroApp,
5384
+ onError: reportError,
5385
+ onRecoveryEvent: this.props.onRecoveryEvent
5386
+ });
5387
+ if (!recoveryEvent) {
5388
+ reportError();
5389
+ }
4907
5390
  }
4908
5391
  reset = () => {
4909
5392
  this.setState({ hasError: false, error: null });
4910
5393
  };
4911
5394
  render() {
4912
5395
  const { hasError, error } = this.state;
4913
- const { children, fallback } = this.props;
5396
+ const { children, chunkRecovery, fallback, isMicroApp } = this.props;
4914
5397
  if (hasError && error) {
4915
5398
  if (typeof fallback === "function") {
4916
5399
  return fallback(error, this.reset);
@@ -4918,12 +5401,86 @@ var ErrorBoundary = class extends Component {
4918
5401
  if (fallback) {
4919
5402
  return fallback;
4920
5403
  }
4921
- return /* @__PURE__ */ jsx2(DefaultErrorFallback, { error, onReset: this.reset });
5404
+ const resolvedIsMicroApp = isMicroApp ?? isMicroAppRuntime();
5405
+ const chunkRecoveryEnabled = chunkRecovery !== void 0 && resolveChunkRecoveryPolicy(chunkRecovery, {
5406
+ isProduction: process.env.NODE_ENV === "production",
5407
+ isMicroApp: resolvedIsMicroApp
5408
+ }).enabled;
5409
+ const canReloadPage = !resolvedIsMicroApp || typeof chunkRecovery === "object" && chunkRecovery.allowInMicroApp === true;
5410
+ return /* @__PURE__ */ jsx2(
5411
+ DefaultErrorFallback,
5412
+ {
5413
+ canReloadPage,
5414
+ error,
5415
+ isChunkError: chunkRecoveryEnabled && isChunkLoadError(error),
5416
+ onReset: this.reset
5417
+ }
5418
+ );
4922
5419
  }
4923
5420
  return children;
4924
5421
  }
4925
5422
  };
4926
- function DefaultErrorFallback({ error, onReset }) {
5423
+ var UNHANDLED_ROUTE_ERROR = /* @__PURE__ */ Symbol("unhandled-route-error");
5424
+ function normalizeRouteError(reason) {
5425
+ if (reason instanceof Error) return reason;
5426
+ if (typeof reason === "string") return new Error(reason);
5427
+ if (isRouteErrorResponse(reason)) {
5428
+ const message = [reason.status, reason.statusText].filter(Boolean).join(" ");
5429
+ const error = new Error(message || "Route request failed");
5430
+ error.name = "RouteErrorResponse";
5431
+ return error;
5432
+ }
5433
+ return new Error("Route rendering failed");
5434
+ }
5435
+ function RouteErrorBoundary({ fallback }) {
5436
+ const reason = useRouteError();
5437
+ const error = useMemo(() => normalizeRouteError(reason), [reason]);
5438
+ const recoveryRuntime = useContext2(ChunkRecoveryRuntimeContext);
5439
+ const handledReasonRef = useRef(UNHANDLED_ROUTE_ERROR);
5440
+ useEffect(() => {
5441
+ if (Object.is(handledReasonRef.current, reason)) return;
5442
+ handledReasonRef.current = reason;
5443
+ const reportError = () => {
5444
+ console.error("[RouteErrorBoundary] Caught error:", error);
5445
+ recoveryRuntime?.onError?.(error);
5446
+ };
5447
+ if (!recoveryRuntime) {
5448
+ reportError();
5449
+ return;
5450
+ }
5451
+ const recoveryEvent = handleChunkLoadError(reason, {
5452
+ source: "route-error-boundary",
5453
+ config: recoveryRuntime.chunkRecovery,
5454
+ isMicroApp: recoveryRuntime.isMicroApp,
5455
+ onError: reportError,
5456
+ onRecoveryEvent: recoveryRuntime.onRecoveryEvent
5457
+ });
5458
+ if (!recoveryEvent) reportError();
5459
+ }, [error, reason, recoveryRuntime]);
5460
+ if (typeof fallback === "function") return fallback(error);
5461
+ if (fallback) return fallback;
5462
+ const chunkRecovery = recoveryRuntime?.chunkRecovery;
5463
+ const resolvedIsMicroApp = recoveryRuntime?.isMicroApp ?? isMicroAppRuntime();
5464
+ const chunkRecoveryEnabled = chunkRecovery !== void 0 && resolveChunkRecoveryPolicy(chunkRecovery, {
5465
+ isProduction: process.env.NODE_ENV === "production",
5466
+ isMicroApp: resolvedIsMicroApp
5467
+ }).enabled;
5468
+ const canReloadPage = !resolvedIsMicroApp || typeof chunkRecovery === "object" && chunkRecovery.allowInMicroApp === true;
5469
+ return /* @__PURE__ */ jsx2(
5470
+ DefaultErrorFallback,
5471
+ {
5472
+ canReloadPage,
5473
+ error,
5474
+ isChunkError: chunkRecoveryEnabled && isChunkLoadError(reason)
5475
+ }
5476
+ );
5477
+ }
5478
+ function DefaultErrorFallback({
5479
+ canReloadPage,
5480
+ error,
5481
+ isChunkError,
5482
+ onReset
5483
+ }) {
4927
5484
  const isDev = process.env.NODE_ENV === "development";
4928
5485
  return /* @__PURE__ */ jsx2(
4929
5486
  "div",
@@ -4949,7 +5506,7 @@ function DefaultErrorFallback({ error, onReset }) {
4949
5506
  },
4950
5507
  children: [
4951
5508
  /* @__PURE__ */ jsx2("h1", { style: { color: "#ff4d4f", marginBottom: 16 }, children: "\u9875\u9762\u51FA\u9519\u4E86" }),
4952
- /* @__PURE__ */ jsx2("p", { style: { color: "#666", marginBottom: 16 }, children: "\u62B1\u6B49\uFF0C\u9875\u9762\u9047\u5230\u4E86\u4E00\u4E9B\u95EE\u9898\u3002\u8BF7\u5C1D\u8BD5\u5237\u65B0\u9875\u9762\u6216\u8054\u7CFB\u7BA1\u7406\u5458\u3002" }),
5509
+ /* @__PURE__ */ jsx2("p", { style: { color: "#666", marginBottom: 16 }, children: canReloadPage ? "\u62B1\u6B49\uFF0C\u9875\u9762\u9047\u5230\u4E86\u4E00\u4E9B\u95EE\u9898\u3002\u8BF7\u5C1D\u8BD5\u5237\u65B0\u9875\u9762\u6216\u8054\u7CFB\u7BA1\u7406\u5458\u3002" : "\u62B1\u6B49\uFF0C\u9875\u9762\u9047\u5230\u4E86\u4E00\u4E9B\u95EE\u9898\u3002\u8BF7\u5173\u95ED\u5F53\u524D\u9875\u9762\u540E\u91CD\u65B0\u6253\u5F00\uFF0C\u6216\u8054\u7CFB\u7BA1\u7406\u5458\u3002" }),
4953
5510
  isDev && /* @__PURE__ */ jsxs(
4954
5511
  "details",
4955
5512
  {
@@ -4983,8 +5540,8 @@ function DefaultErrorFallback({ error, onReset }) {
4983
5540
  ]
4984
5541
  }
4985
5542
  ),
4986
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
4987
- /* @__PURE__ */ jsx2(
5543
+ (canReloadPage || !isChunkError && onReset) && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
5544
+ !isChunkError && onReset && /* @__PURE__ */ jsx2(
4988
5545
  "button",
4989
5546
  {
4990
5547
  onClick: onReset,
@@ -4999,7 +5556,7 @@ function DefaultErrorFallback({ error, onReset }) {
4999
5556
  children: "\u91CD\u8BD5"
5000
5557
  }
5001
5558
  ),
5002
- /* @__PURE__ */ jsx2(
5559
+ canReloadPage && /* @__PURE__ */ jsx2(
5003
5560
  "button",
5004
5561
  {
5005
5562
  onClick: () => window.location.reload(),
@@ -5048,7 +5605,7 @@ function composeProviders(providers) {
5048
5605
  }, children);
5049
5606
  };
5050
5607
  }
5051
- function RootProvider({ config, children }) {
5608
+ function RootProvider({ config, children, isMicroApp }) {
5052
5609
  const {
5053
5610
  appName = "app",
5054
5611
  router,
@@ -5056,7 +5613,8 @@ function RootProvider({ config, children }) {
5056
5613
  strictMode = true,
5057
5614
  antd = { enabled: true },
5058
5615
  providers = [],
5059
- lifecycle
5616
+ lifecycle,
5617
+ chunkRecovery
5060
5618
  } = config;
5061
5619
  const appContextValue = {
5062
5620
  appName,
@@ -5087,8 +5645,30 @@ function RootProvider({ config, children }) {
5087
5645
  ...providers.map((p) => ({ ...p, order: p.order ?? 50 }))
5088
5646
  ];
5089
5647
  const ComposedProviders = composeProviders(allProviders);
5648
+ const rootChunkRecovery = chunkRecovery ?? {
5649
+ enabled: process.env.NODE_ENV === "production"
5650
+ };
5651
+ const resolvedIsMicroApp = isMicroApp ?? isMicroAppRuntime();
5090
5652
  const content = router ? /* @__PURE__ */ jsx3(Suspense, { fallback: /* @__PURE__ */ jsx3(DefaultLoading, {}), children: /* @__PURE__ */ jsx3(RouterProvider, { router }) }) : children;
5091
- const app = /* @__PURE__ */ jsx3(ErrorBoundary, { onError: lifecycle?.onError, children: /* @__PURE__ */ jsx3(ComposedProviders, { children: content }) });
5653
+ const app = /* @__PURE__ */ jsx3(
5654
+ ErrorBoundary,
5655
+ {
5656
+ chunkRecovery: rootChunkRecovery,
5657
+ isMicroApp: resolvedIsMicroApp,
5658
+ onError: lifecycle?.onError,
5659
+ onRecoveryEvent: lifecycle?.onRecoveryEvent,
5660
+ children: /* @__PURE__ */ jsx3(
5661
+ ChunkRecoveryRuntimeProvider,
5662
+ {
5663
+ chunkRecovery: rootChunkRecovery,
5664
+ isMicroApp: resolvedIsMicroApp,
5665
+ onError: lifecycle?.onError,
5666
+ onRecoveryEvent: lifecycle?.onRecoveryEvent,
5667
+ children: /* @__PURE__ */ jsx3(ComposedProviders, { children: content })
5668
+ }
5669
+ )
5670
+ }
5671
+ );
5092
5672
  if (strictMode) {
5093
5673
  return /* @__PURE__ */ jsx3(StrictMode, { children: app });
5094
5674
  }
@@ -5105,6 +5685,7 @@ function createProvider(component, props, order) {
5105
5685
  // src/runtime/bootstrap.tsx
5106
5686
  import { jsx as jsx4 } from "react/jsx-runtime";
5107
5687
  var root = null;
5688
+ var disposeChunkRecoveryHandlers = null;
5108
5689
  async function resolveAppConfig(config) {
5109
5690
  if (config.router || !config.conventionalRoutes) {
5110
5691
  return config;
@@ -5127,6 +5708,10 @@ async function resolveAppConfig(config) {
5127
5708
  }
5128
5709
  async function bootstrap(config) {
5129
5710
  const { rootId = "root", lifecycle } = config;
5711
+ const isMicroApp = isMicroAppEnv();
5712
+ if (!isMicroApp) {
5713
+ clearChunkRecoveryCacheBustParam();
5714
+ }
5130
5715
  const resolvedConfig = await resolveAppConfig(config);
5131
5716
  if (lifecycle?.onBeforeMount) {
5132
5717
  await lifecycle.onBeforeMount();
@@ -5135,8 +5720,22 @@ async function bootstrap(config) {
5135
5720
  if (!rootElement) {
5136
5721
  throw new Error(`\u627E\u4E0D\u5230\u6839\u5143\u7D20 #${rootId}`);
5137
5722
  }
5138
- root = createRoot(rootElement);
5139
- root.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
5723
+ disposeChunkRecoveryHandlers?.();
5724
+ const nextDisposeChunkRecoveryHandlers = installGlobalChunkRecoveryHandlers({
5725
+ config: resolvedConfig.chunkRecovery,
5726
+ isMicroApp,
5727
+ onError: (error) => resolvedConfig.lifecycle?.onError?.(error),
5728
+ onRecoveryEvent: resolvedConfig.lifecycle?.onRecoveryEvent
5729
+ });
5730
+ try {
5731
+ root = createRoot(rootElement);
5732
+ root.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig, isMicroApp }));
5733
+ disposeChunkRecoveryHandlers = nextDisposeChunkRecoveryHandlers;
5734
+ } catch (error) {
5735
+ nextDisposeChunkRecoveryHandlers();
5736
+ root = null;
5737
+ throw error;
5738
+ }
5140
5739
  if (lifecycle?.onMounted) {
5141
5740
  setTimeout(() => {
5142
5741
  lifecycle.onMounted?.();
@@ -5149,6 +5748,8 @@ async function bootstrap(config) {
5149
5748
  }
5150
5749
  }
5151
5750
  function unmount() {
5751
+ disposeChunkRecoveryHandlers?.();
5752
+ disposeChunkRecoveryHandlers = null;
5152
5753
  if (root) {
5153
5754
  root.unmount();
5154
5755
  root = null;
@@ -5156,6 +5757,7 @@ function unmount() {
5156
5757
  }
5157
5758
  function createMicroApp(getConfig) {
5158
5759
  let microRoot = null;
5760
+ let disposeMicroChunkRecoveryHandlers = null;
5159
5761
  return {
5160
5762
  /**
5161
5763
  * 微前端 bootstrap 生命周期
@@ -5179,8 +5781,22 @@ function createMicroApp(getConfig) {
5179
5781
  if (resolvedConfig.lifecycle?.onBeforeMount) {
5180
5782
  await resolvedConfig.lifecycle.onBeforeMount();
5181
5783
  }
5182
- microRoot = createRoot(rootElement);
5183
- microRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
5784
+ disposeMicroChunkRecoveryHandlers?.();
5785
+ const nextDisposeMicroChunkRecoveryHandlers = installGlobalChunkRecoveryHandlers({
5786
+ config: resolvedConfig.chunkRecovery,
5787
+ isMicroApp: true,
5788
+ onError: (error) => resolvedConfig.lifecycle?.onError?.(error),
5789
+ onRecoveryEvent: resolvedConfig.lifecycle?.onRecoveryEvent
5790
+ });
5791
+ try {
5792
+ microRoot = createRoot(rootElement);
5793
+ microRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig, isMicroApp: true }));
5794
+ disposeMicroChunkRecoveryHandlers = nextDisposeMicroChunkRecoveryHandlers;
5795
+ } catch (error) {
5796
+ nextDisposeMicroChunkRecoveryHandlers();
5797
+ microRoot = null;
5798
+ throw error;
5799
+ }
5184
5800
  if (resolvedConfig.lifecycle?.onMounted) {
5185
5801
  setTimeout(() => {
5186
5802
  resolvedConfig.lifecycle?.onMounted?.();
@@ -5200,6 +5816,8 @@ function createMicroApp(getConfig) {
5200
5816
  microRoot.unmount();
5201
5817
  microRoot = null;
5202
5818
  }
5819
+ disposeMicroChunkRecoveryHandlers?.();
5820
+ disposeMicroChunkRecoveryHandlers = null;
5203
5821
  },
5204
5822
  /**
5205
5823
  * 微前端 update 生命周期(可选)
@@ -5227,11 +5845,16 @@ function getMicroAppPublicPath() {
5227
5845
  export {
5228
5846
  AppContext,
5229
5847
  AppContextProvider,
5848
+ CHUNK_RECOVERY_ACTIVE_SYMBOL,
5849
+ CHUNK_RECOVERY_CACHE_BUST_PARAM,
5850
+ CHUNK_RECOVERY_STORAGE_KEY,
5230
5851
  ConventionalRouteGenerator,
5231
5852
  ConventionalRoutePlugin,
5853
+ DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS,
5232
5854
  ErrorBoundary,
5233
5855
  PluginManager,
5234
5856
  RootProvider,
5857
+ RouteErrorBoundary,
5235
5858
  YwkfGenerator,
5236
5859
  YwkfGeneratorPlugin,
5237
5860
  analyticsPlugin,
@@ -5239,6 +5862,8 @@ export {
5239
5862
  bootstrap,
5240
5863
  browserCheckPlugin,
5241
5864
  build,
5865
+ claimChunkRecoveryAttempt,
5866
+ clearChunkRecoveryCacheBustParam,
5242
5867
  createBaseConfig,
5243
5868
  createDevConfig,
5244
5869
  createMicroApp,
@@ -5254,10 +5879,15 @@ export {
5254
5879
  generateConventionalRoutes,
5255
5880
  getDefaultBiomeConfig,
5256
5881
  getMicroAppPublicPath,
5882
+ handleChunkLoadError,
5257
5883
  i18nPlugin,
5884
+ installGlobalChunkRecoveryHandlers,
5885
+ isChunkLoadError,
5258
5886
  isMicroAppEnv,
5887
+ isMicroAppRuntime,
5259
5888
  mockPlugin,
5260
5889
  reactQueryPlugin,
5890
+ resolveChunkRecoveryPolicy,
5261
5891
  tailwindPlugin,
5262
5892
  themePlugin,
5263
5893
  unmount,