@4399ywkf/core 5.1.5 → 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.
@@ -1,3 +1,367 @@
1
+ // src/runtime/chunk-recovery.ts
2
+ var DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS = 3e4;
3
+ var CHUNK_RECOVERY_STORAGE_KEY = "@4399ywkf/core:chunk-recovery-attempt";
4
+ var CHUNK_RECOVERY_CACHE_BUST_PARAM = "__ywkf_chunk_retry";
5
+ var CHUNK_RECOVERY_ACTIVE_SYMBOL = /* @__PURE__ */ Symbol.for(
6
+ "@4399ywkf/core/chunk-recovery-active"
7
+ );
8
+ var CHUNK_RECOVERY_REGISTRY_SYMBOL = /* @__PURE__ */ Symbol.for(
9
+ "@4399ywkf/core/chunk-recovery-registry"
10
+ );
11
+ var CHUNK_LOAD_ERROR_PATTERNS = [
12
+ /\bChunkLoadError\b/i,
13
+ /\bLoading (?:CSS )?chunk\s+\S+\s+failed\b/i,
14
+ /\bFailed to fetch dynamically imported module\b/i,
15
+ /\bImporting a module script failed\b/i,
16
+ /\berror loading dynamically imported module\b/i,
17
+ /\bUnable to preload CSS for\b/i,
18
+ /\bFailed to load module script\b/i
19
+ ];
20
+ function getErrorLikeValue(error, key) {
21
+ if ((typeof error !== "object" || error === null) && typeof error !== "function") {
22
+ return void 0;
23
+ }
24
+ try {
25
+ return error[key];
26
+ } catch {
27
+ return void 0;
28
+ }
29
+ }
30
+ function matchesChunkLoadError(error, depth) {
31
+ if (depth > 3) {
32
+ return false;
33
+ }
34
+ if (typeof error === "string") {
35
+ return CHUNK_LOAD_ERROR_PATTERNS.some((pattern) => pattern.test(error));
36
+ }
37
+ const name = getErrorLikeValue(error, "name");
38
+ const message = getErrorLikeValue(error, "message");
39
+ const code = getErrorLikeValue(error, "code");
40
+ const signature = [name, message].filter((value) => typeof value === "string").join(": ");
41
+ if (code === "CSS_CHUNK_LOAD_FAILED" || CHUNK_LOAD_ERROR_PATTERNS.some((pattern) => pattern.test(signature))) {
42
+ return true;
43
+ }
44
+ const cause = getErrorLikeValue(error, "cause");
45
+ return cause !== void 0 && cause !== error && matchesChunkLoadError(cause, depth + 1);
46
+ }
47
+ function isChunkLoadError(error) {
48
+ return matchesChunkLoadError(error, 0);
49
+ }
50
+ function hashRecoveryIdentity(value) {
51
+ let hash = 2166136261;
52
+ for (let index = 0; index < value.length; index += 1) {
53
+ hash ^= value.charCodeAt(index);
54
+ hash = Math.imul(hash, 16777619);
55
+ }
56
+ return (hash >>> 0).toString(36);
57
+ }
58
+ function getFrameworkBuildId() {
59
+ const buildId = globalThis.__YWKF_BUILD_ID__;
60
+ return typeof buildId === "string" ? buildId.trim() : "";
61
+ }
62
+ function resolveRecoveryId(error, config) {
63
+ const configuredBuildId = typeof config === "object" && typeof config.buildId === "string" ? config.buildId.trim() : "";
64
+ const buildId = configuredBuildId || getFrameworkBuildId();
65
+ if (buildId) {
66
+ return `build-${hashRecoveryIdentity(buildId)}`;
67
+ }
68
+ const request = getErrorLikeValue(error, "request");
69
+ const name = getErrorLikeValue(error, "name");
70
+ const message = getErrorLikeValue(error, "message");
71
+ const signature = [request, name, message].filter((value) => typeof value === "string").join(": ");
72
+ return `error-${hashRecoveryIdentity(signature || String(error))}`;
73
+ }
74
+ function toError(error, fallbackMessage = "Chunk loading failed") {
75
+ if (error instanceof Error) {
76
+ return error;
77
+ }
78
+ if (typeof error === "string") {
79
+ return new Error(error);
80
+ }
81
+ const message = getErrorLikeValue(error, "message");
82
+ const name = getErrorLikeValue(error, "name");
83
+ const normalizedError = new Error(typeof message === "string" ? message : fallbackMessage);
84
+ if (typeof name === "string") {
85
+ normalizedError.name = name;
86
+ }
87
+ return normalizedError;
88
+ }
89
+ function normalizeCooldownMs(value) {
90
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 1e3) {
91
+ return DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS;
92
+ }
93
+ return value;
94
+ }
95
+ function resolveChunkRecoveryPolicy(config, context) {
96
+ const options = typeof config === "object" ? config : void 0;
97
+ const configuredEnabled = typeof config === "boolean" ? config : options?.enabled;
98
+ const enabledForEnvironment = configuredEnabled ?? context.isProduction;
99
+ if (!enabledForEnvironment) {
100
+ return {
101
+ enabled: false,
102
+ disabledReason: configuredEnabled === false ? "configuration" : "environment",
103
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
104
+ globalHandlers: options?.globalHandlers === true
105
+ };
106
+ }
107
+ if (context.isMicroApp && options?.allowInMicroApp !== true) {
108
+ return {
109
+ enabled: false,
110
+ disabledReason: "micro-app",
111
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
112
+ globalHandlers: options?.globalHandlers === true
113
+ };
114
+ }
115
+ return {
116
+ enabled: true,
117
+ cooldownMs: normalizeCooldownMs(options?.cooldownMs),
118
+ globalHandlers: options?.globalHandlers === true
119
+ };
120
+ }
121
+ function parseStoredChunkRecoveryState(value) {
122
+ if (value === null) {
123
+ return { attempts: [] };
124
+ }
125
+ const parsed = JSON.parse(value);
126
+ if (!Array.isArray(parsed.attempts)) {
127
+ throw new Error("Invalid chunk recovery state");
128
+ }
129
+ const attempts = parsed.attempts.map((attempt) => {
130
+ if (typeof attempt !== "object" || attempt === null || typeof attempt.recoveryId !== "string" || typeof attempt.attemptedAt !== "number" || !Number.isFinite(attempt.attemptedAt)) {
131
+ throw new Error("Invalid chunk recovery attempt");
132
+ }
133
+ return attempt;
134
+ });
135
+ if (parsed.lastAttemptAt !== void 0 && (typeof parsed.lastAttemptAt !== "number" || !Number.isFinite(parsed.lastAttemptAt))) {
136
+ throw new Error("Invalid chunk recovery timestamp");
137
+ }
138
+ return { attempts, lastAttemptAt: parsed.lastAttemptAt };
139
+ }
140
+ function claimChunkRecoveryAttempt(storage, options) {
141
+ const {
142
+ cooldownMs = DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS,
143
+ now,
144
+ recoveryId,
145
+ storageKey = CHUNK_RECOVERY_STORAGE_KEY
146
+ } = options;
147
+ let state;
148
+ try {
149
+ state = parseStoredChunkRecoveryState(storage.getItem(storageKey));
150
+ } catch {
151
+ return { claimed: false, reason: "storage-unavailable" };
152
+ }
153
+ const existingAttempt = state.attempts.find((attempt) => attempt.recoveryId === recoveryId);
154
+ if (existingAttempt) {
155
+ return {
156
+ claimed: false,
157
+ reason: "already-attempted",
158
+ attemptedAt: existingAttempt.attemptedAt
159
+ };
160
+ }
161
+ const inCooldown = state.lastAttemptAt !== void 0 && (state.lastAttemptAt > now || now - state.lastAttemptAt < cooldownMs);
162
+ if (inCooldown) {
163
+ return { claimed: false, reason: "cooldown", attemptedAt: state.lastAttemptAt };
164
+ }
165
+ try {
166
+ storage.setItem(
167
+ storageKey,
168
+ JSON.stringify({
169
+ attempts: [...state.attempts, { attemptedAt: now, recoveryId }],
170
+ lastAttemptAt: now
171
+ })
172
+ );
173
+ } catch {
174
+ return { claimed: false, reason: "storage-unavailable" };
175
+ }
176
+ return { claimed: true, attemptedAt: now };
177
+ }
178
+ function isMicroAppRuntime() {
179
+ if (typeof window === "undefined") {
180
+ return false;
181
+ }
182
+ const runtimeWindow = window;
183
+ return Boolean(runtimeWindow.__POWERED_BY_QIANKUN__ || runtimeWindow.__GARFISH__);
184
+ }
185
+ function getSessionStorage() {
186
+ if (typeof window === "undefined") {
187
+ return null;
188
+ }
189
+ try {
190
+ return window.sessionStorage;
191
+ } catch {
192
+ return null;
193
+ }
194
+ }
195
+ function invokeSafely(callback, label) {
196
+ if (!callback) {
197
+ return;
198
+ }
199
+ try {
200
+ callback();
201
+ } catch (error) {
202
+ console.error(`[ywkf] ${label} \u6267\u884C\u5931\u8D25:`, error);
203
+ }
204
+ }
205
+ function schedulePageReplace(timestamp) {
206
+ setTimeout(() => {
207
+ try {
208
+ const reloadUrl = new URL(window.location.href);
209
+ reloadUrl.searchParams.set(CHUNK_RECOVERY_CACHE_BUST_PARAM, String(timestamp));
210
+ window.location.replace(reloadUrl.toString());
211
+ } catch (error) {
212
+ console.error("[ywkf] chunk \u6062\u590D\u5237\u65B0\u5931\u8D25:", error);
213
+ }
214
+ }, 0);
215
+ }
216
+ function clearChunkRecoveryCacheBustParam() {
217
+ if (typeof window === "undefined") {
218
+ return;
219
+ }
220
+ try {
221
+ const currentUrl = new URL(window.location.href);
222
+ if (!currentUrl.searchParams.has(CHUNK_RECOVERY_CACHE_BUST_PARAM)) {
223
+ return;
224
+ }
225
+ currentUrl.searchParams.delete(CHUNK_RECOVERY_CACHE_BUST_PARAM);
226
+ window.history.replaceState(window.history.state, "", currentUrl.toString());
227
+ } catch (error) {
228
+ console.error("[ywkf] \u6E05\u7406 chunk \u6062\u590D\u53C2\u6570\u5931\u8D25:", error);
229
+ }
230
+ }
231
+ function handleChunkLoadErrorInternal(reason, options, trustedChunkEvent) {
232
+ if (!trustedChunkEvent && !isChunkLoadError(reason)) {
233
+ return null;
234
+ }
235
+ const error = toError(reason);
236
+ const timestamp = Date.now();
237
+ const recoveryId = resolveRecoveryId(reason, options.config);
238
+ const isMicroApp = options.isMicroApp ?? isMicroAppRuntime();
239
+ const policy = resolveChunkRecoveryPolicy(options.config, {
240
+ isProduction: process.env.NODE_ENV === "production",
241
+ isMicroApp
242
+ });
243
+ let outcome;
244
+ if (!policy.enabled) {
245
+ outcome = policy.disabledReason === "micro-app" ? "reload-skipped-micro-app" : "reload-skipped-disabled";
246
+ } else {
247
+ const storage = getSessionStorage();
248
+ const claim = storage ? claimChunkRecoveryAttempt(storage, {
249
+ cooldownMs: policy.cooldownMs,
250
+ now: timestamp,
251
+ recoveryId
252
+ }) : { claimed: false, reason: "storage-unavailable" };
253
+ if (claim.claimed) {
254
+ outcome = "reload-scheduled";
255
+ } else if (claim.reason === "already-attempted") {
256
+ outcome = "reload-skipped-already-attempted";
257
+ } else if (claim.reason === "cooldown") {
258
+ outcome = "reload-skipped-cooldown";
259
+ } else {
260
+ outcome = "reload-skipped-storage-unavailable";
261
+ }
262
+ }
263
+ const event = {
264
+ error,
265
+ source: options.source,
266
+ outcome,
267
+ timestamp,
268
+ cooldownMs: policy.cooldownMs,
269
+ recoveryId
270
+ };
271
+ invokeSafely(() => options.onError?.(error), "onError");
272
+ invokeSafely(() => options.onRecoveryEvent?.(event), "onRecoveryEvent");
273
+ if (outcome === "reload-scheduled") {
274
+ schedulePageReplace(timestamp);
275
+ }
276
+ return event;
277
+ }
278
+ function handleChunkLoadError(reason, options) {
279
+ return handleChunkLoadErrorInternal(reason, options, false);
280
+ }
281
+ function getLatestSubscriber(registry) {
282
+ let latest;
283
+ for (const subscriber of registry.subscribers.values()) {
284
+ latest = subscriber;
285
+ }
286
+ return latest;
287
+ }
288
+ function createGlobalChunkRecoveryRegistry() {
289
+ const registry = {
290
+ subscribers: /* @__PURE__ */ new Map()
291
+ };
292
+ registry.handleUnhandledRejection = (event) => {
293
+ const subscriber = getLatestSubscriber(registry);
294
+ if (!subscriber) return;
295
+ const recoveryEvent = handleChunkLoadError(event.reason, {
296
+ ...subscriber,
297
+ source: "unhandled-rejection"
298
+ });
299
+ if (recoveryEvent?.outcome === "reload-scheduled") {
300
+ event.preventDefault();
301
+ }
302
+ };
303
+ registry.handleVitePreloadError = (event) => {
304
+ const subscriber = getLatestSubscriber(registry);
305
+ if (!subscriber) return;
306
+ const payload = event.payload;
307
+ const recoveryEvent = handleChunkLoadErrorInternal(
308
+ payload,
309
+ {
310
+ ...subscriber,
311
+ source: "vite-preload-error"
312
+ },
313
+ true
314
+ );
315
+ if (recoveryEvent?.outcome === "reload-scheduled") {
316
+ event.preventDefault();
317
+ }
318
+ };
319
+ return registry;
320
+ }
321
+ function installGlobalChunkRecoveryHandlers(options) {
322
+ if (typeof window === "undefined") {
323
+ return () => {
324
+ };
325
+ }
326
+ const isMicroApp = options.isMicroApp ?? isMicroAppRuntime();
327
+ const policy = resolveChunkRecoveryPolicy(options.config, {
328
+ isProduction: process.env.NODE_ENV === "production",
329
+ isMicroApp
330
+ });
331
+ if (!policy.enabled || !policy.globalHandlers) {
332
+ return () => {
333
+ };
334
+ }
335
+ const runtimeWindow = window;
336
+ let registry = runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL];
337
+ if (!registry && runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL] === true) {
338
+ return () => {
339
+ };
340
+ }
341
+ if (!registry) {
342
+ registry = createGlobalChunkRecoveryRegistry();
343
+ runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL] = registry;
344
+ runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL] = true;
345
+ window.addEventListener("unhandledrejection", registry.handleUnhandledRejection);
346
+ window.addEventListener("vite:preloadError", registry.handleVitePreloadError);
347
+ }
348
+ const subscriberId = /* @__PURE__ */ Symbol("chunk-recovery-subscriber");
349
+ registry.subscribers.set(subscriberId, { ...options, isMicroApp });
350
+ let disposed = false;
351
+ return () => {
352
+ if (disposed) return;
353
+ disposed = true;
354
+ registry.subscribers.delete(subscriberId);
355
+ if (registry.subscribers.size > 0) return;
356
+ window.removeEventListener("unhandledrejection", registry.handleUnhandledRejection);
357
+ window.removeEventListener("vite:preloadError", registry.handleVitePreloadError);
358
+ if (runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL] === registry) {
359
+ delete runtimeWindow[CHUNK_RECOVERY_REGISTRY_SYMBOL];
360
+ delete runtimeWindow[CHUNK_RECOVERY_ACTIVE_SYMBOL];
361
+ }
362
+ };
363
+ }
364
+
1
365
  // src/runtime/bootstrap.tsx
2
366
  import { createRoot } from "react-dom/client";
3
367
 
@@ -49,8 +413,23 @@ function useIsDev() {
49
413
  }
50
414
 
51
415
  // src/runtime/error-boundary.tsx
52
- import { Component } from "react";
416
+ import {
417
+ Component,
418
+ createContext as createContext2,
419
+ useContext as useContext2,
420
+ useEffect,
421
+ useMemo,
422
+ useRef
423
+ } from "react";
424
+ import { isRouteErrorResponse, useRouteError } from "react-router";
53
425
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
426
+ var ChunkRecoveryRuntimeContext = createContext2(null);
427
+ function ChunkRecoveryRuntimeProvider({
428
+ children,
429
+ ...value
430
+ }) {
431
+ return /* @__PURE__ */ jsx2(ChunkRecoveryRuntimeContext.Provider, { value, children });
432
+ }
54
433
  var ErrorBoundary = class extends Component {
55
434
  constructor(props) {
56
435
  super(props);
@@ -60,15 +439,31 @@ var ErrorBoundary = class extends Component {
60
439
  return { hasError: true, error };
61
440
  }
62
441
  componentDidCatch(error, errorInfo) {
63
- console.error("[ErrorBoundary] Caught error:", error, errorInfo);
64
- this.props.onError?.(error, errorInfo);
442
+ const reportError = () => {
443
+ console.error("[ErrorBoundary] Caught error:", error, errorInfo);
444
+ this.props.onError?.(error, errorInfo);
445
+ };
446
+ if (this.props.chunkRecovery === void 0) {
447
+ reportError();
448
+ return;
449
+ }
450
+ const recoveryEvent = handleChunkLoadError(error, {
451
+ source: "error-boundary",
452
+ config: this.props.chunkRecovery,
453
+ isMicroApp: this.props.isMicroApp,
454
+ onError: reportError,
455
+ onRecoveryEvent: this.props.onRecoveryEvent
456
+ });
457
+ if (!recoveryEvent) {
458
+ reportError();
459
+ }
65
460
  }
66
461
  reset = () => {
67
462
  this.setState({ hasError: false, error: null });
68
463
  };
69
464
  render() {
70
465
  const { hasError, error } = this.state;
71
- const { children, fallback } = this.props;
466
+ const { children, chunkRecovery, fallback, isMicroApp } = this.props;
72
467
  if (hasError && error) {
73
468
  if (typeof fallback === "function") {
74
469
  return fallback(error, this.reset);
@@ -76,12 +471,86 @@ var ErrorBoundary = class extends Component {
76
471
  if (fallback) {
77
472
  return fallback;
78
473
  }
79
- return /* @__PURE__ */ jsx2(DefaultErrorFallback, { error, onReset: this.reset });
474
+ const resolvedIsMicroApp = isMicroApp ?? isMicroAppRuntime();
475
+ const chunkRecoveryEnabled = chunkRecovery !== void 0 && resolveChunkRecoveryPolicy(chunkRecovery, {
476
+ isProduction: process.env.NODE_ENV === "production",
477
+ isMicroApp: resolvedIsMicroApp
478
+ }).enabled;
479
+ const canReloadPage = !resolvedIsMicroApp || typeof chunkRecovery === "object" && chunkRecovery.allowInMicroApp === true;
480
+ return /* @__PURE__ */ jsx2(
481
+ DefaultErrorFallback,
482
+ {
483
+ canReloadPage,
484
+ error,
485
+ isChunkError: chunkRecoveryEnabled && isChunkLoadError(error),
486
+ onReset: this.reset
487
+ }
488
+ );
80
489
  }
81
490
  return children;
82
491
  }
83
492
  };
84
- function DefaultErrorFallback({ error, onReset }) {
493
+ var UNHANDLED_ROUTE_ERROR = /* @__PURE__ */ Symbol("unhandled-route-error");
494
+ function normalizeRouteError(reason) {
495
+ if (reason instanceof Error) return reason;
496
+ if (typeof reason === "string") return new Error(reason);
497
+ if (isRouteErrorResponse(reason)) {
498
+ const message = [reason.status, reason.statusText].filter(Boolean).join(" ");
499
+ const error = new Error(message || "Route request failed");
500
+ error.name = "RouteErrorResponse";
501
+ return error;
502
+ }
503
+ return new Error("Route rendering failed");
504
+ }
505
+ function RouteErrorBoundary({ fallback }) {
506
+ const reason = useRouteError();
507
+ const error = useMemo(() => normalizeRouteError(reason), [reason]);
508
+ const recoveryRuntime = useContext2(ChunkRecoveryRuntimeContext);
509
+ const handledReasonRef = useRef(UNHANDLED_ROUTE_ERROR);
510
+ useEffect(() => {
511
+ if (Object.is(handledReasonRef.current, reason)) return;
512
+ handledReasonRef.current = reason;
513
+ const reportError = () => {
514
+ console.error("[RouteErrorBoundary] Caught error:", error);
515
+ recoveryRuntime?.onError?.(error);
516
+ };
517
+ if (!recoveryRuntime) {
518
+ reportError();
519
+ return;
520
+ }
521
+ const recoveryEvent = handleChunkLoadError(reason, {
522
+ source: "route-error-boundary",
523
+ config: recoveryRuntime.chunkRecovery,
524
+ isMicroApp: recoveryRuntime.isMicroApp,
525
+ onError: reportError,
526
+ onRecoveryEvent: recoveryRuntime.onRecoveryEvent
527
+ });
528
+ if (!recoveryEvent) reportError();
529
+ }, [error, reason, recoveryRuntime]);
530
+ if (typeof fallback === "function") return fallback(error);
531
+ if (fallback) return fallback;
532
+ const chunkRecovery = recoveryRuntime?.chunkRecovery;
533
+ const resolvedIsMicroApp = recoveryRuntime?.isMicroApp ?? isMicroAppRuntime();
534
+ const chunkRecoveryEnabled = chunkRecovery !== void 0 && resolveChunkRecoveryPolicy(chunkRecovery, {
535
+ isProduction: process.env.NODE_ENV === "production",
536
+ isMicroApp: resolvedIsMicroApp
537
+ }).enabled;
538
+ const canReloadPage = !resolvedIsMicroApp || typeof chunkRecovery === "object" && chunkRecovery.allowInMicroApp === true;
539
+ return /* @__PURE__ */ jsx2(
540
+ DefaultErrorFallback,
541
+ {
542
+ canReloadPage,
543
+ error,
544
+ isChunkError: chunkRecoveryEnabled && isChunkLoadError(reason)
545
+ }
546
+ );
547
+ }
548
+ function DefaultErrorFallback({
549
+ canReloadPage,
550
+ error,
551
+ isChunkError,
552
+ onReset
553
+ }) {
85
554
  const isDev = process.env.NODE_ENV === "development";
86
555
  return /* @__PURE__ */ jsx2(
87
556
  "div",
@@ -107,7 +576,7 @@ function DefaultErrorFallback({ error, onReset }) {
107
576
  },
108
577
  children: [
109
578
  /* @__PURE__ */ jsx2("h1", { style: { color: "#ff4d4f", marginBottom: 16 }, children: "\u9875\u9762\u51FA\u9519\u4E86" }),
110
- /* @__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" }),
579
+ /* @__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" }),
111
580
  isDev && /* @__PURE__ */ jsxs(
112
581
  "details",
113
582
  {
@@ -141,8 +610,8 @@ function DefaultErrorFallback({ error, onReset }) {
141
610
  ]
142
611
  }
143
612
  ),
144
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
145
- /* @__PURE__ */ jsx2(
613
+ (canReloadPage || !isChunkError && onReset) && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
614
+ !isChunkError && onReset && /* @__PURE__ */ jsx2(
146
615
  "button",
147
616
  {
148
617
  onClick: onReset,
@@ -157,7 +626,7 @@ function DefaultErrorFallback({ error, onReset }) {
157
626
  children: "\u91CD\u8BD5"
158
627
  }
159
628
  ),
160
- /* @__PURE__ */ jsx2(
629
+ canReloadPage && /* @__PURE__ */ jsx2(
161
630
  "button",
162
631
  {
163
632
  onClick: () => window.location.reload(),
@@ -206,7 +675,7 @@ function composeProviders(providers) {
206
675
  }, children);
207
676
  };
208
677
  }
209
- function RootProvider({ config, children }) {
678
+ function RootProvider({ config, children, isMicroApp }) {
210
679
  const {
211
680
  appName = "app",
212
681
  router,
@@ -214,7 +683,8 @@ function RootProvider({ config, children }) {
214
683
  strictMode = true,
215
684
  antd = { enabled: true },
216
685
  providers = [],
217
- lifecycle
686
+ lifecycle,
687
+ chunkRecovery
218
688
  } = config;
219
689
  const appContextValue = {
220
690
  appName,
@@ -245,8 +715,30 @@ function RootProvider({ config, children }) {
245
715
  ...providers.map((p) => ({ ...p, order: p.order ?? 50 }))
246
716
  ];
247
717
  const ComposedProviders = composeProviders(allProviders);
718
+ const rootChunkRecovery = chunkRecovery ?? {
719
+ enabled: process.env.NODE_ENV === "production"
720
+ };
721
+ const resolvedIsMicroApp = isMicroApp ?? isMicroAppRuntime();
248
722
  const content = router ? /* @__PURE__ */ jsx3(Suspense, { fallback: /* @__PURE__ */ jsx3(DefaultLoading, {}), children: /* @__PURE__ */ jsx3(RouterProvider, { router }) }) : children;
249
- const app = /* @__PURE__ */ jsx3(ErrorBoundary, { onError: lifecycle?.onError, children: /* @__PURE__ */ jsx3(ComposedProviders, { children: content }) });
723
+ const app = /* @__PURE__ */ jsx3(
724
+ ErrorBoundary,
725
+ {
726
+ chunkRecovery: rootChunkRecovery,
727
+ isMicroApp: resolvedIsMicroApp,
728
+ onError: lifecycle?.onError,
729
+ onRecoveryEvent: lifecycle?.onRecoveryEvent,
730
+ children: /* @__PURE__ */ jsx3(
731
+ ChunkRecoveryRuntimeProvider,
732
+ {
733
+ chunkRecovery: rootChunkRecovery,
734
+ isMicroApp: resolvedIsMicroApp,
735
+ onError: lifecycle?.onError,
736
+ onRecoveryEvent: lifecycle?.onRecoveryEvent,
737
+ children: /* @__PURE__ */ jsx3(ComposedProviders, { children: content })
738
+ }
739
+ )
740
+ }
741
+ );
250
742
  if (strictMode) {
251
743
  return /* @__PURE__ */ jsx3(StrictMode, { children: app });
252
744
  }
@@ -263,6 +755,7 @@ function createProvider(component, props, order) {
263
755
  // src/runtime/bootstrap.tsx
264
756
  import { jsx as jsx4 } from "react/jsx-runtime";
265
757
  var root = null;
758
+ var disposeChunkRecoveryHandlers = null;
266
759
  async function resolveAppConfig(config) {
267
760
  if (config.router || !config.conventionalRoutes) {
268
761
  return config;
@@ -285,6 +778,10 @@ async function resolveAppConfig(config) {
285
778
  }
286
779
  async function bootstrap(config) {
287
780
  const { rootId = "root", lifecycle } = config;
781
+ const isMicroApp = isMicroAppEnv();
782
+ if (!isMicroApp) {
783
+ clearChunkRecoveryCacheBustParam();
784
+ }
288
785
  const resolvedConfig = await resolveAppConfig(config);
289
786
  if (lifecycle?.onBeforeMount) {
290
787
  await lifecycle.onBeforeMount();
@@ -293,8 +790,22 @@ async function bootstrap(config) {
293
790
  if (!rootElement) {
294
791
  throw new Error(`\u627E\u4E0D\u5230\u6839\u5143\u7D20 #${rootId}`);
295
792
  }
296
- root = createRoot(rootElement);
297
- root.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
793
+ disposeChunkRecoveryHandlers?.();
794
+ const nextDisposeChunkRecoveryHandlers = installGlobalChunkRecoveryHandlers({
795
+ config: resolvedConfig.chunkRecovery,
796
+ isMicroApp,
797
+ onError: (error) => resolvedConfig.lifecycle?.onError?.(error),
798
+ onRecoveryEvent: resolvedConfig.lifecycle?.onRecoveryEvent
799
+ });
800
+ try {
801
+ root = createRoot(rootElement);
802
+ root.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig, isMicroApp }));
803
+ disposeChunkRecoveryHandlers = nextDisposeChunkRecoveryHandlers;
804
+ } catch (error) {
805
+ nextDisposeChunkRecoveryHandlers();
806
+ root = null;
807
+ throw error;
808
+ }
298
809
  if (lifecycle?.onMounted) {
299
810
  setTimeout(() => {
300
811
  lifecycle.onMounted?.();
@@ -307,6 +818,8 @@ async function bootstrap(config) {
307
818
  }
308
819
  }
309
820
  function unmount() {
821
+ disposeChunkRecoveryHandlers?.();
822
+ disposeChunkRecoveryHandlers = null;
310
823
  if (root) {
311
824
  root.unmount();
312
825
  root = null;
@@ -314,6 +827,7 @@ function unmount() {
314
827
  }
315
828
  function createMicroApp(getConfig) {
316
829
  let microRoot = null;
830
+ let disposeMicroChunkRecoveryHandlers = null;
317
831
  return {
318
832
  /**
319
833
  * 微前端 bootstrap 生命周期
@@ -337,8 +851,22 @@ function createMicroApp(getConfig) {
337
851
  if (resolvedConfig.lifecycle?.onBeforeMount) {
338
852
  await resolvedConfig.lifecycle.onBeforeMount();
339
853
  }
340
- microRoot = createRoot(rootElement);
341
- microRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
854
+ disposeMicroChunkRecoveryHandlers?.();
855
+ const nextDisposeMicroChunkRecoveryHandlers = installGlobalChunkRecoveryHandlers({
856
+ config: resolvedConfig.chunkRecovery,
857
+ isMicroApp: true,
858
+ onError: (error) => resolvedConfig.lifecycle?.onError?.(error),
859
+ onRecoveryEvent: resolvedConfig.lifecycle?.onRecoveryEvent
860
+ });
861
+ try {
862
+ microRoot = createRoot(rootElement);
863
+ microRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig, isMicroApp: true }));
864
+ disposeMicroChunkRecoveryHandlers = nextDisposeMicroChunkRecoveryHandlers;
865
+ } catch (error) {
866
+ nextDisposeMicroChunkRecoveryHandlers();
867
+ microRoot = null;
868
+ throw error;
869
+ }
342
870
  if (resolvedConfig.lifecycle?.onMounted) {
343
871
  setTimeout(() => {
344
872
  resolvedConfig.lifecycle?.onMounted?.();
@@ -358,6 +886,8 @@ function createMicroApp(getConfig) {
358
886
  microRoot.unmount();
359
887
  microRoot = null;
360
888
  }
889
+ disposeMicroChunkRecoveryHandlers?.();
890
+ disposeMicroChunkRecoveryHandlers = null;
361
891
  },
362
892
  /**
363
893
  * 微前端 update 生命周期(可选)
@@ -369,6 +899,7 @@ function createMicroApp(getConfig) {
369
899
  }
370
900
  function createGarfishProvider(getConfig) {
371
901
  let garfishRoot = null;
902
+ let disposeGarfishChunkRecoveryHandlers = null;
372
903
  return function provider(providerContext) {
373
904
  const { dom, basename } = providerContext;
374
905
  return {
@@ -382,8 +913,22 @@ function createGarfishProvider(getConfig) {
382
913
  rootElement.id = rootId;
383
914
  dom.appendChild(rootElement);
384
915
  }
385
- garfishRoot = createRoot(rootElement);
386
- garfishRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
916
+ disposeGarfishChunkRecoveryHandlers?.();
917
+ const nextDisposeGarfishChunkRecoveryHandlers = installGlobalChunkRecoveryHandlers({
918
+ config: resolvedConfig.chunkRecovery,
919
+ isMicroApp: true,
920
+ onError: (error) => resolvedConfig.lifecycle?.onError?.(error),
921
+ onRecoveryEvent: resolvedConfig.lifecycle?.onRecoveryEvent
922
+ });
923
+ try {
924
+ garfishRoot = createRoot(rootElement);
925
+ garfishRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig, isMicroApp: true }));
926
+ disposeGarfishChunkRecoveryHandlers = nextDisposeGarfishChunkRecoveryHandlers;
927
+ } catch (error) {
928
+ nextDisposeGarfishChunkRecoveryHandlers();
929
+ garfishRoot = null;
930
+ throw error;
931
+ }
387
932
  if (resolvedConfig.lifecycle?.onMounted) {
388
933
  setTimeout(() => resolvedConfig.lifecycle?.onMounted?.(), 0);
389
934
  }
@@ -393,6 +938,8 @@ function createGarfishProvider(getConfig) {
393
938
  garfishRoot.unmount();
394
939
  garfishRoot = null;
395
940
  }
941
+ disposeGarfishChunkRecoveryHandlers?.();
942
+ disposeGarfishChunkRecoveryHandlers = null;
396
943
  }
397
944
  };
398
945
  };
@@ -415,14 +962,26 @@ function getMicroAppPublicPath() {
415
962
  export {
416
963
  AppContext,
417
964
  AppContextProvider,
965
+ CHUNK_RECOVERY_ACTIVE_SYMBOL,
966
+ CHUNK_RECOVERY_CACHE_BUST_PARAM,
967
+ CHUNK_RECOVERY_STORAGE_KEY,
968
+ DEFAULT_CHUNK_RECOVERY_COOLDOWN_MS,
418
969
  ErrorBoundary,
419
970
  RootProvider,
971
+ RouteErrorBoundary,
420
972
  bootstrap,
973
+ claimChunkRecoveryAttempt,
974
+ clearChunkRecoveryCacheBustParam,
421
975
  createGarfishProvider,
422
976
  createMicroApp,
423
977
  createProvider,
424
978
  getMicroAppPublicPath,
979
+ handleChunkLoadError,
980
+ installGlobalChunkRecoveryHandlers,
981
+ isChunkLoadError,
425
982
  isMicroAppEnv,
983
+ isMicroAppRuntime,
984
+ resolveChunkRecoveryPolicy,
426
985
  unmount,
427
986
  useApp,
428
987
  useAppName,