@4399ywkf/core 5.1.4 → 5.1.6

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
@@ -2125,6 +2125,7 @@ var defaultConfig = {
2125
2125
  appName: "app",
2126
2126
  appCName: "\u5E94\u7528",
2127
2127
  strictMode: true,
2128
+ reactCompiler: false,
2128
2129
  dev: {
2129
2130
  port: 3e3,
2130
2131
  host: "127.0.0.1",
@@ -2742,12 +2743,13 @@ function LazyRoute({
2742
2743
 
2743
2744
  import React, { lazy, Suspense } from "react";
2744
2745
  import { createBrowserRouter, type RouteObject } from "react-router";
2746
+ import { RouteErrorBoundary } from "@4399ywkf/core/runtime";
2747
+ ${errorImports.length > 0 ? `
2748
+ // \u540C\u6B65\u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6
2749
+ ${errorImports.join("\n")}` : ""}
2745
2750
 
2746
2751
  // \u61D2\u52A0\u8F7D\u9875\u9762\u7EC4\u4EF6
2747
2752
  ${lazyImports.join("\n")}
2748
- ${errorImports.length > 0 ? `
2749
- // \u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6
2750
- ${errorImports.join("\n")}` : ""}
2751
2753
  ${loadingImports.length > 0 ? `
2752
2754
  // \u52A0\u8F7D\u72B6\u6001\u7EC4\u4EF6
2753
2755
  ${loadingImports.join("\n")}` : ""}
@@ -2782,7 +2784,7 @@ export default routes;
2782
2784
  }
2783
2785
  if (route.errorFile) {
2784
2786
  errorImports.push(
2785
- `const ${name}Error = lazy(() => import("@/pages/${toImportPath(route.errorFile)}"));`
2787
+ `import ${name}Error from "@/pages/${toImportPath(route.errorFile)}";`
2786
2788
  );
2787
2789
  }
2788
2790
  if (route.loadingFile) {
@@ -2797,13 +2799,13 @@ export default routes;
2797
2799
  }
2798
2800
  }
2799
2801
  }
2800
- emitRouteArray(routes, parentPath) {
2801
- const items = routes.map((r) => this.emitRouteObject(r, parentPath));
2802
+ emitRouteArray(routes, parentPath, depth = 0) {
2803
+ const items = routes.map((r) => this.emitRouteObject(r, parentPath, depth));
2802
2804
  return `[
2803
2805
  ${items.join(",\n ")}
2804
2806
  ]`;
2805
2807
  }
2806
- emitRouteObject(route, parentPath) {
2808
+ emitRouteObject(route, parentPath, depth) {
2807
2809
  const parts = [];
2808
2810
  const name = route.name;
2809
2811
  if (route.index) {
@@ -2824,11 +2826,13 @@ export default routes;
2824
2826
  parts.push(`element: <LazyRoute Component={${name}Page} />`);
2825
2827
  }
2826
2828
  if (route.errorFile) {
2827
- parts.push(`errorElement: <LazyRoute Component={${name}Error} />`);
2829
+ parts.push(`errorElement: <RouteErrorBoundary fallback={<${name}Error />} />`);
2830
+ } else if (depth === 0) {
2831
+ parts.push(`errorElement: <RouteErrorBoundary />`);
2828
2832
  }
2829
2833
  if (route.children && route.children.length > 0) {
2830
2834
  const childParent = route.pathless ? parentPath : route.path;
2831
- parts.push(`children: ${this.emitRouteArray(route.children, childParent)}`);
2835
+ parts.push(`children: ${this.emitRouteArray(route.children, childParent, depth + 1)}`);
2832
2836
  }
2833
2837
  return `{
2834
2838
  ${parts.join(",\n ")}
@@ -3440,10 +3444,20 @@ var YwkfGeneratorPlugin = class {
3440
3444
  var require2 = createRequire(import.meta.url);
3441
3445
  var __dirname = dirname(fileURLToPath(import.meta.url));
3442
3446
  var coreNodeModules = join12(__dirname, "../../node_modules");
3447
+ function resolveYwkfBuildId(appName) {
3448
+ const releaseId = [
3449
+ process.env.YWKF_BUILD_ID,
3450
+ process.env.CI_COMMIT_SHA,
3451
+ process.env.GITHUB_SHA,
3452
+ process.env.GIT_COMMIT
3453
+ ].find((value) => value?.trim())?.trim();
3454
+ return `${appName}:${releaseId || Date.now().toString(36)}`;
3455
+ }
3443
3456
  function createBaseConfig(config, cwd, options = {}) {
3444
3457
  const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
3445
3458
  const { resolveApp } = createPathResolver(cwd);
3446
- const { appName, appCName, output, html, alias: userAlias, microFrontend, router } = config;
3459
+ const { appName, appCName, output, html, alias: userAlias, microFrontend, router, reactCompiler } = config;
3460
+ const buildId = resolveYwkfBuildId(appName);
3447
3461
  const ywkfOutputDir = resolveApp(".ywkf");
3448
3462
  const defaultAlias = {
3449
3463
  "@": resolveApp("src"),
@@ -3529,7 +3543,8 @@ function createBaseConfig(config, cwd, options = {}) {
3529
3543
  runtime: "automatic",
3530
3544
  development: isDev,
3531
3545
  refresh: isDev
3532
- }
3546
+ },
3547
+ ...reactCompiler ? { reactCompiler } : {}
3533
3548
  },
3534
3549
  target: "es2015"
3535
3550
  },
@@ -3566,6 +3581,7 @@ function createBaseConfig(config, cwd, options = {}) {
3566
3581
  Buffer: ["buffer", "Buffer"]
3567
3582
  }),
3568
3583
  new rspack2.DefinePlugin({
3584
+ "globalThis.__YWKF_BUILD_ID__": JSON.stringify(buildId),
3569
3585
  "process.env": JSON.stringify(process.env)
3570
3586
  }),
3571
3587
  new rspack2.HtmlRspackPlugin({
@@ -4838,6 +4854,370 @@ var ConventionalRoutePlugin = class {
4838
4854
  }
4839
4855
  };
4840
4856
 
4857
+ // src/runtime/chunk-recovery.ts
4858
+ var DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS = 3e4;
4859
+ var CHUNK_RECOVERY_STORAGE_KEY = "@4399ywkf/core:chunk-recovery-attempt";
4860
+ var CHUNK_RECOVERY_CACHE_BUST_PARAM = "__ywkf_chunk_retry";
4861
+ var CHUNK_RECOVERY_ACTIVE_SYMBOL = /* @__PURE__ */ Symbol.for(
4862
+ "@4399ywkf/core/chunk-recovery-active"
4863
+ );
4864
+ var CHUNK_RECOVERY_REGISTRY_SYMBOL = /* @__PURE__ */ Symbol.for(
4865
+ "@4399ywkf/core/chunk-recovery-registry"
4866
+ );
4867
+ var CHUNK_LOAD_ERROR_PATTERNS = [
4868
+ /\bChunkLoadError\b/i,
4869
+ /\bLoading (?:CSS )?chunk\s+\S+\s+failed\b/i,
4870
+ /\bFailed to fetch dynamically imported module\b/i,
4871
+ /\bImporting a module script failed\b/i,
4872
+ /\berror loading dynamically imported module\b/i,
4873
+ /\bUnable to preload CSS for\b/i,
4874
+ /\bFailed to load module script\b/i
4875
+ ];
4876
+ function getErrorLikeValue(error, key) {
4877
+ if ((typeof error !== "object" || error === null) && typeof error !== "function") {
4878
+ return void 0;
4879
+ }
4880
+ try {
4881
+ return error[key];
4882
+ } catch {
4883
+ return void 0;
4884
+ }
4885
+ }
4886
+ function matchesChunkLoadError(error, depth) {
4887
+ if (depth > 3) {
4888
+ return false;
4889
+ }
4890
+ if (typeof error === "string") {
4891
+ return CHUNK_LOAD_ERROR_PATTERNS.some((pattern) => pattern.test(error));
4892
+ }
4893
+ const name = getErrorLikeValue(error, "name");
4894
+ const message = getErrorLikeValue(error, "message");
4895
+ const code = getErrorLikeValue(error, "code");
4896
+ const signature = [name, message].filter((value) => typeof value === "string").join(": ");
4897
+ if (code === "CSS_CHUNK_LOAD_FAILED" || CHUNK_LOAD_ERROR_PATTERNS.some((pattern) => pattern.test(signature))) {
4898
+ return true;
4899
+ }
4900
+ const cause = getErrorLikeValue(error, "cause");
4901
+ return cause !== void 0 && cause !== error && matchesChunkLoadError(cause, depth + 1);
4902
+ }
4903
+ function isChunkLoadError(error) {
4904
+ return matchesChunkLoadError(error, 0);
4905
+ }
4906
+ function hashRecoveryIdentity(value) {
4907
+ let hash = 2166136261;
4908
+ for (let index = 0; index < value.length; index += 1) {
4909
+ hash ^= value.charCodeAt(index);
4910
+ hash = Math.imul(hash, 16777619);
4911
+ }
4912
+ return (hash >>> 0).toString(36);
4913
+ }
4914
+ function getFrameworkBuildId() {
4915
+ const buildId = globalThis.__YWKF_BUILD_ID__;
4916
+ return typeof buildId === "string" ? buildId.trim() : "";
4917
+ }
4918
+ function resolveRecoveryId(error, config) {
4919
+ const configuredBuildId = typeof config === "object" && typeof config.buildId === "string" ? config.buildId.trim() : "";
4920
+ const buildId = configuredBuildId || getFrameworkBuildId();
4921
+ if (buildId) {
4922
+ return `build-${hashRecoveryIdentity(buildId)}`;
4923
+ }
4924
+ const request = getErrorLikeValue(error, "request");
4925
+ const name = getErrorLikeValue(error, "name");
4926
+ const message = getErrorLikeValue(error, "message");
4927
+ const signature = [request, name, message].filter((value) => typeof value === "string").join(": ");
4928
+ return `error-${hashRecoveryIdentity(signature || String(error))}`;
4929
+ }
4930
+ function toError(error, fallbackMessage = "Chunk loading failed") {
4931
+ if (error instanceof Error) {
4932
+ return error;
4933
+ }
4934
+ if (typeof error === "string") {
4935
+ return new Error(error);
4936
+ }
4937
+ const message = getErrorLikeValue(error, "message");
4938
+ const name = getErrorLikeValue(error, "name");
4939
+ const normalizedError = new Error(typeof message === "string" ? message : fallbackMessage);
4940
+ if (typeof name === "string") {
4941
+ normalizedError.name = name;
4942
+ }
4943
+ return normalizedError;
4944
+ }
4945
+ function normalizeCooldownMs(value) {
4946
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 1e3) {
4947
+ return DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS;
4948
+ }
4949
+ return value;
4950
+ }
4951
+ function resolveChunkRecoveryPolicy(config, context) {
4952
+ const options = typeof config === "object" ? config : void 0;
4953
+ const configuredEnabled = typeof config === "boolean" ? config : options?.enabled;
4954
+ const enabledForEnvironment = configuredEnabled ?? context.isProduction;
4955
+ if (!enabledForEnvironment) {
4956
+ return {
4957
+ enabled: false,
4958
+ disabledReason: configuredEnabled === false ? "configuration" : "environment",
4959
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
4960
+ globalHandlers: options?.globalHandlers === true
4961
+ };
4962
+ }
4963
+ if (context.isMicroApp && options?.allowInMicroApp !== true) {
4964
+ return {
4965
+ enabled: false,
4966
+ disabledReason: "micro-app",
4967
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
4968
+ globalHandlers: options?.globalHandlers === true
4969
+ };
4970
+ }
4971
+ return {
4972
+ enabled: true,
4973
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
4974
+ globalHandlers: options?.globalHandlers === true
4975
+ };
4976
+ }
4977
+ function parseStoredChunkRecoveryState(value) {
4978
+ if (value === null) {
4979
+ return { attempts: [] };
4980
+ }
4981
+ const parsed = JSON.parse(value);
4982
+ if (!Array.isArray(parsed.attempts)) {
4983
+ throw new Error("Invalid chunk recovery state");
4984
+ }
4985
+ const attempts = parsed.attempts.map((attempt) => {
4986
+ if (typeof attempt !== "object" || attempt === null || typeof attempt.recoveryId !== "string" || typeof attempt.attemptedAt !== "number" || !Number.isFinite(attempt.attemptedAt)) {
4987
+ throw new Error("Invalid chunk recovery attempt");
4988
+ }
4989
+ return attempt;
4990
+ });
4991
+ if (parsed.lastAttemptAt !== void 0 && (typeof parsed.lastAttemptAt !== "number" || !Number.isFinite(parsed.lastAttemptAt))) {
4992
+ throw new Error("Invalid chunk recovery timestamp");
4993
+ }
4994
+ return { attempts, lastAttemptAt: parsed.lastAttemptAt };
4995
+ }
4996
+ function claimChunkRecoveryAttempt(storage, options) {
4997
+ const {
4998
+ cooldownMs = DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS,
4999
+ now,
5000
+ recoveryId,
5001
+ storageKey = CHUNK_RECOVERY_STORAGE_KEY
5002
+ } = options;
5003
+ let state;
5004
+ try {
5005
+ state = parseStoredChunkRecoveryState(storage.getItem(storageKey));
5006
+ } catch {
5007
+ return { claimed: false, reason: "storage-unavailable" };
5008
+ }
5009
+ const existingAttempt = state.attempts.find((attempt) => attempt.recoveryId === recoveryId);
5010
+ if (existingAttempt) {
5011
+ return {
5012
+ claimed: false,
5013
+ reason: "already-attempted",
5014
+ attemptedAt: existingAttempt.attemptedAt
5015
+ };
5016
+ }
5017
+ const inCooldown = state.lastAttemptAt !== void 0 && (state.lastAttemptAt > now || now - state.lastAttemptAt < cooldownMs);
5018
+ if (inCooldown) {
5019
+ return { claimed: false, reason: "cooldown", attemptedAt: state.lastAttemptAt };
5020
+ }
5021
+ try {
5022
+ storage.setItem(
5023
+ storageKey,
5024
+ JSON.stringify({
5025
+ attempts: [...state.attempts, { attemptedAt: now, recoveryId }],
5026
+ lastAttemptAt: now
5027
+ })
5028
+ );
5029
+ } catch {
5030
+ return { claimed: false, reason: "storage-unavailable" };
5031
+ }
5032
+ return { claimed: true, attemptedAt: now };
5033
+ }
5034
+ function isMicroAppRuntime() {
5035
+ if (typeof window === "undefined") {
5036
+ return false;
5037
+ }
5038
+ const runtimeWindow = window;
5039
+ return Boolean(runtimeWindow.__POWERED_BY_QIANKUN__ || runtimeWindow.__GARFISH__);
5040
+ }
5041
+ function getSessionStorage() {
5042
+ if (typeof window === "undefined") {
5043
+ return null;
5044
+ }
5045
+ try {
5046
+ return window.sessionStorage;
5047
+ } catch {
5048
+ return null;
5049
+ }
5050
+ }
5051
+ function invokeSafely(callback, label) {
5052
+ if (!callback) {
5053
+ return;
5054
+ }
5055
+ try {
5056
+ callback();
5057
+ } catch (error) {
5058
+ console.error(`[ywkf] ${label} \u6267\u884C\u5931\u8D25:`, error);
5059
+ }
5060
+ }
5061
+ function schedulePageReplace(timestamp) {
5062
+ setTimeout(() => {
5063
+ try {
5064
+ const reloadUrl = new URL(window.location.href);
5065
+ reloadUrl.searchParams.set(CHUNK_RECOVERY_CACHE_BUST_PARAM, String(timestamp));
5066
+ window.location.replace(reloadUrl.toString());
5067
+ } catch (error) {
5068
+ console.error("[ywkf] chunk \u6062\u590D\u5237\u65B0\u5931\u8D25:", error);
5069
+ }
5070
+ }, 0);
5071
+ }
5072
+ function clearChunkRecoveryCacheBustParam() {
5073
+ if (typeof window === "undefined") {
5074
+ return;
5075
+ }
5076
+ try {
5077
+ const currentUrl = new URL(window.location.href);
5078
+ if (!currentUrl.searchParams.has(CHUNK_RECOVERY_CACHE_BUST_PARAM)) {
5079
+ return;
5080
+ }
5081
+ currentUrl.searchParams.delete(CHUNK_RECOVERY_CACHE_BUST_PARAM);
5082
+ window.history.replaceState(window.history.state, "", currentUrl.toString());
5083
+ } catch (error) {
5084
+ console.error("[ywkf] \u6E05\u7406 chunk \u6062\u590D\u53C2\u6570\u5931\u8D25:", error);
5085
+ }
5086
+ }
5087
+ function handleChunkLoadErrorInternal(reason, options, trustedChunkEvent) {
5088
+ if (!trustedChunkEvent && !isChunkLoadError(reason)) {
5089
+ return null;
5090
+ }
5091
+ const error = toError(reason);
5092
+ const timestamp = Date.now();
5093
+ const recoveryId = resolveRecoveryId(reason, options.config);
5094
+ const isMicroApp = options.isMicroApp ?? isMicroAppRuntime();
5095
+ const policy = resolveChunkRecoveryPolicy(options.config, {
5096
+ isProduction: process.env.NODE_ENV === "production",
5097
+ isMicroApp
5098
+ });
5099
+ let outcome;
5100
+ if (!policy.enabled) {
5101
+ outcome = policy.disabledReason === "micro-app" ? "reload-skipped-micro-app" : "reload-skipped-disabled";
5102
+ } else {
5103
+ const storage = getSessionStorage();
5104
+ const claim = storage ? claimChunkRecoveryAttempt(storage, {
5105
+ cooldownMs: policy.cooldownMs,
5106
+ now: timestamp,
5107
+ recoveryId
5108
+ }) : { claimed: false, reason: "storage-unavailable" };
5109
+ if (claim.claimed) {
5110
+ outcome = "reload-scheduled";
5111
+ } else if (claim.reason === "already-attempted") {
5112
+ outcome = "reload-skipped-already-attempted";
5113
+ } else if (claim.reason === "cooldown") {
5114
+ outcome = "reload-skipped-cooldown";
5115
+ } else {
5116
+ outcome = "reload-skipped-storage-unavailable";
5117
+ }
5118
+ }
5119
+ const event = {
5120
+ error,
5121
+ source: options.source,
5122
+ outcome,
5123
+ timestamp,
5124
+ cooldownMs: policy.cooldownMs,
5125
+ recoveryId
5126
+ };
5127
+ invokeSafely(() => options.onError?.(error), "onError");
5128
+ invokeSafely(() => options.onRecoveryEvent?.(event), "onRecoveryEvent");
5129
+ if (outcome === "reload-scheduled") {
5130
+ schedulePageReplace(timestamp);
5131
+ }
5132
+ return event;
5133
+ }
5134
+ function handleChunkLoadError(reason, options) {
5135
+ return handleChunkLoadErrorInternal(reason, options, false);
5136
+ }
5137
+ function getLatestSubscriber(registry) {
5138
+ let latest;
5139
+ for (const subscriber of registry.subscribers.values()) {
5140
+ latest = subscriber;
5141
+ }
5142
+ return latest;
5143
+ }
5144
+ function createGlobalChunkRecoveryRegistry() {
5145
+ const registry = {
5146
+ subscribers: /* @__PURE__ */ new Map()
5147
+ };
5148
+ registry.handleUnhandledRejection = (event) => {
5149
+ const subscriber = getLatestSubscriber(registry);
5150
+ if (!subscriber) return;
5151
+ const recoveryEvent = handleChunkLoadError(event.reason, {
5152
+ ...subscriber,
5153
+ source: "unhandled-rejection"
5154
+ });
5155
+ if (recoveryEvent?.outcome === "reload-scheduled") {
5156
+ event.preventDefault();
5157
+ }
5158
+ };
5159
+ registry.handleVitePreloadError = (event) => {
5160
+ const subscriber = getLatestSubscriber(registry);
5161
+ if (!subscriber) return;
5162
+ const payload = event.payload;
5163
+ const recoveryEvent = handleChunkLoadErrorInternal(
5164
+ payload,
5165
+ {
5166
+ ...subscriber,
5167
+ source: "vite-preload-error"
5168
+ },
5169
+ true
5170
+ );
5171
+ if (recoveryEvent?.outcome === "reload-scheduled") {
5172
+ event.preventDefault();
5173
+ }
5174
+ };
5175
+ return registry;
5176
+ }
5177
+ function installGlobalChunkRecoveryHandlers(options) {
5178
+ if (typeof window === "undefined") {
5179
+ return () => {
5180
+ };
5181
+ }
5182
+ const isMicroApp = options.isMicroApp ?? isMicroAppRuntime();
5183
+ const policy = resolveChunkRecoveryPolicy(options.config, {
5184
+ isProduction: process.env.NODE_ENV === "production",
5185
+ isMicroApp
5186
+ });
5187
+ if (!policy.enabled || !policy.globalHandlers) {
5188
+ return () => {
5189
+ };
5190
+ }
5191
+ const runtimeWindow = window;
5192
+ let registry = runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL];
5193
+ if (!registry && runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL] === true) {
5194
+ return () => {
5195
+ };
5196
+ }
5197
+ if (!registry) {
5198
+ registry = createGlobalChunkRecoveryRegistry();
5199
+ runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL] = registry;
5200
+ runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL] = true;
5201
+ window.addEventListener("unhandledrejection", registry.handleUnhandledRejection);
5202
+ window.addEventListener("vite:preloadError", registry.handleVitePreloadError);
5203
+ }
5204
+ const subscriberId = /* @__PURE__ */ Symbol("chunk-recovery-subscriber");
5205
+ registry.subscribers.set(subscriberId, { ...options, isMicroApp });
5206
+ let disposed = false;
5207
+ return () => {
5208
+ if (disposed) return;
5209
+ disposed = true;
5210
+ registry.subscribers.delete(subscriberId);
5211
+ if (registry.subscribers.size > 0) return;
5212
+ window.removeEventListener("unhandledrejection", registry.handleUnhandledRejection);
5213
+ window.removeEventListener("vite:preloadError", registry.handleVitePreloadError);
5214
+ if (runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL] === registry) {
5215
+ delete runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL];
5216
+ delete runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL];
5217
+ }
5218
+ };
5219
+ }
5220
+
4841
5221
  // src/runtime/bootstrap.tsx
4842
5222
  import { createRoot } from "react-dom/client";
4843
5223
 
@@ -4889,8 +5269,23 @@ function useIsDev() {
4889
5269
  }
4890
5270
 
4891
5271
  // src/runtime/error-boundary.tsx
4892
- import { Component } from "react";
5272
+ import {
5273
+ Component,
5274
+ createContext as createContext2,
5275
+ useContext as useContext2,
5276
+ useEffect,
5277
+ useMemo,
5278
+ useRef
5279
+ } from "react";
5280
+ import { isRouteErrorResponse, useRouteError } from "react-router";
4893
5281
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
5282
+ var ChunkRecoveryRuntimeContext = createContext2(null);
5283
+ function ChunkRecoveryRuntimeProvider({
5284
+ children,
5285
+ ...value
5286
+ }) {
5287
+ return /* @__PURE__ */ jsx2(ChunkRecoveryRuntimeContext.Provider, { value, children });
5288
+ }
4894
5289
  var ErrorBoundary = class extends Component {
4895
5290
  constructor(props) {
4896
5291
  super(props);
@@ -4900,15 +5295,31 @@ var ErrorBoundary = class extends Component {
4900
5295
  return { hasError: true, error };
4901
5296
  }
4902
5297
  componentDidCatch(error, errorInfo) {
4903
- console.error("[ErrorBoundary] Caught error:", error, errorInfo);
4904
- this.props.onError?.(error, errorInfo);
5298
+ const reportError = () => {
5299
+ console.error("[ErrorBoundary] Caught error:", error, errorInfo);
5300
+ this.props.onError?.(error, errorInfo);
5301
+ };
5302
+ if (this.props.chunkRecovery === void 0) {
5303
+ reportError();
5304
+ return;
5305
+ }
5306
+ const recoveryEvent = handleChunkLoadError(error, {
5307
+ source: "error-boundary",
5308
+ config: this.props.chunkRecovery,
5309
+ isMicroApp: this.props.isMicroApp,
5310
+ onError: reportError,
5311
+ onRecoveryEvent: this.props.onRecoveryEvent
5312
+ });
5313
+ if (!recoveryEvent) {
5314
+ reportError();
5315
+ }
4905
5316
  }
4906
5317
  reset = () => {
4907
5318
  this.setState({ hasError: false, error: null });
4908
5319
  };
4909
5320
  render() {
4910
5321
  const { hasError, error } = this.state;
4911
- const { children, fallback } = this.props;
5322
+ const { children, chunkRecovery, fallback, isMicroApp } = this.props;
4912
5323
  if (hasError && error) {
4913
5324
  if (typeof fallback === "function") {
4914
5325
  return fallback(error, this.reset);
@@ -4916,12 +5327,86 @@ var ErrorBoundary = class extends Component {
4916
5327
  if (fallback) {
4917
5328
  return fallback;
4918
5329
  }
4919
- return /* @__PURE__ */ jsx2(DefaultErrorFallback, { error, onReset: this.reset });
5330
+ const resolvedIsMicroApp = isMicroApp ?? isMicroAppRuntime();
5331
+ const chunkRecoveryEnabled = chunkRecovery !== void 0 && resolveChunkRecoveryPolicy(chunkRecovery, {
5332
+ isProduction: process.env.NODE_ENV === "production",
5333
+ isMicroApp: resolvedIsMicroApp
5334
+ }).enabled;
5335
+ const canReloadPage = !resolvedIsMicroApp || typeof chunkRecovery === "object" && chunkRecovery.allowInMicroApp === true;
5336
+ return /* @__PURE__ */ jsx2(
5337
+ DefaultErrorFallback,
5338
+ {
5339
+ canReloadPage,
5340
+ error,
5341
+ isChunkError: chunkRecoveryEnabled && isChunkLoadError(error),
5342
+ onReset: this.reset
5343
+ }
5344
+ );
4920
5345
  }
4921
5346
  return children;
4922
5347
  }
4923
5348
  };
4924
- function DefaultErrorFallback({ error, onReset }) {
5349
+ var UNHANDLED_ROUTE_ERROR = /* @__PURE__ */ Symbol("unhandled-route-error");
5350
+ function normalizeRouteError(reason) {
5351
+ if (reason instanceof Error) return reason;
5352
+ if (typeof reason === "string") return new Error(reason);
5353
+ if (isRouteErrorResponse(reason)) {
5354
+ const message = [reason.status, reason.statusText].filter(Boolean).join(" ");
5355
+ const error = new Error(message || "Route request failed");
5356
+ error.name = "RouteErrorResponse";
5357
+ return error;
5358
+ }
5359
+ return new Error("Route rendering failed");
5360
+ }
5361
+ function RouteErrorBoundary({ fallback }) {
5362
+ const reason = useRouteError();
5363
+ const error = useMemo(() => normalizeRouteError(reason), [reason]);
5364
+ const recoveryRuntime = useContext2(ChunkRecoveryRuntimeContext);
5365
+ const handledReasonRef = useRef(UNHANDLED_ROUTE_ERROR);
5366
+ useEffect(() => {
5367
+ if (Object.is(handledReasonRef.current, reason)) return;
5368
+ handledReasonRef.current = reason;
5369
+ const reportError = () => {
5370
+ console.error("[RouteErrorBoundary] Caught error:", error);
5371
+ recoveryRuntime?.onError?.(error);
5372
+ };
5373
+ if (!recoveryRuntime) {
5374
+ reportError();
5375
+ return;
5376
+ }
5377
+ const recoveryEvent = handleChunkLoadError(reason, {
5378
+ source: "route-error-boundary",
5379
+ config: recoveryRuntime.chunkRecovery,
5380
+ isMicroApp: recoveryRuntime.isMicroApp,
5381
+ onError: reportError,
5382
+ onRecoveryEvent: recoveryRuntime.onRecoveryEvent
5383
+ });
5384
+ if (!recoveryEvent) reportError();
5385
+ }, [error, reason, recoveryRuntime]);
5386
+ if (typeof fallback === "function") return fallback(error);
5387
+ if (fallback) return fallback;
5388
+ const chunkRecovery = recoveryRuntime?.chunkRecovery;
5389
+ const resolvedIsMicroApp = recoveryRuntime?.isMicroApp ?? isMicroAppRuntime();
5390
+ const chunkRecoveryEnabled = chunkRecovery !== void 0 && resolveChunkRecoveryPolicy(chunkRecovery, {
5391
+ isProduction: process.env.NODE_ENV === "production",
5392
+ isMicroApp: resolvedIsMicroApp
5393
+ }).enabled;
5394
+ const canReloadPage = !resolvedIsMicroApp || typeof chunkRecovery === "object" && chunkRecovery.allowInMicroApp === true;
5395
+ return /* @__PURE__ */ jsx2(
5396
+ DefaultErrorFallback,
5397
+ {
5398
+ canReloadPage,
5399
+ error,
5400
+ isChunkError: chunkRecoveryEnabled && isChunkLoadError(reason)
5401
+ }
5402
+ );
5403
+ }
5404
+ function DefaultErrorFallback({
5405
+ canReloadPage,
5406
+ error,
5407
+ isChunkError,
5408
+ onReset
5409
+ }) {
4925
5410
  const isDev = process.env.NODE_ENV === "development";
4926
5411
  return /* @__PURE__ */ jsx2(
4927
5412
  "div",
@@ -4947,7 +5432,7 @@ function DefaultErrorFallback({ error, onReset }) {
4947
5432
  },
4948
5433
  children: [
4949
5434
  /* @__PURE__ */ jsx2("h1", { style: { color: "#ff4d4f", marginBottom: 16 }, children: "\u9875\u9762\u51FA\u9519\u4E86" }),
4950
- /* @__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" }),
5435
+ /* @__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" }),
4951
5436
  isDev && /* @__PURE__ */ jsxs(
4952
5437
  "details",
4953
5438
  {
@@ -4981,8 +5466,8 @@ function DefaultErrorFallback({ error, onReset }) {
4981
5466
  ]
4982
5467
  }
4983
5468
  ),
4984
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
4985
- /* @__PURE__ */ jsx2(
5469
+ (canReloadPage || !isChunkError && onReset) && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
5470
+ !isChunkError && onReset && /* @__PURE__ */ jsx2(
4986
5471
  "button",
4987
5472
  {
4988
5473
  onClick: onReset,
@@ -4997,7 +5482,7 @@ function DefaultErrorFallback({ error, onReset }) {
4997
5482
  children: "\u91CD\u8BD5"
4998
5483
  }
4999
5484
  ),
5000
- /* @__PURE__ */ jsx2(
5485
+ canReloadPage && /* @__PURE__ */ jsx2(
5001
5486
  "button",
5002
5487
  {
5003
5488
  onClick: () => window.location.reload(),
@@ -5046,7 +5531,7 @@ function composeProviders(providers) {
5046
5531
  }, children);
5047
5532
  };
5048
5533
  }
5049
- function RootProvider({ config, children }) {
5534
+ function RootProvider({ config, children, isMicroApp }) {
5050
5535
  const {
5051
5536
  appName = "app",
5052
5537
  router,
@@ -5054,7 +5539,8 @@ function RootProvider({ config, children }) {
5054
5539
  strictMode = true,
5055
5540
  antd = { enabled: true },
5056
5541
  providers = [],
5057
- lifecycle
5542
+ lifecycle,
5543
+ chunkRecovery
5058
5544
  } = config;
5059
5545
  const appContextValue = {
5060
5546
  appName,
@@ -5085,8 +5571,30 @@ function RootProvider({ config, children }) {
5085
5571
  ...providers.map((p) => ({ ...p, order: p.order ?? 50 }))
5086
5572
  ];
5087
5573
  const ComposedProviders = composeProviders(allProviders);
5574
+ const rootChunkRecovery = chunkRecovery ?? {
5575
+ enabled: process.env.NODE_ENV === "production"
5576
+ };
5577
+ const resolvedIsMicroApp = isMicroApp ?? isMicroAppRuntime();
5088
5578
  const content = router ? /* @__PURE__ */ jsx3(Suspense, { fallback: /* @__PURE__ */ jsx3(DefaultLoading, {}), children: /* @__PURE__ */ jsx3(RouterProvider, { router }) }) : children;
5089
- const app = /* @__PURE__ */ jsx3(ErrorBoundary, { onError: lifecycle?.onError, children: /* @__PURE__ */ jsx3(ComposedProviders, { children: content }) });
5579
+ const app = /* @__PURE__ */ jsx3(
5580
+ ErrorBoundary,
5581
+ {
5582
+ chunkRecovery: rootChunkRecovery,
5583
+ isMicroApp: resolvedIsMicroApp,
5584
+ onError: lifecycle?.onError,
5585
+ onRecoveryEvent: lifecycle?.onRecoveryEvent,
5586
+ children: /* @__PURE__ */ jsx3(
5587
+ ChunkRecoveryRuntimeProvider,
5588
+ {
5589
+ chunkRecovery: rootChunkRecovery,
5590
+ isMicroApp: resolvedIsMicroApp,
5591
+ onError: lifecycle?.onError,
5592
+ onRecoveryEvent: lifecycle?.onRecoveryEvent,
5593
+ children: /* @__PURE__ */ jsx3(ComposedProviders, { children: content })
5594
+ }
5595
+ )
5596
+ }
5597
+ );
5090
5598
  if (strictMode) {
5091
5599
  return /* @__PURE__ */ jsx3(StrictMode, { children: app });
5092
5600
  }
@@ -5103,6 +5611,7 @@ function createProvider(component, props, order) {
5103
5611
  // src/runtime/bootstrap.tsx
5104
5612
  import { jsx as jsx4 } from "react/jsx-runtime";
5105
5613
  var root = null;
5614
+ var disposeChunkRecoveryHandlers = null;
5106
5615
  async function resolveAppConfig(config) {
5107
5616
  if (config.router || !config.conventionalRoutes) {
5108
5617
  return config;
@@ -5125,6 +5634,10 @@ async function resolveAppConfig(config) {
5125
5634
  }
5126
5635
  async function bootstrap(config) {
5127
5636
  const { rootId = "root", lifecycle } = config;
5637
+ const isMicroApp = isMicroAppEnv();
5638
+ if (!isMicroApp) {
5639
+ clearChunkRecoveryCacheBustParam();
5640
+ }
5128
5641
  const resolvedConfig = await resolveAppConfig(config);
5129
5642
  if (lifecycle?.onBeforeMount) {
5130
5643
  await lifecycle.onBeforeMount();
@@ -5133,8 +5646,22 @@ async function bootstrap(config) {
5133
5646
  if (!rootElement) {
5134
5647
  throw new Error(`\u627E\u4E0D\u5230\u6839\u5143\u7D20 #${rootId}`);
5135
5648
  }
5136
- root = createRoot(rootElement);
5137
- root.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
5649
+ disposeChunkRecoveryHandlers?.();
5650
+ const nextDisposeChunkRecoveryHandlers = installGlobalChunkRecoveryHandlers({
5651
+ config: resolvedConfig.chunkRecovery,
5652
+ isMicroApp,
5653
+ onError: (error) => resolvedConfig.lifecycle?.onError?.(error),
5654
+ onRecoveryEvent: resolvedConfig.lifecycle?.onRecoveryEvent
5655
+ });
5656
+ try {
5657
+ root = createRoot(rootElement);
5658
+ root.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig, isMicroApp }));
5659
+ disposeChunkRecoveryHandlers = nextDisposeChunkRecoveryHandlers;
5660
+ } catch (error) {
5661
+ nextDisposeChunkRecoveryHandlers();
5662
+ root = null;
5663
+ throw error;
5664
+ }
5138
5665
  if (lifecycle?.onMounted) {
5139
5666
  setTimeout(() => {
5140
5667
  lifecycle.onMounted?.();
@@ -5147,6 +5674,8 @@ async function bootstrap(config) {
5147
5674
  }
5148
5675
  }
5149
5676
  function unmount() {
5677
+ disposeChunkRecoveryHandlers?.();
5678
+ disposeChunkRecoveryHandlers = null;
5150
5679
  if (root) {
5151
5680
  root.unmount();
5152
5681
  root = null;
@@ -5154,6 +5683,7 @@ function unmount() {
5154
5683
  }
5155
5684
  function createMicroApp(getConfig) {
5156
5685
  let microRoot = null;
5686
+ let disposeMicroChunkRecoveryHandlers = null;
5157
5687
  return {
5158
5688
  /**
5159
5689
  * 微前端 bootstrap 生命周期
@@ -5177,8 +5707,22 @@ function createMicroApp(getConfig) {
5177
5707
  if (resolvedConfig.lifecycle?.onBeforeMount) {
5178
5708
  await resolvedConfig.lifecycle.onBeforeMount();
5179
5709
  }
5180
- microRoot = createRoot(rootElement);
5181
- microRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
5710
+ disposeMicroChunkRecoveryHandlers?.();
5711
+ const nextDisposeMicroChunkRecoveryHandlers = installGlobalChunkRecoveryHandlers({
5712
+ config: resolvedConfig.chunkRecovery,
5713
+ isMicroApp: true,
5714
+ onError: (error) => resolvedConfig.lifecycle?.onError?.(error),
5715
+ onRecoveryEvent: resolvedConfig.lifecycle?.onRecoveryEvent
5716
+ });
5717
+ try {
5718
+ microRoot = createRoot(rootElement);
5719
+ microRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig, isMicroApp: true }));
5720
+ disposeMicroChunkRecoveryHandlers = nextDisposeMicroChunkRecoveryHandlers;
5721
+ } catch (error) {
5722
+ nextDisposeMicroChunkRecoveryHandlers();
5723
+ microRoot = null;
5724
+ throw error;
5725
+ }
5182
5726
  if (resolvedConfig.lifecycle?.onMounted) {
5183
5727
  setTimeout(() => {
5184
5728
  resolvedConfig.lifecycle?.onMounted?.();
@@ -5198,6 +5742,8 @@ function createMicroApp(getConfig) {
5198
5742
  microRoot.unmount();
5199
5743
  microRoot = null;
5200
5744
  }
5745
+ disposeMicroChunkRecoveryHandlers?.();
5746
+ disposeMicroChunkRecoveryHandlers = null;
5201
5747
  },
5202
5748
  /**
5203
5749
  * 微前端 update 生命周期(可选)
@@ -5225,11 +5771,16 @@ function getMicroAppPublicPath() {
5225
5771
  export {
5226
5772
  AppContext,
5227
5773
  AppContextProvider,
5774
+ CHUNK_RECOVERY_ACTIVE_SYMBOL,
5775
+ CHUNK_RECOVERY_CACHE_BUST_PARAM,
5776
+ CHUNK_RECOVERY_STORAGE_KEY,
5228
5777
  ConventionalRouteGenerator,
5229
5778
  ConventionalRoutePlugin,
5779
+ DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS,
5230
5780
  ErrorBoundary,
5231
5781
  PluginManager,
5232
5782
  RootProvider,
5783
+ RouteErrorBoundary,
5233
5784
  YwkfGenerator,
5234
5785
  YwkfGeneratorPlugin,
5235
5786
  analyticsPlugin,
@@ -5237,6 +5788,8 @@ export {
5237
5788
  bootstrap,
5238
5789
  browserCheckPlugin,
5239
5790
  build,
5791
+ claimChunkRecoveryAttempt,
5792
+ clearChunkRecoveryCacheBustParam,
5240
5793
  createBaseConfig,
5241
5794
  createDevConfig,
5242
5795
  createMicroApp,
@@ -5252,10 +5805,15 @@ export {
5252
5805
  generateConventionalRoutes,
5253
5806
  getDefaultBiomeConfig,
5254
5807
  getMicroAppPublicPath,
5808
+ handleChunkLoadError,
5255
5809
  i18nPlugin,
5810
+ installGlobalChunkRecoveryHandlers,
5811
+ isChunkLoadError,
5256
5812
  isMicroAppEnv,
5813
+ isMicroAppRuntime,
5257
5814
  mockPlugin,
5258
5815
  reactQueryPlugin,
5816
+ resolveChunkRecoveryPolicy,
5259
5817
  tailwindPlugin,
5260
5818
  themePlugin,
5261
5819
  unmount,