@vlian/framework 1.2.25 → 1.2.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/dist/analytics.umd.js +208 -2395
  2. package/dist/analytics.umd.js.map +1 -1
  3. package/dist/core/Test.cjs +2 -2
  4. package/dist/core/Test.cjs.map +1 -1
  5. package/dist/core/Test.js +1 -1
  6. package/dist/core/Test.js.map +1 -1
  7. package/dist/core/config/ConfigLoader.cjs +7 -7
  8. package/dist/core/config/ConfigLoader.cjs.map +1 -1
  9. package/dist/core/config/ConfigLoader.js +1 -1
  10. package/dist/core/config/ConfigLoader.js.map +1 -1
  11. package/dist/core/error/ErrorBoundary.cjs +6 -6
  12. package/dist/core/error/ErrorBoundary.cjs.map +1 -1
  13. package/dist/core/error/ErrorBoundary.d.ts +1 -1
  14. package/dist/core/error/ErrorBoundary.js +2 -2
  15. package/dist/core/error/ErrorBoundary.js.map +1 -1
  16. package/dist/core/error/ErrorHandler.cjs +19 -19
  17. package/dist/core/error/ErrorHandler.cjs.map +1 -1
  18. package/dist/core/error/ErrorHandler.d.ts +2 -2
  19. package/dist/core/error/ErrorHandler.js +2 -2
  20. package/dist/core/error/ErrorHandler.js.map +1 -1
  21. package/dist/core/event/AppEventBus.cjs +5 -5
  22. package/dist/core/event/AppEventBus.cjs.map +1 -1
  23. package/dist/core/event/AppEventBus.js +1 -1
  24. package/dist/core/event/AppEventBus.js.map +1 -1
  25. package/dist/core/initialization/InitializationErrorThrower.cjs.map +1 -1
  26. package/dist/core/initialization/InitializationErrorThrower.js.map +1 -1
  27. package/dist/core/initialization/initialization.cjs +3 -3
  28. package/dist/core/initialization/initialization.cjs.map +1 -1
  29. package/dist/core/initialization/initialization.d.ts +1 -1
  30. package/dist/core/initialization/initialization.js +1 -1
  31. package/dist/core/initialization/initialization.js.map +1 -1
  32. package/dist/core/initialization/initializationErrorState.cjs +2 -2
  33. package/dist/core/initialization/initializationErrorState.cjs.map +1 -1
  34. package/dist/core/initialization/initializationErrorState.d.ts +1 -1
  35. package/dist/core/initialization/initializationErrorState.js +1 -1
  36. package/dist/core/initialization/initializationErrorState.js.map +1 -1
  37. package/dist/core/kernel/defaultAdapters.cjs +14 -13
  38. package/dist/core/kernel/defaultAdapters.cjs.map +1 -1
  39. package/dist/core/kernel/defaultAdapters.js +2 -1
  40. package/dist/core/kernel/defaultAdapters.js.map +1 -1
  41. package/dist/core/kernel/types.d.ts +1 -1
  42. package/dist/core/kernel/types.js.map +1 -1
  43. package/dist/core/router/RouterManager.cjs +9 -9
  44. package/dist/core/router/RouterManager.cjs.map +1 -1
  45. package/dist/core/router/RouterManager.js +1 -1
  46. package/dist/core/router/RouterManager.js.map +1 -1
  47. package/dist/core/router/adapter/AdapterManager.cjs +10 -10
  48. package/dist/core/router/adapter/AdapterManager.cjs.map +1 -1
  49. package/dist/core/router/adapter/AdapterManager.js +1 -1
  50. package/dist/core/router/adapter/AdapterManager.js.map +1 -1
  51. package/dist/core/router/adapter/react-router/ReactRouterAdapter.cjs +4 -4
  52. package/dist/core/router/adapter/react-router/ReactRouterAdapter.cjs.map +1 -1
  53. package/dist/core/router/adapter/react-router/ReactRouterAdapter.js +1 -1
  54. package/dist/core/router/adapter/react-router/ReactRouterAdapter.js.map +1 -1
  55. package/dist/core/router/dynamic/DynamicRouteManager.cjs +8 -8
  56. package/dist/core/router/dynamic/DynamicRouteManager.cjs.map +1 -1
  57. package/dist/core/router/dynamic/DynamicRouteManager.js +1 -1
  58. package/dist/core/router/dynamic/DynamicRouteManager.js.map +1 -1
  59. package/dist/core/router/errors/RouterError.cjs +4 -4
  60. package/dist/core/router/errors/RouterError.cjs.map +1 -1
  61. package/dist/core/router/errors/RouterError.d.ts +1 -1
  62. package/dist/core/router/errors/RouterError.js +1 -1
  63. package/dist/core/router/errors/RouterError.js.map +1 -1
  64. package/dist/core/router/lifecycle/RouterLifecycleManager.cjs +8 -8
  65. package/dist/core/router/lifecycle/RouterLifecycleManager.cjs.map +1 -1
  66. package/dist/core/router/lifecycle/RouterLifecycleManager.js +1 -1
  67. package/dist/core/router/lifecycle/RouterLifecycleManager.js.map +1 -1
  68. package/dist/core/router/middleware/RouterMiddlewareManager.cjs +11 -11
  69. package/dist/core/router/middleware/RouterMiddlewareManager.cjs.map +1 -1
  70. package/dist/core/router/middleware/RouterMiddlewareManager.js +1 -1
  71. package/dist/core/router/middleware/RouterMiddlewareManager.js.map +1 -1
  72. package/dist/core/router/middleware/auth.cjs +4 -4
  73. package/dist/core/router/middleware/auth.cjs.map +1 -1
  74. package/dist/core/router/middleware/auth.js +1 -1
  75. package/dist/core/router/middleware/auth.js.map +1 -1
  76. package/dist/core/router/monitoring/RouterMonitoring.cjs +2 -2
  77. package/dist/core/router/monitoring/RouterMonitoring.cjs.map +1 -1
  78. package/dist/core/router/monitoring/RouterMonitoring.js +2 -2
  79. package/dist/core/router/monitoring/RouterMonitoring.js.map +1 -1
  80. package/dist/core/router/navigation/RouterNavigation.cjs +7 -7
  81. package/dist/core/router/navigation/RouterNavigation.cjs.map +1 -1
  82. package/dist/core/router/navigation/RouterNavigation.js +1 -1
  83. package/dist/core/router/navigation/RouterNavigation.js.map +1 -1
  84. package/dist/core/router/performance/RouteCache.cjs +7 -7
  85. package/dist/core/router/performance/RouteCache.cjs.map +1 -1
  86. package/dist/core/router/performance/RouteCache.js +1 -1
  87. package/dist/core/router/performance/RouteCache.js.map +1 -1
  88. package/dist/core/router/performance/RoutePreloader.cjs +6 -6
  89. package/dist/core/router/performance/RoutePreloader.cjs.map +1 -1
  90. package/dist/core/router/performance/RoutePreloader.js +1 -1
  91. package/dist/core/router/performance/RoutePreloader.js.map +1 -1
  92. package/dist/core/router/plugin/RouterPluginManager.cjs +8 -8
  93. package/dist/core/router/plugin/RouterPluginManager.cjs.map +1 -1
  94. package/dist/core/router/plugin/RouterPluginManager.js +1 -1
  95. package/dist/core/router/plugin/RouterPluginManager.js.map +1 -1
  96. package/dist/core/router/utils/adapters/react-router/transform.cjs +6 -2
  97. package/dist/core/router/utils/adapters/react-router/transform.cjs.map +1 -1
  98. package/dist/core/router/utils/adapters/react-router/transform.js +5 -1
  99. package/dist/core/router/utils/adapters/react-router/transform.js.map +1 -1
  100. package/dist/core/router/utils/transform.cjs +13 -12
  101. package/dist/core/router/utils/transform.cjs.map +1 -1
  102. package/dist/core/router/utils/transform.js +2 -1
  103. package/dist/core/router/utils/transform.js.map +1 -1
  104. package/dist/core/router/validation/RouterConfigValidator.cjs +2 -2
  105. package/dist/core/router/validation/RouterConfigValidator.cjs.map +1 -1
  106. package/dist/core/router/validation/RouterConfigValidator.js +1 -1
  107. package/dist/core/router/validation/RouterConfigValidator.js.map +1 -1
  108. package/dist/core/router/version/RouteVersionManager.cjs +6 -6
  109. package/dist/core/router/version/RouteVersionManager.cjs.map +1 -1
  110. package/dist/core/router/version/RouteVersionManager.js +1 -1
  111. package/dist/core/router/version/RouteVersionManager.js.map +1 -1
  112. package/dist/core/splash/SplashScreen.cjs +4 -4
  113. package/dist/core/splash/SplashScreen.cjs.map +1 -1
  114. package/dist/core/splash/SplashScreen.js +1 -1
  115. package/dist/core/splash/SplashScreen.js.map +1 -1
  116. package/dist/core/startup/initializeServices.cjs +15 -15
  117. package/dist/core/startup/initializeServices.cjs.map +1 -1
  118. package/dist/core/startup/initializeServices.d.ts +2 -2
  119. package/dist/core/startup/initializeServices.js +3 -3
  120. package/dist/core/startup/initializeServices.js.map +1 -1
  121. package/dist/core/startup/performanceTracker.cjs.map +1 -1
  122. package/dist/core/startup/performanceTracker.d.ts +1 -1
  123. package/dist/core/startup/performanceTracker.js.map +1 -1
  124. package/dist/core/startup/renderApp.cjs +3 -3
  125. package/dist/core/startup/renderApp.cjs.map +1 -1
  126. package/dist/core/startup/renderApp.d.ts +1 -1
  127. package/dist/core/startup/renderApp.js +2 -2
  128. package/dist/core/startup/renderApp.js.map +1 -1
  129. package/dist/core/startup/startApp.cjs +24 -24
  130. package/dist/core/startup/startApp.cjs.map +1 -1
  131. package/dist/core/startup/startApp.js +5 -5
  132. package/dist/core/startup/startApp.js.map +1 -1
  133. package/dist/core/types.d.ts +3 -3
  134. package/dist/core/types.js.map +1 -1
  135. package/dist/index.cjs +15 -2
  136. package/dist/index.cjs.map +1 -1
  137. package/dist/index.d.ts +6 -4
  138. package/dist/index.js +5 -4
  139. package/dist/index.js.map +1 -1
  140. package/dist/index.umd.js +2150 -23125
  141. package/dist/index.umd.js.map +1 -1
  142. package/dist/kernel/constants.cjs +67 -0
  143. package/dist/kernel/constants.cjs.map +1 -0
  144. package/dist/kernel/constants.d.ts +5 -0
  145. package/dist/kernel/constants.js +43 -0
  146. package/dist/kernel/constants.js.map +1 -0
  147. package/dist/kernel/index.cjs +40 -0
  148. package/dist/kernel/index.cjs.map +1 -0
  149. package/dist/kernel/index.d.ts +3 -0
  150. package/dist/kernel/index.js +4 -0
  151. package/dist/kernel/index.js.map +1 -0
  152. package/dist/kernel/kernel.cjs +296 -0
  153. package/dist/kernel/kernel.cjs.map +1 -0
  154. package/dist/kernel/kernel.d.ts +40 -0
  155. package/dist/kernel/kernel.js +272 -0
  156. package/dist/kernel/kernel.js.map +1 -0
  157. package/dist/kernel/manager/cacheManager.cjs +46 -0
  158. package/dist/kernel/manager/cacheManager.cjs.map +1 -0
  159. package/dist/kernel/manager/cacheManager.d.ts +6 -0
  160. package/dist/kernel/manager/cacheManager.js +36 -0
  161. package/dist/kernel/manager/cacheManager.js.map +1 -0
  162. package/dist/kernel/manager/i18nManager.cjs +68 -0
  163. package/dist/kernel/manager/i18nManager.cjs.map +1 -0
  164. package/dist/kernel/manager/i18nManager.d.ts +8 -0
  165. package/dist/kernel/manager/i18nManager.js +58 -0
  166. package/dist/kernel/manager/i18nManager.js.map +1 -0
  167. package/dist/kernel/manager/index.cjs +30 -0
  168. package/dist/kernel/manager/index.cjs.map +1 -0
  169. package/dist/kernel/manager/index.d.ts +4 -0
  170. package/dist/kernel/manager/index.js +6 -0
  171. package/dist/kernel/manager/index.js.map +1 -0
  172. package/dist/kernel/manager/loggerManager.cjs +70 -0
  173. package/dist/kernel/manager/loggerManager.cjs.map +1 -0
  174. package/dist/kernel/manager/loggerManager.d.ts +14 -0
  175. package/dist/kernel/manager/loggerManager.js +60 -0
  176. package/dist/kernel/manager/loggerManager.js.map +1 -0
  177. package/dist/kernel/manager/persistence.cjs +93 -0
  178. package/dist/kernel/manager/persistence.cjs.map +1 -0
  179. package/dist/kernel/manager/persistence.d.ts +3 -0
  180. package/dist/kernel/manager/persistence.js +75 -0
  181. package/dist/kernel/manager/persistence.js.map +1 -0
  182. package/dist/kernel/manager/themeManager.cjs +85 -0
  183. package/dist/kernel/manager/themeManager.cjs.map +1 -0
  184. package/dist/kernel/manager/themeManager.d.ts +9 -0
  185. package/dist/kernel/manager/themeManager.js +75 -0
  186. package/dist/kernel/manager/themeManager.js.map +1 -0
  187. package/dist/kernel/types.cjs +6 -0
  188. package/dist/kernel/types.cjs.map +1 -0
  189. package/dist/kernel/types.d.ts +72 -0
  190. package/dist/kernel/types.js +3 -0
  191. package/dist/kernel/types.js.map +1 -0
  192. package/dist/library/storage/encryption.cjs +12 -13
  193. package/dist/library/storage/encryption.cjs.map +1 -1
  194. package/dist/library/storage/encryption.js +1 -2
  195. package/dist/library/storage/encryption.js.map +1 -1
  196. package/dist/request/adapter.d.ts +1 -0
  197. package/dist/request/core.d.ts +1 -0
  198. package/dist/request/index.d.ts +1 -42
  199. package/dist/request/plugin/csrfPlugin.d.ts +2 -2
  200. package/dist/request/plugin/queue.d.ts +2 -2
  201. package/dist/request/plugin.d.ts +1 -0
  202. package/dist/request/runtime.d.ts +1 -0
  203. package/dist/request/types.d.ts +1 -394
  204. package/dist/request/utils.d.ts +1 -0
  205. package/dist/state.umd.js +1 -1
  206. package/dist/utils/csrf.cjs +13 -152
  207. package/dist/utils/csrf.cjs.map +1 -1
  208. package/dist/utils/csrf.d.ts +1 -72
  209. package/dist/utils/csrf.js +1 -142
  210. package/dist/utils/csrf.js.map +1 -1
  211. package/dist/utils/errors/ErrorCodes.cjs +6 -76
  212. package/dist/utils/errors/ErrorCodes.cjs.map +1 -1
  213. package/dist/utils/errors/ErrorCodes.d.ts +1 -45
  214. package/dist/utils/errors/ErrorCodes.js +1 -84
  215. package/dist/utils/errors/ErrorCodes.js.map +1 -1
  216. package/dist/utils/errors.cjs +53 -326
  217. package/dist/utils/errors.cjs.map +1 -1
  218. package/dist/utils/errors.d.ts +19 -172
  219. package/dist/utils/errors.js +16 -352
  220. package/dist/utils/errors.js.map +1 -1
  221. package/dist/utils/logger.cjs +5 -374
  222. package/dist/utils/logger.cjs.map +1 -1
  223. package/dist/utils/logger.d.ts +2 -189
  224. package/dist/utils/logger.js +1 -379
  225. package/dist/utils/logger.js.map +1 -1
  226. package/dist/utils/logger.types.cjs +3 -12
  227. package/dist/utils/logger.types.cjs.map +1 -1
  228. package/dist/utils/logger.types.d.ts +2 -57
  229. package/dist/utils/logger.types.js +1 -10
  230. package/dist/utils/logger.types.js.map +1 -1
  231. package/dist/utils/monitoring.cjs +11 -302
  232. package/dist/utils/monitoring.cjs.map +1 -1
  233. package/dist/utils/monitoring.d.ts +1 -163
  234. package/dist/utils/monitoring.js +1 -294
  235. package/dist/utils/monitoring.js.map +1 -1
  236. package/dist/utils/performance.cjs +5 -352
  237. package/dist/utils/performance.cjs.map +1 -1
  238. package/dist/utils/performance.d.ts +2 -246
  239. package/dist/utils/performance.js +1 -354
  240. package/dist/utils/performance.js.map +1 -1
  241. package/dist/utils/resourceLoader.cjs +5 -303
  242. package/dist/utils/resourceLoader.cjs.map +1 -1
  243. package/dist/utils/resourceLoader.d.ts +2 -130
  244. package/dist/utils/resourceLoader.js +1 -305
  245. package/dist/utils/resourceLoader.js.map +1 -1
  246. package/dist/utils/runtimeSecurity.cjs +2 -140
  247. package/dist/utils/runtimeSecurity.cjs.map +1 -1
  248. package/dist/utils/runtimeSecurity.d.ts +2 -104
  249. package/dist/utils/runtimeSecurity.js +1 -141
  250. package/dist/utils/runtimeSecurity.js.map +1 -1
  251. package/dist/utils/security.cjs +3 -314
  252. package/dist/utils/security.cjs.map +1 -1
  253. package/dist/utils/security.d.ts +2 -80
  254. package/dist/utils/security.js +1 -311
  255. package/dist/utils/security.js.map +1 -1
  256. package/dist/utils/traceId.cjs +10 -111
  257. package/dist/utils/traceId.cjs.map +1 -1
  258. package/dist/utils/traceId.d.ts +1 -63
  259. package/dist/utils/traceId.js +1 -116
  260. package/dist/utils/traceId.js.map +1 -1
  261. package/dist/utils/validation.cjs +3 -173
  262. package/dist/utils/validation.cjs.map +1 -1
  263. package/dist/utils/validation.d.ts +2 -110
  264. package/dist/utils/validation.js +1 -175
  265. package/dist/utils/validation.js.map +1 -1
  266. package/package.json +15 -24
  267. package/dist/lazy/index.cjs +0 -104
  268. package/dist/lazy/index.cjs.map +0 -1
  269. package/dist/lazy/index.d.ts +0 -19
  270. package/dist/lazy/index.js +0 -24
  271. package/dist/lazy/index.js.map +0 -1
  272. package/dist/request/adapter/RequestAdapter.cjs +0 -78
  273. package/dist/request/adapter/RequestAdapter.cjs.map +0 -1
  274. package/dist/request/adapter/axiosAdapter.cjs +0 -164
  275. package/dist/request/adapter/axiosAdapter.cjs.map +0 -1
  276. package/dist/request/adapter/fetchAdapter.cjs +0 -134
  277. package/dist/request/adapter/fetchAdapter.cjs.map +0 -1
  278. package/dist/request/adapter/index.cjs +0 -80
  279. package/dist/request/adapter/index.cjs.map +0 -1
  280. package/dist/request/adapter/kyAdapter.cjs +0 -191
  281. package/dist/request/adapter/kyAdapter.cjs.map +0 -1
  282. package/dist/request/adapter/undiciAdapter.cjs +0 -213
  283. package/dist/request/adapter/undiciAdapter.cjs.map +0 -1
  284. package/dist/request/core/RequestClient.cjs +0 -558
  285. package/dist/request/core/RequestClient.cjs.map +0 -1
  286. package/dist/request/core/index.cjs +0 -15
  287. package/dist/request/core/index.cjs.map +0 -1
  288. package/dist/request/index.cjs +0 -149
  289. package/dist/request/index.cjs.map +0 -1
  290. package/dist/request/plugin/RequestPlugin.cjs +0 -218
  291. package/dist/request/plugin/RequestPlugin.cjs.map +0 -1
  292. package/dist/request/plugin/cache.cjs +0 -269
  293. package/dist/request/plugin/cache.cjs.map +0 -1
  294. package/dist/request/plugin/csrfPlugin.cjs +0 -40
  295. package/dist/request/plugin/csrfPlugin.cjs.map +0 -1
  296. package/dist/request/plugin/index.cjs +0 -53
  297. package/dist/request/plugin/index.cjs.map +0 -1
  298. package/dist/request/plugin/monitoring.cjs +0 -216
  299. package/dist/request/plugin/monitoring.cjs.map +0 -1
  300. package/dist/request/plugin/queue.cjs +0 -140
  301. package/dist/request/plugin/queue.cjs.map +0 -1
  302. package/dist/request/plugin/retry.cjs +0 -98
  303. package/dist/request/plugin/retry.cjs.map +0 -1
  304. package/dist/request/plugin/validation.cjs +0 -121
  305. package/dist/request/plugin/validation.cjs.map +0 -1
  306. package/dist/request/runtime/RequestContext.cjs +0 -77
  307. package/dist/request/runtime/RequestContext.cjs.map +0 -1
  308. package/dist/request/runtime/index.cjs +0 -32
  309. package/dist/request/runtime/index.cjs.map +0 -1
  310. package/dist/request/types.cjs +0 -112
  311. package/dist/request/types.cjs.map +0 -1
  312. package/dist/request/utils/RequestQueueManager.cjs +0 -168
  313. package/dist/request/utils/RequestQueueManager.cjs.map +0 -1
  314. package/dist/request/utils/dependencyCheck.cjs +0 -237
  315. package/dist/request/utils/dependencyCheck.cjs.map +0 -1
  316. package/dist/request/utils/index.cjs +0 -30
  317. package/dist/request/utils/index.cjs.map +0 -1
  318. package/dist/request.umd.js +0 -5392
  319. package/dist/request.umd.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/config/ConfigLoader.ts"],"sourcesContent":["/**\n * 配置文件加载模块\n * 支持从JSON/YAML文件加载配置\n * 优化:支持环境变量配置、配置热更新\n */\n\nimport { logger } from '../../utils';\nimport type { StartOptions } from '../types';\n\n/**\n * 配置文件格式\n */\nexport enum ConfigFileFormat {\n JSON = 'json',\n YAML = 'yaml',\n}\n\n/**\n * 配置文件加载选项\n */\nexport interface ConfigFileLoadOptions {\n /**\n * 配置文件路径\n */\n path?: string;\n\n /**\n * 配置文件格式\n */\n format?: ConfigFileFormat;\n\n /**\n * 是否必需(如果为false,文件不存在时不报错)\n */\n required?: boolean;\n\n /**\n * 默认配置(文件不存在时使用)\n */\n defaultConfig?: Partial<StartOptions>;\n}\n\n/**\n * 配置变更监听器\n */\nexport type ConfigChangeListener = (newConfig: Partial<StartOptions>, oldConfig: Partial<StartOptions>) => void;\n\n/**\n * 配置变更通知管理器\n */\nclass ConfigChangeNotifier {\n private listeners: Set<ConfigChangeListener> = new Set();\n\n /**\n * 添加配置变更监听器\n */\n addListener(listener: ConfigChangeListener): () => void {\n this.listeners.add(listener);\n // 返回取消监听的函数\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * 移除配置变更监听器\n */\n removeListener(listener: ConfigChangeListener): void {\n this.listeners.delete(listener);\n }\n\n /**\n * 通知配置变更\n */\n notify(newConfig: Partial<StartOptions>, oldConfig: Partial<StartOptions>): void {\n this.listeners.forEach((listener) => {\n try {\n listener(newConfig, oldConfig);\n } catch (error) {\n logger.warn('配置变更监听器执行失败:', error);\n }\n });\n }\n\n /**\n * 清除所有监听器\n */\n clear(): void {\n this.listeners.clear();\n }\n}\n\n/**\n * 全局配置变更通知器实例\n */\nconst configChangeNotifier = new ConfigChangeNotifier();\n\n/**\n * 配置加载器\n */\nexport class ConfigLoader {\n /**\n * 从JSON文件加载配置\n * \n * @param path - 文件路径\n * @returns 配置对象\n */\n static async loadFromJSON(path: string): Promise<Partial<StartOptions>> {\n try {\n // 在浏览器环境中,需要通过网络请求加载\n if (typeof window !== 'undefined') {\n const response = await fetch(path);\n if (!response.ok) {\n throw new Error(`无法加载配置文件: ${path} (${response.status})`);\n }\n return await response.json();\n }\n\n // 在Node.js环境中,使用fs模块\n if (typeof require !== 'undefined') {\n const fs = require('fs');\n const content = fs.readFileSync(path, 'utf-8');\n return JSON.parse(content);\n }\n\n throw new Error('不支持的环境:无法加载配置文件');\n } catch (error) {\n logger.error(`加载JSON配置文件失败: ${path}`, error);\n throw error;\n }\n }\n\n /**\n * 从YAML文件加载配置\n * \n * @param path - 文件路径\n * @returns 配置对象\n */\n static async loadFromYAML(path: string): Promise<Partial<StartOptions>> {\n try {\n // 在浏览器环境中,需要通过网络请求加载\n if (typeof window !== 'undefined') {\n const response = await fetch(path);\n if (!response.ok) {\n throw new Error(`无法加载配置文件: ${path} (${response.status})`);\n }\n const text = await response.text();\n \n // 需要YAML解析库,这里假设有yaml库\n // 如果没有,可以提示用户安装\n if (typeof (window as any).YAML !== 'undefined') {\n return (window as any).YAML.parse(text);\n }\n \n // 降级:尝试作为JSON解析(如果YAML格式简单)\n try {\n return JSON.parse(text);\n } catch {\n throw new Error('需要YAML解析库,请安装 yaml 包');\n }\n }\n\n // 在Node.js环境中,使用fs模块和yaml库\n if (typeof require !== 'undefined') {\n const fs = require('fs');\n const yaml = require('yaml');\n const content = fs.readFileSync(path, 'utf-8');\n return yaml.parse(content);\n }\n\n throw new Error('不支持的环境:无法加载配置文件');\n } catch (error) {\n logger.error(`加载YAML配置文件失败: ${path}`, error);\n throw error;\n }\n }\n\n /**\n * 从文件加载配置\n * \n * @param options - 加载选项\n * @returns 配置对象\n */\n static async loadFromFile(\n options: ConfigFileLoadOptions\n ): Promise<Partial<StartOptions>> {\n const {\n path,\n format = ConfigFileFormat.JSON,\n required = false,\n defaultConfig = {},\n } = options;\n\n if (!path) {\n if (required) {\n throw new Error('配置文件路径未指定');\n }\n return defaultConfig;\n }\n\n try {\n let config: Partial<StartOptions>;\n\n switch (format) {\n case ConfigFileFormat.JSON:\n config = await this.loadFromJSON(path);\n break;\n case ConfigFileFormat.YAML:\n config = await this.loadFromYAML(path);\n break;\n default:\n throw new Error(`不支持的配置文件格式: ${format}`);\n }\n\n logger.info(`成功加载配置文件: ${path}`);\n return config;\n } catch (error) {\n if (required) {\n throw error;\n }\n\n logger.warn(`配置文件加载失败,使用默认配置: ${path}`, error);\n return defaultConfig;\n }\n }\n\n /**\n * 合并配置(深度合并)\n * \n * @param base - 基础配置\n * @param override - 覆盖配置\n * @returns 合并后的配置\n */\n static mergeConfig(\n base: Partial<StartOptions>,\n override: Partial<StartOptions>\n ): Partial<StartOptions> {\n const merged = { ...base };\n\n for (const key in override) {\n if (!Object.prototype.hasOwnProperty.call(override, key)) {\n continue;\n }\n\n const baseValue = (merged as Record<string, unknown>)[key];\n const overrideValue = (override as Record<string, unknown>)[key];\n\n if (\n baseValue &&\n overrideValue &&\n typeof baseValue === 'object' &&\n typeof overrideValue === 'object' &&\n !Array.isArray(baseValue) &&\n !Array.isArray(overrideValue)\n ) {\n // 深度合并对象\n (merged as Record<string, unknown>)[key] = this.mergeConfig(\n baseValue as Partial<StartOptions>,\n overrideValue as Partial<StartOptions>\n );\n } else {\n // 直接覆盖\n (merged as Record<string, unknown>)[key] = overrideValue;\n }\n }\n\n return merged;\n }\n\n /**\n * 从多个源加载并合并配置\n * \n * @param sources - 配置源列表(按优先级从低到高)\n * @returns 合并后的配置\n */\n static async loadFromMultipleSources(\n sources: Array<Partial<StartOptions> | ConfigFileLoadOptions>\n ): Promise<Partial<StartOptions>> {\n let merged: Partial<StartOptions> = {};\n\n for (const source of sources) {\n if ('path' in source && 'format' in source) {\n // 配置文件\n const fileConfig = await this.loadFromFile(source);\n merged = this.mergeConfig(merged, fileConfig);\n } else if (!('path' in source) && !('format' in source)) {\n // 配置对象\n merged = this.mergeConfig(merged, source as Partial<StartOptions>);\n }\n }\n\n return merged;\n }\n\n /**\n * 从环境变量加载配置\n * \n * 支持以下环境变量格式:\n * - FRAMEWORK_LOGGER_LEVEL=DEBUG\n * - FRAMEWORK_SHOW_SPLASH_SCREEN=always\n * - FRAMEWORK_MIN_SPLASH_SCREEN_TIME=1000\n * \n * @param prefix - 环境变量前缀,默认为 'FRAMEWORK_'\n * @returns 配置对象\n */\n static loadFromEnv(prefix: string = 'FRAMEWORK_'): Partial<StartOptions> {\n const config: Partial<StartOptions> = {};\n\n if (typeof process === 'undefined' || !process.env) {\n return config;\n }\n\n // 遍历所有环境变量\n for (const [key, value] of Object.entries(process.env)) {\n if (!key.startsWith(prefix)) {\n continue;\n }\n\n // 移除前缀并转换为驼峰命名\n const configKey = key\n .slice(prefix.length)\n .toLowerCase()\n .split('_')\n .map((part, index) => \n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)\n )\n .join('');\n\n // 尝试解析值\n let parsedValue: unknown = value;\n if (value === 'true' || value === 'false') {\n parsedValue = value === 'true';\n } else if (!isNaN(Number(value))) {\n parsedValue = Number(value);\n } else if (value?.startsWith('{') || value?.startsWith('[')) {\n try {\n parsedValue = JSON.parse(value);\n } catch {\n // 解析失败,使用原始值\n }\n }\n\n // 设置配置值\n (config as any)[configKey] = parsedValue;\n }\n\n logger.debug('从环境变量加载配置:', config);\n return config;\n }\n\n /**\n * 从多个源加载配置(包括环境变量)\n * \n * 优化:明确优先级,从低到高:\n * 1. 默认配置\n * 2. 配置文件\n * 3. 环境变量\n * 4. 代码配置(通过 sources 参数传入)\n * \n * @param sources - 配置源列表(代码配置,优先级最高)\n * @param defaultConfig - 默认配置(优先级最低)\n * @param includeEnv - 是否包含环境变量,默认为 true\n * @param envPrefix - 环境变量前缀,默认为 'FRAMEWORK_'\n * @returns 合并后的配置\n */\n static async loadFromAllSources(\n sources: Array<Partial<StartOptions> | ConfigFileLoadOptions> = [],\n defaultConfig: Partial<StartOptions> = {},\n includeEnv: boolean = true,\n envPrefix: string = 'FRAMEWORK_'\n ): Promise<Partial<StartOptions>> {\n // 保存旧配置用于变更通知\n const oldConfig: Partial<StartOptions> = { ...defaultConfig };\n\n // 1. 从默认配置开始\n let merged: Partial<StartOptions> = { ...defaultConfig };\n\n // 2. 加载配置文件(优先级高于默认配置)\n const fileConfigs = await this.loadFromMultipleSources(sources.filter(\n (source): source is ConfigFileLoadOptions => \n typeof source === 'object' && 'path' in source\n ));\n merged = this.mergeConfig(merged, fileConfigs);\n\n // 3. 加载环境变量(优先级高于配置文件)\n if (includeEnv) {\n const envConfig = this.loadFromEnv(envPrefix);\n merged = this.mergeConfig(merged, envConfig);\n }\n\n // 4. 合并代码配置(优先级最高)\n const codeConfigs = sources.filter(\n (source): source is Partial<StartOptions> => \n typeof source === 'object' && !('path' in source)\n );\n for (const codeConfig of codeConfigs) {\n merged = this.mergeConfig(merged, codeConfig);\n }\n\n // 通知配置变更\n configChangeNotifier.notify(merged, oldConfig);\n\n return merged;\n }\n\n /**\n * 添加配置变更监听器\n * \n * @param listener - 配置变更监听器\n * @returns 取消监听的函数\n */\n static onConfigChange(listener: ConfigChangeListener): () => void {\n return configChangeNotifier.addListener(listener);\n }\n\n /**\n * 移除配置变更监听器\n */\n static removeConfigChangeListener(listener: ConfigChangeListener): void {\n configChangeNotifier.removeListener(listener);\n }\n}\n"],"names":["ConfigFileFormat","ConfigLoader","ConfigChangeNotifier","addListener","listener","listeners","add","delete","removeListener","notify","newConfig","oldConfig","forEach","error","logger","warn","clear","Set","configChangeNotifier","loadFromJSON","path","window","response","fetch","ok","Error","status","json","require","fs","content","readFileSync","JSON","parse","loadFromYAML","text","YAML","yaml","loadFromFile","options","format","required","defaultConfig","config","info","mergeConfig","base","override","merged","key","Object","prototype","hasOwnProperty","call","baseValue","overrideValue","Array","isArray","loadFromMultipleSources","sources","source","fileConfig","loadFromEnv","prefix","process","env","value","entries","startsWith","configKey","slice","length","toLowerCase","split","map","part","index","charAt","toUpperCase","join","parsedValue","isNaN","Number","debug","loadFromAllSources","includeEnv","envPrefix","fileConfigs","filter","envConfig","codeConfigs","codeConfig","onConfigChange","removeConfigChangeListener"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/config/ConfigLoader.ts"],"sourcesContent":["/**\n * 配置文件加载模块\n * 支持从JSON/YAML文件加载配置\n * 优化:支持环境变量配置、配置热更新\n */\n\nimport { logger } from '@vlian/logger';\nimport type { StartOptions } from '../types';\n\n/**\n * 配置文件格式\n */\nexport enum ConfigFileFormat {\n JSON = 'json',\n YAML = 'yaml',\n}\n\n/**\n * 配置文件加载选项\n */\nexport interface ConfigFileLoadOptions {\n /**\n * 配置文件路径\n */\n path?: string;\n\n /**\n * 配置文件格式\n */\n format?: ConfigFileFormat;\n\n /**\n * 是否必需(如果为false,文件不存在时不报错)\n */\n required?: boolean;\n\n /**\n * 默认配置(文件不存在时使用)\n */\n defaultConfig?: Partial<StartOptions>;\n}\n\n/**\n * 配置变更监听器\n */\nexport type ConfigChangeListener = (newConfig: Partial<StartOptions>, oldConfig: Partial<StartOptions>) => void;\n\n/**\n * 配置变更通知管理器\n */\nclass ConfigChangeNotifier {\n private listeners: Set<ConfigChangeListener> = new Set();\n\n /**\n * 添加配置变更监听器\n */\n addListener(listener: ConfigChangeListener): () => void {\n this.listeners.add(listener);\n // 返回取消监听的函数\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * 移除配置变更监听器\n */\n removeListener(listener: ConfigChangeListener): void {\n this.listeners.delete(listener);\n }\n\n /**\n * 通知配置变更\n */\n notify(newConfig: Partial<StartOptions>, oldConfig: Partial<StartOptions>): void {\n this.listeners.forEach((listener) => {\n try {\n listener(newConfig, oldConfig);\n } catch (error) {\n logger.warn('配置变更监听器执行失败:', error);\n }\n });\n }\n\n /**\n * 清除所有监听器\n */\n clear(): void {\n this.listeners.clear();\n }\n}\n\n/**\n * 全局配置变更通知器实例\n */\nconst configChangeNotifier = new ConfigChangeNotifier();\n\n/**\n * 配置加载器\n */\nexport class ConfigLoader {\n /**\n * 从JSON文件加载配置\n * \n * @param path - 文件路径\n * @returns 配置对象\n */\n static async loadFromJSON(path: string): Promise<Partial<StartOptions>> {\n try {\n // 在浏览器环境中,需要通过网络请求加载\n if (typeof window !== 'undefined') {\n const response = await fetch(path);\n if (!response.ok) {\n throw new Error(`无法加载配置文件: ${path} (${response.status})`);\n }\n return await response.json();\n }\n\n // 在Node.js环境中,使用fs模块\n if (typeof require !== 'undefined') {\n const fs = require('fs');\n const content = fs.readFileSync(path, 'utf-8');\n return JSON.parse(content);\n }\n\n throw new Error('不支持的环境:无法加载配置文件');\n } catch (error) {\n logger.error(`加载JSON配置文件失败: ${path}`, error);\n throw error;\n }\n }\n\n /**\n * 从YAML文件加载配置\n * \n * @param path - 文件路径\n * @returns 配置对象\n */\n static async loadFromYAML(path: string): Promise<Partial<StartOptions>> {\n try {\n // 在浏览器环境中,需要通过网络请求加载\n if (typeof window !== 'undefined') {\n const response = await fetch(path);\n if (!response.ok) {\n throw new Error(`无法加载配置文件: ${path} (${response.status})`);\n }\n const text = await response.text();\n \n // 需要YAML解析库,这里假设有yaml库\n // 如果没有,可以提示用户安装\n if (typeof (window as any).YAML !== 'undefined') {\n return (window as any).YAML.parse(text);\n }\n \n // 降级:尝试作为JSON解析(如果YAML格式简单)\n try {\n return JSON.parse(text);\n } catch {\n throw new Error('需要YAML解析库,请安装 yaml 包');\n }\n }\n\n // 在Node.js环境中,使用fs模块和yaml库\n if (typeof require !== 'undefined') {\n const fs = require('fs');\n const yaml = require('yaml');\n const content = fs.readFileSync(path, 'utf-8');\n return yaml.parse(content);\n }\n\n throw new Error('不支持的环境:无法加载配置文件');\n } catch (error) {\n logger.error(`加载YAML配置文件失败: ${path}`, error);\n throw error;\n }\n }\n\n /**\n * 从文件加载配置\n * \n * @param options - 加载选项\n * @returns 配置对象\n */\n static async loadFromFile(\n options: ConfigFileLoadOptions\n ): Promise<Partial<StartOptions>> {\n const {\n path,\n format = ConfigFileFormat.JSON,\n required = false,\n defaultConfig = {},\n } = options;\n\n if (!path) {\n if (required) {\n throw new Error('配置文件路径未指定');\n }\n return defaultConfig;\n }\n\n try {\n let config: Partial<StartOptions>;\n\n switch (format) {\n case ConfigFileFormat.JSON:\n config = await this.loadFromJSON(path);\n break;\n case ConfigFileFormat.YAML:\n config = await this.loadFromYAML(path);\n break;\n default:\n throw new Error(`不支持的配置文件格式: ${format}`);\n }\n\n logger.info(`成功加载配置文件: ${path}`);\n return config;\n } catch (error) {\n if (required) {\n throw error;\n }\n\n logger.warn(`配置文件加载失败,使用默认配置: ${path}`, error);\n return defaultConfig;\n }\n }\n\n /**\n * 合并配置(深度合并)\n * \n * @param base - 基础配置\n * @param override - 覆盖配置\n * @returns 合并后的配置\n */\n static mergeConfig(\n base: Partial<StartOptions>,\n override: Partial<StartOptions>\n ): Partial<StartOptions> {\n const merged = { ...base };\n\n for (const key in override) {\n if (!Object.prototype.hasOwnProperty.call(override, key)) {\n continue;\n }\n\n const baseValue = (merged as Record<string, unknown>)[key];\n const overrideValue = (override as Record<string, unknown>)[key];\n\n if (\n baseValue &&\n overrideValue &&\n typeof baseValue === 'object' &&\n typeof overrideValue === 'object' &&\n !Array.isArray(baseValue) &&\n !Array.isArray(overrideValue)\n ) {\n // 深度合并对象\n (merged as Record<string, unknown>)[key] = this.mergeConfig(\n baseValue as Partial<StartOptions>,\n overrideValue as Partial<StartOptions>\n );\n } else {\n // 直接覆盖\n (merged as Record<string, unknown>)[key] = overrideValue;\n }\n }\n\n return merged;\n }\n\n /**\n * 从多个源加载并合并配置\n * \n * @param sources - 配置源列表(按优先级从低到高)\n * @returns 合并后的配置\n */\n static async loadFromMultipleSources(\n sources: Array<Partial<StartOptions> | ConfigFileLoadOptions>\n ): Promise<Partial<StartOptions>> {\n let merged: Partial<StartOptions> = {};\n\n for (const source of sources) {\n if ('path' in source && 'format' in source) {\n // 配置文件\n const fileConfig = await this.loadFromFile(source);\n merged = this.mergeConfig(merged, fileConfig);\n } else if (!('path' in source) && !('format' in source)) {\n // 配置对象\n merged = this.mergeConfig(merged, source as Partial<StartOptions>);\n }\n }\n\n return merged;\n }\n\n /**\n * 从环境变量加载配置\n * \n * 支持以下环境变量格式:\n * - FRAMEWORK_LOGGER_LEVEL=DEBUG\n * - FRAMEWORK_SHOW_SPLASH_SCREEN=always\n * - FRAMEWORK_MIN_SPLASH_SCREEN_TIME=1000\n * \n * @param prefix - 环境变量前缀,默认为 'FRAMEWORK_'\n * @returns 配置对象\n */\n static loadFromEnv(prefix: string = 'FRAMEWORK_'): Partial<StartOptions> {\n const config: Partial<StartOptions> = {};\n\n if (typeof process === 'undefined' || !process.env) {\n return config;\n }\n\n // 遍历所有环境变量\n for (const [key, value] of Object.entries(process.env)) {\n if (!key.startsWith(prefix)) {\n continue;\n }\n\n // 移除前缀并转换为驼峰命名\n const configKey = key\n .slice(prefix.length)\n .toLowerCase()\n .split('_')\n .map((part, index) => \n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)\n )\n .join('');\n\n // 尝试解析值\n let parsedValue: unknown = value;\n if (value === 'true' || value === 'false') {\n parsedValue = value === 'true';\n } else if (!isNaN(Number(value))) {\n parsedValue = Number(value);\n } else if (value?.startsWith('{') || value?.startsWith('[')) {\n try {\n parsedValue = JSON.parse(value);\n } catch {\n // 解析失败,使用原始值\n }\n }\n\n // 设置配置值\n (config as any)[configKey] = parsedValue;\n }\n\n logger.debug('从环境变量加载配置:', config);\n return config;\n }\n\n /**\n * 从多个源加载配置(包括环境变量)\n * \n * 优化:明确优先级,从低到高:\n * 1. 默认配置\n * 2. 配置文件\n * 3. 环境变量\n * 4. 代码配置(通过 sources 参数传入)\n * \n * @param sources - 配置源列表(代码配置,优先级最高)\n * @param defaultConfig - 默认配置(优先级最低)\n * @param includeEnv - 是否包含环境变量,默认为 true\n * @param envPrefix - 环境变量前缀,默认为 'FRAMEWORK_'\n * @returns 合并后的配置\n */\n static async loadFromAllSources(\n sources: Array<Partial<StartOptions> | ConfigFileLoadOptions> = [],\n defaultConfig: Partial<StartOptions> = {},\n includeEnv: boolean = true,\n envPrefix: string = 'FRAMEWORK_'\n ): Promise<Partial<StartOptions>> {\n // 保存旧配置用于变更通知\n const oldConfig: Partial<StartOptions> = { ...defaultConfig };\n\n // 1. 从默认配置开始\n let merged: Partial<StartOptions> = { ...defaultConfig };\n\n // 2. 加载配置文件(优先级高于默认配置)\n const fileConfigs = await this.loadFromMultipleSources(sources.filter(\n (source): source is ConfigFileLoadOptions => \n typeof source === 'object' && 'path' in source\n ));\n merged = this.mergeConfig(merged, fileConfigs);\n\n // 3. 加载环境变量(优先级高于配置文件)\n if (includeEnv) {\n const envConfig = this.loadFromEnv(envPrefix);\n merged = this.mergeConfig(merged, envConfig);\n }\n\n // 4. 合并代码配置(优先级最高)\n const codeConfigs = sources.filter(\n (source): source is Partial<StartOptions> => \n typeof source === 'object' && !('path' in source)\n );\n for (const codeConfig of codeConfigs) {\n merged = this.mergeConfig(merged, codeConfig);\n }\n\n // 通知配置变更\n configChangeNotifier.notify(merged, oldConfig);\n\n return merged;\n }\n\n /**\n * 添加配置变更监听器\n * \n * @param listener - 配置变更监听器\n * @returns 取消监听的函数\n */\n static onConfigChange(listener: ConfigChangeListener): () => void {\n return configChangeNotifier.addListener(listener);\n }\n\n /**\n * 移除配置变更监听器\n */\n static removeConfigChangeListener(listener: ConfigChangeListener): void {\n configChangeNotifier.removeListener(listener);\n }\n}\n"],"names":["ConfigFileFormat","ConfigLoader","ConfigChangeNotifier","addListener","listener","listeners","add","delete","removeListener","notify","newConfig","oldConfig","forEach","error","logger","warn","clear","Set","configChangeNotifier","loadFromJSON","path","window","response","fetch","ok","Error","status","json","require","fs","content","readFileSync","JSON","parse","loadFromYAML","text","YAML","yaml","loadFromFile","options","format","required","defaultConfig","config","info","mergeConfig","base","override","merged","key","Object","prototype","hasOwnProperty","call","baseValue","overrideValue","Array","isArray","loadFromMultipleSources","sources","source","fileConfig","loadFromEnv","prefix","process","env","value","entries","startsWith","configKey","slice","length","toLowerCase","split","map","part","index","charAt","toUpperCase","join","parsedValue","isNaN","Number","debug","loadFromAllSources","includeEnv","envPrefix","fileConfigs","filter","envConfig","codeConfigs","codeConfig","onConfigChange","removeConfigChangeListener"],"mappings":"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"}
@@ -15,7 +15,7 @@ function _define_property(obj, key, value) {
15
15
  * 配置文件加载模块
16
16
  * 支持从JSON/YAML文件加载配置
17
17
  * 优化:支持环境变量配置、配置热更新
18
- */ import { logger } from "../../utils";
18
+ */ import { logger } from "@vlian/logger";
19
19
  /**
20
20
  * 配置文件格式
21
21
  */ export var ConfigFileFormat = /*#__PURE__*/ function(ConfigFileFormat) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/config/ConfigLoader.ts"],"sourcesContent":["/**\n * 配置文件加载模块\n * 支持从JSON/YAML文件加载配置\n * 优化:支持环境变量配置、配置热更新\n */\n\nimport { logger } from '../../utils';\nimport type { StartOptions } from '../types';\n\n/**\n * 配置文件格式\n */\nexport enum ConfigFileFormat {\n JSON = 'json',\n YAML = 'yaml',\n}\n\n/**\n * 配置文件加载选项\n */\nexport interface ConfigFileLoadOptions {\n /**\n * 配置文件路径\n */\n path?: string;\n\n /**\n * 配置文件格式\n */\n format?: ConfigFileFormat;\n\n /**\n * 是否必需(如果为false,文件不存在时不报错)\n */\n required?: boolean;\n\n /**\n * 默认配置(文件不存在时使用)\n */\n defaultConfig?: Partial<StartOptions>;\n}\n\n/**\n * 配置变更监听器\n */\nexport type ConfigChangeListener = (newConfig: Partial<StartOptions>, oldConfig: Partial<StartOptions>) => void;\n\n/**\n * 配置变更通知管理器\n */\nclass ConfigChangeNotifier {\n private listeners: Set<ConfigChangeListener> = new Set();\n\n /**\n * 添加配置变更监听器\n */\n addListener(listener: ConfigChangeListener): () => void {\n this.listeners.add(listener);\n // 返回取消监听的函数\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * 移除配置变更监听器\n */\n removeListener(listener: ConfigChangeListener): void {\n this.listeners.delete(listener);\n }\n\n /**\n * 通知配置变更\n */\n notify(newConfig: Partial<StartOptions>, oldConfig: Partial<StartOptions>): void {\n this.listeners.forEach((listener) => {\n try {\n listener(newConfig, oldConfig);\n } catch (error) {\n logger.warn('配置变更监听器执行失败:', error);\n }\n });\n }\n\n /**\n * 清除所有监听器\n */\n clear(): void {\n this.listeners.clear();\n }\n}\n\n/**\n * 全局配置变更通知器实例\n */\nconst configChangeNotifier = new ConfigChangeNotifier();\n\n/**\n * 配置加载器\n */\nexport class ConfigLoader {\n /**\n * 从JSON文件加载配置\n * \n * @param path - 文件路径\n * @returns 配置对象\n */\n static async loadFromJSON(path: string): Promise<Partial<StartOptions>> {\n try {\n // 在浏览器环境中,需要通过网络请求加载\n if (typeof window !== 'undefined') {\n const response = await fetch(path);\n if (!response.ok) {\n throw new Error(`无法加载配置文件: ${path} (${response.status})`);\n }\n return await response.json();\n }\n\n // 在Node.js环境中,使用fs模块\n if (typeof require !== 'undefined') {\n const fs = require('fs');\n const content = fs.readFileSync(path, 'utf-8');\n return JSON.parse(content);\n }\n\n throw new Error('不支持的环境:无法加载配置文件');\n } catch (error) {\n logger.error(`加载JSON配置文件失败: ${path}`, error);\n throw error;\n }\n }\n\n /**\n * 从YAML文件加载配置\n * \n * @param path - 文件路径\n * @returns 配置对象\n */\n static async loadFromYAML(path: string): Promise<Partial<StartOptions>> {\n try {\n // 在浏览器环境中,需要通过网络请求加载\n if (typeof window !== 'undefined') {\n const response = await fetch(path);\n if (!response.ok) {\n throw new Error(`无法加载配置文件: ${path} (${response.status})`);\n }\n const text = await response.text();\n \n // 需要YAML解析库,这里假设有yaml库\n // 如果没有,可以提示用户安装\n if (typeof (window as any).YAML !== 'undefined') {\n return (window as any).YAML.parse(text);\n }\n \n // 降级:尝试作为JSON解析(如果YAML格式简单)\n try {\n return JSON.parse(text);\n } catch {\n throw new Error('需要YAML解析库,请安装 yaml 包');\n }\n }\n\n // 在Node.js环境中,使用fs模块和yaml库\n if (typeof require !== 'undefined') {\n const fs = require('fs');\n const yaml = require('yaml');\n const content = fs.readFileSync(path, 'utf-8');\n return yaml.parse(content);\n }\n\n throw new Error('不支持的环境:无法加载配置文件');\n } catch (error) {\n logger.error(`加载YAML配置文件失败: ${path}`, error);\n throw error;\n }\n }\n\n /**\n * 从文件加载配置\n * \n * @param options - 加载选项\n * @returns 配置对象\n */\n static async loadFromFile(\n options: ConfigFileLoadOptions\n ): Promise<Partial<StartOptions>> {\n const {\n path,\n format = ConfigFileFormat.JSON,\n required = false,\n defaultConfig = {},\n } = options;\n\n if (!path) {\n if (required) {\n throw new Error('配置文件路径未指定');\n }\n return defaultConfig;\n }\n\n try {\n let config: Partial<StartOptions>;\n\n switch (format) {\n case ConfigFileFormat.JSON:\n config = await this.loadFromJSON(path);\n break;\n case ConfigFileFormat.YAML:\n config = await this.loadFromYAML(path);\n break;\n default:\n throw new Error(`不支持的配置文件格式: ${format}`);\n }\n\n logger.info(`成功加载配置文件: ${path}`);\n return config;\n } catch (error) {\n if (required) {\n throw error;\n }\n\n logger.warn(`配置文件加载失败,使用默认配置: ${path}`, error);\n return defaultConfig;\n }\n }\n\n /**\n * 合并配置(深度合并)\n * \n * @param base - 基础配置\n * @param override - 覆盖配置\n * @returns 合并后的配置\n */\n static mergeConfig(\n base: Partial<StartOptions>,\n override: Partial<StartOptions>\n ): Partial<StartOptions> {\n const merged = { ...base };\n\n for (const key in override) {\n if (!Object.prototype.hasOwnProperty.call(override, key)) {\n continue;\n }\n\n const baseValue = (merged as Record<string, unknown>)[key];\n const overrideValue = (override as Record<string, unknown>)[key];\n\n if (\n baseValue &&\n overrideValue &&\n typeof baseValue === 'object' &&\n typeof overrideValue === 'object' &&\n !Array.isArray(baseValue) &&\n !Array.isArray(overrideValue)\n ) {\n // 深度合并对象\n (merged as Record<string, unknown>)[key] = this.mergeConfig(\n baseValue as Partial<StartOptions>,\n overrideValue as Partial<StartOptions>\n );\n } else {\n // 直接覆盖\n (merged as Record<string, unknown>)[key] = overrideValue;\n }\n }\n\n return merged;\n }\n\n /**\n * 从多个源加载并合并配置\n * \n * @param sources - 配置源列表(按优先级从低到高)\n * @returns 合并后的配置\n */\n static async loadFromMultipleSources(\n sources: Array<Partial<StartOptions> | ConfigFileLoadOptions>\n ): Promise<Partial<StartOptions>> {\n let merged: Partial<StartOptions> = {};\n\n for (const source of sources) {\n if ('path' in source && 'format' in source) {\n // 配置文件\n const fileConfig = await this.loadFromFile(source);\n merged = this.mergeConfig(merged, fileConfig);\n } else if (!('path' in source) && !('format' in source)) {\n // 配置对象\n merged = this.mergeConfig(merged, source as Partial<StartOptions>);\n }\n }\n\n return merged;\n }\n\n /**\n * 从环境变量加载配置\n * \n * 支持以下环境变量格式:\n * - FRAMEWORK_LOGGER_LEVEL=DEBUG\n * - FRAMEWORK_SHOW_SPLASH_SCREEN=always\n * - FRAMEWORK_MIN_SPLASH_SCREEN_TIME=1000\n * \n * @param prefix - 环境变量前缀,默认为 'FRAMEWORK_'\n * @returns 配置对象\n */\n static loadFromEnv(prefix: string = 'FRAMEWORK_'): Partial<StartOptions> {\n const config: Partial<StartOptions> = {};\n\n if (typeof process === 'undefined' || !process.env) {\n return config;\n }\n\n // 遍历所有环境变量\n for (const [key, value] of Object.entries(process.env)) {\n if (!key.startsWith(prefix)) {\n continue;\n }\n\n // 移除前缀并转换为驼峰命名\n const configKey = key\n .slice(prefix.length)\n .toLowerCase()\n .split('_')\n .map((part, index) => \n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)\n )\n .join('');\n\n // 尝试解析值\n let parsedValue: unknown = value;\n if (value === 'true' || value === 'false') {\n parsedValue = value === 'true';\n } else if (!isNaN(Number(value))) {\n parsedValue = Number(value);\n } else if (value?.startsWith('{') || value?.startsWith('[')) {\n try {\n parsedValue = JSON.parse(value);\n } catch {\n // 解析失败,使用原始值\n }\n }\n\n // 设置配置值\n (config as any)[configKey] = parsedValue;\n }\n\n logger.debug('从环境变量加载配置:', config);\n return config;\n }\n\n /**\n * 从多个源加载配置(包括环境变量)\n * \n * 优化:明确优先级,从低到高:\n * 1. 默认配置\n * 2. 配置文件\n * 3. 环境变量\n * 4. 代码配置(通过 sources 参数传入)\n * \n * @param sources - 配置源列表(代码配置,优先级最高)\n * @param defaultConfig - 默认配置(优先级最低)\n * @param includeEnv - 是否包含环境变量,默认为 true\n * @param envPrefix - 环境变量前缀,默认为 'FRAMEWORK_'\n * @returns 合并后的配置\n */\n static async loadFromAllSources(\n sources: Array<Partial<StartOptions> | ConfigFileLoadOptions> = [],\n defaultConfig: Partial<StartOptions> = {},\n includeEnv: boolean = true,\n envPrefix: string = 'FRAMEWORK_'\n ): Promise<Partial<StartOptions>> {\n // 保存旧配置用于变更通知\n const oldConfig: Partial<StartOptions> = { ...defaultConfig };\n\n // 1. 从默认配置开始\n let merged: Partial<StartOptions> = { ...defaultConfig };\n\n // 2. 加载配置文件(优先级高于默认配置)\n const fileConfigs = await this.loadFromMultipleSources(sources.filter(\n (source): source is ConfigFileLoadOptions => \n typeof source === 'object' && 'path' in source\n ));\n merged = this.mergeConfig(merged, fileConfigs);\n\n // 3. 加载环境变量(优先级高于配置文件)\n if (includeEnv) {\n const envConfig = this.loadFromEnv(envPrefix);\n merged = this.mergeConfig(merged, envConfig);\n }\n\n // 4. 合并代码配置(优先级最高)\n const codeConfigs = sources.filter(\n (source): source is Partial<StartOptions> => \n typeof source === 'object' && !('path' in source)\n );\n for (const codeConfig of codeConfigs) {\n merged = this.mergeConfig(merged, codeConfig);\n }\n\n // 通知配置变更\n configChangeNotifier.notify(merged, oldConfig);\n\n return merged;\n }\n\n /**\n * 添加配置变更监听器\n * \n * @param listener - 配置变更监听器\n * @returns 取消监听的函数\n */\n static onConfigChange(listener: ConfigChangeListener): () => void {\n return configChangeNotifier.addListener(listener);\n }\n\n /**\n * 移除配置变更监听器\n */\n static removeConfigChangeListener(listener: ConfigChangeListener): void {\n configChangeNotifier.removeListener(listener);\n }\n}\n"],"names":["logger","ConfigFileFormat","ConfigChangeNotifier","addListener","listener","listeners","add","delete","removeListener","notify","newConfig","oldConfig","forEach","error","warn","clear","Set","configChangeNotifier","ConfigLoader","loadFromJSON","path","window","response","fetch","ok","Error","status","json","require","fs","content","readFileSync","JSON","parse","loadFromYAML","text","YAML","yaml","loadFromFile","options","format","required","defaultConfig","config","info","mergeConfig","base","override","merged","key","Object","prototype","hasOwnProperty","call","baseValue","overrideValue","Array","isArray","loadFromMultipleSources","sources","source","fileConfig","loadFromEnv","prefix","process","env","value","entries","startsWith","configKey","slice","length","toLowerCase","split","map","part","index","charAt","toUpperCase","join","parsedValue","isNaN","Number","debug","loadFromAllSources","includeEnv","envPrefix","fileConfigs","filter","envConfig","codeConfigs","codeConfig","onConfigChange","removeConfigChangeListener"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/config/ConfigLoader.ts"],"sourcesContent":["/**\n * 配置文件加载模块\n * 支持从JSON/YAML文件加载配置\n * 优化:支持环境变量配置、配置热更新\n */\n\nimport { logger } from '@vlian/logger';\nimport type { StartOptions } from '../types';\n\n/**\n * 配置文件格式\n */\nexport enum ConfigFileFormat {\n JSON = 'json',\n YAML = 'yaml',\n}\n\n/**\n * 配置文件加载选项\n */\nexport interface ConfigFileLoadOptions {\n /**\n * 配置文件路径\n */\n path?: string;\n\n /**\n * 配置文件格式\n */\n format?: ConfigFileFormat;\n\n /**\n * 是否必需(如果为false,文件不存在时不报错)\n */\n required?: boolean;\n\n /**\n * 默认配置(文件不存在时使用)\n */\n defaultConfig?: Partial<StartOptions>;\n}\n\n/**\n * 配置变更监听器\n */\nexport type ConfigChangeListener = (newConfig: Partial<StartOptions>, oldConfig: Partial<StartOptions>) => void;\n\n/**\n * 配置变更通知管理器\n */\nclass ConfigChangeNotifier {\n private listeners: Set<ConfigChangeListener> = new Set();\n\n /**\n * 添加配置变更监听器\n */\n addListener(listener: ConfigChangeListener): () => void {\n this.listeners.add(listener);\n // 返回取消监听的函数\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * 移除配置变更监听器\n */\n removeListener(listener: ConfigChangeListener): void {\n this.listeners.delete(listener);\n }\n\n /**\n * 通知配置变更\n */\n notify(newConfig: Partial<StartOptions>, oldConfig: Partial<StartOptions>): void {\n this.listeners.forEach((listener) => {\n try {\n listener(newConfig, oldConfig);\n } catch (error) {\n logger.warn('配置变更监听器执行失败:', error);\n }\n });\n }\n\n /**\n * 清除所有监听器\n */\n clear(): void {\n this.listeners.clear();\n }\n}\n\n/**\n * 全局配置变更通知器实例\n */\nconst configChangeNotifier = new ConfigChangeNotifier();\n\n/**\n * 配置加载器\n */\nexport class ConfigLoader {\n /**\n * 从JSON文件加载配置\n * \n * @param path - 文件路径\n * @returns 配置对象\n */\n static async loadFromJSON(path: string): Promise<Partial<StartOptions>> {\n try {\n // 在浏览器环境中,需要通过网络请求加载\n if (typeof window !== 'undefined') {\n const response = await fetch(path);\n if (!response.ok) {\n throw new Error(`无法加载配置文件: ${path} (${response.status})`);\n }\n return await response.json();\n }\n\n // 在Node.js环境中,使用fs模块\n if (typeof require !== 'undefined') {\n const fs = require('fs');\n const content = fs.readFileSync(path, 'utf-8');\n return JSON.parse(content);\n }\n\n throw new Error('不支持的环境:无法加载配置文件');\n } catch (error) {\n logger.error(`加载JSON配置文件失败: ${path}`, error);\n throw error;\n }\n }\n\n /**\n * 从YAML文件加载配置\n * \n * @param path - 文件路径\n * @returns 配置对象\n */\n static async loadFromYAML(path: string): Promise<Partial<StartOptions>> {\n try {\n // 在浏览器环境中,需要通过网络请求加载\n if (typeof window !== 'undefined') {\n const response = await fetch(path);\n if (!response.ok) {\n throw new Error(`无法加载配置文件: ${path} (${response.status})`);\n }\n const text = await response.text();\n \n // 需要YAML解析库,这里假设有yaml库\n // 如果没有,可以提示用户安装\n if (typeof (window as any).YAML !== 'undefined') {\n return (window as any).YAML.parse(text);\n }\n \n // 降级:尝试作为JSON解析(如果YAML格式简单)\n try {\n return JSON.parse(text);\n } catch {\n throw new Error('需要YAML解析库,请安装 yaml 包');\n }\n }\n\n // 在Node.js环境中,使用fs模块和yaml库\n if (typeof require !== 'undefined') {\n const fs = require('fs');\n const yaml = require('yaml');\n const content = fs.readFileSync(path, 'utf-8');\n return yaml.parse(content);\n }\n\n throw new Error('不支持的环境:无法加载配置文件');\n } catch (error) {\n logger.error(`加载YAML配置文件失败: ${path}`, error);\n throw error;\n }\n }\n\n /**\n * 从文件加载配置\n * \n * @param options - 加载选项\n * @returns 配置对象\n */\n static async loadFromFile(\n options: ConfigFileLoadOptions\n ): Promise<Partial<StartOptions>> {\n const {\n path,\n format = ConfigFileFormat.JSON,\n required = false,\n defaultConfig = {},\n } = options;\n\n if (!path) {\n if (required) {\n throw new Error('配置文件路径未指定');\n }\n return defaultConfig;\n }\n\n try {\n let config: Partial<StartOptions>;\n\n switch (format) {\n case ConfigFileFormat.JSON:\n config = await this.loadFromJSON(path);\n break;\n case ConfigFileFormat.YAML:\n config = await this.loadFromYAML(path);\n break;\n default:\n throw new Error(`不支持的配置文件格式: ${format}`);\n }\n\n logger.info(`成功加载配置文件: ${path}`);\n return config;\n } catch (error) {\n if (required) {\n throw error;\n }\n\n logger.warn(`配置文件加载失败,使用默认配置: ${path}`, error);\n return defaultConfig;\n }\n }\n\n /**\n * 合并配置(深度合并)\n * \n * @param base - 基础配置\n * @param override - 覆盖配置\n * @returns 合并后的配置\n */\n static mergeConfig(\n base: Partial<StartOptions>,\n override: Partial<StartOptions>\n ): Partial<StartOptions> {\n const merged = { ...base };\n\n for (const key in override) {\n if (!Object.prototype.hasOwnProperty.call(override, key)) {\n continue;\n }\n\n const baseValue = (merged as Record<string, unknown>)[key];\n const overrideValue = (override as Record<string, unknown>)[key];\n\n if (\n baseValue &&\n overrideValue &&\n typeof baseValue === 'object' &&\n typeof overrideValue === 'object' &&\n !Array.isArray(baseValue) &&\n !Array.isArray(overrideValue)\n ) {\n // 深度合并对象\n (merged as Record<string, unknown>)[key] = this.mergeConfig(\n baseValue as Partial<StartOptions>,\n overrideValue as Partial<StartOptions>\n );\n } else {\n // 直接覆盖\n (merged as Record<string, unknown>)[key] = overrideValue;\n }\n }\n\n return merged;\n }\n\n /**\n * 从多个源加载并合并配置\n * \n * @param sources - 配置源列表(按优先级从低到高)\n * @returns 合并后的配置\n */\n static async loadFromMultipleSources(\n sources: Array<Partial<StartOptions> | ConfigFileLoadOptions>\n ): Promise<Partial<StartOptions>> {\n let merged: Partial<StartOptions> = {};\n\n for (const source of sources) {\n if ('path' in source && 'format' in source) {\n // 配置文件\n const fileConfig = await this.loadFromFile(source);\n merged = this.mergeConfig(merged, fileConfig);\n } else if (!('path' in source) && !('format' in source)) {\n // 配置对象\n merged = this.mergeConfig(merged, source as Partial<StartOptions>);\n }\n }\n\n return merged;\n }\n\n /**\n * 从环境变量加载配置\n * \n * 支持以下环境变量格式:\n * - FRAMEWORK_LOGGER_LEVEL=DEBUG\n * - FRAMEWORK_SHOW_SPLASH_SCREEN=always\n * - FRAMEWORK_MIN_SPLASH_SCREEN_TIME=1000\n * \n * @param prefix - 环境变量前缀,默认为 'FRAMEWORK_'\n * @returns 配置对象\n */\n static loadFromEnv(prefix: string = 'FRAMEWORK_'): Partial<StartOptions> {\n const config: Partial<StartOptions> = {};\n\n if (typeof process === 'undefined' || !process.env) {\n return config;\n }\n\n // 遍历所有环境变量\n for (const [key, value] of Object.entries(process.env)) {\n if (!key.startsWith(prefix)) {\n continue;\n }\n\n // 移除前缀并转换为驼峰命名\n const configKey = key\n .slice(prefix.length)\n .toLowerCase()\n .split('_')\n .map((part, index) => \n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)\n )\n .join('');\n\n // 尝试解析值\n let parsedValue: unknown = value;\n if (value === 'true' || value === 'false') {\n parsedValue = value === 'true';\n } else if (!isNaN(Number(value))) {\n parsedValue = Number(value);\n } else if (value?.startsWith('{') || value?.startsWith('[')) {\n try {\n parsedValue = JSON.parse(value);\n } catch {\n // 解析失败,使用原始值\n }\n }\n\n // 设置配置值\n (config as any)[configKey] = parsedValue;\n }\n\n logger.debug('从环境变量加载配置:', config);\n return config;\n }\n\n /**\n * 从多个源加载配置(包括环境变量)\n * \n * 优化:明确优先级,从低到高:\n * 1. 默认配置\n * 2. 配置文件\n * 3. 环境变量\n * 4. 代码配置(通过 sources 参数传入)\n * \n * @param sources - 配置源列表(代码配置,优先级最高)\n * @param defaultConfig - 默认配置(优先级最低)\n * @param includeEnv - 是否包含环境变量,默认为 true\n * @param envPrefix - 环境变量前缀,默认为 'FRAMEWORK_'\n * @returns 合并后的配置\n */\n static async loadFromAllSources(\n sources: Array<Partial<StartOptions> | ConfigFileLoadOptions> = [],\n defaultConfig: Partial<StartOptions> = {},\n includeEnv: boolean = true,\n envPrefix: string = 'FRAMEWORK_'\n ): Promise<Partial<StartOptions>> {\n // 保存旧配置用于变更通知\n const oldConfig: Partial<StartOptions> = { ...defaultConfig };\n\n // 1. 从默认配置开始\n let merged: Partial<StartOptions> = { ...defaultConfig };\n\n // 2. 加载配置文件(优先级高于默认配置)\n const fileConfigs = await this.loadFromMultipleSources(sources.filter(\n (source): source is ConfigFileLoadOptions => \n typeof source === 'object' && 'path' in source\n ));\n merged = this.mergeConfig(merged, fileConfigs);\n\n // 3. 加载环境变量(优先级高于配置文件)\n if (includeEnv) {\n const envConfig = this.loadFromEnv(envPrefix);\n merged = this.mergeConfig(merged, envConfig);\n }\n\n // 4. 合并代码配置(优先级最高)\n const codeConfigs = sources.filter(\n (source): source is Partial<StartOptions> => \n typeof source === 'object' && !('path' in source)\n );\n for (const codeConfig of codeConfigs) {\n merged = this.mergeConfig(merged, codeConfig);\n }\n\n // 通知配置变更\n configChangeNotifier.notify(merged, oldConfig);\n\n return merged;\n }\n\n /**\n * 添加配置变更监听器\n * \n * @param listener - 配置变更监听器\n * @returns 取消监听的函数\n */\n static onConfigChange(listener: ConfigChangeListener): () => void {\n return configChangeNotifier.addListener(listener);\n }\n\n /**\n * 移除配置变更监听器\n */\n static removeConfigChangeListener(listener: ConfigChangeListener): void {\n configChangeNotifier.removeListener(listener);\n }\n}\n"],"names":["logger","ConfigFileFormat","ConfigChangeNotifier","addListener","listener","listeners","add","delete","removeListener","notify","newConfig","oldConfig","forEach","error","warn","clear","Set","configChangeNotifier","ConfigLoader","loadFromJSON","path","window","response","fetch","ok","Error","status","json","require","fs","content","readFileSync","JSON","parse","loadFromYAML","text","YAML","yaml","loadFromFile","options","format","required","defaultConfig","config","info","mergeConfig","base","override","merged","key","Object","prototype","hasOwnProperty","call","baseValue","overrideValue","Array","isArray","loadFromMultipleSources","sources","source","fileConfig","loadFromEnv","prefix","process","env","value","entries","startsWith","configKey","slice","length","toLowerCase","split","map","part","index","charAt","toUpperCase","join","parsedValue","isNaN","Number","debug","loadFromAllSources","includeEnv","envPrefix","fileConfigs","filter","envConfig","codeConfigs","codeConfig","onConfigChange","removeConfigChangeListener"],"mappings":"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"}
@@ -22,8 +22,8 @@ _export(exports, {
22
22
  const _jsxruntime = require("react/jsx-runtime");
23
23
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
24
24
  const _reacterrorboundary = require("react-error-boundary");
25
- const _utils = require("../../utils");
26
- const _errors = require("../../utils/errors");
25
+ const _logger = require("@vlian/logger");
26
+ const _utils = require("@vlian/utils");
27
27
  const _initialization = require("../initialization");
28
28
  const _ErrorHandler = require("./ErrorHandler");
29
29
  function _getRequireWildcardCache(nodeInterop) {
@@ -74,7 +74,7 @@ function ErrorBoundary({ children, fallback, onError, showInConsole = true, rese
74
74
  const errorHandler = (0, _react.useMemo)(()=>(0, _ErrorHandler.getDefaultErrorHandler)(), []);
75
75
  // 处理错误,将标准 Error 转换为 FrameworkError
76
76
  const handleError = (0, _react.useCallback)(async (error, errorInfo)=>{
77
- const normalizedError = _errors.errorUtils.normalizeError(error);
77
+ const normalizedError = _utils.errorUtils.normalizeError(error);
78
78
  // 将 react-error-boundary 的 errorInfo 转换为 React 的 ErrorInfo 格式
79
79
  const reactErrorInfo = {
80
80
  componentStack: errorInfo.componentStack || ''
@@ -86,7 +86,7 @@ function ErrorBoundary({ children, fallback, onError, showInConsole = true, rese
86
86
  });
87
87
  // 如果错误处理器没有处理错误,使用默认处理
88
88
  if (!handleResult.handled) {
89
- _utils.logger.error('错误边界捕获到错误:', {
89
+ _logger.logger.error('错误边界捕获到错误:', {
90
90
  error: normalizedError.toJSON(),
91
91
  errorInfo: {
92
92
  componentStack: reactErrorInfo.componentStack
@@ -119,7 +119,7 @@ function ErrorBoundary({ children, fallback, onError, showInConsole = true, rese
119
119
  // 默认 fallback UI(使用无框架 HTML 组件)
120
120
  // 使用内部组件来管理重试计数状态,使用 React.memo 优化性能
121
121
  const DefaultFallbackComponent = /*#__PURE__*/ _react.default.memo(({ error, resetErrorBoundary })=>{
122
- const normalizedError = _errors.errorUtils.normalizeError(error);
122
+ const normalizedError = _utils.errorUtils.normalizeError(error);
123
123
  const [retryCount, setRetryCount] = (0, _react.useState)(retryCountRef.current);
124
124
  const [showDetails, setShowDetails] = (0, _react.useState)(false);
125
125
  // 同步 ref 的值到 state,确保组件重新创建时能读取到最新的重试计数
@@ -349,7 +349,7 @@ function ErrorBoundary({ children, fallback, onError, showInConsole = true, rese
349
349
  };
350
350
  // 如果提供了自定义 fallback,使用自定义的
351
351
  const fallbackRender = fallback ? ({ error, resetErrorBoundary })=>{
352
- const normalizedError = _errors.errorUtils.normalizeError(error);
352
+ const normalizedError = _utils.errorUtils.normalizeError(error);
353
353
  return fallback(normalizedError, ()=>{
354
354
  handleReset();
355
355
  resetErrorBoundary();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/error/ErrorBoundary.tsx"],"sourcesContent":["import React, { type ReactNode, type ErrorInfo, useState, useRef, useEffect, useCallback, useMemo } from 'react';\nimport { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';\nimport { logger } from '../../utils';\nimport { errorUtils } from '../../utils/errors';\nimport type { FrameworkError } from '../../utils/errors';\nimport { initializationErrorState } from '../initialization';\nimport { getDefaultErrorHandler } from './ErrorHandler';\n\n/**\n * 错误边界组件属性\n */\nexport interface ErrorBoundaryProps {\n /**\n * 子组件\n */\n children: ReactNode;\n /**\n * 错误回退 UI(函数形式)\n * \n * 如果不提供,将使用默认错误 UI(框架内置实现,居中显示)。\n * 如果提供,将使用自定义的错误 UI。\n * \n * @param error - 标准化后的框架错误对象\n * @param resetError - 重置错误边界的函数,调用后可以尝试重新渲染子组件\n * @returns 错误 UI 的 React 节点\n * \n * @example\n * ```tsx\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h2>自定义错误标题</h2>\n * <p>{error.message}</p>\n * <button onClick={reset}>重试</button>\n * </div>\n * )}\n * >\n * <App />\n * </ErrorBoundary>\n * ```\n */\n fallback?: (error: FrameworkError, resetError: () => void) => ReactNode;\n /**\n * 错误回调\n */\n onError?: (error: FrameworkError, errorInfo: ErrorInfo) => void;\n /**\n * 是否在控制台显示错误\n */\n showInConsole?: boolean;\n /**\n * 重置键数组,当这些值变化时自动重置错误边界\n */\n resetKeys?: Array<string | number>;\n /**\n * 重置错误时的回调\n */\n onReset?: () => void;\n /**\n * 重置键变化时的回调\n */\n onResetKeysChange?: (prevKeys: Array<string | number> | undefined, nextKeys: Array<string | number> | undefined) => void;\n}\n\n/**\n * 错误边界组件\n * \n * 基于 react-error-boundary 实现,集成了框架的错误处理系统\n * 用于捕获 React 组件树中的错误,防止整个应用崩溃\n * \n * 默认使用框架内置错误 UI,居中显示。\n * 可以通过 fallback 属性自定义错误 UI。\n * \n * @example\n * ```tsx\n * // 使用默认错误 UI(推荐)\n * <ErrorBoundary>\n * <App />\n * </ErrorBoundary>\n * \n * // 自定义错误 UI\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h1>出错了</h1>\n * <p>{error.message}</p>\n * <button onClick={reset}>重试</button>\n * </div>\n * )}\n * >\n * <App />\n * </ErrorBoundary>\n * ```\n */\nexport function ErrorBoundary({\n children,\n fallback,\n onError,\n showInConsole = true,\n resetKeys,\n onReset,\n onResetKeysChange,\n}: ErrorBoundaryProps): ReactNode {\n // 使用 ref 存储重试计数,在多次错误之间保持状态\n const retryCountRef = useRef<number>(0);\n\n // 优化:使用统一的错误处理器\n const errorHandler = useMemo(() => getDefaultErrorHandler(), []);\n\n // 处理错误,将标准 Error 转换为 FrameworkError\n const handleError = useCallback(async (error: Error, errorInfo: { componentStack?: string | null }): Promise<void> => {\n const normalizedError = errorUtils.normalizeError(error);\n\n // 将 react-error-boundary 的 errorInfo 转换为 React 的 ErrorInfo 格式\n const reactErrorInfo: ErrorInfo = {\n componentStack: errorInfo.componentStack || '',\n };\n\n // 使用统一的错误处理器处理错误\n const handleResult = await errorHandler.handleError(normalizedError, {\n componentStack: reactErrorInfo.componentStack,\n source: 'ErrorBoundary',\n });\n\n // 如果错误处理器没有处理错误,使用默认处理\n if (!handleResult.handled) {\n logger.error('错误边界捕获到错误:', {\n error: normalizedError.toJSON(),\n errorInfo: {\n componentStack: reactErrorInfo.componentStack,\n },\n });\n }\n\n // 调用错误回调\n if (onError) {\n onError(normalizedError, reactErrorInfo);\n }\n\n // 在控制台显示错误(如果配置了)\n if (showInConsole) {\n console.error('错误边界捕获到错误:', normalizedError);\n console.error('错误信息:', reactErrorInfo);\n }\n }, [errorHandler, onError, showInConsole]);\n\n // 处理重置\n const handleReset = (): void => {\n // 清除初始化错误状态\n initializationErrorState.clearError();\n // 调用用户提供的重置回调\n if (onReset) {\n onReset();\n }\n };\n\n // 默认 fallback UI(使用无框架 HTML 组件)\n // 使用内部组件来管理重试计数状态,使用 React.memo 优化性能\n const DefaultFallbackComponent = React.memo(({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n const normalizedError = errorUtils.normalizeError(error);\n const [retryCount, setRetryCount] = useState<number>(retryCountRef.current);\n const [showDetails, setShowDetails] = useState(false);\n\n // 同步 ref 的值到 state,确保组件重新创建时能读取到最新的重试计数\n useEffect(() => {\n setRetryCount(retryCountRef.current);\n }, []);\n\n const handleRetry = useCallback((): void => {\n // 增加重试计数\n const newCount = retryCount + 1;\n retryCountRef.current = newCount;\n setRetryCount(newCount);\n \n // 执行重置\n handleReset();\n resetErrorBoundary();\n }, [retryCount, resetErrorBoundary]);\n\n const handleReload = useCallback((): void => {\n // 刷新页面\n window.location.reload();\n }, []);\n\n // 构建用户友好的错误提示\n const userFriendlyMessage = useMemo(() => {\n // 根据错误类型提供不同的提示\n if (normalizedError.code === 'NETWORK_ERROR') {\n return '网络连接失败,请检查您的网络设置后再尝试';\n }\n if (normalizedError.code === 'TIMEOUT_ERROR') {\n return '请求超时,请稍后再尝试';\n }\n if (normalizedError.code === 'VALIDATION_ERROR') {\n return '数据验证失败,请检查输入内容';\n }\n // 默认提示\n return '应用运行时发生了错误,请稍后再尝试';\n }, [normalizedError.code]);\n\n // 构建按钮数组\n const extraButtons = useMemo(() => {\n const buttons = [\n <button\n key=\"retry\"\n onClick={handleRetry}\n aria-label=\"重试加载应用\"\n style={{\n border: 'none',\n background: 'var(--app-primary-color, #2f80ed)',\n color: '#fff',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n 重试\n </button>,\n <button\n key=\"details\"\n onClick={() => setShowDetails(!showDetails)}\n aria-label={showDetails ? '隐藏错误详情' : '显示错误详情'}\n style={{\n border: '1px solid #cfd7df',\n background: '#fff',\n color: '#1f2933',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n {showDetails ? '隐藏详情' : '查看详情'}\n </button>,\n ];\n\n // 如果重试次数 >= 1,添加刷新页面按钮(提前显示)\n if (retryCount >= 1) {\n buttons.push(\n <button\n key=\"reload\"\n onClick={handleReload}\n aria-label=\"刷新页面\"\n style={{\n border: '1px solid #cfd7df',\n background: '#fff',\n color: '#1f2933',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n 刷新页面\n </button>\n );\n }\n\n return buttons;\n }, [retryCount, handleRetry, handleReload, showDetails]);\n\n return (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '100vh',\n padding: '24px',\n }}\n >\n <div\n style={{\n maxWidth: 580,\n width: '100%',\n textAlign: 'center',\n border: '1px solid #dde5ec',\n borderRadius: 10,\n padding: 24,\n background: '#fff',\n }}\n >\n <div style={{ fontSize: 40, lineHeight: '44px' }}>⚠️</div>\n <h2 style={{ margin: '8px 0 4px' }}>出错了</h2>\n <p style={{ marginTop: 0, color: '#52606d' }}>{userFriendlyMessage}</p>\n <div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>\n {extraButtons}\n </div>\n </div>\n {showDetails && (\n <div\n style={{\n marginTop: 24,\n padding: 16,\n backgroundColor: '#f5f5f5',\n borderRadius: 4,\n maxWidth: 800,\n width: '100%',\n maxHeight: 400,\n overflow: 'auto',\n }}\n >\n <div style={{ marginBottom: 8, fontWeight: 'bold' }}>错误详情:</div>\n <div style={{ fontFamily: 'monospace', fontSize: 12 }}>\n <div><strong>错误类型:</strong>{normalizedError.name}</div>\n <div><strong>错误代码:</strong>{normalizedError.code}</div>\n <div><strong>错误信息:</strong>{normalizedError.message}</div>\n {normalizedError.stack && (\n <div style={{ marginTop: 8 }}>\n <strong>堆栈信息:</strong>\n <pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>\n {normalizedError.stack}\n </pre>\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n );\n });\n \n DefaultFallbackComponent.displayName = 'DefaultFallbackComponent';\n\n const defaultFallback = ({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n return <DefaultFallbackComponent error={error} resetErrorBoundary={resetErrorBoundary} />;\n };\n\n // 如果提供了自定义 fallback,使用自定义的\n const fallbackRender = fallback\n ? ({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n const normalizedError = errorUtils.normalizeError(error);\n return fallback(normalizedError, () => {\n handleReset();\n resetErrorBoundary();\n });\n }\n : defaultFallback;\n\n // 处理重置时清除重试计数\n const handleResetWithCount = (): void => {\n retryCountRef.current = 0;\n handleReset();\n };\n\n // 处理 resetKeys 变化时清除重试计数\n const handleResetKeysChange = (\n prevKeys: Array<string | number> | undefined,\n nextKeys: Array<string | number> | undefined\n ): void => {\n retryCountRef.current = 0;\n if (onResetKeysChange) {\n onResetKeysChange(prevKeys, nextKeys);\n }\n };\n\n // 构建 react-error-boundary 的 props\n const errorBoundaryProps = {\n fallbackRender,\n onError: handleError,\n onReset: handleResetWithCount,\n resetKeys,\n onResetKeysChange: handleResetKeysChange,\n } as any;\n\n return (\n <ReactErrorBoundary {...errorBoundaryProps}>\n {children}\n </ReactErrorBoundary>\n );\n}\n\n/**\n * 带错误边界的 HOC\n */\nexport function withErrorBoundary<P extends object>(\n Component: React.ComponentType<P>,\n errorBoundaryProps?: Omit<ErrorBoundaryProps, 'children'>\n): React.ComponentType<P> {\n const WrappedComponent = (props: P) => {\n return (\n <ErrorBoundary {...errorBoundaryProps}>\n <Component {...props} />\n </ErrorBoundary>\n );\n };\n\n WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name || 'Component'})`;\n\n return WrappedComponent;\n}\n\n/**\n * 使用错误处理器的 Hook\n * \n * 用于在函数组件中处理异步错误\n * \n * 注意:这个 Hook 需要配合 ErrorBoundary 使用。当调用返回的错误处理函数时,\n * 错误会被设置到初始化错误状态,然后通过 InitializationErrorThrower 组件抛出。\n * \n * @example\n * ```tsx\n * import { useErrorHandler } from '@vlian/framework';\n * \n * function MyComponent() {\n * const handleError = useErrorHandler();\n * \n * useEffect(() => {\n * async function fetchData() {\n * try {\n * await someAsyncOperation();\n * } catch (error) {\n * handleError(error);\n * }\n * }\n * fetchData();\n * }, [handleError]);\n * }\n * ```\n */\nexport function useErrorHandler(): (error: unknown) => void {\n return (error: unknown) => {\n // 将错误设置到初始化错误状态\n // InitializationErrorThrower 组件会检测到错误并抛出,让 ErrorBoundary 捕获\n initializationErrorState.setError(error);\n };\n}\n"],"names":["ErrorBoundary","useErrorHandler","withErrorBoundary","children","fallback","onError","showInConsole","resetKeys","onReset","onResetKeysChange","retryCountRef","useRef","errorHandler","useMemo","getDefaultErrorHandler","handleError","useCallback","error","errorInfo","normalizedError","errorUtils","normalizeError","reactErrorInfo","componentStack","handleResult","source","handled","logger","toJSON","console","handleReset","initializationErrorState","clearError","DefaultFallbackComponent","React","memo","resetErrorBoundary","retryCount","setRetryCount","useState","current","showDetails","setShowDetails","useEffect","handleRetry","newCount","handleReload","window","location","reload","userFriendlyMessage","code","extraButtons","buttons","button","onClick","aria-label","style","border","background","color","borderRadius","padding","cursor","push","div","role","aria-live","aria-atomic","display","flexDirection","alignItems","justifyContent","minHeight","maxWidth","width","textAlign","fontSize","lineHeight","h2","margin","p","marginTop","gap","flexWrap","backgroundColor","maxHeight","overflow","marginBottom","fontWeight","fontFamily","strong","name","message","stack","pre","whiteSpace","wordBreak","displayName","defaultFallback","fallbackRender","handleResetWithCount","handleResetKeysChange","prevKeys","nextKeys","errorBoundaryProps","ReactErrorBoundary","Component","WrappedComponent","props","setError"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/error/ErrorBoundary.tsx"],"sourcesContent":["import React, { type ReactNode, type ErrorInfo, useState, useRef, useEffect, useCallback, useMemo } from 'react';\nimport { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';\nimport { logger } from '@vlian/logger';\nimport { errorUtils } from '@vlian/utils';\nimport type { FrameworkError } from '@vlian/utils';\nimport { initializationErrorState } from '../initialization';\nimport { getDefaultErrorHandler } from './ErrorHandler';\n\n/**\n * 错误边界组件属性\n */\nexport interface ErrorBoundaryProps {\n /**\n * 子组件\n */\n children: ReactNode;\n /**\n * 错误回退 UI(函数形式)\n * \n * 如果不提供,将使用默认错误 UI(框架内置实现,居中显示)。\n * 如果提供,将使用自定义的错误 UI。\n * \n * @param error - 标准化后的框架错误对象\n * @param resetError - 重置错误边界的函数,调用后可以尝试重新渲染子组件\n * @returns 错误 UI 的 React 节点\n * \n * @example\n * ```tsx\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h2>自定义错误标题</h2>\n * <p>{error.message}</p>\n * <button onClick={reset}>重试</button>\n * </div>\n * )}\n * >\n * <App />\n * </ErrorBoundary>\n * ```\n */\n fallback?: (error: FrameworkError, resetError: () => void) => ReactNode;\n /**\n * 错误回调\n */\n onError?: (error: FrameworkError, errorInfo: ErrorInfo) => void;\n /**\n * 是否在控制台显示错误\n */\n showInConsole?: boolean;\n /**\n * 重置键数组,当这些值变化时自动重置错误边界\n */\n resetKeys?: Array<string | number>;\n /**\n * 重置错误时的回调\n */\n onReset?: () => void;\n /**\n * 重置键变化时的回调\n */\n onResetKeysChange?: (prevKeys: Array<string | number> | undefined, nextKeys: Array<string | number> | undefined) => void;\n}\n\n/**\n * 错误边界组件\n * \n * 基于 react-error-boundary 实现,集成了框架的错误处理系统\n * 用于捕获 React 组件树中的错误,防止整个应用崩溃\n * \n * 默认使用框架内置错误 UI,居中显示。\n * 可以通过 fallback 属性自定义错误 UI。\n * \n * @example\n * ```tsx\n * // 使用默认错误 UI(推荐)\n * <ErrorBoundary>\n * <App />\n * </ErrorBoundary>\n * \n * // 自定义错误 UI\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h1>出错了</h1>\n * <p>{error.message}</p>\n * <button onClick={reset}>重试</button>\n * </div>\n * )}\n * >\n * <App />\n * </ErrorBoundary>\n * ```\n */\nexport function ErrorBoundary({\n children,\n fallback,\n onError,\n showInConsole = true,\n resetKeys,\n onReset,\n onResetKeysChange,\n}: ErrorBoundaryProps): ReactNode {\n // 使用 ref 存储重试计数,在多次错误之间保持状态\n const retryCountRef = useRef<number>(0);\n\n // 优化:使用统一的错误处理器\n const errorHandler = useMemo(() => getDefaultErrorHandler(), []);\n\n // 处理错误,将标准 Error 转换为 FrameworkError\n const handleError = useCallback(async (error: Error, errorInfo: { componentStack?: string | null }): Promise<void> => {\n const normalizedError = errorUtils.normalizeError(error);\n\n // 将 react-error-boundary 的 errorInfo 转换为 React 的 ErrorInfo 格式\n const reactErrorInfo: ErrorInfo = {\n componentStack: errorInfo.componentStack || '',\n };\n\n // 使用统一的错误处理器处理错误\n const handleResult = await errorHandler.handleError(normalizedError, {\n componentStack: reactErrorInfo.componentStack,\n source: 'ErrorBoundary',\n });\n\n // 如果错误处理器没有处理错误,使用默认处理\n if (!handleResult.handled) {\n logger.error('错误边界捕获到错误:', {\n error: normalizedError.toJSON(),\n errorInfo: {\n componentStack: reactErrorInfo.componentStack,\n },\n });\n }\n\n // 调用错误回调\n if (onError) {\n onError(normalizedError, reactErrorInfo);\n }\n\n // 在控制台显示错误(如果配置了)\n if (showInConsole) {\n console.error('错误边界捕获到错误:', normalizedError);\n console.error('错误信息:', reactErrorInfo);\n }\n }, [errorHandler, onError, showInConsole]);\n\n // 处理重置\n const handleReset = (): void => {\n // 清除初始化错误状态\n initializationErrorState.clearError();\n // 调用用户提供的重置回调\n if (onReset) {\n onReset();\n }\n };\n\n // 默认 fallback UI(使用无框架 HTML 组件)\n // 使用内部组件来管理重试计数状态,使用 React.memo 优化性能\n const DefaultFallbackComponent = React.memo(({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n const normalizedError = errorUtils.normalizeError(error);\n const [retryCount, setRetryCount] = useState<number>(retryCountRef.current);\n const [showDetails, setShowDetails] = useState(false);\n\n // 同步 ref 的值到 state,确保组件重新创建时能读取到最新的重试计数\n useEffect(() => {\n setRetryCount(retryCountRef.current);\n }, []);\n\n const handleRetry = useCallback((): void => {\n // 增加重试计数\n const newCount = retryCount + 1;\n retryCountRef.current = newCount;\n setRetryCount(newCount);\n \n // 执行重置\n handleReset();\n resetErrorBoundary();\n }, [retryCount, resetErrorBoundary]);\n\n const handleReload = useCallback((): void => {\n // 刷新页面\n window.location.reload();\n }, []);\n\n // 构建用户友好的错误提示\n const userFriendlyMessage = useMemo(() => {\n // 根据错误类型提供不同的提示\n if (normalizedError.code === 'NETWORK_ERROR') {\n return '网络连接失败,请检查您的网络设置后再尝试';\n }\n if (normalizedError.code === 'TIMEOUT_ERROR') {\n return '请求超时,请稍后再尝试';\n }\n if (normalizedError.code === 'VALIDATION_ERROR') {\n return '数据验证失败,请检查输入内容';\n }\n // 默认提示\n return '应用运行时发生了错误,请稍后再尝试';\n }, [normalizedError.code]);\n\n // 构建按钮数组\n const extraButtons = useMemo(() => {\n const buttons = [\n <button\n key=\"retry\"\n onClick={handleRetry}\n aria-label=\"重试加载应用\"\n style={{\n border: 'none',\n background: 'var(--app-primary-color, #2f80ed)',\n color: '#fff',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n 重试\n </button>,\n <button\n key=\"details\"\n onClick={() => setShowDetails(!showDetails)}\n aria-label={showDetails ? '隐藏错误详情' : '显示错误详情'}\n style={{\n border: '1px solid #cfd7df',\n background: '#fff',\n color: '#1f2933',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n {showDetails ? '隐藏详情' : '查看详情'}\n </button>,\n ];\n\n // 如果重试次数 >= 1,添加刷新页面按钮(提前显示)\n if (retryCount >= 1) {\n buttons.push(\n <button\n key=\"reload\"\n onClick={handleReload}\n aria-label=\"刷新页面\"\n style={{\n border: '1px solid #cfd7df',\n background: '#fff',\n color: '#1f2933',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n 刷新页面\n </button>\n );\n }\n\n return buttons;\n }, [retryCount, handleRetry, handleReload, showDetails]);\n\n return (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '100vh',\n padding: '24px',\n }}\n >\n <div\n style={{\n maxWidth: 580,\n width: '100%',\n textAlign: 'center',\n border: '1px solid #dde5ec',\n borderRadius: 10,\n padding: 24,\n background: '#fff',\n }}\n >\n <div style={{ fontSize: 40, lineHeight: '44px' }}>⚠️</div>\n <h2 style={{ margin: '8px 0 4px' }}>出错了</h2>\n <p style={{ marginTop: 0, color: '#52606d' }}>{userFriendlyMessage}</p>\n <div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>\n {extraButtons}\n </div>\n </div>\n {showDetails && (\n <div\n style={{\n marginTop: 24,\n padding: 16,\n backgroundColor: '#f5f5f5',\n borderRadius: 4,\n maxWidth: 800,\n width: '100%',\n maxHeight: 400,\n overflow: 'auto',\n }}\n >\n <div style={{ marginBottom: 8, fontWeight: 'bold' }}>错误详情:</div>\n <div style={{ fontFamily: 'monospace', fontSize: 12 }}>\n <div><strong>错误类型:</strong>{normalizedError.name}</div>\n <div><strong>错误代码:</strong>{normalizedError.code}</div>\n <div><strong>错误信息:</strong>{normalizedError.message}</div>\n {normalizedError.stack && (\n <div style={{ marginTop: 8 }}>\n <strong>堆栈信息:</strong>\n <pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>\n {normalizedError.stack}\n </pre>\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n );\n });\n \n DefaultFallbackComponent.displayName = 'DefaultFallbackComponent';\n\n const defaultFallback = ({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n return <DefaultFallbackComponent error={error} resetErrorBoundary={resetErrorBoundary} />;\n };\n\n // 如果提供了自定义 fallback,使用自定义的\n const fallbackRender = fallback\n ? ({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n const normalizedError = errorUtils.normalizeError(error);\n return fallback(normalizedError, () => {\n handleReset();\n resetErrorBoundary();\n });\n }\n : defaultFallback;\n\n // 处理重置时清除重试计数\n const handleResetWithCount = (): void => {\n retryCountRef.current = 0;\n handleReset();\n };\n\n // 处理 resetKeys 变化时清除重试计数\n const handleResetKeysChange = (\n prevKeys: Array<string | number> | undefined,\n nextKeys: Array<string | number> | undefined\n ): void => {\n retryCountRef.current = 0;\n if (onResetKeysChange) {\n onResetKeysChange(prevKeys, nextKeys);\n }\n };\n\n // 构建 react-error-boundary 的 props\n const errorBoundaryProps = {\n fallbackRender,\n onError: handleError,\n onReset: handleResetWithCount,\n resetKeys,\n onResetKeysChange: handleResetKeysChange,\n } as any;\n\n return (\n <ReactErrorBoundary {...errorBoundaryProps}>\n {children}\n </ReactErrorBoundary>\n );\n}\n\n/**\n * 带错误边界的 HOC\n */\nexport function withErrorBoundary<P extends object>(\n Component: React.ComponentType<P>,\n errorBoundaryProps?: Omit<ErrorBoundaryProps, 'children'>\n): React.ComponentType<P> {\n const WrappedComponent = (props: P) => {\n return (\n <ErrorBoundary {...errorBoundaryProps}>\n <Component {...props} />\n </ErrorBoundary>\n );\n };\n\n WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name || 'Component'})`;\n\n return WrappedComponent;\n}\n\n/**\n * 使用错误处理器的 Hook\n * \n * 用于在函数组件中处理异步错误\n * \n * 注意:这个 Hook 需要配合 ErrorBoundary 使用。当调用返回的错误处理函数时,\n * 错误会被设置到初始化错误状态,然后通过 InitializationErrorThrower 组件抛出。\n * \n * @example\n * ```tsx\n * import { useErrorHandler } from '@vlian/framework';\n * \n * function MyComponent() {\n * const handleError = useErrorHandler();\n * \n * useEffect(() => {\n * async function fetchData() {\n * try {\n * await someAsyncOperation();\n * } catch (error) {\n * handleError(error);\n * }\n * }\n * fetchData();\n * }, [handleError]);\n * }\n * ```\n */\nexport function useErrorHandler(): (error: unknown) => void {\n return (error: unknown) => {\n // 将错误设置到初始化错误状态\n // InitializationErrorThrower 组件会检测到错误并抛出,让 ErrorBoundary 捕获\n initializationErrorState.setError(error);\n };\n}\n"],"names":["ErrorBoundary","useErrorHandler","withErrorBoundary","children","fallback","onError","showInConsole","resetKeys","onReset","onResetKeysChange","retryCountRef","useRef","errorHandler","useMemo","getDefaultErrorHandler","handleError","useCallback","error","errorInfo","normalizedError","errorUtils","normalizeError","reactErrorInfo","componentStack","handleResult","source","handled","logger","toJSON","console","handleReset","initializationErrorState","clearError","DefaultFallbackComponent","React","memo","resetErrorBoundary","retryCount","setRetryCount","useState","current","showDetails","setShowDetails","useEffect","handleRetry","newCount","handleReload","window","location","reload","userFriendlyMessage","code","extraButtons","buttons","button","onClick","aria-label","style","border","background","color","borderRadius","padding","cursor","push","div","role","aria-live","aria-atomic","display","flexDirection","alignItems","justifyContent","minHeight","maxWidth","width","textAlign","fontSize","lineHeight","h2","margin","p","marginTop","gap","flexWrap","backgroundColor","maxHeight","overflow","marginBottom","fontWeight","fontFamily","strong","name","message","stack","pre","whiteSpace","wordBreak","displayName","defaultFallback","fallbackRender","handleResetWithCount","handleResetKeysChange","prevKeys","nextKeys","errorBoundaryProps","ReactErrorBoundary","Component","WrappedComponent","props","setError"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import React, { type ReactNode, type ErrorInfo } from 'react';
2
- import type { FrameworkError } from '../../utils/errors';
2
+ import type { FrameworkError } from '@vlian/utils';
3
3
  /**
4
4
  * 错误边界组件属性
5
5
  */
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
3
3
  import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary";
4
- import { logger } from "../../utils";
5
- import { errorUtils } from "../../utils/errors";
4
+ import { logger } from "@vlian/logger";
5
+ import { errorUtils } from "@vlian/utils";
6
6
  import { initializationErrorState } from "../initialization";
7
7
  import { getDefaultErrorHandler } from "./ErrorHandler";
8
8
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/error/ErrorBoundary.tsx"],"sourcesContent":["import React, { type ReactNode, type ErrorInfo, useState, useRef, useEffect, useCallback, useMemo } from 'react';\nimport { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';\nimport { logger } from '../../utils';\nimport { errorUtils } from '../../utils/errors';\nimport type { FrameworkError } from '../../utils/errors';\nimport { initializationErrorState } from '../initialization';\nimport { getDefaultErrorHandler } from './ErrorHandler';\n\n/**\n * 错误边界组件属性\n */\nexport interface ErrorBoundaryProps {\n /**\n * 子组件\n */\n children: ReactNode;\n /**\n * 错误回退 UI(函数形式)\n * \n * 如果不提供,将使用默认错误 UI(框架内置实现,居中显示)。\n * 如果提供,将使用自定义的错误 UI。\n * \n * @param error - 标准化后的框架错误对象\n * @param resetError - 重置错误边界的函数,调用后可以尝试重新渲染子组件\n * @returns 错误 UI 的 React 节点\n * \n * @example\n * ```tsx\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h2>自定义错误标题</h2>\n * <p>{error.message}</p>\n * <button onClick={reset}>重试</button>\n * </div>\n * )}\n * >\n * <App />\n * </ErrorBoundary>\n * ```\n */\n fallback?: (error: FrameworkError, resetError: () => void) => ReactNode;\n /**\n * 错误回调\n */\n onError?: (error: FrameworkError, errorInfo: ErrorInfo) => void;\n /**\n * 是否在控制台显示错误\n */\n showInConsole?: boolean;\n /**\n * 重置键数组,当这些值变化时自动重置错误边界\n */\n resetKeys?: Array<string | number>;\n /**\n * 重置错误时的回调\n */\n onReset?: () => void;\n /**\n * 重置键变化时的回调\n */\n onResetKeysChange?: (prevKeys: Array<string | number> | undefined, nextKeys: Array<string | number> | undefined) => void;\n}\n\n/**\n * 错误边界组件\n * \n * 基于 react-error-boundary 实现,集成了框架的错误处理系统\n * 用于捕获 React 组件树中的错误,防止整个应用崩溃\n * \n * 默认使用框架内置错误 UI,居中显示。\n * 可以通过 fallback 属性自定义错误 UI。\n * \n * @example\n * ```tsx\n * // 使用默认错误 UI(推荐)\n * <ErrorBoundary>\n * <App />\n * </ErrorBoundary>\n * \n * // 自定义错误 UI\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h1>出错了</h1>\n * <p>{error.message}</p>\n * <button onClick={reset}>重试</button>\n * </div>\n * )}\n * >\n * <App />\n * </ErrorBoundary>\n * ```\n */\nexport function ErrorBoundary({\n children,\n fallback,\n onError,\n showInConsole = true,\n resetKeys,\n onReset,\n onResetKeysChange,\n}: ErrorBoundaryProps): ReactNode {\n // 使用 ref 存储重试计数,在多次错误之间保持状态\n const retryCountRef = useRef<number>(0);\n\n // 优化:使用统一的错误处理器\n const errorHandler = useMemo(() => getDefaultErrorHandler(), []);\n\n // 处理错误,将标准 Error 转换为 FrameworkError\n const handleError = useCallback(async (error: Error, errorInfo: { componentStack?: string | null }): Promise<void> => {\n const normalizedError = errorUtils.normalizeError(error);\n\n // 将 react-error-boundary 的 errorInfo 转换为 React 的 ErrorInfo 格式\n const reactErrorInfo: ErrorInfo = {\n componentStack: errorInfo.componentStack || '',\n };\n\n // 使用统一的错误处理器处理错误\n const handleResult = await errorHandler.handleError(normalizedError, {\n componentStack: reactErrorInfo.componentStack,\n source: 'ErrorBoundary',\n });\n\n // 如果错误处理器没有处理错误,使用默认处理\n if (!handleResult.handled) {\n logger.error('错误边界捕获到错误:', {\n error: normalizedError.toJSON(),\n errorInfo: {\n componentStack: reactErrorInfo.componentStack,\n },\n });\n }\n\n // 调用错误回调\n if (onError) {\n onError(normalizedError, reactErrorInfo);\n }\n\n // 在控制台显示错误(如果配置了)\n if (showInConsole) {\n console.error('错误边界捕获到错误:', normalizedError);\n console.error('错误信息:', reactErrorInfo);\n }\n }, [errorHandler, onError, showInConsole]);\n\n // 处理重置\n const handleReset = (): void => {\n // 清除初始化错误状态\n initializationErrorState.clearError();\n // 调用用户提供的重置回调\n if (onReset) {\n onReset();\n }\n };\n\n // 默认 fallback UI(使用无框架 HTML 组件)\n // 使用内部组件来管理重试计数状态,使用 React.memo 优化性能\n const DefaultFallbackComponent = React.memo(({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n const normalizedError = errorUtils.normalizeError(error);\n const [retryCount, setRetryCount] = useState<number>(retryCountRef.current);\n const [showDetails, setShowDetails] = useState(false);\n\n // 同步 ref 的值到 state,确保组件重新创建时能读取到最新的重试计数\n useEffect(() => {\n setRetryCount(retryCountRef.current);\n }, []);\n\n const handleRetry = useCallback((): void => {\n // 增加重试计数\n const newCount = retryCount + 1;\n retryCountRef.current = newCount;\n setRetryCount(newCount);\n \n // 执行重置\n handleReset();\n resetErrorBoundary();\n }, [retryCount, resetErrorBoundary]);\n\n const handleReload = useCallback((): void => {\n // 刷新页面\n window.location.reload();\n }, []);\n\n // 构建用户友好的错误提示\n const userFriendlyMessage = useMemo(() => {\n // 根据错误类型提供不同的提示\n if (normalizedError.code === 'NETWORK_ERROR') {\n return '网络连接失败,请检查您的网络设置后再尝试';\n }\n if (normalizedError.code === 'TIMEOUT_ERROR') {\n return '请求超时,请稍后再尝试';\n }\n if (normalizedError.code === 'VALIDATION_ERROR') {\n return '数据验证失败,请检查输入内容';\n }\n // 默认提示\n return '应用运行时发生了错误,请稍后再尝试';\n }, [normalizedError.code]);\n\n // 构建按钮数组\n const extraButtons = useMemo(() => {\n const buttons = [\n <button\n key=\"retry\"\n onClick={handleRetry}\n aria-label=\"重试加载应用\"\n style={{\n border: 'none',\n background: 'var(--app-primary-color, #2f80ed)',\n color: '#fff',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n 重试\n </button>,\n <button\n key=\"details\"\n onClick={() => setShowDetails(!showDetails)}\n aria-label={showDetails ? '隐藏错误详情' : '显示错误详情'}\n style={{\n border: '1px solid #cfd7df',\n background: '#fff',\n color: '#1f2933',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n {showDetails ? '隐藏详情' : '查看详情'}\n </button>,\n ];\n\n // 如果重试次数 >= 1,添加刷新页面按钮(提前显示)\n if (retryCount >= 1) {\n buttons.push(\n <button\n key=\"reload\"\n onClick={handleReload}\n aria-label=\"刷新页面\"\n style={{\n border: '1px solid #cfd7df',\n background: '#fff',\n color: '#1f2933',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n 刷新页面\n </button>\n );\n }\n\n return buttons;\n }, [retryCount, handleRetry, handleReload, showDetails]);\n\n return (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '100vh',\n padding: '24px',\n }}\n >\n <div\n style={{\n maxWidth: 580,\n width: '100%',\n textAlign: 'center',\n border: '1px solid #dde5ec',\n borderRadius: 10,\n padding: 24,\n background: '#fff',\n }}\n >\n <div style={{ fontSize: 40, lineHeight: '44px' }}>⚠️</div>\n <h2 style={{ margin: '8px 0 4px' }}>出错了</h2>\n <p style={{ marginTop: 0, color: '#52606d' }}>{userFriendlyMessage}</p>\n <div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>\n {extraButtons}\n </div>\n </div>\n {showDetails && (\n <div\n style={{\n marginTop: 24,\n padding: 16,\n backgroundColor: '#f5f5f5',\n borderRadius: 4,\n maxWidth: 800,\n width: '100%',\n maxHeight: 400,\n overflow: 'auto',\n }}\n >\n <div style={{ marginBottom: 8, fontWeight: 'bold' }}>错误详情:</div>\n <div style={{ fontFamily: 'monospace', fontSize: 12 }}>\n <div><strong>错误类型:</strong>{normalizedError.name}</div>\n <div><strong>错误代码:</strong>{normalizedError.code}</div>\n <div><strong>错误信息:</strong>{normalizedError.message}</div>\n {normalizedError.stack && (\n <div style={{ marginTop: 8 }}>\n <strong>堆栈信息:</strong>\n <pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>\n {normalizedError.stack}\n </pre>\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n );\n });\n \n DefaultFallbackComponent.displayName = 'DefaultFallbackComponent';\n\n const defaultFallback = ({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n return <DefaultFallbackComponent error={error} resetErrorBoundary={resetErrorBoundary} />;\n };\n\n // 如果提供了自定义 fallback,使用自定义的\n const fallbackRender = fallback\n ? ({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n const normalizedError = errorUtils.normalizeError(error);\n return fallback(normalizedError, () => {\n handleReset();\n resetErrorBoundary();\n });\n }\n : defaultFallback;\n\n // 处理重置时清除重试计数\n const handleResetWithCount = (): void => {\n retryCountRef.current = 0;\n handleReset();\n };\n\n // 处理 resetKeys 变化时清除重试计数\n const handleResetKeysChange = (\n prevKeys: Array<string | number> | undefined,\n nextKeys: Array<string | number> | undefined\n ): void => {\n retryCountRef.current = 0;\n if (onResetKeysChange) {\n onResetKeysChange(prevKeys, nextKeys);\n }\n };\n\n // 构建 react-error-boundary 的 props\n const errorBoundaryProps = {\n fallbackRender,\n onError: handleError,\n onReset: handleResetWithCount,\n resetKeys,\n onResetKeysChange: handleResetKeysChange,\n } as any;\n\n return (\n <ReactErrorBoundary {...errorBoundaryProps}>\n {children}\n </ReactErrorBoundary>\n );\n}\n\n/**\n * 带错误边界的 HOC\n */\nexport function withErrorBoundary<P extends object>(\n Component: React.ComponentType<P>,\n errorBoundaryProps?: Omit<ErrorBoundaryProps, 'children'>\n): React.ComponentType<P> {\n const WrappedComponent = (props: P) => {\n return (\n <ErrorBoundary {...errorBoundaryProps}>\n <Component {...props} />\n </ErrorBoundary>\n );\n };\n\n WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name || 'Component'})`;\n\n return WrappedComponent;\n}\n\n/**\n * 使用错误处理器的 Hook\n * \n * 用于在函数组件中处理异步错误\n * \n * 注意:这个 Hook 需要配合 ErrorBoundary 使用。当调用返回的错误处理函数时,\n * 错误会被设置到初始化错误状态,然后通过 InitializationErrorThrower 组件抛出。\n * \n * @example\n * ```tsx\n * import { useErrorHandler } from '@vlian/framework';\n * \n * function MyComponent() {\n * const handleError = useErrorHandler();\n * \n * useEffect(() => {\n * async function fetchData() {\n * try {\n * await someAsyncOperation();\n * } catch (error) {\n * handleError(error);\n * }\n * }\n * fetchData();\n * }, [handleError]);\n * }\n * ```\n */\nexport function useErrorHandler(): (error: unknown) => void {\n return (error: unknown) => {\n // 将错误设置到初始化错误状态\n // InitializationErrorThrower 组件会检测到错误并抛出,让 ErrorBoundary 捕获\n initializationErrorState.setError(error);\n };\n}\n"],"names":["React","useState","useRef","useEffect","useCallback","useMemo","ErrorBoundary","ReactErrorBoundary","logger","errorUtils","initializationErrorState","getDefaultErrorHandler","children","fallback","onError","showInConsole","resetKeys","onReset","onResetKeysChange","retryCountRef","errorHandler","handleError","error","errorInfo","normalizedError","normalizeError","reactErrorInfo","componentStack","handleResult","source","handled","toJSON","console","handleReset","clearError","DefaultFallbackComponent","memo","resetErrorBoundary","retryCount","setRetryCount","current","showDetails","setShowDetails","handleRetry","newCount","handleReload","window","location","reload","userFriendlyMessage","code","extraButtons","buttons","button","onClick","aria-label","style","border","background","color","borderRadius","padding","cursor","push","div","role","aria-live","aria-atomic","display","flexDirection","alignItems","justifyContent","minHeight","maxWidth","width","textAlign","fontSize","lineHeight","h2","margin","p","marginTop","gap","flexWrap","backgroundColor","maxHeight","overflow","marginBottom","fontWeight","fontFamily","strong","name","message","stack","pre","whiteSpace","wordBreak","displayName","defaultFallback","fallbackRender","handleResetWithCount","handleResetKeysChange","prevKeys","nextKeys","errorBoundaryProps","withErrorBoundary","Component","WrappedComponent","props","useErrorHandler","setError"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/error/ErrorBoundary.tsx"],"sourcesContent":["import React, { type ReactNode, type ErrorInfo, useState, useRef, useEffect, useCallback, useMemo } from 'react';\nimport { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';\nimport { logger } from '@vlian/logger';\nimport { errorUtils } from '@vlian/utils';\nimport type { FrameworkError } from '@vlian/utils';\nimport { initializationErrorState } from '../initialization';\nimport { getDefaultErrorHandler } from './ErrorHandler';\n\n/**\n * 错误边界组件属性\n */\nexport interface ErrorBoundaryProps {\n /**\n * 子组件\n */\n children: ReactNode;\n /**\n * 错误回退 UI(函数形式)\n * \n * 如果不提供,将使用默认错误 UI(框架内置实现,居中显示)。\n * 如果提供,将使用自定义的错误 UI。\n * \n * @param error - 标准化后的框架错误对象\n * @param resetError - 重置错误边界的函数,调用后可以尝试重新渲染子组件\n * @returns 错误 UI 的 React 节点\n * \n * @example\n * ```tsx\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h2>自定义错误标题</h2>\n * <p>{error.message}</p>\n * <button onClick={reset}>重试</button>\n * </div>\n * )}\n * >\n * <App />\n * </ErrorBoundary>\n * ```\n */\n fallback?: (error: FrameworkError, resetError: () => void) => ReactNode;\n /**\n * 错误回调\n */\n onError?: (error: FrameworkError, errorInfo: ErrorInfo) => void;\n /**\n * 是否在控制台显示错误\n */\n showInConsole?: boolean;\n /**\n * 重置键数组,当这些值变化时自动重置错误边界\n */\n resetKeys?: Array<string | number>;\n /**\n * 重置错误时的回调\n */\n onReset?: () => void;\n /**\n * 重置键变化时的回调\n */\n onResetKeysChange?: (prevKeys: Array<string | number> | undefined, nextKeys: Array<string | number> | undefined) => void;\n}\n\n/**\n * 错误边界组件\n * \n * 基于 react-error-boundary 实现,集成了框架的错误处理系统\n * 用于捕获 React 组件树中的错误,防止整个应用崩溃\n * \n * 默认使用框架内置错误 UI,居中显示。\n * 可以通过 fallback 属性自定义错误 UI。\n * \n * @example\n * ```tsx\n * // 使用默认错误 UI(推荐)\n * <ErrorBoundary>\n * <App />\n * </ErrorBoundary>\n * \n * // 自定义错误 UI\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h1>出错了</h1>\n * <p>{error.message}</p>\n * <button onClick={reset}>重试</button>\n * </div>\n * )}\n * >\n * <App />\n * </ErrorBoundary>\n * ```\n */\nexport function ErrorBoundary({\n children,\n fallback,\n onError,\n showInConsole = true,\n resetKeys,\n onReset,\n onResetKeysChange,\n}: ErrorBoundaryProps): ReactNode {\n // 使用 ref 存储重试计数,在多次错误之间保持状态\n const retryCountRef = useRef<number>(0);\n\n // 优化:使用统一的错误处理器\n const errorHandler = useMemo(() => getDefaultErrorHandler(), []);\n\n // 处理错误,将标准 Error 转换为 FrameworkError\n const handleError = useCallback(async (error: Error, errorInfo: { componentStack?: string | null }): Promise<void> => {\n const normalizedError = errorUtils.normalizeError(error);\n\n // 将 react-error-boundary 的 errorInfo 转换为 React 的 ErrorInfo 格式\n const reactErrorInfo: ErrorInfo = {\n componentStack: errorInfo.componentStack || '',\n };\n\n // 使用统一的错误处理器处理错误\n const handleResult = await errorHandler.handleError(normalizedError, {\n componentStack: reactErrorInfo.componentStack,\n source: 'ErrorBoundary',\n });\n\n // 如果错误处理器没有处理错误,使用默认处理\n if (!handleResult.handled) {\n logger.error('错误边界捕获到错误:', {\n error: normalizedError.toJSON(),\n errorInfo: {\n componentStack: reactErrorInfo.componentStack,\n },\n });\n }\n\n // 调用错误回调\n if (onError) {\n onError(normalizedError, reactErrorInfo);\n }\n\n // 在控制台显示错误(如果配置了)\n if (showInConsole) {\n console.error('错误边界捕获到错误:', normalizedError);\n console.error('错误信息:', reactErrorInfo);\n }\n }, [errorHandler, onError, showInConsole]);\n\n // 处理重置\n const handleReset = (): void => {\n // 清除初始化错误状态\n initializationErrorState.clearError();\n // 调用用户提供的重置回调\n if (onReset) {\n onReset();\n }\n };\n\n // 默认 fallback UI(使用无框架 HTML 组件)\n // 使用内部组件来管理重试计数状态,使用 React.memo 优化性能\n const DefaultFallbackComponent = React.memo(({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n const normalizedError = errorUtils.normalizeError(error);\n const [retryCount, setRetryCount] = useState<number>(retryCountRef.current);\n const [showDetails, setShowDetails] = useState(false);\n\n // 同步 ref 的值到 state,确保组件重新创建时能读取到最新的重试计数\n useEffect(() => {\n setRetryCount(retryCountRef.current);\n }, []);\n\n const handleRetry = useCallback((): void => {\n // 增加重试计数\n const newCount = retryCount + 1;\n retryCountRef.current = newCount;\n setRetryCount(newCount);\n \n // 执行重置\n handleReset();\n resetErrorBoundary();\n }, [retryCount, resetErrorBoundary]);\n\n const handleReload = useCallback((): void => {\n // 刷新页面\n window.location.reload();\n }, []);\n\n // 构建用户友好的错误提示\n const userFriendlyMessage = useMemo(() => {\n // 根据错误类型提供不同的提示\n if (normalizedError.code === 'NETWORK_ERROR') {\n return '网络连接失败,请检查您的网络设置后再尝试';\n }\n if (normalizedError.code === 'TIMEOUT_ERROR') {\n return '请求超时,请稍后再尝试';\n }\n if (normalizedError.code === 'VALIDATION_ERROR') {\n return '数据验证失败,请检查输入内容';\n }\n // 默认提示\n return '应用运行时发生了错误,请稍后再尝试';\n }, [normalizedError.code]);\n\n // 构建按钮数组\n const extraButtons = useMemo(() => {\n const buttons = [\n <button\n key=\"retry\"\n onClick={handleRetry}\n aria-label=\"重试加载应用\"\n style={{\n border: 'none',\n background: 'var(--app-primary-color, #2f80ed)',\n color: '#fff',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n 重试\n </button>,\n <button\n key=\"details\"\n onClick={() => setShowDetails(!showDetails)}\n aria-label={showDetails ? '隐藏错误详情' : '显示错误详情'}\n style={{\n border: '1px solid #cfd7df',\n background: '#fff',\n color: '#1f2933',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n {showDetails ? '隐藏详情' : '查看详情'}\n </button>,\n ];\n\n // 如果重试次数 >= 1,添加刷新页面按钮(提前显示)\n if (retryCount >= 1) {\n buttons.push(\n <button\n key=\"reload\"\n onClick={handleReload}\n aria-label=\"刷新页面\"\n style={{\n border: '1px solid #cfd7df',\n background: '#fff',\n color: '#1f2933',\n borderRadius: 6,\n padding: '8px 14px',\n cursor: 'pointer',\n }}\n >\n 刷新页面\n </button>\n );\n }\n\n return buttons;\n }, [retryCount, handleRetry, handleReload, showDetails]);\n\n return (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '100vh',\n padding: '24px',\n }}\n >\n <div\n style={{\n maxWidth: 580,\n width: '100%',\n textAlign: 'center',\n border: '1px solid #dde5ec',\n borderRadius: 10,\n padding: 24,\n background: '#fff',\n }}\n >\n <div style={{ fontSize: 40, lineHeight: '44px' }}>⚠️</div>\n <h2 style={{ margin: '8px 0 4px' }}>出错了</h2>\n <p style={{ marginTop: 0, color: '#52606d' }}>{userFriendlyMessage}</p>\n <div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>\n {extraButtons}\n </div>\n </div>\n {showDetails && (\n <div\n style={{\n marginTop: 24,\n padding: 16,\n backgroundColor: '#f5f5f5',\n borderRadius: 4,\n maxWidth: 800,\n width: '100%',\n maxHeight: 400,\n overflow: 'auto',\n }}\n >\n <div style={{ marginBottom: 8, fontWeight: 'bold' }}>错误详情:</div>\n <div style={{ fontFamily: 'monospace', fontSize: 12 }}>\n <div><strong>错误类型:</strong>{normalizedError.name}</div>\n <div><strong>错误代码:</strong>{normalizedError.code}</div>\n <div><strong>错误信息:</strong>{normalizedError.message}</div>\n {normalizedError.stack && (\n <div style={{ marginTop: 8 }}>\n <strong>堆栈信息:</strong>\n <pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>\n {normalizedError.stack}\n </pre>\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n );\n });\n \n DefaultFallbackComponent.displayName = 'DefaultFallbackComponent';\n\n const defaultFallback = ({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n return <DefaultFallbackComponent error={error} resetErrorBoundary={resetErrorBoundary} />;\n };\n\n // 如果提供了自定义 fallback,使用自定义的\n const fallbackRender = fallback\n ? ({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }): ReactNode => {\n const normalizedError = errorUtils.normalizeError(error);\n return fallback(normalizedError, () => {\n handleReset();\n resetErrorBoundary();\n });\n }\n : defaultFallback;\n\n // 处理重置时清除重试计数\n const handleResetWithCount = (): void => {\n retryCountRef.current = 0;\n handleReset();\n };\n\n // 处理 resetKeys 变化时清除重试计数\n const handleResetKeysChange = (\n prevKeys: Array<string | number> | undefined,\n nextKeys: Array<string | number> | undefined\n ): void => {\n retryCountRef.current = 0;\n if (onResetKeysChange) {\n onResetKeysChange(prevKeys, nextKeys);\n }\n };\n\n // 构建 react-error-boundary 的 props\n const errorBoundaryProps = {\n fallbackRender,\n onError: handleError,\n onReset: handleResetWithCount,\n resetKeys,\n onResetKeysChange: handleResetKeysChange,\n } as any;\n\n return (\n <ReactErrorBoundary {...errorBoundaryProps}>\n {children}\n </ReactErrorBoundary>\n );\n}\n\n/**\n * 带错误边界的 HOC\n */\nexport function withErrorBoundary<P extends object>(\n Component: React.ComponentType<P>,\n errorBoundaryProps?: Omit<ErrorBoundaryProps, 'children'>\n): React.ComponentType<P> {\n const WrappedComponent = (props: P) => {\n return (\n <ErrorBoundary {...errorBoundaryProps}>\n <Component {...props} />\n </ErrorBoundary>\n );\n };\n\n WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name || 'Component'})`;\n\n return WrappedComponent;\n}\n\n/**\n * 使用错误处理器的 Hook\n * \n * 用于在函数组件中处理异步错误\n * \n * 注意:这个 Hook 需要配合 ErrorBoundary 使用。当调用返回的错误处理函数时,\n * 错误会被设置到初始化错误状态,然后通过 InitializationErrorThrower 组件抛出。\n * \n * @example\n * ```tsx\n * import { useErrorHandler } from '@vlian/framework';\n * \n * function MyComponent() {\n * const handleError = useErrorHandler();\n * \n * useEffect(() => {\n * async function fetchData() {\n * try {\n * await someAsyncOperation();\n * } catch (error) {\n * handleError(error);\n * }\n * }\n * fetchData();\n * }, [handleError]);\n * }\n * ```\n */\nexport function useErrorHandler(): (error: unknown) => void {\n return (error: unknown) => {\n // 将错误设置到初始化错误状态\n // InitializationErrorThrower 组件会检测到错误并抛出,让 ErrorBoundary 捕获\n initializationErrorState.setError(error);\n };\n}\n"],"names":["React","useState","useRef","useEffect","useCallback","useMemo","ErrorBoundary","ReactErrorBoundary","logger","errorUtils","initializationErrorState","getDefaultErrorHandler","children","fallback","onError","showInConsole","resetKeys","onReset","onResetKeysChange","retryCountRef","errorHandler","handleError","error","errorInfo","normalizedError","normalizeError","reactErrorInfo","componentStack","handleResult","source","handled","toJSON","console","handleReset","clearError","DefaultFallbackComponent","memo","resetErrorBoundary","retryCount","setRetryCount","current","showDetails","setShowDetails","handleRetry","newCount","handleReload","window","location","reload","userFriendlyMessage","code","extraButtons","buttons","button","onClick","aria-label","style","border","background","color","borderRadius","padding","cursor","push","div","role","aria-live","aria-atomic","display","flexDirection","alignItems","justifyContent","minHeight","maxWidth","width","textAlign","fontSize","lineHeight","h2","margin","p","marginTop","gap","flexWrap","backgroundColor","maxHeight","overflow","marginBottom","fontWeight","fontFamily","strong","name","message","stack","pre","whiteSpace","wordBreak","displayName","defaultFallback","fallbackRender","handleResetWithCount","handleResetKeysChange","prevKeys","nextKeys","errorBoundaryProps","withErrorBoundary","Component","WrappedComponent","props","useErrorHandler","setError"],"mappings":"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"}
@@ -29,8 +29,8 @@ _export(exports, {
29
29
  return setDefaultErrorHandler;
30
30
  }
31
31
  });
32
- const _utils = require("../../utils");
33
- const _errors = require("../../utils/errors");
32
+ const _logger = require("@vlian/logger");
33
+ const _utils = require("@vlian/utils");
34
34
  function _define_property(obj, key, value) {
35
35
  if (key in obj) {
36
36
  Object.defineProperty(obj, key, {
@@ -76,9 +76,9 @@ let ErrorHandler = class ErrorHandler {
76
76
  * @returns 错误处理结果
77
77
  */ async handleError(error, context) {
78
78
  // 标准化错误对象
79
- const normalizedError = _errors.errorUtils.normalizeError(error);
79
+ const normalizedError = _utils.errorUtils.normalizeError(error);
80
80
  // 创建包含上下文信息的错误对象(由于 context 是只读的,需要创建新对象)
81
- const errorWithContext = context ? new _errors.FrameworkError(normalizedError.message, normalizedError.type, normalizedError.severity, {
81
+ const errorWithContext = context ? new _utils.FrameworkError(normalizedError.message, normalizedError.type, normalizedError.severity, {
82
82
  code: normalizedError.code,
83
83
  originalError: normalizedError.originalError,
84
84
  context: {
@@ -150,7 +150,7 @@ let ErrorHandler = class ErrorHandler {
150
150
  break;
151
151
  }
152
152
  } catch (strategyError) {
153
- _utils.logger.warn(`错误处理策略 ${strategy} 执行失败:`, strategyError);
153
+ _logger.logger.warn(`错误处理策略 ${strategy} 执行失败:`, strategyError);
154
154
  }
155
155
  }
156
156
  return handled;
@@ -158,23 +158,23 @@ let ErrorHandler = class ErrorHandler {
158
158
  /**
159
159
  * 记录错误
160
160
  */ logError(error) {
161
- const errorInfo = _errors.errorUtils.extractErrorInfo(error);
161
+ const errorInfo = _utils.errorUtils.extractErrorInfo(error);
162
162
  // 根据错误严重程度选择日志级别
163
163
  switch(error.severity){
164
- case _errors.ErrorSeverity.CRITICAL:
165
- case _errors.ErrorSeverity.HIGH:
166
- _utils.logger.error('错误:', errorInfo);
164
+ case _utils.ErrorSeverity.CRITICAL:
165
+ case _utils.ErrorSeverity.HIGH:
166
+ _logger.logger.error('错误:', errorInfo);
167
167
  break;
168
- case _errors.ErrorSeverity.MEDIUM:
169
- _utils.logger.warn('警告:', errorInfo);
168
+ case _utils.ErrorSeverity.MEDIUM:
169
+ _logger.logger.warn('警告:', errorInfo);
170
170
  break;
171
- case _errors.ErrorSeverity.LOW:
172
- _utils.logger.info('信息:', errorInfo);
171
+ case _utils.ErrorSeverity.LOW:
172
+ _logger.logger.info('信息:', errorInfo);
173
173
  break;
174
174
  }
175
175
  // 开发环境显示详细错误信息
176
176
  if (this.config.showDetailedErrorsInDev && process.env.NODE_ENV === 'development') {
177
- const detailedError = _errors.errorUtils.formatErrorForDev(error);
177
+ const detailedError = _utils.errorUtils.formatErrorForDev(error);
178
178
  console.error(detailedError);
179
179
  }
180
180
  }
@@ -182,7 +182,7 @@ let ErrorHandler = class ErrorHandler {
182
182
  * 上报错误
183
183
  */ async reportError(error) {
184
184
  if (!this.monitoring) {
185
- _utils.logger.debug('监控服务未配置,跳过错误上报');
185
+ _logger.logger.debug('监控服务未配置,跳过错误上报');
186
186
  return;
187
187
  }
188
188
  try {
@@ -193,7 +193,7 @@ let ErrorHandler = class ErrorHandler {
193
193
  context: error.context
194
194
  });
195
195
  } catch (reportError) {
196
- _utils.logger.warn('错误上报失败:', reportError);
196
+ _logger.logger.warn('错误上报失败:', reportError);
197
197
  }
198
198
  }
199
199
  /**
@@ -213,13 +213,13 @@ let ErrorHandler = class ErrorHandler {
213
213
  if (options.fallback) {
214
214
  try {
215
215
  const fallbackValue = await Promise.resolve(options.fallback());
216
- _utils.logger.info('错误已通过降级方案恢复');
216
+ _logger.logger.info('错误已通过降级方案恢复');
217
217
  return {
218
218
  recoverable: true,
219
219
  value: fallbackValue
220
220
  };
221
221
  } catch (fallbackError) {
222
- _utils.logger.warn('降级方案执行失败:', fallbackError);
222
+ _logger.logger.warn('降级方案执行失败:', fallbackError);
223
223
  }
224
224
  }
225
225
  // 尝试重试(如果错误可恢复且满足重试条件)
@@ -229,7 +229,7 @@ let ErrorHandler = class ErrorHandler {
229
229
  break;
230
230
  }
231
231
  const delay = exponentialBackoff ? retryDelay * Math.pow(2, attempt) : retryDelay;
232
- _utils.logger.debug(`错误恢复重试 ${attempt + 1}/${maxRetries},${delay}ms 后重试...`);
232
+ _logger.logger.debug(`错误恢复重试 ${attempt + 1}/${maxRetries},${delay}ms 后重试...`);
233
233
  await new Promise((resolve)=>setTimeout(resolve, delay));
234
234
  // 这里应该调用原始操作的重试逻辑
235
235
  // 由于错误处理器不知道原始操作,这里只返回可恢复状态