@vlian/framework 1.2.60 → 1.2.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics.umd.js +1 -1
- package/dist/components/index.cjs +1 -1
- package/dist/components/index.d.ts +0 -4
- package/dist/components/index.js +1 -1
- package/dist/core-bak/Test.cjs +1 -0
- package/dist/core-bak/Test.d.ts +5 -0
- package/dist/core-bak/Test.js +1 -0
- package/dist/core-bak/app/AppContext.cjs +1 -0
- package/dist/core-bak/app/AppContext.d.ts +113 -0
- package/dist/core-bak/app/AppContext.js +1 -0
- package/dist/core-bak/app/AppContext.types.cjs +1 -0
- package/dist/core-bak/app/AppContext.types.d.ts +146 -0
- package/dist/core-bak/app/AppContext.types.js +1 -0
- package/dist/core-bak/app/BasicLayout.cjs +1 -0
- package/dist/core-bak/app/BasicLayout.d.ts +15 -0
- package/dist/core-bak/app/BasicLayout.js +1 -0
- package/dist/core-bak/app/DefaultApp.cjs +1 -0
- package/dist/core-bak/app/DefaultApp.d.ts +10 -0
- package/dist/core-bak/app/DefaultApp.js +1 -0
- package/dist/core-bak/app/index.cjs +1 -0
- package/dist/core-bak/app/index.d.ts +8 -0
- package/dist/core-bak/app/index.js +1 -0
- package/dist/core-bak/config/AppConfig.cjs +1 -0
- package/dist/core-bak/config/AppConfig.d.ts +88 -0
- package/dist/core-bak/config/AppConfig.js +1 -0
- package/dist/core-bak/config/ConfigLoader.cjs +1 -0
- package/dist/core-bak/config/ConfigLoader.d.ts +118 -0
- package/dist/core-bak/config/ConfigLoader.js +1 -0
- package/dist/core-bak/config/ConfigValidator.cjs +2 -0
- package/dist/core-bak/config/ConfigValidator.d.ts +84 -0
- package/dist/core-bak/config/ConfigValidator.js +2 -0
- package/dist/core-bak/config/index.cjs +1 -0
- package/dist/core-bak/config/index.d.ts +6 -0
- package/dist/core-bak/config/index.js +1 -0
- package/dist/core-bak/dev/DevTools.cjs +1 -0
- package/dist/core-bak/dev/DevTools.d.ts +16 -0
- package/dist/core-bak/dev/DevTools.js +1 -0
- package/dist/core-bak/error/ErrorBoundary.cjs +1 -0
- package/dist/core-bak/error/ErrorBoundary.d.ts +121 -0
- package/dist/core-bak/error/ErrorBoundary.js +1 -0
- package/dist/core-bak/error/ErrorHandler.cjs +1 -0
- package/dist/core-bak/error/ErrorHandler.d.ts +171 -0
- package/dist/core-bak/error/ErrorHandler.js +1 -0
- package/dist/core-bak/error/index.cjs +1 -0
- package/dist/core-bak/error/index.d.ts +7 -0
- package/dist/core-bak/error/index.js +1 -0
- package/dist/core-bak/event/AppEventBus.cjs +1 -0
- package/dist/core-bak/event/AppEventBus.d.ts +178 -0
- package/dist/core-bak/event/AppEventBus.js +1 -0
- package/dist/core-bak/event/frameworkEvents.cjs +1 -0
- package/dist/core-bak/event/frameworkEvents.d.ts +69 -0
- package/dist/core-bak/event/frameworkEvents.js +1 -0
- package/dist/core-bak/event/hooks.cjs +1 -0
- package/dist/core-bak/event/hooks.d.ts +57 -0
- package/dist/core-bak/event/hooks.js +1 -0
- package/dist/core-bak/event/index.cjs +1 -0
- package/dist/core-bak/event/index.d.ts +10 -0
- package/dist/core-bak/event/index.js +1 -0
- package/dist/core-bak/event/types.cjs +1 -0
- package/dist/core-bak/event/types.d.ts +269 -0
- package/dist/core-bak/event/types.js +1 -0
- package/dist/core-bak/event/useEventBus.cjs +1 -0
- package/dist/core-bak/event/useEventBus.d.ts +28 -0
- package/dist/core-bak/event/useEventBus.js +1 -0
- package/dist/core-bak/index.cjs +1 -0
- package/dist/core-bak/index.d.ts +54 -0
- package/dist/core-bak/index.js +1 -0
- package/dist/core-bak/initialization/InitializationErrorThrower.cjs +1 -0
- package/dist/core-bak/initialization/InitializationErrorThrower.d.ts +7 -0
- package/dist/core-bak/initialization/InitializationErrorThrower.js +1 -0
- package/dist/core-bak/initialization/index.cjs +1 -0
- package/dist/core-bak/initialization/index.d.ts +7 -0
- package/dist/core-bak/initialization/index.js +1 -0
- package/dist/core-bak/initialization/initialization.cjs +1 -0
- package/dist/core-bak/initialization/initialization.d.ts +69 -0
- package/dist/core-bak/initialization/initialization.js +1 -0
- package/dist/core-bak/initialization/initializationErrorState.cjs +1 -0
- package/dist/core-bak/initialization/initializationErrorState.d.ts +35 -0
- package/dist/core-bak/initialization/initializationErrorState.js +1 -0
- package/dist/core-bak/kernel/defaultAdapters.cjs +1 -0
- package/dist/core-bak/kernel/defaultAdapters.d.ts +2 -0
- package/dist/core-bak/kernel/defaultAdapters.js +1 -0
- package/dist/core-bak/kernel/errors.cjs +1 -0
- package/dist/core-bak/kernel/errors.d.ts +18 -0
- package/dist/core-bak/kernel/errors.js +1 -0
- package/dist/core-bak/kernel/index.cjs +1 -0
- package/dist/core-bak/kernel/index.d.ts +3 -0
- package/dist/core-bak/kernel/index.js +1 -0
- package/dist/core-bak/kernel/startKernel.cjs +1 -0
- package/dist/core-bak/kernel/startKernel.d.ts +2 -0
- package/dist/core-bak/kernel/startKernel.js +1 -0
- package/dist/core-bak/kernel/types.cjs +1 -0
- package/dist/core-bak/kernel/types.d.ts +114 -0
- package/dist/core-bak/kernel/types.js +1 -0
- package/dist/core-bak/middleware.cjs +1 -0
- package/dist/core-bak/middleware.d.ts +36 -0
- package/dist/core-bak/middleware.js +1 -0
- package/dist/core-bak/plugin/PluginEventBus.cjs +1 -0
- package/dist/core-bak/plugin/PluginEventBus.d.ts +169 -0
- package/dist/core-bak/plugin/PluginEventBus.js +1 -0
- package/dist/core-bak/plugin/PluginSandbox.cjs +1 -0
- package/dist/core-bak/plugin/PluginSandbox.d.ts +101 -0
- package/dist/core-bak/plugin/PluginSandbox.js +1 -0
- package/dist/core-bak/plugin.cjs +1 -0
- package/dist/core-bak/plugin.d.ts +224 -0
- package/dist/core-bak/plugin.js +1 -0
- package/dist/core-bak/router/RouterManager.cjs +1 -0
- package/dist/core-bak/router/RouterManager.d.ts +87 -0
- package/dist/core-bak/router/RouterManager.js +1 -0
- package/dist/core-bak/router/adapter/AdapterManager.cjs +1 -0
- package/dist/core-bak/router/adapter/AdapterManager.d.ts +94 -0
- package/dist/core-bak/router/adapter/AdapterManager.js +1 -0
- package/dist/core-bak/router/adapter/index.cjs +1 -0
- package/dist/core-bak/router/adapter/index.d.ts +6 -0
- package/dist/core-bak/router/adapter/index.js +1 -0
- package/dist/core-bak/router/adapter/react-router/ReactRouterAdapter.cjs +1 -0
- package/dist/core-bak/router/adapter/react-router/ReactRouterAdapter.d.ts +43 -0
- package/dist/core-bak/router/adapter/react-router/ReactRouterAdapter.js +1 -0
- package/dist/core-bak/router/adapter/react-router/index.cjs +1 -0
- package/dist/core-bak/router/adapter/react-router/index.d.ts +4 -0
- package/dist/core-bak/router/adapter/react-router/index.js +1 -0
- package/dist/core-bak/router/adapter/types.cjs +1 -0
- package/dist/core-bak/router/adapter/types.d.ts +111 -0
- package/dist/core-bak/router/adapter/types.js +1 -0
- package/dist/core-bak/router/dev/RouterDevTools.cjs +1 -0
- package/dist/core-bak/router/dev/RouterDevTools.d.ts +5 -0
- package/dist/core-bak/router/dev/RouterDevTools.js +1 -0
- package/dist/core-bak/router/dev/index.cjs +1 -0
- package/dist/core-bak/router/dev/index.d.ts +4 -0
- package/dist/core-bak/router/dev/index.js +1 -0
- package/dist/core-bak/router/dynamic/DynamicRouteManager.cjs +1 -0
- package/dist/core-bak/router/dynamic/DynamicRouteManager.d.ts +71 -0
- package/dist/core-bak/router/dynamic/DynamicRouteManager.js +1 -0
- package/dist/core-bak/router/dynamic/index.cjs +1 -0
- package/dist/core-bak/router/dynamic/index.d.ts +4 -0
- package/dist/core-bak/router/dynamic/index.js +1 -0
- package/dist/core-bak/router/errors/RouterError.cjs +1 -0
- package/dist/core-bak/router/errors/RouterError.d.ts +48 -0
- package/dist/core-bak/router/errors/RouterError.js +1 -0
- package/dist/core-bak/router/errors/index.cjs +1 -0
- package/dist/core-bak/router/errors/index.d.ts +4 -0
- package/dist/core-bak/router/errors/index.js +1 -0
- package/dist/core-bak/router/index.cjs +1 -0
- package/dist/core-bak/router/index.d.ts +16 -0
- package/dist/core-bak/router/index.js +1 -0
- package/dist/core-bak/router/lifecycle/RouterLifecycleManager.cjs +1 -0
- package/dist/core-bak/router/lifecycle/RouterLifecycleManager.d.ts +56 -0
- package/dist/core-bak/router/lifecycle/RouterLifecycleManager.js +1 -0
- package/dist/core-bak/router/lifecycle/index.cjs +1 -0
- package/dist/core-bak/router/lifecycle/index.d.ts +4 -0
- package/dist/core-bak/router/lifecycle/index.js +1 -0
- package/dist/core-bak/router/middleware/RouterMiddlewareManager.cjs +1 -0
- package/dist/core-bak/router/middleware/RouterMiddlewareManager.d.ts +61 -0
- package/dist/core-bak/router/middleware/RouterMiddlewareManager.js +1 -0
- package/dist/core-bak/router/middleware/auth.cjs +1 -0
- package/dist/core-bak/router/middleware/auth.d.ts +54 -0
- package/dist/core-bak/router/middleware/auth.js +1 -0
- package/dist/core-bak/router/middleware/index.cjs +1 -0
- package/dist/core-bak/router/middleware/index.d.ts +6 -0
- package/dist/core-bak/router/middleware/index.js +1 -0
- package/dist/core-bak/router/middleware/types.cjs +1 -0
- package/dist/core-bak/router/middleware/types.d.ts +82 -0
- package/dist/core-bak/router/middleware/types.js +1 -0
- package/dist/core-bak/router/monitoring/RouterMonitoring.cjs +1 -0
- package/dist/core-bak/router/monitoring/RouterMonitoring.d.ts +134 -0
- package/dist/core-bak/router/monitoring/RouterMonitoring.js +1 -0
- package/dist/core-bak/router/monitoring/index.cjs +1 -0
- package/dist/core-bak/router/monitoring/index.d.ts +4 -0
- package/dist/core-bak/router/monitoring/index.js +1 -0
- package/dist/core-bak/router/navigation/RouterNavigation.cjs +1 -0
- package/dist/core-bak/router/navigation/RouterNavigation.d.ts +43 -0
- package/dist/core-bak/router/navigation/RouterNavigation.js +1 -0
- package/dist/core-bak/router/navigation/index.cjs +1 -0
- package/dist/core-bak/router/navigation/index.d.ts +4 -0
- package/dist/core-bak/router/navigation/index.js +1 -0
- package/dist/core-bak/router/performance/RouteCache.cjs +1 -0
- package/dist/core-bak/router/performance/RouteCache.d.ts +113 -0
- package/dist/core-bak/router/performance/RouteCache.js +1 -0
- package/dist/core-bak/router/performance/RoutePreloader.cjs +1 -0
- package/dist/core-bak/router/performance/RoutePreloader.d.ts +111 -0
- package/dist/core-bak/router/performance/RoutePreloader.js +1 -0
- package/dist/core-bak/router/performance/index.cjs +1 -0
- package/dist/core-bak/router/performance/index.d.ts +5 -0
- package/dist/core-bak/router/performance/index.js +1 -0
- package/dist/core-bak/router/plugin/RouterPluginManager.cjs +1 -0
- package/dist/core-bak/router/plugin/RouterPluginManager.d.ts +74 -0
- package/dist/core-bak/router/plugin/RouterPluginManager.js +1 -0
- package/dist/core-bak/router/plugin/index.cjs +1 -0
- package/dist/core-bak/router/plugin/index.d.ts +5 -0
- package/dist/core-bak/router/plugin/index.js +1 -0
- package/dist/core-bak/router/plugin/types.cjs +1 -0
- package/dist/core-bak/router/plugin/types.d.ts +153 -0
- package/dist/core-bak/router/plugin/types.js +1 -0
- package/dist/core-bak/router/types.cjs +1 -0
- package/dist/core-bak/router/types.d.ts +464 -0
- package/dist/core-bak/router/types.js +1 -0
- package/dist/core-bak/router/utils/adapters/react-router/RouteErrorBoundary.cjs +1 -0
- package/dist/core-bak/router/utils/adapters/react-router/RouteErrorBoundary.d.ts +2 -0
- package/dist/core-bak/router/utils/adapters/react-router/RouteErrorBoundary.js +1 -0
- package/dist/core-bak/router/utils/adapters/react-router/transform.cjs +1 -0
- package/dist/core-bak/router/utils/adapters/react-router/transform.d.ts +5 -0
- package/dist/core-bak/router/utils/adapters/react-router/transform.js +1 -0
- package/dist/core-bak/router/utils/transform.cjs +1 -0
- package/dist/core-bak/router/utils/transform.d.ts +348 -0
- package/dist/core-bak/router/utils/transform.js +1 -0
- package/dist/core-bak/router/validation/RouterConfigValidator.cjs +2 -0
- package/dist/core-bak/router/validation/RouterConfigValidator.d.ts +121 -0
- package/dist/core-bak/router/validation/RouterConfigValidator.js +2 -0
- package/dist/core-bak/router/validation/index.cjs +1 -0
- package/dist/core-bak/router/validation/index.d.ts +5 -0
- package/dist/core-bak/router/validation/index.js +1 -0
- package/dist/core-bak/router/validation/schema.cjs +1 -0
- package/dist/core-bak/router/validation/schema.d.ts +180 -0
- package/dist/core-bak/router/validation/schema.js +1 -0
- package/dist/core-bak/router/version/RouteVersionManager.cjs +1 -0
- package/dist/core-bak/router/version/RouteVersionManager.d.ts +124 -0
- package/dist/core-bak/router/version/RouteVersionManager.js +1 -0
- package/dist/core-bak/router/version/index.cjs +1 -0
- package/dist/core-bak/router/version/index.d.ts +4 -0
- package/dist/core-bak/router/version/index.js +1 -0
- package/dist/core-bak/splash/SplashScreen.cjs +1 -0
- package/dist/core-bak/splash/SplashScreen.d.ts +14 -0
- package/dist/core-bak/splash/SplashScreen.js +1 -0
- package/dist/core-bak/splash/index.cjs +1 -0
- package/dist/core-bak/splash/index.d.ts +5 -0
- package/dist/core-bak/splash/index.js +1 -0
- package/dist/core-bak/splash/splashScreenUtils.cjs +1 -0
- package/dist/core-bak/splash/splashScreenUtils.d.ts +7 -0
- package/dist/core-bak/splash/splashScreenUtils.js +1 -0
- package/dist/core-bak/startup/AppInstance.cjs +1 -0
- package/dist/core-bak/startup/AppInstance.d.ts +138 -0
- package/dist/core-bak/startup/AppInstance.js +1 -0
- package/dist/core-bak/startup/environment.cjs +1 -0
- package/dist/core-bak/startup/environment.d.ts +87 -0
- package/dist/core-bak/startup/environment.js +1 -0
- package/dist/core-bak/startup/index.cjs +1 -0
- package/dist/core-bak/startup/index.d.ts +7 -0
- package/dist/core-bak/startup/index.js +1 -0
- package/dist/core-bak/startup/initializeServices.cjs +1 -0
- package/dist/core-bak/startup/initializeServices.d.ts +68 -0
- package/dist/core-bak/startup/initializeServices.js +1 -0
- package/dist/core-bak/startup/performanceTracker.cjs +1 -0
- package/dist/core-bak/startup/performanceTracker.d.ts +104 -0
- package/dist/core-bak/startup/performanceTracker.js +1 -0
- package/dist/core-bak/startup/renderApp.cjs +1 -0
- package/dist/core-bak/startup/renderApp.d.ts +70 -0
- package/dist/core-bak/startup/renderApp.js +1 -0
- package/dist/core-bak/startup/startApp.cjs +1 -0
- package/dist/core-bak/startup/startApp.d.ts +14 -0
- package/dist/core-bak/startup/startApp.js +1 -0
- package/dist/core-bak/types.cjs +1 -0
- package/dist/core-bak/types.d.ts +501 -0
- package/dist/core-bak/types.js +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/index.umd.js +31 -246
- package/dist/kernel/types.d.ts +46 -3
- package/dist/state.umd.js +1 -1
- package/package.json +7 -12
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{logger}from"@vlian/logger";export var ConfigFileFormat=/*#__PURE__*/function(ConfigFileFormat){ConfigFileFormat["JSON"]="json";ConfigFileFormat["YAML"]="yaml";return ConfigFileFormat}({});let ConfigChangeNotifier=class ConfigChangeNotifier{addListener(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}removeListener(listener){this.listeners.delete(listener)}notify(newConfig,oldConfig){this.listeners.forEach(listener=>{try{listener(newConfig,oldConfig)}catch(error){logger.warn("配置变更监听器执行失败:",error)}})}clear(){this.listeners.clear()}constructor(){_define_property(this,"listeners",new Set)}};const configChangeNotifier=new ConfigChangeNotifier;export class ConfigLoader{static async loadFromJSON(path){try{if(typeof window!=="undefined"){const response=await fetch(path);if(!response.ok){throw new Error(`无法加载配置文件: ${path} (${response.status})`)}return await response.json()}if(typeof require!=="undefined"){const fs=require("fs");const content=fs.readFileSync(path,"utf-8");return JSON.parse(content)}throw new Error("不支持的环境:无法加载配置文件")}catch(error){logger.error(`加载JSON配置文件失败: ${path}`,error);throw error}}static async loadFromYAML(path){try{if(typeof window!=="undefined"){const response=await fetch(path);if(!response.ok){throw new Error(`无法加载配置文件: ${path} (${response.status})`)}const text=await response.text();if(typeof window.YAML!=="undefined"){return window.YAML.parse(text)}try{return JSON.parse(text)}catch{throw new Error("需要YAML解析库,请安装 yaml 包")}}if(typeof require!=="undefined"){const fs=require("fs");const yaml=require("yaml");const content=fs.readFileSync(path,"utf-8");return yaml.parse(content)}throw new Error("不支持的环境:无法加载配置文件")}catch(error){logger.error(`加载YAML配置文件失败: ${path}`,error);throw error}}static async loadFromFile(options){const{path,format="json",required=false,defaultConfig={}}=options;if(!path){if(required){throw new Error("配置文件路径未指定")}return defaultConfig}try{let config;switch(format){case"json":config=await this.loadFromJSON(path);break;case"yaml":config=await this.loadFromYAML(path);break;default:throw new Error(`不支持的配置文件格式: ${format}`)}logger.info(`成功加载配置文件: ${path}`);return config}catch(error){if(required){throw error}logger.warn(`配置文件加载失败,使用默认配置: ${path}`,error);return defaultConfig}}static mergeConfig(base,override){const merged={...base};for(const key in override){if(!Object.prototype.hasOwnProperty.call(override,key)){continue}const baseValue=merged[key];const overrideValue=override[key];if(baseValue&&overrideValue&&typeof baseValue==="object"&&typeof overrideValue==="object"&&!Array.isArray(baseValue)&&!Array.isArray(overrideValue)){merged[key]=this.mergeConfig(baseValue,overrideValue)}else{merged[key]=overrideValue}}return merged}static async loadFromMultipleSources(sources){let merged={};for(const source of sources){if("path"in source&&"format"in source){const fileConfig=await this.loadFromFile(source);merged=this.mergeConfig(merged,fileConfig)}else if(!("path"in source)&&!("format"in source)){merged=this.mergeConfig(merged,source)}}return merged}static loadFromEnv(prefix="FRAMEWORK_"){const config={};if(typeof process==="undefined"||!process.env){return config}for(const[key,value]of Object.entries(process.env)){if(!key.startsWith(prefix)){continue}const configKey=key.slice(prefix.length).toLowerCase().split("_").map((part,index)=>index===0?part:part.charAt(0).toUpperCase()+part.slice(1)).join("");let parsedValue=value;if(value==="true"||value==="false"){parsedValue=value==="true"}else if(!isNaN(Number(value))){parsedValue=Number(value)}else if(value?.startsWith("{")||value?.startsWith("[")){try{parsedValue=JSON.parse(value)}catch{}}config[configKey]=parsedValue}logger.debug("从环境变量加载配置:",config);return config}static async loadFromAllSources(sources=[],defaultConfig={},includeEnv=true,envPrefix="FRAMEWORK_"){const oldConfig={...defaultConfig};let merged={...defaultConfig};const fileConfigs=await this.loadFromMultipleSources(sources.filter(source=>typeof source==="object"&&"path"in source));merged=this.mergeConfig(merged,fileConfigs);if(includeEnv){const envConfig=this.loadFromEnv(envPrefix);merged=this.mergeConfig(merged,envConfig)}const codeConfigs=sources.filter(source=>typeof source==="object"&&!("path"in source));for(const codeConfig of codeConfigs){merged=this.mergeConfig(merged,codeConfig)}configChangeNotifier.notify(merged,oldConfig);return merged}static onConfigChange(listener){return configChangeNotifier.addListener(listener)}static removeConfigChangeListener(listener){configChangeNotifier.removeListener(listener)}}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get ConfigValidationError(){return ConfigValidationError},get ConfigValidator(){return ConfigValidator}});const _zod=require("zod");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}let ConfigValidationError=class ConfigValidationError extends Error{constructor(message,errors){super(message),_define_property(this,"errors",void 0),this.errors=errors;this.name="ConfigValidationError"}};let ConfigValidator=class ConfigValidator{static validate(config,schema){const result=schema.safeParse(config);if(!result.success){throw new ConfigValidationError("配置验证失败",result.error)}return result.data}static safeValidate(config,schema){const result=schema.safeParse(config);if(result.success){return{success:true,data:result.data}}return{success:false,error:result.error}}static createStartOptionsSchema(){return _zod.z.object({loggerLevel:_zod.z.enum(["DEBUG","INFO","WARN","ERROR"]).optional(),showSplashScreen:_zod.z.enum(["auto","always","never","first-time-only"]).optional(),minSplashScreenTime:_zod.z.number().positive().optional(),security:_zod.z.object({enableXSSProtection:_zod.z.boolean().optional(),validateConfig:_zod.z.boolean().optional(),sanitizeUserInput:_zod.z.boolean().optional(),csp:_zod.z.object({defaultSrc:_zod.z.array(_zod.z.string()).optional(),scriptSrc:_zod.z.array(_zod.z.string()).optional(),styleSrc:_zod.z.array(_zod.z.string()).optional(),imgSrc:_zod.z.array(_zod.z.string()).optional(),connectSrc:_zod.z.array(_zod.z.string()).optional(),fontSrc:_zod.z.array(_zod.z.string()).optional(),frameSrc:_zod.z.array(_zod.z.string()).optional(),allowUnsafeInline:_zod.z.boolean().optional(),allowUnsafeEval:_zod.z.boolean().optional()}).optional()}).optional()}).passthrough()}static validateWithFriendlyErrors(config,schema){const result=schema.safeParse(config);if(!result.success){const errorMessages=result.error.issues.map(issue=>{const path=issue.path.length>0?issue.path.join("."):"root";return`配置项 "${path}": ${issue.message}`});const friendlyMessage=`配置验证失败:
|
|
2
|
+
${errorMessages.join("\n")}`;throw new ConfigValidationError(friendlyMessage,result.error)}return result.data}};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置验证模块
|
|
3
|
+
* 使用Zod进行配置验证
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
/**
|
|
7
|
+
* 配置验证错误
|
|
8
|
+
*/
|
|
9
|
+
export declare class ConfigValidationError extends Error {
|
|
10
|
+
readonly errors: z.ZodError;
|
|
11
|
+
constructor(message: string, errors: z.ZodError);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 配置验证器
|
|
15
|
+
*/
|
|
16
|
+
export declare class ConfigValidator {
|
|
17
|
+
/**
|
|
18
|
+
* 验证配置
|
|
19
|
+
*
|
|
20
|
+
* @param config - 要验证的配置
|
|
21
|
+
* @param schema - Zod模式
|
|
22
|
+
* @returns 验证后的配置
|
|
23
|
+
* @throws ConfigValidationError 如果配置验证失败
|
|
24
|
+
*/
|
|
25
|
+
static validate<T>(config: unknown, schema: z.ZodSchema<T>): T;
|
|
26
|
+
/**
|
|
27
|
+
* 安全验证配置(不抛出异常)
|
|
28
|
+
*
|
|
29
|
+
* @param config - 要验证的配置
|
|
30
|
+
* @param schema - Zod模式
|
|
31
|
+
* @returns 验证结果
|
|
32
|
+
*/
|
|
33
|
+
static safeValidate<T>(config: unknown, schema: z.ZodSchema<T>): {
|
|
34
|
+
success: true;
|
|
35
|
+
data: T;
|
|
36
|
+
} | {
|
|
37
|
+
success: false;
|
|
38
|
+
error: z.ZodError;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* 创建StartOptions的部分验证模式
|
|
42
|
+
* 注意:这是一个基础模式,可以根据需要扩展
|
|
43
|
+
*/
|
|
44
|
+
static createStartOptionsSchema(): z.ZodObject<{
|
|
45
|
+
loggerLevel: z.ZodOptional<z.ZodEnum<{
|
|
46
|
+
DEBUG: "DEBUG";
|
|
47
|
+
INFO: "INFO";
|
|
48
|
+
WARN: "WARN";
|
|
49
|
+
ERROR: "ERROR";
|
|
50
|
+
}>>;
|
|
51
|
+
showSplashScreen: z.ZodOptional<z.ZodEnum<{
|
|
52
|
+
auto: "auto";
|
|
53
|
+
always: "always";
|
|
54
|
+
never: "never";
|
|
55
|
+
"first-time-only": "first-time-only";
|
|
56
|
+
}>>;
|
|
57
|
+
minSplashScreenTime: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
security: z.ZodOptional<z.ZodObject<{
|
|
59
|
+
enableXSSProtection: z.ZodOptional<z.ZodBoolean>;
|
|
60
|
+
validateConfig: z.ZodOptional<z.ZodBoolean>;
|
|
61
|
+
sanitizeUserInput: z.ZodOptional<z.ZodBoolean>;
|
|
62
|
+
csp: z.ZodOptional<z.ZodObject<{
|
|
63
|
+
defaultSrc: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
64
|
+
scriptSrc: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
65
|
+
styleSrc: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
66
|
+
imgSrc: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
67
|
+
connectSrc: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
68
|
+
fontSrc: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
69
|
+
frameSrc: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
70
|
+
allowUnsafeInline: z.ZodOptional<z.ZodBoolean>;
|
|
71
|
+
allowUnsafeEval: z.ZodOptional<z.ZodBoolean>;
|
|
72
|
+
}, z.core.$strip>>;
|
|
73
|
+
}, z.core.$strip>>;
|
|
74
|
+
}, z.core.$loose>;
|
|
75
|
+
/**
|
|
76
|
+
* 验证配置并提供友好的错误信息
|
|
77
|
+
*
|
|
78
|
+
* @param config - 要验证的配置
|
|
79
|
+
* @param schema - Zod模式
|
|
80
|
+
* @returns 验证后的配置
|
|
81
|
+
* @throws ConfigValidationError 如果配置验证失败,包含详细的错误信息
|
|
82
|
+
*/
|
|
83
|
+
static validateWithFriendlyErrors<T>(config: unknown, schema: z.ZodSchema<T>): T;
|
|
84
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{z}from"zod";export class ConfigValidationError extends Error{constructor(message,errors){super(message),_define_property(this,"errors",void 0),this.errors=errors;this.name="ConfigValidationError"}}export class ConfigValidator{static validate(config,schema){const result=schema.safeParse(config);if(!result.success){throw new ConfigValidationError("配置验证失败",result.error)}return result.data}static safeValidate(config,schema){const result=schema.safeParse(config);if(result.success){return{success:true,data:result.data}}return{success:false,error:result.error}}static createStartOptionsSchema(){return z.object({loggerLevel:z.enum(["DEBUG","INFO","WARN","ERROR"]).optional(),showSplashScreen:z.enum(["auto","always","never","first-time-only"]).optional(),minSplashScreenTime:z.number().positive().optional(),security:z.object({enableXSSProtection:z.boolean().optional(),validateConfig:z.boolean().optional(),sanitizeUserInput:z.boolean().optional(),csp:z.object({defaultSrc:z.array(z.string()).optional(),scriptSrc:z.array(z.string()).optional(),styleSrc:z.array(z.string()).optional(),imgSrc:z.array(z.string()).optional(),connectSrc:z.array(z.string()).optional(),fontSrc:z.array(z.string()).optional(),frameSrc:z.array(z.string()).optional(),allowUnsafeInline:z.boolean().optional(),allowUnsafeEval:z.boolean().optional()}).optional()}).optional()}).passthrough()}static validateWithFriendlyErrors(config,schema){const result=schema.safeParse(config);if(!result.success){const errorMessages=result.error.issues.map(issue=>{const path=issue.path.length>0?issue.path.join("."):"root";return`配置项 "${path}": ${issue.message}`});const friendlyMessage=`配置验证失败:
|
|
2
|
+
${errorMessages.join("\n")}`;throw new ConfigValidationError(friendlyMessage,result.error)}return result.data}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"appConfig",{enumerable:true,get:function(){return _AppConfig.appConfig}});_export_star(require("./ConfigValidator"),exports);_export_star(require("./ConfigLoader"),exports);const _AppConfig=require("./AppConfig");function _export_star(from,to){Object.keys(from).forEach(function(k){if(k!=="default"&&!Object.prototype.hasOwnProperty.call(to,k)){Object.defineProperty(to,k,{enumerable:true,get:function(){return from[k]}})}});return from}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./ConfigValidator";export*from"./ConfigLoader";export{appConfig}from"./AppConfig";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get DevTools(){return DevTools},get DevToolsTrigger(){return DevToolsTrigger}});const _jsxruntime=require("react/jsx-runtime");const _react=require("react");const _app=require("../app");function toPrettyJson(value){try{return JSON.stringify(value,null,2)}catch{return String(value)}}function DevTools({appInstance,visible,onClose}){const[performanceMetrics,setPerformanceMetrics]=(0,_react.useState)({});const stateManager=(0,_app.useStateManager)();(0,_react.useEffect)(()=>{if(!visible){return}const tracker=appInstance.getPerformanceTracker();if(!tracker){return}const metrics=tracker.getMetrics();setPerformanceMetrics({serviceInitDuration:metrics.serviceInitDuration||0,initializationDuration:metrics.initializationDuration||0,firstRenderDuration:metrics.firstRenderDuration||0,totalStartupDuration:metrics.totalStartupDuration||0,fcp:metrics.fcp||0,lcp:metrics.lcp||0,fid:metrics.fid||0,cls:metrics.cls||0})},[visible,appInstance]);if(!visible){return null}const options=appInstance.getOptions();const allStates=stateManager?.getAllStates?.()||[];return(0,_jsxruntime.jsxs)("div",{style:{position:"fixed",top:0,right:0,width:"min(680px, 95vw)",height:"100vh",zIndex:9999,background:"#fff",borderLeft:"1px solid #d9e2ec",padding:16,overflowY:"auto",boxShadow:"-8px 0 20px rgba(0, 0, 0, 0.08)"},children:[(0,_jsxruntime.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,_jsxruntime.jsx)("h3",{style:{margin:0},children:"DevTools"}),(0,_jsxruntime.jsx)("button",{onClick:onClose,style:{border:"none",background:"transparent",cursor:"pointer"},children:"关闭"})]}),(0,_jsxruntime.jsxs)("section",{style:{marginTop:12},children:[(0,_jsxruntime.jsx)("h4",{children:"实例"}),(0,_jsxruntime.jsxs)("div",{children:["id: ",appInstance.id]}),(0,_jsxruntime.jsxs)("div",{children:["initialized: ",String(appInstance.isInitialized())]})]}),(0,_jsxruntime.jsxs)("section",{style:{marginTop:12},children:[(0,_jsxruntime.jsx)("h4",{children:"性能"}),(0,_jsxruntime.jsx)("pre",{style:{background:"#f7fafc",padding:10,borderRadius:8},children:toPrettyJson(performanceMetrics)})]}),(0,_jsxruntime.jsxs)("section",{style:{marginTop:12},children:[(0,_jsxruntime.jsx)("h4",{children:"状态管理"}),(0,_jsxruntime.jsxs)("div",{children:["stateCount: ",allStates.length]}),(0,_jsxruntime.jsx)("pre",{style:{background:"#f7fafc",padding:10,borderRadius:8},children:toPrettyJson(allStates.slice(0,10))})]}),(0,_jsxruntime.jsxs)("section",{style:{marginTop:12},children:[(0,_jsxruntime.jsx)("h4",{children:"配置"}),(0,_jsxruntime.jsx)("pre",{style:{background:"#f7fafc",padding:10,borderRadius:8,whiteSpace:"pre-wrap"},children:toPrettyJson(options)})]})]})}function resolvePosition(position){const style={position:"fixed",zIndex:9998};const pos=position||"bottom-right";if(pos.includes("bottom"))style.bottom=16;if(pos.includes("top"))style.top=16;if(pos.includes("right"))style.right=16;if(pos.includes("left"))style.left=16;return style}function DevToolsTrigger({appInstance,position="bottom-right"}){const[visible,setVisible]=(0,_react.useState)(false);if(process.env.NODE_ENV!=="development"){return null}return(0,_jsxruntime.jsxs)(_jsxruntime.Fragment,{children:[(0,_jsxruntime.jsx)("button",{onClick:()=>setVisible(true),style:{...resolvePosition(position),border:"none",background:"#2f80ed",color:"#fff",borderRadius:999,padding:"8px 14px",cursor:"pointer"},children:"DevTools"}),(0,_jsxruntime.jsx)(DevTools,{appInstance:appInstance,visible:visible,onClose:()=>setVisible(false)})]})}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 开发工具模块(无 UI 框架依赖)
|
|
3
|
+
*/
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
|
+
import type { AppInstance } from '../startup/AppInstance';
|
|
6
|
+
export interface DevToolsProps {
|
|
7
|
+
appInstance: AppInstance;
|
|
8
|
+
visible?: boolean;
|
|
9
|
+
onClose?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function DevTools({ appInstance, visible, onClose }: DevToolsProps): ReactNode;
|
|
12
|
+
export interface DevToolsTriggerProps {
|
|
13
|
+
appInstance: AppInstance;
|
|
14
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
15
|
+
}
|
|
16
|
+
export declare function DevToolsTrigger({ appInstance, position }: DevToolsTriggerProps): ReactNode;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState,useEffect}from"react";import{useStateManager}from"../app";function toPrettyJson(value){try{return JSON.stringify(value,null,2)}catch{return String(value)}}export function DevTools({appInstance,visible,onClose}){const[performanceMetrics,setPerformanceMetrics]=useState({});const stateManager=useStateManager();useEffect(()=>{if(!visible){return}const tracker=appInstance.getPerformanceTracker();if(!tracker){return}const metrics=tracker.getMetrics();setPerformanceMetrics({serviceInitDuration:metrics.serviceInitDuration||0,initializationDuration:metrics.initializationDuration||0,firstRenderDuration:metrics.firstRenderDuration||0,totalStartupDuration:metrics.totalStartupDuration||0,fcp:metrics.fcp||0,lcp:metrics.lcp||0,fid:metrics.fid||0,cls:metrics.cls||0})},[visible,appInstance]);if(!visible){return null}const options=appInstance.getOptions();const allStates=stateManager?.getAllStates?.()||[];return _jsxs("div",{style:{position:"fixed",top:0,right:0,width:"min(680px, 95vw)",height:"100vh",zIndex:9999,background:"#fff",borderLeft:"1px solid #d9e2ec",padding:16,overflowY:"auto",boxShadow:"-8px 0 20px rgba(0, 0, 0, 0.08)"},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[_jsx("h3",{style:{margin:0},children:"DevTools"}),_jsx("button",{onClick:onClose,style:{border:"none",background:"transparent",cursor:"pointer"},children:"关闭"})]}),_jsxs("section",{style:{marginTop:12},children:[_jsx("h4",{children:"实例"}),_jsxs("div",{children:["id: ",appInstance.id]}),_jsxs("div",{children:["initialized: ",String(appInstance.isInitialized())]})]}),_jsxs("section",{style:{marginTop:12},children:[_jsx("h4",{children:"性能"}),_jsx("pre",{style:{background:"#f7fafc",padding:10,borderRadius:8},children:toPrettyJson(performanceMetrics)})]}),_jsxs("section",{style:{marginTop:12},children:[_jsx("h4",{children:"状态管理"}),_jsxs("div",{children:["stateCount: ",allStates.length]}),_jsx("pre",{style:{background:"#f7fafc",padding:10,borderRadius:8},children:toPrettyJson(allStates.slice(0,10))})]}),_jsxs("section",{style:{marginTop:12},children:[_jsx("h4",{children:"配置"}),_jsx("pre",{style:{background:"#f7fafc",padding:10,borderRadius:8,whiteSpace:"pre-wrap"},children:toPrettyJson(options)})]})]})}function resolvePosition(position){const style={position:"fixed",zIndex:9998};const pos=position||"bottom-right";if(pos.includes("bottom"))style.bottom=16;if(pos.includes("top"))style.top=16;if(pos.includes("right"))style.right=16;if(pos.includes("left"))style.left=16;return style}export function DevToolsTrigger({appInstance,position="bottom-right"}){const[visible,setVisible]=useState(false);if(process.env.NODE_ENV!=="development"){return null}return _jsxs(_Fragment,{children:[_jsx("button",{onClick:()=>setVisible(true),style:{...resolvePosition(position),border:"none",background:"#2f80ed",color:"#fff",borderRadius:999,padding:"8px 14px",cursor:"pointer"},children:"DevTools"}),_jsx(DevTools,{appInstance:appInstance,visible:visible,onClose:()=>setVisible(false)})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get ErrorBoundary(){return ErrorBoundary},get useErrorHandler(){return useErrorHandler},get withErrorBoundary(){return withErrorBoundary}});const _jsxruntime=require("react/jsx-runtime");const _react=/*#__PURE__*/_interop_require_wildcard(require("react"));const _reacterrorboundary=require("react-error-boundary");const _logger=require("@vlian/logger");const _utils=require("@vlian/utils");const _initialization=require("../initialization");const _ErrorHandler=require("./ErrorHandler");function _getRequireWildcardCache(nodeInterop){if(typeof WeakMap!=="function")return null;var cacheBabelInterop=new WeakMap;var cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interop_require_wildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule){return obj}if(obj===null||typeof obj!=="object"&&typeof obj!=="function"){return{default:obj}}var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj)){return cache.get(obj)}var newObj={__proto__:null};var hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj){if(key!=="default"&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;if(desc&&(desc.get||desc.set)){Object.defineProperty(newObj,key,desc)}else{newObj[key]=obj[key]}}}newObj.default=obj;if(cache){cache.set(obj,newObj)}return newObj}function ErrorBoundary({children,fallback,onError,showInConsole=true,resetKeys,onReset,onResetKeysChange}){const retryCountRef=(0,_react.useRef)(0);const errorHandler=(0,_react.useMemo)(()=>(0,_ErrorHandler.getDefaultErrorHandler)(),[]);const handleError=(0,_react.useCallback)(async(error,errorInfo)=>{const normalizedError=_utils.errorUtils.normalizeError(error);const reactErrorInfo={componentStack:errorInfo.componentStack||""};const handleResult=await errorHandler.handleError(normalizedError,{componentStack:reactErrorInfo.componentStack,source:"ErrorBoundary"});if(!handleResult.handled){_logger.logger.error("错误边界捕获到错误:",{error:normalizedError.toJSON(),errorInfo:{componentStack:reactErrorInfo.componentStack}})}if(onError){onError(normalizedError,reactErrorInfo)}if(showInConsole){console.error("错误边界捕获到错误:",normalizedError);console.error("错误信息:",reactErrorInfo)}},[errorHandler,onError,showInConsole]);const handleReset=()=>{_initialization.initializationErrorState.clearError();if(onReset){onReset()}};const DefaultFallbackComponent=_react.default.memo(({error,resetErrorBoundary})=>{const normalizedError=_utils.errorUtils.normalizeError(error);const[retryCount,setRetryCount]=(0,_react.useState)(retryCountRef.current);const[showDetails,setShowDetails]=(0,_react.useState)(false);(0,_react.useEffect)(()=>{setRetryCount(retryCountRef.current)},[]);const handleRetry=(0,_react.useCallback)(()=>{const newCount=retryCount+1;retryCountRef.current=newCount;setRetryCount(newCount);handleReset();resetErrorBoundary()},[retryCount,resetErrorBoundary]);const handleReload=(0,_react.useCallback)(()=>{window.location.reload()},[]);const userFriendlyMessage=(0,_react.useMemo)(()=>{if(normalizedError.code==="NETWORK_ERROR"){return"网络连接失败,请检查您的网络设置后再尝试"}if(normalizedError.code==="TIMEOUT_ERROR"){return"请求超时,请稍后再尝试"}if(normalizedError.code==="VALIDATION_ERROR"){return"数据验证失败,请检查输入内容"}return"应用运行时发生了错误,请稍后再尝试"},[normalizedError.code]);const extraButtons=(0,_react.useMemo)(()=>{const buttons=[(0,_jsxruntime.jsx)("button",{onClick:handleRetry,"aria-label":"重试加载应用",style:{border:"none",background:"var(--app-primary-color, #2f80ed)",color:"#fff",borderRadius:6,padding:"8px 14px",cursor:"pointer"},children:"重试"},"retry"),(0,_jsxruntime.jsx)("button",{onClick:()=>setShowDetails(!showDetails),"aria-label":showDetails?"隐藏错误详情":"显示错误详情",style:{border:"1px solid #cfd7df",background:"#fff",color:"#1f2933",borderRadius:6,padding:"8px 14px",cursor:"pointer"},children:showDetails?"隐藏详情":"查看详情"},"details")];if(retryCount>=1){buttons.push((0,_jsxruntime.jsx)("button",{onClick:handleReload,"aria-label":"刷新页面",style:{border:"1px solid #cfd7df",background:"#fff",color:"#1f2933",borderRadius:6,padding:"8px 14px",cursor:"pointer"},children:"刷新页面"},"reload"))}return buttons},[retryCount,handleRetry,handleReload,showDetails]);return(0,_jsxruntime.jsxs)("div",{role:"alert","aria-live":"assertive","aria-atomic":"true",style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",minHeight:"100vh",padding:"24px"},children:[(0,_jsxruntime.jsxs)("div",{style:{maxWidth:580,width:"100%",textAlign:"center",border:"1px solid #dde5ec",borderRadius:10,padding:24,background:"#fff"},children:[(0,_jsxruntime.jsx)("div",{style:{fontSize:40,lineHeight:"44px"},children:"⚠️"}),(0,_jsxruntime.jsx)("h2",{style:{margin:"8px 0 4px"},children:"出错了"}),(0,_jsxruntime.jsx)("p",{style:{marginTop:0,color:"#52606d"},children:userFriendlyMessage}),(0,_jsxruntime.jsx)("div",{style:{display:"flex",gap:8,justifyContent:"center",flexWrap:"wrap"},children:extraButtons})]}),showDetails&&(0,_jsxruntime.jsxs)("div",{style:{marginTop:24,padding:16,backgroundColor:"#f5f5f5",borderRadius:4,maxWidth:800,width:"100%",maxHeight:400,overflow:"auto"},children:[(0,_jsxruntime.jsx)("div",{style:{marginBottom:8,fontWeight:"bold"},children:"错误详情:"}),(0,_jsxruntime.jsxs)("div",{style:{fontFamily:"monospace",fontSize:12},children:[(0,_jsxruntime.jsxs)("div",{children:[(0,_jsxruntime.jsx)("strong",{children:"错误类型:"}),normalizedError.name]}),(0,_jsxruntime.jsxs)("div",{children:[(0,_jsxruntime.jsx)("strong",{children:"错误代码:"}),normalizedError.code]}),(0,_jsxruntime.jsxs)("div",{children:[(0,_jsxruntime.jsx)("strong",{children:"错误信息:"}),normalizedError.message]}),normalizedError.stack&&(0,_jsxruntime.jsxs)("div",{style:{marginTop:8},children:[(0,_jsxruntime.jsx)("strong",{children:"堆栈信息:"}),(0,_jsxruntime.jsx)("pre",{style:{whiteSpace:"pre-wrap",wordBreak:"break-word"},children:normalizedError.stack})]})]})]})]})});DefaultFallbackComponent.displayName="DefaultFallbackComponent";const defaultFallback=({error,resetErrorBoundary})=>{return(0,_jsxruntime.jsx)(DefaultFallbackComponent,{error:error,resetErrorBoundary:resetErrorBoundary})};const fallbackRender=fallback?({error,resetErrorBoundary})=>{const normalizedError=_utils.errorUtils.normalizeError(error);return fallback(normalizedError,()=>{handleReset();resetErrorBoundary()})}:defaultFallback;const handleResetWithCount=()=>{retryCountRef.current=0;handleReset()};const handleResetKeysChange=(prevKeys,nextKeys)=>{retryCountRef.current=0;if(onResetKeysChange){onResetKeysChange(prevKeys,nextKeys)}};const errorBoundaryProps={fallbackRender,onError:handleError,onReset:handleResetWithCount,resetKeys,onResetKeysChange:handleResetKeysChange};return(0,_jsxruntime.jsx)(_reacterrorboundary.ErrorBoundary,{...errorBoundaryProps,children:children})}function withErrorBoundary(Component,errorBoundaryProps){const WrappedComponent=props=>{return(0,_jsxruntime.jsx)(ErrorBoundary,{...errorBoundaryProps,children:(0,_jsxruntime.jsx)(Component,{...props})})};WrappedComponent.displayName=`withErrorBoundary(${Component.displayName||Component.name||"Component"})`;return WrappedComponent}function useErrorHandler(){return error=>{_initialization.initializationErrorState.setError(error)}}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React, { type ReactNode, type ErrorInfo } from 'react';
|
|
2
|
+
import type { FrameworkError } from '@vlian/utils';
|
|
3
|
+
/**
|
|
4
|
+
* 错误边界组件属性
|
|
5
|
+
*/
|
|
6
|
+
export interface ErrorBoundaryProps {
|
|
7
|
+
/**
|
|
8
|
+
* 子组件
|
|
9
|
+
*/
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* 错误回退 UI(函数形式)
|
|
13
|
+
*
|
|
14
|
+
* 如果不提供,将使用默认错误 UI(框架内置实现,居中显示)。
|
|
15
|
+
* 如果提供,将使用自定义的错误 UI。
|
|
16
|
+
*
|
|
17
|
+
* @param error - 标准化后的框架错误对象
|
|
18
|
+
* @param resetError - 重置错误边界的函数,调用后可以尝试重新渲染子组件
|
|
19
|
+
* @returns 错误 UI 的 React 节点
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <ErrorBoundary
|
|
24
|
+
* fallback={(error, reset) => (
|
|
25
|
+
* <div>
|
|
26
|
+
* <h2>自定义错误标题</h2>
|
|
27
|
+
* <p>{error.message}</p>
|
|
28
|
+
* <button onClick={reset}>重试</button>
|
|
29
|
+
* </div>
|
|
30
|
+
* )}
|
|
31
|
+
* >
|
|
32
|
+
* <App />
|
|
33
|
+
* </ErrorBoundary>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
fallback?: (error: FrameworkError, resetError: () => void) => ReactNode;
|
|
37
|
+
/**
|
|
38
|
+
* 错误回调
|
|
39
|
+
*/
|
|
40
|
+
onError?: (error: FrameworkError, errorInfo: ErrorInfo) => void;
|
|
41
|
+
/**
|
|
42
|
+
* 是否在控制台显示错误
|
|
43
|
+
*/
|
|
44
|
+
showInConsole?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* 重置键数组,当这些值变化时自动重置错误边界
|
|
47
|
+
*/
|
|
48
|
+
resetKeys?: Array<string | number>;
|
|
49
|
+
/**
|
|
50
|
+
* 重置错误时的回调
|
|
51
|
+
*/
|
|
52
|
+
onReset?: () => void;
|
|
53
|
+
/**
|
|
54
|
+
* 重置键变化时的回调
|
|
55
|
+
*/
|
|
56
|
+
onResetKeysChange?: (prevKeys: Array<string | number> | undefined, nextKeys: Array<string | number> | undefined) => void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 错误边界组件
|
|
60
|
+
*
|
|
61
|
+
* 基于 react-error-boundary 实现,集成了框架的错误处理系统
|
|
62
|
+
* 用于捕获 React 组件树中的错误,防止整个应用崩溃
|
|
63
|
+
*
|
|
64
|
+
* 默认使用框架内置错误 UI,居中显示。
|
|
65
|
+
* 可以通过 fallback 属性自定义错误 UI。
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* // 使用默认错误 UI(推荐)
|
|
70
|
+
* <ErrorBoundary>
|
|
71
|
+
* <App />
|
|
72
|
+
* </ErrorBoundary>
|
|
73
|
+
*
|
|
74
|
+
* // 自定义错误 UI
|
|
75
|
+
* <ErrorBoundary
|
|
76
|
+
* fallback={(error, reset) => (
|
|
77
|
+
* <div>
|
|
78
|
+
* <h1>出错了</h1>
|
|
79
|
+
* <p>{error.message}</p>
|
|
80
|
+
* <button onClick={reset}>重试</button>
|
|
81
|
+
* </div>
|
|
82
|
+
* )}
|
|
83
|
+
* >
|
|
84
|
+
* <App />
|
|
85
|
+
* </ErrorBoundary>
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function ErrorBoundary({ children, fallback, onError, showInConsole, resetKeys, onReset, onResetKeysChange, }: ErrorBoundaryProps): ReactNode;
|
|
89
|
+
/**
|
|
90
|
+
* 带错误边界的 HOC
|
|
91
|
+
*/
|
|
92
|
+
export declare function withErrorBoundary<P extends object>(Component: React.ComponentType<P>, errorBoundaryProps?: Omit<ErrorBoundaryProps, 'children'>): React.ComponentType<P>;
|
|
93
|
+
/**
|
|
94
|
+
* 使用错误处理器的 Hook
|
|
95
|
+
*
|
|
96
|
+
* 用于在函数组件中处理异步错误
|
|
97
|
+
*
|
|
98
|
+
* 注意:这个 Hook 需要配合 ErrorBoundary 使用。当调用返回的错误处理函数时,
|
|
99
|
+
* 错误会被设置到初始化错误状态,然后通过 InitializationErrorThrower 组件抛出。
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```tsx
|
|
103
|
+
* import { useErrorHandler } from '@vlian/framework';
|
|
104
|
+
*
|
|
105
|
+
* function MyComponent() {
|
|
106
|
+
* const handleError = useErrorHandler();
|
|
107
|
+
*
|
|
108
|
+
* useEffect(() => {
|
|
109
|
+
* async function fetchData() {
|
|
110
|
+
* try {
|
|
111
|
+
* await someAsyncOperation();
|
|
112
|
+
* } catch (error) {
|
|
113
|
+
* handleError(error);
|
|
114
|
+
* }
|
|
115
|
+
* }
|
|
116
|
+
* fetchData();
|
|
117
|
+
* }, [handleError]);
|
|
118
|
+
* }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare function useErrorHandler(): (error: unknown) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useState,useRef,useEffect,useCallback,useMemo}from"react";import{ErrorBoundary as ReactErrorBoundary}from"react-error-boundary";import{logger}from"@vlian/logger";import{errorUtils}from"@vlian/utils";import{initializationErrorState}from"../initialization";import{getDefaultErrorHandler}from"./ErrorHandler";export function ErrorBoundary({children,fallback,onError,showInConsole=true,resetKeys,onReset,onResetKeysChange}){const retryCountRef=useRef(0);const errorHandler=useMemo(()=>getDefaultErrorHandler(),[]);const handleError=useCallback(async(error,errorInfo)=>{const normalizedError=errorUtils.normalizeError(error);const reactErrorInfo={componentStack:errorInfo.componentStack||""};const handleResult=await errorHandler.handleError(normalizedError,{componentStack:reactErrorInfo.componentStack,source:"ErrorBoundary"});if(!handleResult.handled){logger.error("错误边界捕获到错误:",{error:normalizedError.toJSON(),errorInfo:{componentStack:reactErrorInfo.componentStack}})}if(onError){onError(normalizedError,reactErrorInfo)}if(showInConsole){console.error("错误边界捕获到错误:",normalizedError);console.error("错误信息:",reactErrorInfo)}},[errorHandler,onError,showInConsole]);const handleReset=()=>{initializationErrorState.clearError();if(onReset){onReset()}};const DefaultFallbackComponent=React.memo(({error,resetErrorBoundary})=>{const normalizedError=errorUtils.normalizeError(error);const[retryCount,setRetryCount]=useState(retryCountRef.current);const[showDetails,setShowDetails]=useState(false);useEffect(()=>{setRetryCount(retryCountRef.current)},[]);const handleRetry=useCallback(()=>{const newCount=retryCount+1;retryCountRef.current=newCount;setRetryCount(newCount);handleReset();resetErrorBoundary()},[retryCount,resetErrorBoundary]);const handleReload=useCallback(()=>{window.location.reload()},[]);const userFriendlyMessage=useMemo(()=>{if(normalizedError.code==="NETWORK_ERROR"){return"网络连接失败,请检查您的网络设置后再尝试"}if(normalizedError.code==="TIMEOUT_ERROR"){return"请求超时,请稍后再尝试"}if(normalizedError.code==="VALIDATION_ERROR"){return"数据验证失败,请检查输入内容"}return"应用运行时发生了错误,请稍后再尝试"},[normalizedError.code]);const extraButtons=useMemo(()=>{const buttons=[_jsx("button",{onClick:handleRetry,"aria-label":"重试加载应用",style:{border:"none",background:"var(--app-primary-color, #2f80ed)",color:"#fff",borderRadius:6,padding:"8px 14px",cursor:"pointer"},children:"重试"},"retry"),_jsx("button",{onClick:()=>setShowDetails(!showDetails),"aria-label":showDetails?"隐藏错误详情":"显示错误详情",style:{border:"1px solid #cfd7df",background:"#fff",color:"#1f2933",borderRadius:6,padding:"8px 14px",cursor:"pointer"},children:showDetails?"隐藏详情":"查看详情"},"details")];if(retryCount>=1){buttons.push(_jsx("button",{onClick:handleReload,"aria-label":"刷新页面",style:{border:"1px solid #cfd7df",background:"#fff",color:"#1f2933",borderRadius:6,padding:"8px 14px",cursor:"pointer"},children:"刷新页面"},"reload"))}return buttons},[retryCount,handleRetry,handleReload,showDetails]);return _jsxs("div",{role:"alert","aria-live":"assertive","aria-atomic":"true",style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",minHeight:"100vh",padding:"24px"},children:[_jsxs("div",{style:{maxWidth:580,width:"100%",textAlign:"center",border:"1px solid #dde5ec",borderRadius:10,padding:24,background:"#fff"},children:[_jsx("div",{style:{fontSize:40,lineHeight:"44px"},children:"⚠️"}),_jsx("h2",{style:{margin:"8px 0 4px"},children:"出错了"}),_jsx("p",{style:{marginTop:0,color:"#52606d"},children:userFriendlyMessage}),_jsx("div",{style:{display:"flex",gap:8,justifyContent:"center",flexWrap:"wrap"},children:extraButtons})]}),showDetails&&_jsxs("div",{style:{marginTop:24,padding:16,backgroundColor:"#f5f5f5",borderRadius:4,maxWidth:800,width:"100%",maxHeight:400,overflow:"auto"},children:[_jsx("div",{style:{marginBottom:8,fontWeight:"bold"},children:"错误详情:"}),_jsxs("div",{style:{fontFamily:"monospace",fontSize:12},children:[_jsxs("div",{children:[_jsx("strong",{children:"错误类型:"}),normalizedError.name]}),_jsxs("div",{children:[_jsx("strong",{children:"错误代码:"}),normalizedError.code]}),_jsxs("div",{children:[_jsx("strong",{children:"错误信息:"}),normalizedError.message]}),normalizedError.stack&&_jsxs("div",{style:{marginTop:8},children:[_jsx("strong",{children:"堆栈信息:"}),_jsx("pre",{style:{whiteSpace:"pre-wrap",wordBreak:"break-word"},children:normalizedError.stack})]})]})]})]})});DefaultFallbackComponent.displayName="DefaultFallbackComponent";const defaultFallback=({error,resetErrorBoundary})=>{return _jsx(DefaultFallbackComponent,{error:error,resetErrorBoundary:resetErrorBoundary})};const fallbackRender=fallback?({error,resetErrorBoundary})=>{const normalizedError=errorUtils.normalizeError(error);return fallback(normalizedError,()=>{handleReset();resetErrorBoundary()})}:defaultFallback;const handleResetWithCount=()=>{retryCountRef.current=0;handleReset()};const handleResetKeysChange=(prevKeys,nextKeys)=>{retryCountRef.current=0;if(onResetKeysChange){onResetKeysChange(prevKeys,nextKeys)}};const errorBoundaryProps={fallbackRender,onError:handleError,onReset:handleResetWithCount,resetKeys,onResetKeysChange:handleResetKeysChange};return _jsx(ReactErrorBoundary,{...errorBoundaryProps,children:children})}export function withErrorBoundary(Component,errorBoundaryProps){const WrappedComponent=props=>{return _jsx(ErrorBoundary,{...errorBoundaryProps,children:_jsx(Component,{...props})})};WrappedComponent.displayName=`withErrorBoundary(${Component.displayName||Component.name||"Component"})`;return WrappedComponent}export function useErrorHandler(){return error=>{initializationErrorState.setError(error)}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get ErrorHandler(){return ErrorHandler},get ErrorHandlingStrategy(){return ErrorHandlingStrategy},get getDefaultErrorHandler(){return getDefaultErrorHandler},get setDefaultErrorHandler(){return setDefaultErrorHandler}});const _logger=require("@vlian/logger");const _utils=require("@vlian/utils");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}var ErrorHandlingStrategy=/*#__PURE__*/function(ErrorHandlingStrategy){ErrorHandlingStrategy["LOG"]="LOG";ErrorHandlingStrategy["REPORT"]="REPORT";ErrorHandlingStrategy["RECOVER"]="RECOVER";ErrorHandlingStrategy["FALLBACK"]="FALLBACK";ErrorHandlingStrategy["IGNORE"]="IGNORE";return ErrorHandlingStrategy}({});let ErrorHandler=class ErrorHandler{setMonitoring(monitoring){this.monitoring=monitoring}async handleError(error,context){const normalizedError=_utils.errorUtils.normalizeError(error);const errorWithContext=context?new _utils.FrameworkError(normalizedError.message,normalizedError.type,normalizedError.severity,{code:normalizedError.code,originalError:normalizedError.originalError,context:{...normalizedError.context,...context},recoverable:normalizedError.recoverable}):normalizedError;const strategies=this.determineStrategies(errorWithContext);const handled=await this.executeStrategies(errorWithContext,strategies);let recoveredValue;let recoverable=false;if(handled&&errorWithContext.recoverable&&this.config.recoveryOptions){const recoveryResult=await this.attemptRecovery(errorWithContext);recoverable=recoveryResult.recoverable;recoveredValue=recoveryResult.value}return{handled,recoverable,recoveredValue,strategies,error:errorWithContext}}determineStrategies(error){if(this.config.typeStrategies[error.type]){return this.config.typeStrategies[error.type]}if(this.config.severityStrategies[error.severity]){return this.config.severityStrategies[error.severity]}return this.config.defaultStrategies}async executeStrategies(error,strategies){let handled=false;for(const strategy of strategies){try{switch(strategy){case"LOG":this.logError(error);handled=true;break;case"REPORT":await this.reportError(error);handled=true;break;case"RECOVER":handled=true;break;case"FALLBACK":handled=true;break;case"IGNORE":handled=true;break}}catch(strategyError){_logger.logger.warn(`错误处理策略 ${strategy} 执行失败:`,strategyError)}}return handled}logError(error){const errorInfo=_utils.errorUtils.extractErrorInfo(error);switch(error.severity){case _utils.ErrorSeverity.CRITICAL:case _utils.ErrorSeverity.HIGH:_logger.logger.error("错误:",errorInfo);break;case _utils.ErrorSeverity.MEDIUM:_logger.logger.warn("警告:",errorInfo);break;case _utils.ErrorSeverity.LOW:_logger.logger.info("信息:",errorInfo);break}if(this.config.showDetailedErrorsInDev&&process.env.NODE_ENV==="development"){const detailedError=_utils.errorUtils.formatErrorForDev(error);console.error(detailedError)}}async reportError(error){if(!this.monitoring){_logger.logger.debug("监控服务未配置,跳过错误上报");return}try{this.monitoring.captureError(error,{severity:error.severity,type:error.type,code:error.code,context:error.context})}catch(reportError){_logger.logger.warn("错误上报失败:",reportError)}}async attemptRecovery(error){const options=this.config.recoveryOptions;if(!options){return{recoverable:false}}const maxRetries=options.maxRetries??3;const retryDelay=options.retryDelay??1e3;const exponentialBackoff=options.exponentialBackoff??true;const shouldRetry=options.shouldRetry??(()=>true);if(options.fallback){try{const fallbackValue=await Promise.resolve(options.fallback());_logger.logger.info("错误已通过降级方案恢复");return{recoverable:true,value:fallbackValue}}catch(fallbackError){_logger.logger.warn("降级方案执行失败:",fallbackError)}}if(error.recoverable&&shouldRetry(error,0)){for(let attempt=0;attempt<maxRetries;attempt++){if(!shouldRetry(error,attempt)){break}const delay=exponentialBackoff?retryDelay*Math.pow(2,attempt):retryDelay;_logger.logger.debug(`错误恢复重试 ${attempt+1}/${maxRetries},${delay}ms 后重试...`);await new Promise(resolve=>setTimeout(resolve,delay))}}return{recoverable:error.recoverable}}static create(config){return new ErrorHandler(config)}constructor(config={}){_define_property(this,"config",void 0);_define_property(this,"monitoring",void 0);this.monitoring=config.monitoring;this.config={defaultStrategies:config.defaultStrategies??["LOG","REPORT"],severityStrategies:config.severityStrategies??{},typeStrategies:config.typeStrategies??{},recoveryOptions:config.recoveryOptions??{},showDetailedErrorsInDev:config.showDetailedErrorsInDev??true}}};let defaultErrorHandler=null;function getDefaultErrorHandler(){if(!defaultErrorHandler){defaultErrorHandler=new ErrorHandler}return defaultErrorHandler}function setDefaultErrorHandler(handler){defaultErrorHandler=handler}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 统一错误处理器
|
|
3
|
+
*
|
|
4
|
+
* 优化:
|
|
5
|
+
* 1. 统一错误处理策略:记录、上报、恢复、降级
|
|
6
|
+
* 2. 错误分类:致命、可恢复、警告
|
|
7
|
+
* 3. 错误恢复机制:自动重试、降级方案
|
|
8
|
+
*/
|
|
9
|
+
import { ErrorSeverity, ErrorType, FrameworkError } from '@vlian/utils';
|
|
10
|
+
import type { MonitoringService } from '@vlian/monitoring';
|
|
11
|
+
/**
|
|
12
|
+
* 错误处理策略
|
|
13
|
+
*/
|
|
14
|
+
export declare enum ErrorHandlingStrategy {
|
|
15
|
+
/**
|
|
16
|
+
* 记录错误(日志)
|
|
17
|
+
*/
|
|
18
|
+
LOG = "LOG",
|
|
19
|
+
/**
|
|
20
|
+
* 上报错误(监控服务)
|
|
21
|
+
*/
|
|
22
|
+
REPORT = "REPORT",
|
|
23
|
+
/**
|
|
24
|
+
* 尝试恢复错误
|
|
25
|
+
*/
|
|
26
|
+
RECOVER = "RECOVER",
|
|
27
|
+
/**
|
|
28
|
+
* 降级处理
|
|
29
|
+
*/
|
|
30
|
+
FALLBACK = "FALLBACK",
|
|
31
|
+
/**
|
|
32
|
+
* 忽略错误
|
|
33
|
+
*/
|
|
34
|
+
IGNORE = "IGNORE"
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 错误恢复选项
|
|
38
|
+
*/
|
|
39
|
+
export interface ErrorRecoveryOptions {
|
|
40
|
+
/**
|
|
41
|
+
* 最大重试次数
|
|
42
|
+
* @default 3
|
|
43
|
+
*/
|
|
44
|
+
maxRetries?: number;
|
|
45
|
+
/**
|
|
46
|
+
* 重试延迟(毫秒)
|
|
47
|
+
* @default 1000
|
|
48
|
+
*/
|
|
49
|
+
retryDelay?: number;
|
|
50
|
+
/**
|
|
51
|
+
* 是否启用指数退避
|
|
52
|
+
* @default true
|
|
53
|
+
*/
|
|
54
|
+
exponentialBackoff?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* 重试条件函数
|
|
57
|
+
*/
|
|
58
|
+
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
59
|
+
/**
|
|
60
|
+
* 降级方案函数
|
|
61
|
+
*/
|
|
62
|
+
fallback?: () => Promise<unknown> | unknown;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 错误处理配置
|
|
66
|
+
*/
|
|
67
|
+
export interface ErrorHandlerConfig {
|
|
68
|
+
/**
|
|
69
|
+
* 监控服务实例(可选)
|
|
70
|
+
*/
|
|
71
|
+
monitoring?: MonitoringService;
|
|
72
|
+
/**
|
|
73
|
+
* 默认错误处理策略
|
|
74
|
+
* @default [ErrorHandlingStrategy.LOG, ErrorHandlingStrategy.REPORT]
|
|
75
|
+
*/
|
|
76
|
+
defaultStrategies?: ErrorHandlingStrategy[];
|
|
77
|
+
/**
|
|
78
|
+
* 错误严重程度对应的处理策略
|
|
79
|
+
*/
|
|
80
|
+
severityStrategies?: Partial<Record<ErrorSeverity, ErrorHandlingStrategy[]>>;
|
|
81
|
+
/**
|
|
82
|
+
* 错误类型对应的处理策略
|
|
83
|
+
*/
|
|
84
|
+
typeStrategies?: Partial<Record<ErrorType, ErrorHandlingStrategy[]>>;
|
|
85
|
+
/**
|
|
86
|
+
* 错误恢复选项
|
|
87
|
+
*/
|
|
88
|
+
recoveryOptions?: ErrorRecoveryOptions;
|
|
89
|
+
/**
|
|
90
|
+
* 是否在开发环境显示详细错误信息
|
|
91
|
+
* @default true
|
|
92
|
+
*/
|
|
93
|
+
showDetailedErrorsInDev?: boolean;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 错误处理结果
|
|
97
|
+
*/
|
|
98
|
+
export interface ErrorHandleResult {
|
|
99
|
+
/**
|
|
100
|
+
* 是否已处理
|
|
101
|
+
*/
|
|
102
|
+
handled: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* 是否可恢复
|
|
105
|
+
*/
|
|
106
|
+
recoverable: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* 恢复后的结果(如果已恢复)
|
|
109
|
+
*/
|
|
110
|
+
recoveredValue?: unknown;
|
|
111
|
+
/**
|
|
112
|
+
* 使用的处理策略
|
|
113
|
+
*/
|
|
114
|
+
strategies: ErrorHandlingStrategy[];
|
|
115
|
+
/**
|
|
116
|
+
* 错误信息
|
|
117
|
+
*/
|
|
118
|
+
error: FrameworkError;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 统一错误处理器
|
|
122
|
+
*/
|
|
123
|
+
export declare class ErrorHandler {
|
|
124
|
+
private config;
|
|
125
|
+
private monitoring?;
|
|
126
|
+
constructor(config?: ErrorHandlerConfig);
|
|
127
|
+
/**
|
|
128
|
+
* 设置监控服务
|
|
129
|
+
*/
|
|
130
|
+
setMonitoring(monitoring: MonitoringService): void;
|
|
131
|
+
/**
|
|
132
|
+
* 处理错误
|
|
133
|
+
*
|
|
134
|
+
* @param error - 错误对象
|
|
135
|
+
* @param context - 错误上下文
|
|
136
|
+
* @returns 错误处理结果
|
|
137
|
+
*/
|
|
138
|
+
handleError(error: unknown, context?: Record<string, unknown>): Promise<ErrorHandleResult>;
|
|
139
|
+
/**
|
|
140
|
+
* 确定错误处理策略
|
|
141
|
+
*/
|
|
142
|
+
private determineStrategies;
|
|
143
|
+
/**
|
|
144
|
+
* 执行处理策略
|
|
145
|
+
*/
|
|
146
|
+
private executeStrategies;
|
|
147
|
+
/**
|
|
148
|
+
* 记录错误
|
|
149
|
+
*/
|
|
150
|
+
private logError;
|
|
151
|
+
/**
|
|
152
|
+
* 上报错误
|
|
153
|
+
*/
|
|
154
|
+
private reportError;
|
|
155
|
+
/**
|
|
156
|
+
* 尝试恢复错误
|
|
157
|
+
*/
|
|
158
|
+
private attemptRecovery;
|
|
159
|
+
/**
|
|
160
|
+
* 创建错误处理器的便捷方法
|
|
161
|
+
*/
|
|
162
|
+
static create(config?: ErrorHandlerConfig): ErrorHandler;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 获取默认错误处理器
|
|
166
|
+
*/
|
|
167
|
+
export declare function getDefaultErrorHandler(): ErrorHandler;
|
|
168
|
+
/**
|
|
169
|
+
* 设置默认错误处理器
|
|
170
|
+
*/
|
|
171
|
+
export declare function setDefaultErrorHandler(handler: ErrorHandler): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{logger}from"@vlian/logger";import{errorUtils,ErrorSeverity,FrameworkError}from"@vlian/utils";export var ErrorHandlingStrategy=/*#__PURE__*/function(ErrorHandlingStrategy){ErrorHandlingStrategy["LOG"]="LOG";ErrorHandlingStrategy["REPORT"]="REPORT";ErrorHandlingStrategy["RECOVER"]="RECOVER";ErrorHandlingStrategy["FALLBACK"]="FALLBACK";ErrorHandlingStrategy["IGNORE"]="IGNORE";return ErrorHandlingStrategy}({});export class ErrorHandler{setMonitoring(monitoring){this.monitoring=monitoring}async handleError(error,context){const normalizedError=errorUtils.normalizeError(error);const errorWithContext=context?new FrameworkError(normalizedError.message,normalizedError.type,normalizedError.severity,{code:normalizedError.code,originalError:normalizedError.originalError,context:{...normalizedError.context,...context},recoverable:normalizedError.recoverable}):normalizedError;const strategies=this.determineStrategies(errorWithContext);const handled=await this.executeStrategies(errorWithContext,strategies);let recoveredValue;let recoverable=false;if(handled&&errorWithContext.recoverable&&this.config.recoveryOptions){const recoveryResult=await this.attemptRecovery(errorWithContext);recoverable=recoveryResult.recoverable;recoveredValue=recoveryResult.value}return{handled,recoverable,recoveredValue,strategies,error:errorWithContext}}determineStrategies(error){if(this.config.typeStrategies[error.type]){return this.config.typeStrategies[error.type]}if(this.config.severityStrategies[error.severity]){return this.config.severityStrategies[error.severity]}return this.config.defaultStrategies}async executeStrategies(error,strategies){let handled=false;for(const strategy of strategies){try{switch(strategy){case"LOG":this.logError(error);handled=true;break;case"REPORT":await this.reportError(error);handled=true;break;case"RECOVER":handled=true;break;case"FALLBACK":handled=true;break;case"IGNORE":handled=true;break}}catch(strategyError){logger.warn(`错误处理策略 ${strategy} 执行失败:`,strategyError)}}return handled}logError(error){const errorInfo=errorUtils.extractErrorInfo(error);switch(error.severity){case ErrorSeverity.CRITICAL:case ErrorSeverity.HIGH:logger.error("错误:",errorInfo);break;case ErrorSeverity.MEDIUM:logger.warn("警告:",errorInfo);break;case ErrorSeverity.LOW:logger.info("信息:",errorInfo);break}if(this.config.showDetailedErrorsInDev&&process.env.NODE_ENV==="development"){const detailedError=errorUtils.formatErrorForDev(error);console.error(detailedError)}}async reportError(error){if(!this.monitoring){logger.debug("监控服务未配置,跳过错误上报");return}try{this.monitoring.captureError(error,{severity:error.severity,type:error.type,code:error.code,context:error.context})}catch(reportError){logger.warn("错误上报失败:",reportError)}}async attemptRecovery(error){const options=this.config.recoveryOptions;if(!options){return{recoverable:false}}const maxRetries=options.maxRetries??3;const retryDelay=options.retryDelay??1e3;const exponentialBackoff=options.exponentialBackoff??true;const shouldRetry=options.shouldRetry??(()=>true);if(options.fallback){try{const fallbackValue=await Promise.resolve(options.fallback());logger.info("错误已通过降级方案恢复");return{recoverable:true,value:fallbackValue}}catch(fallbackError){logger.warn("降级方案执行失败:",fallbackError)}}if(error.recoverable&&shouldRetry(error,0)){for(let attempt=0;attempt<maxRetries;attempt++){if(!shouldRetry(error,attempt)){break}const delay=exponentialBackoff?retryDelay*Math.pow(2,attempt):retryDelay;logger.debug(`错误恢复重试 ${attempt+1}/${maxRetries},${delay}ms 后重试...`);await new Promise(resolve=>setTimeout(resolve,delay))}}return{recoverable:error.recoverable}}static create(config){return new ErrorHandler(config)}constructor(config={}){_define_property(this,"config",void 0);_define_property(this,"monitoring",void 0);this.monitoring=config.monitoring;this.config={defaultStrategies:config.defaultStrategies??["LOG","REPORT"],severityStrategies:config.severityStrategies??{},typeStrategies:config.typeStrategies??{},recoveryOptions:config.recoveryOptions??{},showDetailedErrorsInDev:config.showDetailedErrorsInDev??true}}}let defaultErrorHandler=null;export function getDefaultErrorHandler(){if(!defaultErrorHandler){defaultErrorHandler=new ErrorHandler}return defaultErrorHandler}export function setDefaultErrorHandler(handler){defaultErrorHandler=handler}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get ErrorBoundary(){return _ErrorBoundary.ErrorBoundary},get ErrorHandler(){return _ErrorHandler.ErrorHandler},get getDefaultErrorHandler(){return _ErrorHandler.getDefaultErrorHandler},get setDefaultErrorHandler(){return _ErrorHandler.setDefaultErrorHandler},get useErrorHandler(){return _ErrorBoundary.useErrorHandler},get withErrorBoundary(){return _ErrorBoundary.withErrorBoundary}});const _ErrorBoundary=require("./ErrorBoundary");const _ErrorHandler=require("./ErrorHandler");
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 错误处理模块统一导出
|
|
3
|
+
*/
|
|
4
|
+
export { ErrorBoundary, withErrorBoundary, useErrorHandler } from './ErrorBoundary';
|
|
5
|
+
export type { ErrorBoundaryProps } from './ErrorBoundary';
|
|
6
|
+
export { ErrorHandler, getDefaultErrorHandler, setDefaultErrorHandler } from './ErrorHandler';
|
|
7
|
+
export type { ErrorHandlingStrategy, ErrorHandlerConfig, ErrorHandleResult, ErrorRecoveryOptions, } from './ErrorHandler';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{ErrorBoundary,withErrorBoundary,useErrorHandler}from"./ErrorBoundary";export{ErrorHandler,getDefaultErrorHandler,setDefaultErrorHandler}from"./ErrorHandler";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"AppEventBus",{enumerable:true,get:function(){return AppEventBus}});const _logger=require("@vlian/logger");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}let AppEventBus=class AppEventBus{on(event,listener,options={}){const fullEventName=this.getFullEventName(event);const listenerId=Symbol(`listener_${++this.listenerIdCounter}`);const listenerWithOptions={listener:listener,options:{priority:options.priority??100,once:options.once??false,namespace:options.namespace??this.config.namespacePrefix,async:options.async??true},id:listenerId};if(!this.listeners.has(fullEventName)){this.listeners.set(fullEventName,new Set)}const listeners=this.listeners.get(fullEventName);listeners.add(listenerWithOptions);const sortedListeners=Array.from(listeners).sort((a,b)=>a.options.priority-b.options.priority);this.listeners.set(fullEventName,new Set(sortedListeners));return()=>{const currentListeners=this.listeners.get(fullEventName);if(currentListeners){currentListeners.delete(listenerWithOptions);if(currentListeners.size===0){this.listeners.delete(fullEventName)}}}}off(event,listener){const fullEventName=this.getFullEventName(event);const listeners=this.listeners.get(fullEventName);if(!listeners){return}if(listener){for(const item of listeners){if(item.listener===listener){listeners.delete(item);break}}}else{listeners.clear()}if(listeners.size===0){this.listeners.delete(fullEventName)}}once(event,listener,options){return this.on(event,listener,{...options,once:true})}async emit(event,data,metadata){const fullEventName=this.getFullEventName(event);const startTime=this.config.enablePerformanceMonitoring?performance.now():0;if(this.config.enableValidation&&!this.config.validator(fullEventName,data)){_logger.logger.warn(`事件 ${fullEventName} 验证失败,已忽略`);return}if(this.config.enableSecurityMode&&metadata?.source){if(!this.config.allowedSources.includes(metadata.source)){_logger.logger.warn(`事件 ${fullEventName} 的来源 ${metadata.source} 不在白名单中,已忽略`);return}}const eventMetadata={source:metadata?.source,timestamp:metadata?.timestamp??Date.now(),eventId:metadata?.eventId??`event_${++this.eventCounter}`,priority:metadata?.priority??0,instanceId:metadata?.instanceId??this.config.instanceId,namespace:metadata?.namespace??this.config.namespacePrefix,...metadata};const eventWrapper={data:data,metadata:eventMetadata};if(this.middlewares.length>0){let middlewareIndex=0;let nextCalled=false;const executeMiddleware=async()=>{if(middlewareIndex<this.middlewares.length){nextCalled=false;const currentIndex=middlewareIndex;const middleware=this.middlewares[middlewareIndex++];const next=async()=>{nextCalled=true;if(currentIndex<this.middlewares.length-1){await executeMiddleware()}};await Promise.resolve(middleware(fullEventName,data,eventMetadata,next));if(!nextCalled){return}}};await executeMiddleware();if(!nextCalled&&this.middlewares.length>0){return}}const listeners=this.listeners.get(fullEventName);if(this.config.enableTracking){const executionTime=this.config.enablePerformanceMonitoring?performance.now()-startTime:undefined;this.recordEventHistory(fullEventName,data,eventMetadata,executionTime)}if(!listeners||listeners.size===0){this.updateStats(fullEventName,0);return}const executionPromises=[];const listenersToRemove=[];for(const listenerWithOptions of listeners){const executeListener=async()=>{try{if(listenerWithOptions.options.async){await Promise.resolve(listenerWithOptions.listener(eventWrapper.data,eventWrapper.metadata))}else{listenerWithOptions.listener(eventWrapper.data,eventWrapper.metadata)}if(listenerWithOptions.options.once){listenersToRemove.push(listenerWithOptions)}}catch(error){_logger.logger.error(`事件 ${fullEventName} 的监听器执行失败:`,error)}};executionPromises.push(executeListener())}await Promise.allSettled(executionPromises);for(const listenerToRemove of listenersToRemove){listeners.delete(listenerToRemove)}if(listeners.size===0){this.listeners.delete(fullEventName)}const executionTime=this.config.enablePerformanceMonitoring?performance.now()-startTime:0;this.updateStats(fullEventName,executionTime,listeners.size)}emitSync(event,data,metadata){this.emit(event,data,metadata).catch(error=>{_logger.logger.error(`同步发布事件 ${event} 失败:`,error)})}use(middleware){this.middlewares.push(middleware);return()=>{const index=this.middlewares.indexOf(middleware);if(index>-1){this.middlewares.splice(index,1)}}}clear(){this.listeners.clear()}clearHistory(){this.eventHistory=[]}listenerCount(event){if(event){const fullEventName=this.getFullEventName(event);return this.listeners.get(fullEventName)?.size||0}let count=0;for(const listeners of this.listeners.values()){count+=listeners.size}return count}eventNames(){return Array.from(this.listeners.keys())}getEventHistory(event,limit){if(!this.config.enableTracking){return[]}let history=this.eventHistory;if(event){const fullEventName=this.getFullEventName(event);history=history.filter(h=>h.event===fullEventName)}if(limit){history=history.slice(-limit)}return[...history]}getStats(event){if(event){const fullEventName=this.getFullEventName(event);return this.stats.get(fullEventName)||{event:fullEventName,count:0,avgExecutionTime:0,listenerCount:0,lastTriggered:0}}return new Map(this.stats)}getConfig(){return{...this.config}}updateConfig(config){this.config={...this.config,...config}}destroy(){this.clear();this.clearHistory();this.middlewares=[];this.stats.clear()}getFullEventName(event){if(this.config.namespacePrefix){return`${this.config.namespacePrefix}${event}`}return event}recordEventHistory(event,data,metadata,executionTime){this.eventHistory.push({event,data,metadata,executionTime});if(this.eventHistory.length>this.config.maxHistorySize){this.eventHistory.shift()}}updateStats(event,executionTime,listenerCount=0){if(!this.config.enablePerformanceMonitoring){return}const existingStats=this.stats.get(event);if(existingStats){const totalTime=existingStats.avgExecutionTime*existingStats.count+executionTime;const newCount=existingStats.count+1;this.stats.set(event,{...existingStats,count:newCount,avgExecutionTime:totalTime/newCount,listenerCount,lastTriggered:Date.now()})}else{this.stats.set(event,{event,count:1,avgExecutionTime:executionTime,listenerCount,lastTriggered:Date.now()})}}constructor(config={}){_define_property(this,"listeners",new Map);_define_property(this,"eventHistory",[]);_define_property(this,"middlewares",[]);_define_property(this,"stats",new Map);_define_property(this,"config",void 0);_define_property(this,"eventCounter",0);_define_property(this,"listenerIdCounter",0);this.config={enableTracking:config.enableTracking??process.env.NODE_ENV==="development",maxHistorySize:config.maxHistorySize??100,enableValidation:config.enableValidation??false,validator:config.validator??(()=>true),enablePerformanceMonitoring:config.enablePerformanceMonitoring??process.env.NODE_ENV==="development",namespacePrefix:config.namespacePrefix??"",instanceId:config.instanceId,enableSecurityMode:config.enableSecurityMode??false,allowedSources:config.allowedSources??[]}}};
|